From e2ede4262f2bd8304fb359fd8b1124813712f180 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 4 Mar 2014 15:45:27 +0100 Subject: [PATCH 1/3] MDL-30106 forum: New web service mod_forum_get_forum_discussion_posts --- lib/db/services.php | 5 +- mod/forum/db/services.php | 9 ++ mod/forum/externallib.php | 165 +++++++++++++++++++++++++++ mod/forum/tests/externallib_test.php | 128 +++++++++++++++++++++ mod/forum/tests/generator/lib.php | 24 ++++ 5 files changed, 330 insertions(+), 1 deletion(-) diff --git a/lib/db/services.php b/lib/db/services.php index 820e5278b67..cc81df42d0c 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -947,7 +947,10 @@ $services = array( 'message_airnotifier_is_system_configured', 'message_airnotifier_are_notification_preferences_configured', 'core_grades_get_grades', - 'core_grades_update_grades'), + 'core_grades_update_grades', + 'mod_forum_get_forums_by_courses', + 'mod_forum_get_forum_discussions', + 'mod_forum_get_forum_discussion_posts'), 'enabled' => 0, 'restrictedusers' => 0, 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, diff --git a/mod/forum/db/services.php b/mod/forum/db/services.php index 29d666b1711..9c735f8b62e 100644 --- a/mod/forum/db/services.php +++ b/mod/forum/db/services.php @@ -43,5 +43,14 @@ $functions = array( 'description' => 'Returns a list of forum discussions contained within a given set of forums.', 'type' => 'read', 'capabilities' => 'mod/forum:viewdiscussion, mod/forum:viewqandawithoutposting' + ), + + 'mod_forum_get_forum_discussion_posts' => array( + 'classname' => 'mod_forum_external', + 'methodname' => 'get_forum_discussion_posts', + 'classpath' => 'mod/forum/externallib.php', + 'description' => 'Returns a list of forum posts for a discussion.', + 'type' => 'read', + 'capabilities' => 'mod/forum:viewdiscussion, mod/forum:viewqandawithoutposting' ) ); diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php index 862d299f4f4..139358e8610 100644 --- a/mod/forum/externallib.php +++ b/mod/forum/externallib.php @@ -354,4 +354,169 @@ class mod_forum_external extends external_api { ) ); } + + /** + * Describes the parameters for get_forum_discussion_posts. + * + * @return external_external_function_parameters + * @since Moodle 2.7 + */ + public static function get_forum_discussion_posts_parameters() { + return new external_function_parameters ( + array( + 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED), + 'sortby' => new external_value(PARAM_ALPHA, + 'sort by this element: id, created or modified', VALUE_DEFAULT, 'created'), + 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC') + ) + ); + } + + /** + * Returns a list of forum posts for a discussion + * + * @param int $discussionid the post ids + * @param string $sortby sort by this element (id, created or modified) + * @param string $sortdirection sort direction: ASC or DESC + * + * @return array the forum post details + * @since Moodle 2.7 + */ + public static function get_forum_discussion_posts($discussionid, $sortby = "id", $sortdirection = "ASC") { + global $CFG, $DB, $USER; + + $warnings = array(); + + // Validate the parameter. + $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(), + array( + 'discussionid' => $discussionid, + 'sortby' => $sortby, + 'sortdirection' => $sortdirection)); + + // Compact/extract functions are not recommended. + $discussionid = $params['discussionid']; + $sortby = $params['sortby']; + $sortdirection = $params['sortdirection']; + + $sortallowedvalues = array('id', 'created', 'modified'); + if (!in_array($sortby, $sortallowedvalues)) { + throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . + 'allowed values are: ' . implode(',', $sortallowedvalues)); + } + + $sortdirection = strtoupper($sortdirection); + $directionallowedvalues = array('ASC', 'DESC'); + if (!in_array($sortdirection, $directionallowedvalues)) { + throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . + 'allowed values are: ' . implode(',', $directionallowedvalues)); + } + + $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST); + $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST); + $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST); + $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST); + + // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..). + $modcontext = context_module::instance($cm->id); + self::validate_context($modcontext); + + // This require must be here, see mod/forum/discuss.php. + require_once($CFG->dirroot . "/mod/forum/lib.php"); + + // Check they have the view forum capability. + require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum'); + + if (! $post = forum_get_post_full($discussion->firstpost)) { + throw new moodle_exception('notexists', 'forum'); + } + + // This function check groups, qanda, timed discussions, etc. + if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) { + throw new moodle_exception('noviewdiscussionspermission', 'forum'); + } + + $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext); + + // We will add this field in the response. + $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); + + $forumtracked = forum_tp_is_tracked($forum); + + $sort = 'p.' . $sortby . ' ' . $sortdirection; + $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked); + + foreach ($posts as $pid => $post) { + + if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) { + $warning = array(); + $warning['item'] = 'post'; + $warning['itemid'] = $post->id; + $warning['warningcode'] = '1'; + $warning['message'] = 'You can\'t see this post'; + $warnings[] = $warning; + continue; + } + + // Function forum_get_all_discussion_posts adds postread field. + if (!isset($post->postread)) { + $posts[$pid]->postread = false; + } + $posts[$pid]->canreply = $canreply; + $posts[$pid]->children = array(); + if (!empty($posts[$pid]->children)) { + $posts[$pid]->children = array_keys($posts[$pid]->children); + } + + $user = new stdclass(); + $user = username_load_fields_from_object($user, $post); + $posts[$pid]->userfullname = fullname($user, $canviewfullname); + + $posts[$pid] = (array) $post; + } + + $result = array(); + $result['posts'] = $posts; + $result['warnings'] = $warnings; + return $result; + } + + /** + * Describes the get_forum_discussion_posts return value. + * + * @return external_single_structure + * @since Moodle 2.7 + */ + public static function get_forum_discussion_posts_returns() { + return new external_single_structure( + array( + 'posts' => new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'Post id'), + 'discussion' => new external_value(PARAM_INT, 'Discussion id'), + 'parent' => new external_value(PARAM_INT, 'Parent id'), + 'userid' => new external_value(PARAM_INT, 'User id'), + 'created' => new external_value(PARAM_INT, 'Creation time'), + 'modified' => new external_value(PARAM_INT, 'Time modified'), + 'mailed' => new external_value(PARAM_INT, 'Mailed?'), + 'subject' => new external_value(PARAM_TEXT, 'The post subject'), + 'message' => new external_value(PARAM_RAW, 'The post message'), + 'messageformat' => new external_value(PARAM_INT, 'The post message format'), + 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'), + 'attachment' => new external_value(PARAM_RAW, 'Attachments'), + 'totalscore' => new external_value(PARAM_INT, 'The post message total score'), + 'mailnow' => new external_value(PARAM_INT, 'Mail now?'), + 'children' => new external_multiple_structure(new external_value(PARAM_INT, 'children post id')), + 'canreply' => new external_value(PARAM_BOOL, 'The user can reply to posts?'), + 'postread' => new external_value(PARAM_BOOL, 'The post was read'), + 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name') + ), 'post' + ) + ), + 'warnings' => new external_warnings() + ) + ); + } + } diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php index e78f863bf56..9f85647600d 100644 --- a/mod/forum/tests/externallib_test.php +++ b/mod/forum/tests/externallib_test.php @@ -384,4 +384,132 @@ class mod_forum_external_testcase extends externallib_advanced_testcase { $this->assertEquals('requireloginerror', $e->errorcode); } } + + /** + * Test get forum posts + */ + public function test_mod_forum_get_forum_discussion_posts() { + global $CFG; + + $this->resetAfterTest(true); + + // Set the CFG variable to allow track forums. + $CFG->forum_trackreadposts = true; + + // Create a user who can track forums. + $record = new stdClass(); + $record->trackforums = true; + $user1 = self::getDataGenerator()->create_user($record); + // Create a bunch of other users to post. + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + // Set the first created user to the test user. + self::setUser($user1); + + // Create course to add the module. + $course1 = self::getDataGenerator()->create_course(); + + // Forum with tracking off. + $record = new stdClass(); + $record->course = $course1->id; + $record->trackingtype = FORUM_TRACKING_OFF; + $forum1 = self::getDataGenerator()->create_module('forum', $record); + $forum1context = context_module::instance($forum1->cmid); + + // Add discussions to the forums. + $record = new stdClass(); + $record->course = $course1->id; + $record->userid = $user1->id; + $record->forum = $forum1->id; + $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); + + $record = new stdClass(); + $record->course = $course1->id; + $record->userid = $user2->id; + $record->forum = $forum1->id; + $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); + + // Add 2 replies to the discussion 1 from different users. + $record = new stdClass(); + $record->discussion = $discussion1->id; + $record->parent = $discussion1->firstpost; + $record->userid = $user2->id; + $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); + + $record->parent = $discussion1reply1->id; + $record->userid = $user3->id; + $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); + + // Enrol the user in the course. + $enrol = enrol_get_plugin('manual'); + // Following line enrol and assign default role id to the user. + // So the user automatically gets mod/forum:viewdiscussion on all forums of the course. + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user3->id, $course1->id); + + // Create what we expect to be returned when querying the discussion. + $expectedposts = array( + 'posts' => array(), + 'warnings' => array(), + ); + $expectedposts['posts'][] = array( + 'id' => $discussion1reply2->id, + 'discussion' => $discussion1reply2->discussion, + 'parent' => $discussion1reply2->parent, + 'userid' => $discussion1reply2->userid, + 'created' => $discussion1reply2->created, + 'modified' => $discussion1reply2->modified, + 'mailed' => $discussion1reply2->mailed, + 'subject' => $discussion1reply2->subject, + 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php', + $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id), + 'messageformat' => $discussion1reply2->messageformat, + 'messagetrust' => $discussion1reply2->messagetrust, + 'attachment' => $discussion1reply2->attachment, + 'totalscore' => $discussion1reply2->totalscore, + 'mailnow' => $discussion1reply2->mailnow, + 'children' => array(), + 'canreply' => true, + 'postread' => false, + 'userfullname' => fullname($user3) + ); + $expectedposts['posts'][] = array( + 'id' => $discussion1reply1->id, + 'discussion' => $discussion1reply1->discussion, + 'parent' => $discussion1reply1->parent, + 'userid' => $discussion1reply1->userid, + 'created' => $discussion1reply1->created, + 'modified' => $discussion1reply1->modified, + 'mailed' => $discussion1reply1->mailed, + 'subject' => $discussion1reply1->subject, + 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php', + $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id), + 'messageformat' => $discussion1reply1->messageformat, + 'messagetrust' => $discussion1reply1->messagetrust, + 'attachment' => $discussion1reply1->attachment, + 'totalscore' => $discussion1reply1->totalscore, + 'mailnow' => $discussion1reply1->mailnow, + 'children' => array(), + 'canreply' => true, + 'postread' => false, + 'userfullname' => fullname($user2) + ); + + // Test a discussion with two additional posts (total 3 posts). + $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC'); + $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); + $this->assertEquals(3, count($posts['posts'])); + + // Unset the initial discussion post. + array_pop($posts['posts']); + $this->assertEquals($expectedposts, $posts); + + // Test discussion without additional posts. There should be only one post (the one created by the discussion). + $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC'); + $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); + $this->assertEquals(1, count($posts['posts'])); + + } } diff --git a/mod/forum/tests/generator/lib.php b/mod/forum/tests/generator/lib.php index 449ec180a3a..a371c15ec6d 100644 --- a/mod/forum/tests/generator/lib.php +++ b/mod/forum/tests/generator/lib.php @@ -198,6 +198,30 @@ class mod_forum_generator extends testing_module_generator { $record['modified'] = $time; } + if (!isset($record['mailed'])) { + $record['mailed'] = 1; + } + + if (!isset($record['messageformat'])) { + $record['messageformat'] = 1; + } + + if (!isset($record['messagetrust'])) { + $record['messagetrust'] = 1; + } + + if (!isset($record['attachment'])) { + $record['attachment'] = ""; + } + + if (!isset($record['totalscore'])) { + $record['totalscore'] = 0; + } + + if (!isset($record['mailnow'])) { + $record['mailnow'] = 0; + } + $record = (object) $record; // Add the post. From 4ed154615406d888c6350b30d62217577b7a609e Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 2 May 2014 11:22:29 +0200 Subject: [PATCH 2/3] MDL-30106 forum: Bump version number (upgrading service and installing new ws) --- mod/forum/version.php | 2 +- version.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/forum/version.php b/mod/forum/version.php index b278e3fc834..ecb0e9c1be2 100644 --- a/mod/forum/version.php +++ b/mod/forum/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2014042200; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2014042201; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2013110500; // Requires this Moodle version $plugin->component = 'mod_forum'; // Full name of the plugin (used for diagnostics) $plugin->cron = 60; diff --git a/version.php b/version.php index 5362e6a78db..224d51ff240 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2014050200.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2014050200.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. From fb8840d29159ff012fea880717ddeb0cf429b510 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 2 May 2014 11:38:17 +0200 Subject: [PATCH 3/3] MDL-30106 forum: get_posts WS fixes in data generator and def. values --- mod/forum/externallib.php | 5 +++-- mod/forum/tests/externallib_test.php | 2 +- mod/forum/tests/generator/lib.php | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php index 139358e8610..98b144cd4f9 100644 --- a/mod/forum/externallib.php +++ b/mod/forum/externallib.php @@ -382,7 +382,7 @@ class mod_forum_external extends external_api { * @return array the forum post details * @since Moodle 2.7 */ - public static function get_forum_discussion_posts($discussionid, $sortby = "id", $sortdirection = "ASC") { + public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") { global $CFG, $DB, $USER; $warnings = array(); @@ -463,9 +463,10 @@ class mod_forum_external extends external_api { $posts[$pid]->postread = false; } $posts[$pid]->canreply = $canreply; - $posts[$pid]->children = array(); if (!empty($posts[$pid]->children)) { $posts[$pid]->children = array_keys($posts[$pid]->children); + } else { + $posts[$pid]->children = array(); } $user = new stdclass(); diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php index 9f85647600d..21c46290817 100644 --- a/mod/forum/tests/externallib_test.php +++ b/mod/forum/tests/externallib_test.php @@ -491,7 +491,7 @@ class mod_forum_external_testcase extends externallib_advanced_testcase { 'attachment' => $discussion1reply1->attachment, 'totalscore' => $discussion1reply1->totalscore, 'mailnow' => $discussion1reply1->mailnow, - 'children' => array(), + 'children' => array(4), 'canreply' => true, 'postread' => false, 'userfullname' => fullname($user2) diff --git a/mod/forum/tests/generator/lib.php b/mod/forum/tests/generator/lib.php index a371c15ec6d..8d91e307849 100644 --- a/mod/forum/tests/generator/lib.php +++ b/mod/forum/tests/generator/lib.php @@ -199,15 +199,15 @@ class mod_forum_generator extends testing_module_generator { } if (!isset($record['mailed'])) { - $record['mailed'] = 1; + $record['mailed'] = 0; } if (!isset($record['messageformat'])) { - $record['messageformat'] = 1; + $record['messageformat'] = 0; } if (!isset($record['messagetrust'])) { - $record['messagetrust'] = 1; + $record['messagetrust'] = 0; } if (!isset($record['attachment'])) {