From 7ab43ac84aa541f68652a1a53eae3fca028f77d8 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 4 Aug 2015 17:43:25 +0200 Subject: [PATCH] MDL-50994 mod_forum: New Web Service mod_forum_add_discussion --- lib/db/services.php | 1 + mod/forum/db/services.php | 9 ++ mod/forum/externallib.php | 166 +++++++++++++++++++++++++++ mod/forum/tests/externallib_test.php | 149 +++++++++++++++++++++++- version.php | 2 +- 5 files changed, 325 insertions(+), 2 deletions(-) diff --git a/lib/db/services.php b/lib/db/services.php index ba378420a7c..727867cb212 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1125,6 +1125,7 @@ $services = array( 'mod_forum_get_forum_discussions_paginated', 'mod_forum_get_forum_discussion_posts', 'mod_forum_add_discussion_post', + 'mod_forum_add_discussion', 'core_files_get_files', 'core_message_get_messages', 'core_message_create_contacts', diff --git a/mod/forum/db/services.php b/mod/forum/db/services.php index 7754e8dee29..03b3763181b 100644 --- a/mod/forum/db/services.php +++ b/mod/forum/db/services.php @@ -90,4 +90,13 @@ $functions = array( 'type' => 'write', 'capabilities' => 'mod/forum:replypost' ), + + 'mod_forum_add_discussion' => array( + 'classname' => 'mod_forum_external', + 'methodname' => 'add_discussion', + 'classpath' => 'mod/forum/externallib.php', + 'description' => 'Add a new discussion into an existing forum.', + 'type' => 'write', + 'capabilities' => 'mod/forum:startdiscussion' + ), ); diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php index b3365c676d7..e34410fc8b5 100644 --- a/mod/forum/externallib.php +++ b/mod/forum/externallib.php @@ -1120,4 +1120,170 @@ class mod_forum_external extends external_api { ); } + /** + * Returns description of method parameters + * + * @return external_function_parameters + * @since Moodle 3.0 + */ + public static function add_discussion_parameters() { + return new external_function_parameters( + array( + 'forumid' => new external_value(PARAM_INT, 'forum instance id'), + 'subject' => new external_value(PARAM_TEXT, 'new discussion subject'), + 'message' => new external_value(PARAM_RAW, 'new discussion message (only html format allowed)'), + 'groupid' => new external_value(PARAM_INT, 'the user course group, default to 0', VALUE_DEFAULT, -1), + 'options' => new external_multiple_structure ( + new external_single_structure( + array( + 'name' => new external_value(PARAM_ALPHANUM, + 'The allowed keys (value format) are: + discussionsubscribe (bool); subscribe to the discussion?, default to true + '), + 'value' => new external_value(PARAM_RAW, 'the value of the option, + this param is validated in the external function.' + ) + ) + ), 'Options', VALUE_DEFAULT, array()) + ) + ); + } + + /** + * Add a new discussion into an existing forum. + * + * @param int $forumid the forum instance id + * @param string $subject new discussion subject + * @param string $message new discussion message (only html format allowed) + * @param int $groupid the user course group + * @param array $options optional settings + * @return array of warnings and the new discussion id + * @since Moodle 3.0 + * @throws moodle_exception + */ + public static function add_discussion($forumid, $subject, $message, $groupid = -1, $options = array()) { + global $DB, $CFG; + require_once($CFG->dirroot . "/mod/forum/lib.php"); + + $params = self::validate_parameters(self::add_discussion_parameters(), + array( + 'forumid' => $forumid, + 'subject' => $subject, + 'message' => $message, + 'groupid' => $groupid, + 'options' => $options + )); + // Validate options. + $options = array( + 'discussionsubscribe' => true + ); + foreach ($params['options'] as $option) { + $name = trim($option['name']); + switch ($name) { + case 'discussionsubscribe': + $value = clean_param($option['value'], PARAM_BOOL); + break; + default: + throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); + } + $options[$name] = $value; + } + + $warnings = array(); + + // Request and permission validation. + $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST); + list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum'); + + $context = context_module::instance($cm->id); + self::validate_context($context); + + // Normalize group. + if (!groups_get_activity_groupmode($cm)) { + // Groups not supported, force to -1. + $groupid = -1; + } else { + // Check if we receive the default or and empty value for groupid, + // in this case, get the group for the user in the activity. + if ($groupid === -1 or empty($params['groupid'])) { + $groupid = groups_get_activity_group($cm); + } else { + // Here we rely in the group passed, forum_user_can_post_discussion will validate the group. + $groupid = $params['groupid']; + } + } + + if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) { + throw new moodle_exception('cannotcreatediscussion', 'forum'); + } + + $thresholdwarning = forum_check_throttling($forum, $cm); + forum_check_blocking_threshold($thresholdwarning); + + // Create the discussion. + $discussion = new stdClass(); + $discussion->course = $course->id; + $discussion->forum = $forum->id; + $discussion->message = $params['message']; + $discussion->messageformat = FORMAT_HTML; // Force formatting for now. + $discussion->messagetrust = trusttext_trusted($context); + $discussion->itemid = 0; + $discussion->groupid = $groupid; + $discussion->mailnow = 0; + $discussion->subject = $params['subject']; + $discussion->name = $discussion->subject; + $discussion->timestart = 0; + $discussion->timeend = 0; + + if ($discussionid = forum_add_discussion($discussion)) { + + $discussion->id = $discussionid; + + // Trigger events and completion. + + $params = array( + 'context' => $context, + 'objectid' => $discussion->id, + 'other' => array( + 'forumid' => $forum->id, + ) + ); + $event = \mod_forum\event\discussion_created::create($params); + $event->add_record_snapshot('forum_discussions', $discussion); + $event->trigger(); + + $completion = new completion_info($course); + if ($completion->is_enabled($cm) && + ($forum->completiondiscussions || $forum->completionposts)) { + $completion->update_state($cm, COMPLETION_COMPLETE); + } + + $settings = new stdClass(); + $settings->discussionsubscribe = $options['discussionsubscribe']; + forum_post_subscription($settings, $forum, $discussion); + } else { + throw new moodle_exception('couldnotadd', 'forum'); + } + + $result = array(); + $result['discussionid'] = $discussionid; + $result['warnings'] = $warnings; + return $result; + } + + /** + * Returns description of method result value + * + * @return external_description + * @since Moodle 3.0 + */ + public static function add_discussion_returns() { + return new external_single_structure( + array( + 'discussionid' => new external_value(PARAM_INT, 'new discussion id'), + 'warnings' => new external_warnings() + ) + ); + } + } diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php index 26ef46666e4..97972d6079a 100644 --- a/mod/forum/tests/externallib_test.php +++ b/mod/forum/tests/externallib_test.php @@ -1,5 +1,4 @@ resetAfterTest(true); + + // Create courses to add the modules. + $course = self::getDataGenerator()->create_course(); + + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + + // First forum with tracking off. + $record = new stdClass(); + $record->course = $course->id; + $record->type = 'news'; + $forum = self::getDataGenerator()->create_module('forum', $record); + + self::setUser($user1); + $this->getDataGenerator()->enrol_user($user1->id, $course->id); + + try { + mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...'); + $this->fail('Exception expected due to invalid permissions.'); + } catch (moodle_exception $e) { + $this->assertEquals('cannotcreatediscussion', $e->errorcode); + } + + self::setAdminUser(); + $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...'); + $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion); + + $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id); + $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions); + + $this->assertCount(1, $discussions['discussions']); + $this->assertCount(0, $discussions['warnings']); + + $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']); + $this->assertEquals(-1, $discussions['discussions'][0]['groupid']); + $this->assertEquals('the subject', $discussions['discussions'][0]['subject']); + $this->assertEquals('some text here...', $discussions['discussions'][0]['message']); + + } + + /** + * Test adding discussions in a course with gorups + */ + public function test_add_discussion_in_course_with_groups() { + global $CFG; + + $this->resetAfterTest(true); + + // Create course to add the module. + $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0)); + $user = self::getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user->id, $course->id); + + // Forum forcing separate gropus. + $record = new stdClass(); + $record->course = $course->id; + $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS)); + + // Try to post (user not enrolled). + self::setUser($user); + + // The user is not enroled in any group, try to post in a forum with separate groups. + try { + mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...'); + $this->fail('Exception expected due to invalid group permissions.'); + } catch (moodle_exception $e) { + $this->assertEquals('cannotcreatediscussion', $e->errorcode); + } + + try { + mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0); + $this->fail('Exception expected due to invalid group permissions.'); + } catch (moodle_exception $e) { + $this->assertEquals('cannotcreatediscussion', $e->errorcode); + } + + // Create a group. + $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + + // Try to post in a group the user is not enrolled. + try { + mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id); + $this->fail('Exception expected due to invalid group permissions.'); + } catch (moodle_exception $e) { + $this->assertEquals('cannotcreatediscussion', $e->errorcode); + } + + // Add the user to a group. + groups_add_member($group->id, $user->id); + + // Try to post in a group the user is not enrolled. + try { + mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1); + $this->fail('Exception expected due to invalid group.'); + } catch (moodle_exception $e) { + $this->assertEquals('cannotcreatediscussion', $e->errorcode); + } + + // Nost add the discussion using a valid group. + $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id); + $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion); + + $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id); + $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions); + + $this->assertCount(1, $discussions['discussions']); + $this->assertCount(0, $discussions['warnings']); + $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']); + $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']); + + // Now add a discussions without indicating a group. The function should guess the correct group. + $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...'); + $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion); + + $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id); + $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions); + + $this->assertCount(2, $discussions['discussions']); + $this->assertCount(0, $discussions['warnings']); + $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']); + $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']); + + // Enrol the same user in other group. + $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + groups_add_member($group2->id, $user->id); + + // Now add a discussions without indicating a group. The function should guess the correct group (the first one). + $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...'); + $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion); + + $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id); + $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions); + + $this->assertCount(3, $discussions['discussions']); + $this->assertCount(0, $discussions['warnings']); + $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']); + $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']); + $this->assertEquals($group->id, $discussions['discussions'][2]['groupid']); + + } + } diff --git a/version.php b/version.php index aaf23726acf..626a117a4d7 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2015091000.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2015091000.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.