From 2b9fe87dcad443f799e6f19ab59209ea6792a5b4 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Wed, 12 Dec 2012 13:08:03 +0800 Subject: [PATCH] MDL-37247 mod_forum: adding webservice function to allow for the retrieval of forum instances --- mod/forum/db/services.php | 38 +++++++ mod/forum/externallib.php | 150 +++++++++++++++++++++++++++ mod/forum/tests/externallib_test.php | 143 +++++++++++++++++++++++++ mod/forum/version.php | 2 +- 4 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 mod/forum/db/services.php create mode 100644 mod/forum/externallib.php create mode 100644 mod/forum/tests/externallib_test.php diff --git a/mod/forum/db/services.php b/mod/forum/db/services.php new file mode 100644 index 00000000000..7c7535ff811 --- /dev/null +++ b/mod/forum/db/services.php @@ -0,0 +1,38 @@ +. + +/** + * Forum external functions and service definitions. + * + * @package mod_forum + * @copyright 2012 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$functions = array( + + 'mod_forum_get_forums_by_courses' => array( + 'classname' => 'mod_forum_external', + 'methodname' => 'get_forums_by_courses', + 'classpath' => 'mod/forum/externallib.php', + 'description' => 'Returns a list of forum instances in a provided set of courses, if + no courses are provided then all the forum instances the user has access to will be + returned.', + 'type' => 'read', + 'capabilities' => 'mod/forum:viewdiscussion' + ) +); diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php new file mode 100644 index 00000000000..fd9306add40 --- /dev/null +++ b/mod/forum/externallib.php @@ -0,0 +1,150 @@ +. + +/** + * External forum API + * + * @package mod_forum + * @copyright 2012 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +require_once("$CFG->libdir/externallib.php"); + +class mod_forum_external extends external_api { + + /** + * Describes the parameters for get_forum + * + * @return external_external_function_parameters + * @since Moodle 2.5 + */ + public static function get_forums_by_courses_parameters() { + return new external_function_parameters ( + array( + 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID', + '', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course IDs', VALUE_DEFAULT, array()), + ) + ); + } + + /** + * Returns a list of forums in a provided list of courses, + * if no list is provided all forums that the user can view + * will be returned. + * + * @param array $courseids the course ids + * @return array the forum details + * @since Moodle 2.5 + */ + public static function get_forums_by_courses($courseids = array()) { + global $CFG, $DB, $USER; + + require_once($CFG->dirroot . "/mod/forum/lib.php"); + + if (empty($courseids)) { + // Get all the courses the user can view. + $courseids = array_keys(enrol_get_my_courses()); + } else { + $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids)); + $courseids = $params['courseids']; + } + + // Array to store the forums to return. + $arrforums = array(); + + // Go through the courseids and return the forums. + if (!empty($courseids)) { + // Keep track of the course ids we have performed a require_course_login check on to avoid repeating. + $arrcourseschecked = array(); + // Convert array to string to use in query. + list($subsql, $params) = $DB->get_in_or_equal($courseids); + $sql = "SELECT * + FROM {forum} + WHERE course $subsql"; + if ($forums = $DB->get_records_sql($sql, $params)) { + foreach ($forums as $forum) { + // Check that that user can view this course if check not performed yet. + if (!in_array($forum->course, $arrcourseschecked)) { + // Get the course context. + $context = context_course::instance($forum->course); + // Check the user can function in this context. + self::validate_context($context); + // Add to the array. + $arrcourseschecked[] = $forum->course; + } + // Get the course module. + $cm = get_coursemodule_from_instance('forum', $forum->id, 0, false, MUST_EXIST); + // Get the module context. + $context = context_module::instance($cm->id); + // Check they have the view forum capability. + require_capability('mod/forum:viewdiscussion', $context); + // Format the intro before being returning using the format setting. + list($forum->intro, $forum->introformat) = external_format_text($forum->intro, $forum->introformat, + $context->id, 'mod_forum', 'intro', 0); + // Add the course module id to the object, this information is useful. + $forum->cmid = $cm->id; + // Add the forum to the array to return. + $arrforums[$forum->id] = (array) $forum; + } + } + } + + return $arrforums; + } + + /** + * Describes the get_forum return value + * + * @return external_single_structure + * @since Moodle 2.5 + */ + public static function get_forums_by_courses_returns() { + return new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'Forum id'), + 'course' => new external_value(PARAM_TEXT, 'Course id'), + 'type' => new external_value(PARAM_TEXT, 'The forum type'), + 'name' => new external_value(PARAM_TEXT, 'Forum name'), + 'intro' => new external_value(PARAM_RAW, 'The forum intro'), + 'introformat' => new external_format_value('intro'), + 'assessed' => new external_value(PARAM_INT, 'Aggregate type'), + 'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'), + 'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'), + 'scale' => new external_value(PARAM_INT, 'Scale'), + 'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'), + 'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'), + 'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'), + 'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'), + 'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'), + 'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'), + 'timemodified' => new external_value(PARAM_INT, 'Time modified'), + 'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'), + 'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'), + 'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'), + 'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'), + 'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'), + 'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'), + 'cmid' => new external_value(PARAM_INT, 'Course module id') + ), 'forum' + ) + ); + } +} diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php new file mode 100644 index 00000000000..c2ad61500a5 --- /dev/null +++ b/mod/forum/tests/externallib_test.php @@ -0,0 +1,143 @@ +. + +/** + * The module forums external functions unit tests + * + * @package mod_forum + * @category external + * @copyright 2012 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); + +class mod_forum_external_testcase extends externallib_advanced_testcase { + + /** + * Tests set up + */ + protected function setUp() { + global $CFG; + + require_once($CFG->dirroot . '/mod/forum/externallib.php'); + } + + /** + * Test get forums + */ + public function test_mod_forum_get_forums_by_courses() { + global $USER, $CFG, $DB; + + $this->resetAfterTest(true); + + // Create a user. + $user = self::getDataGenerator()->create_user(); + + // Set to the user. + self::setUser($user); + + // Create courses to add the modules. + $course1 = self::getDataGenerator()->create_course(); + $course2 = self::getDataGenerator()->create_course(); + + // First forum. + $record = new stdClass(); + $record->introformat = FORMAT_HTML; + $record->course = $course1->id; + $forum1 = self::getDataGenerator()->create_module('forum', $record); + + // Second forum. + $record = new stdClass(); + $record->introformat = FORMAT_HTML; + $record->course = $course2->id; + $forum2 = self::getDataGenerator()->create_module('forum', $record); + + // Check the forum was correctly created. + $this->assertEquals(2, $DB->count_records_select('forum', 'id = :forum1 OR id = :forum2', + array('forum1' => $forum1->id, 'forum2' => $forum2->id))); + + // Enrol the user in two courses. + // Enrol them in the first course. + $enrol = enrol_get_plugin('manual'); + $enrolinstances = enrol_get_instances($course1->id, true); + foreach ($enrolinstances as $courseenrolinstance) { + if ($courseenrolinstance->enrol == "manual") { + $instance1 = $courseenrolinstance; + break; + } + } + $enrol->enrol_user($instance1, $user->id); + // Now enrol into the second course. + $enrolinstances = enrol_get_instances($course2->id, true); + foreach ($enrolinstances as $courseenrolinstance) { + if ($courseenrolinstance->enrol == "manual") { + $instance2 = $courseenrolinstance; + break; + } + } + $enrol->enrol_user($instance2, $user->id); + + // Assign capabilities to view forums for forum 1. + $cm1 = get_coursemodule_from_id('forum', $forum1->id, 0, false, MUST_EXIST); + $context1 = context_module::instance($cm1->id); + $roleid1 = $this->assignUserCapability('mod/forum:viewdiscussion', $context1->id); + + // Assign capabilities to view forums for forum 2. + $cm2 = get_coursemodule_from_id('forum', $forum2->id, 0, false, MUST_EXIST); + $context2 = context_module::instance($cm2->id); + $newrole = create_role('Role 2', 'role2', 'Role 2 description'); + $roleid2 = $this->assignUserCapability('mod/forum:viewdiscussion', $context2->id, $newrole); + + // Create what we expect to be returned when querying the two courses. + $expectedforums = array(); + $expectedforums[$forum1->id] = (array) $forum1; + $expectedforums[$forum2->id] = (array) $forum2; + + // Call the external function passing course ids. + $forums = mod_forum_external::get_forums_by_courses(array($course1->id, $course2->id)); + $this->assertEquals($forums, $expectedforums); + external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums); + + // Call the external function without passing course id. + $forums = mod_forum_external::get_forums_by_courses(); + $this->assertEquals($forums, $expectedforums); + external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums); + + // Unenrol user from second course and alter expected forums. + $enrol->unenrol_user($instance2, $user->id); + unset($expectedforums[$forum2->id]); + + // Call the external function without passing course id. + $forums = mod_forum_external::get_forums_by_courses(); + $this->assertEquals($forums, $expectedforums); + external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $forums); + + // Call for the second course we unenrolled the user from, make sure exception thrown. + $this->setExpectedException('require_login_exception'); + mod_forum_external::get_forums_by_courses(array($course2->id)); + + // Call without required capability. + $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $roleid1); + $this->setExpectedException('required_capability_exception'); + mod_forum_external::get_forums_by_courses(array($course1->id)); + } +} diff --git a/mod/forum/version.php b/mod/forum/version.php index 0a9384e95f6..d43c1fae053 100644 --- a/mod/forum/version.php +++ b/mod/forum/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); -$module->version = 2012112900; // The current module version (Date: YYYYMMDDXX) +$module->version = 2012112901; // The current module version (Date: YYYYMMDDXX) $module->requires = 2012112900; // Requires this Moodle version $module->component = 'mod_forum'; // Full name of the plugin (used for diagnostics) $module->cron = 60;