diff --git a/lib/db/services.php b/lib/db/services.php index d1dbce6d416..7f20cf602d5 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1124,6 +1124,7 @@ $services = array( 'mod_forum_get_forums_by_courses', 'mod_forum_get_forum_discussions_paginated', 'mod_forum_get_forum_discussion_posts', + 'mod_forum_add_discussion_post', '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 d80ddd045de..7754e8dee29 100644 --- a/mod/forum/db/services.php +++ b/mod/forum/db/services.php @@ -81,4 +81,13 @@ $functions = array( 'type' => 'write', 'capabilities' => 'mod/forum:viewdiscussion' ), + + 'mod_forum_add_discussion_post' => array( + 'classname' => 'mod_forum_external', + 'methodname' => 'add_discussion_post', + 'classpath' => 'mod/forum/externallib.php', + 'description' => 'Create new posts into an existing discussion.', + 'type' => 'write', + 'capabilities' => 'mod/forum:replypost' + ), ); diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php index 340c88c2b9f..b27f9e6a612 100644 --- a/mod/forum/externallib.php +++ b/mod/forum/externallib.php @@ -965,4 +965,159 @@ 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_post_parameters() { + return new external_function_parameters( + array( + 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to + (can be the initial discussion post'), + 'subject' => new external_value(PARAM_TEXT, 'new post subject'), + 'message' => new external_value(PARAM_RAW, 'new post message (only html format allowed)'), + '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()) + ) + ); + } + + /** + * Create new posts into an existing discussion. + * + * @param int $postid the post id we are going to reply to + * @param string $subject new post subject + * @param string $message new post message (only html format allowed) + * @param array $options optional settings + * @return array of warnings and the new post id + * @since Moodle 3.0 + * @throws moodle_exception + */ + public static function add_discussion_post($postid, $subject, $message, $options = array()) { + global $DB, $CFG, $USER; + require_once($CFG->dirroot . "/mod/forum/lib.php"); + + $params = self::validate_parameters(self::add_discussion_post_parameters(), + array( + 'postid' => $postid, + 'subject' => $subject, + 'message' => $message, + '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(); + + if (! $parent = forum_get_post_full($params['postid'])) { + throw new moodle_exception('invalidparentpostid', 'forum'); + } + + if (! $discussion = $DB->get_record("forum_discussions", array("id" => $parent->discussion))) { + throw new moodle_exception('notpartofdiscussion', 'forum'); + } + + // Request and permission validation. + $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST); + list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum'); + + $context = context_module::instance($cm->id); + self::validate_context($context); + + if (!forum_user_can_post($forum, $discussion, $USER, $cm, $course, $context)) { + throw new moodle_exception('nopostforum', 'forum'); + } + + $thresholdwarning = forum_check_throttling($forum, $cm); + forum_check_blocking_threshold($thresholdwarning); + + // Create the post. + $post = new stdClass(); + $post->discussion = $discussion->id; + $post->parent = $parent->id; + $post->subject = $params['subject']; + $post->message = $params['message']; + $post->messageformat = FORMAT_HTML; // Force formatting for now. + $post->messagetrust = trusttext_trusted($context); + $post->itemid = 0; + + if ($postid = forum_add_new_post($post, null)) { + + $post->id = $postid; + + // Trigger events and completion. + $params = array( + 'context' => $context, + 'objectid' => $post->id, + 'other' => array( + 'discussionid' => $discussion->id, + 'forumid' => $forum->id, + 'forumtype' => $forum->type, + ) + ); + $event = \mod_forum\event\post_created::create($params); + $event->add_record_snapshot('forum_posts', $post); + $event->add_record_snapshot('forum_discussions', $discussion); + $event->trigger(); + + // Update completion state. + $completion = new completion_info($course); + if ($completion->is_enabled($cm) && + ($forum->completionreplies || $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['postid'] = $postid; + $result['warnings'] = $warnings; + return $result; + } + + /** + * Returns description of method result value + * + * @return external_description + * @since Moodle 3.0 + */ + public static function add_discussion_post_returns() { + return new external_single_structure( + array( + 'postid' => new external_value(PARAM_INT, 'new post id'), + 'warnings' => new external_warnings() + ) + ); + } + } diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php index 2627ffa7a10..7f095b48b7a 100644 --- a/mod/forum/tests/externallib_test.php +++ b/mod/forum/tests/externallib_test.php @@ -762,4 +762,78 @@ class mod_forum_external_testcase extends externallib_advanced_testcase { $this->assertCount(0, $discussions['warnings']); } + + /** + * Test add_discussion_post + */ + public function test_add_discussion_post() { + global $CFG; + + $this->resetAfterTest(true); + + $user = self::getDataGenerator()->create_user(); + $otheruser = self::getDataGenerator()->create_user(); + + self::setAdminUser(); + + // Create course to add the module. + $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0)); + + // Forum with tracking off. + $record = new stdClass(); + $record->course = $course->id; + $forum = self::getDataGenerator()->create_module('forum', $record); + $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST); + $forumcontext = context_module::instance($forum->cmid); + + // Add discussions to the forums. + $record = new stdClass(); + $record->course = $course->id; + $record->userid = $user->id; + $record->forum = $forum->id; + $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); + + // Try to post (user not enrolled). + self::setUser($user); + try { + mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...'); + $this->fail('Exception expected due to being unenrolled from the course.'); + } catch (moodle_exception $e) { + $this->assertEquals('requireloginerror', $e->errorcode); + } + + $this->getDataGenerator()->enrol_user($user->id, $course->id); + $this->getDataGenerator()->enrol_user($otheruser->id, $course->id); + + $post = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...'); + $post = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $post); + + $posts = mod_forum_external::get_forum_discussion_posts($discussion->id); + $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); + // We receive the discussion and the post. + $this->assertEquals(2, count($posts['posts'])); + $this->assertEquals($post['postid'], $posts['posts'][1]['id']); + $this->assertEquals('some subject', $posts['posts'][1]['subject']); + $this->assertEquals('some text here...', $posts['posts'][1]['message']); + + // Check not posting in groups the user is not member of. + $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); + groups_add_member($group->id, $otheruser->id); + + $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS)); + $record->forum = $forum->id; + $record->userid = $otheruser->id; + $record->groupid = $group->id; + $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); + + try { + mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...'); + $this->fail('Exception expected due to invalid permissions for posting.'); + } catch (moodle_exception $e) { + // Expect debugging since we are switching context, and this is something WS_SERVER mode don't like. + $this->assertDebuggingCalled(); + $this->assertEquals('nopostforum', $e->errorcode); + } + + } } diff --git a/mod/forum/version.php b/mod/forum/version.php index 2caf094cb05..b6c95cfb4bc 100644 --- a/mod/forum/version.php +++ b/mod/forum/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2015051101; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2015051102; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2015050500; // Requires this Moodle version $plugin->component = 'mod_forum'; // Full name of the plugin (used for diagnostics) diff --git a/version.php b/version.php index 34f1ac75cb2..024de437c55 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2015090803.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2015090900.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.