20c7890206d8f79c6a43de68997637ada5c028ec
[moodle.git] / mod / forum / externallib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * External forum API
20  *
21  * @package    mod_forum
22  * @copyright  2012 Mark Nelson <markn@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die;
28 require_once("$CFG->libdir/externallib.php");
30 class mod_forum_external extends external_api {
32     /**
33      * Describes the parameters for get_forum.
34      *
35      * @return external_external_function_parameters
36      * @since Moodle 2.5
37      */
38     public static function get_forums_by_courses_parameters() {
39         return new external_function_parameters (
40             array(
41                 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID',
42                         '', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Course IDs', VALUE_DEFAULT, array()),
43             )
44         );
45     }
47     /**
48      * Returns a list of forums in a provided list of courses,
49      * if no list is provided all forums that the user can view
50      * will be returned.
51      *
52      * @param array $courseids the course ids
53      * @return array the forum details
54      * @since Moodle 2.5
55      */
56     public static function get_forums_by_courses($courseids = array()) {
57         global $CFG;
59         require_once($CFG->dirroot . "/mod/forum/lib.php");
61         $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids));
63         if (empty($params['courseids'])) {
64             $params['courseids'] = array_keys(enrol_get_my_courses());
65         }
67         // Array to store the forums to return.
68         $arrforums = array();
69         $warnings = array();
71         // Ensure there are courseids to loop through.
72         if (!empty($params['courseids'])) {
74             list($courses, $warnings) = external_util::validate_courses($params['courseids']);
76             // Get the forums in this course. This function checks users visibility permissions.
77             $forums = get_all_instances_in_courses("forum", $courses);
78             foreach ($forums as $forum) {
80                 $course = $courses[$forum->course];
81                 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
82                 $context = context_module::instance($cm->id);
84                 // Skip forums we are not allowed to see discussions.
85                 if (!has_capability('mod/forum:viewdiscussion', $context)) {
86                     continue;
87                 }
89                 $forum->name = external_format_string($forum->name, $context->id);
90                 // Format the intro before being returning using the format setting.
91                 list($forum->intro, $forum->introformat) = external_format_text($forum->intro, $forum->introformat,
92                                                                                 $context->id, 'mod_forum', 'intro', 0);
93                 // Discussions count. This function does static request cache.
94                 $forum->numdiscussions = forum_count_discussions($forum, $cm, $course);
95                 $forum->cmid = $forum->coursemodule;
96                 $forum->cancreatediscussions = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
98                 // Add the forum to the array to return.
99                 $arrforums[$forum->id] = $forum;
100             }
101         }
103         return $arrforums;
104     }
106     /**
107      * Describes the get_forum return value.
108      *
109      * @return external_single_structure
110      * @since Moodle 2.5
111      */
112      public static function get_forums_by_courses_returns() {
113         return new external_multiple_structure(
114             new external_single_structure(
115                 array(
116                     'id' => new external_value(PARAM_INT, 'Forum id'),
117                     'course' => new external_value(PARAM_INT, 'Course id'),
118                     'type' => new external_value(PARAM_TEXT, 'The forum type'),
119                     'name' => new external_value(PARAM_RAW, 'Forum name'),
120                     'intro' => new external_value(PARAM_RAW, 'The forum intro'),
121                     'introformat' => new external_format_value('intro'),
122                     'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
123                     'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
124                     'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
125                     'scale' => new external_value(PARAM_INT, 'Scale'),
126                     'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'),
127                     'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'),
128                     'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'),
129                     'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'),
130                     'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'),
131                     'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'),
132                     'timemodified' => new external_value(PARAM_INT, 'Time modified'),
133                     'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'),
134                     'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'),
135                     'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'),
136                     'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'),
137                     'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'),
138                     'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'),
139                     'cmid' => new external_value(PARAM_INT, 'Course module id'),
140                     'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL),
141                     'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL),
142                 ), 'forum'
143             )
144         );
145     }
147     /**
148      * Describes the parameters for get_forum_discussions.
149      *
150      * @return external_external_function_parameters
151      * @since Moodle 2.5
152      * @deprecated Moodle 2.8 MDL-46458 - Please do not call this function any more.
153      * @see get_forum_discussions_paginated
154      */
155     public static function get_forum_discussions_parameters() {
156         return new external_function_parameters (
157             array(
158                 'forumids' => new external_multiple_structure(new external_value(PARAM_INT, 'forum ID',
159                         '', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Forum IDs', VALUE_REQUIRED),
160                 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
161                 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
162             )
163         );
164     }
166     /**
167      * Returns a list of forum discussions as well as a summary of the discussion
168      * in a provided list of forums.
169      *
170      * @param array $forumids the forum ids
171      * @param int $limitfrom limit from SQL data
172      * @param int $limitnum limit number SQL data
173      *
174      * @return array the forum discussion details
175      * @since Moodle 2.5
176      * @deprecated Moodle 2.8 MDL-46458 - Please do not call this function any more.
177      * @see get_forum_discussions_paginated
178      */
179     public static function get_forum_discussions($forumids, $limitfrom = 0, $limitnum = 0) {
180         global $CFG, $DB, $USER;
182         require_once($CFG->dirroot . "/mod/forum/lib.php");
184         // Validate the parameter.
185         $params = self::validate_parameters(self::get_forum_discussions_parameters(),
186             array(
187                 'forumids'  => $forumids,
188                 'limitfrom' => $limitfrom,
189                 'limitnum'  => $limitnum,
190             ));
191         $forumids  = $params['forumids'];
192         $limitfrom = $params['limitfrom'];
193         $limitnum  = $params['limitnum'];
195         // Array to store the forum discussions to return.
196         $arrdiscussions = array();
197         // Keep track of the users we have looked up in the DB.
198         $arrusers = array();
200         // Loop through them.
201         foreach ($forumids as $id) {
202             // Get the forum object.
203             $forum = $DB->get_record('forum', array('id' => $id), '*', MUST_EXIST);
204             $course = get_course($forum->course);
206             $modinfo = get_fast_modinfo($course);
207             $forums  = $modinfo->get_instances_of('forum');
208             $cm = $forums[$forum->id];
210             // Get the module context.
211             $modcontext = context_module::instance($cm->id);
213             // Validate the context.
214             self::validate_context($modcontext);
216             require_capability('mod/forum:viewdiscussion', $modcontext);
218             // Get the discussions for this forum.
219             $params = array();
221             $groupselect = "";
222             $groupmode = groups_get_activity_groupmode($cm, $course);
224             if ($groupmode and $groupmode != VISIBLEGROUPS and !has_capability('moodle/site:accessallgroups', $modcontext)) {
225                 // Get all the discussions from all the groups this user belongs to.
226                 $usergroups = groups_get_user_groups($course->id);
227                 if (!empty($usergroups['0'])) {
228                     list($sql, $params) = $DB->get_in_or_equal($usergroups['0']);
229                     $groupselect = "AND (groupid $sql OR groupid = -1)";
230                 }
231             }
232             array_unshift($params, $id);
233             $select = "forum = ? $groupselect";
235             if ($discussions = $DB->get_records_select('forum_discussions', $select, $params, 'timemodified DESC', '*',
236                                                             $limitfrom, $limitnum)) {
238                 // Check if they can view full names.
239                 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
240                 // Get the unreads array, this takes a forum id and returns data for all discussions.
241                 $unreads = array();
242                 if ($cantrack = forum_tp_can_track_forums($forum)) {
243                     if ($forumtracked = forum_tp_is_tracked($forum)) {
244                         $unreads = forum_get_discussions_unread($cm);
245                     }
246                 }
247                 // The forum function returns the replies for all the discussions in a given forum.
248                 $replies = forum_count_discussion_replies($id);
250                 foreach ($discussions as $discussion) {
251                     // This function checks capabilities, timed discussions, groups and qanda forums posting.
252                     if (!forum_user_can_see_discussion($forum, $discussion, $modcontext)) {
253                         continue;
254                     }
256                     $usernamefields = user_picture::fields();
257                     // If we don't have the users details then perform DB call.
258                     if (empty($arrusers[$discussion->userid])) {
259                         $arrusers[$discussion->userid] = $DB->get_record('user', array('id' => $discussion->userid),
260                                 $usernamefields, MUST_EXIST);
261                     }
262                     // Get the subject.
263                     $subject = $DB->get_field('forum_posts', 'subject', array('id' => $discussion->firstpost), MUST_EXIST);
264                     // Create object to return.
265                     $return = new stdClass();
266                     $return->id = (int) $discussion->id;
267                     $return->course = $discussion->course;
268                     $return->forum = $discussion->forum;
269                     $return->name = $discussion->name;
270                     $return->userid = $discussion->userid;
271                     $return->groupid = $discussion->groupid;
272                     $return->assessed = $discussion->assessed;
273                     $return->timemodified = (int) $discussion->timemodified;
274                     $return->usermodified = $discussion->usermodified;
275                     $return->timestart = $discussion->timestart;
276                     $return->timeend = $discussion->timeend;
277                     $return->firstpost = (int) $discussion->firstpost;
278                     $return->firstuserfullname = fullname($arrusers[$discussion->userid], $canviewfullname);
279                     $return->firstuserimagealt = $arrusers[$discussion->userid]->imagealt;
280                     $return->firstuserpicture = $arrusers[$discussion->userid]->picture;
281                     $return->firstuseremail = $arrusers[$discussion->userid]->email;
282                     $return->subject = $subject;
283                     $return->numunread = '';
284                     if ($cantrack && $forumtracked) {
285                         if (isset($unreads[$discussion->id])) {
286                             $return->numunread = (int) $unreads[$discussion->id];
287                         }
288                     }
289                     // Check if there are any replies to this discussion.
290                     if (!empty($replies[$discussion->id])) {
291                          $return->numreplies = (int) $replies[$discussion->id]->replies;
292                          $return->lastpost = (int) $replies[$discussion->id]->lastpostid;
293                     } else { // No replies, so the last post will be the first post.
294                         $return->numreplies = 0;
295                         $return->lastpost = (int) $discussion->firstpost;
296                     }
297                     // Get the last post as well as the user who made it.
298                     $lastpost = $DB->get_record('forum_posts', array('id' => $return->lastpost), '*', MUST_EXIST);
299                     if (empty($arrusers[$lastpost->userid])) {
300                         $arrusers[$lastpost->userid] = $DB->get_record('user', array('id' => $lastpost->userid),
301                                 $usernamefields, MUST_EXIST);
302                     }
303                     $return->lastuserid = $lastpost->userid;
304                     $return->lastuserfullname = fullname($arrusers[$lastpost->userid], $canviewfullname);
305                     $return->lastuserimagealt = $arrusers[$lastpost->userid]->imagealt;
306                     $return->lastuserpicture = $arrusers[$lastpost->userid]->picture;
307                     $return->lastuseremail = $arrusers[$lastpost->userid]->email;
308                     // Add the discussion statistics to the array to return.
309                     $arrdiscussions[$return->id] = (array) $return;
310                 }
311             }
312         }
314         return $arrdiscussions;
315     }
317     /**
318      * Describes the get_forum_discussions return value.
319      *
320      * @return external_single_structure
321      * @since Moodle 2.5
322      * @deprecated Moodle 2.8 MDL-46458 - Please do not call this function any more.
323      * @see get_forum_discussions_paginated
324      */
325      public static function get_forum_discussions_returns() {
326         return new external_multiple_structure(
327             new external_single_structure(
328                 array(
329                     'id' => new external_value(PARAM_INT, 'Forum id'),
330                     'course' => new external_value(PARAM_INT, 'Course id'),
331                     'forum' => new external_value(PARAM_INT, 'The forum id'),
332                     'name' => new external_value(PARAM_TEXT, 'Discussion name'),
333                     'userid' => new external_value(PARAM_INT, 'User id'),
334                     'groupid' => new external_value(PARAM_INT, 'Group id'),
335                     'assessed' => new external_value(PARAM_INT, 'Is this assessed?'),
336                     'timemodified' => new external_value(PARAM_INT, 'Time modified'),
337                     'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
338                     'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
339                     'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
340                     'firstpost' => new external_value(PARAM_INT, 'The first post in the discussion'),
341                     'firstuserfullname' => new external_value(PARAM_TEXT, 'The discussion creators fullname'),
342                     'firstuserimagealt' => new external_value(PARAM_TEXT, 'The discussion creators image alt'),
343                     'firstuserpicture' => new external_value(PARAM_INT, 'The discussion creators profile picture'),
344                     'firstuseremail' => new external_value(PARAM_TEXT, 'The discussion creators email'),
345                     'subject' => new external_value(PARAM_TEXT, 'The discussion subject'),
346                     'numreplies' => new external_value(PARAM_TEXT, 'The number of replies in the discussion'),
347                     'numunread' => new external_value(PARAM_TEXT, 'The number of unread posts, blank if this value is
348                         not available due to forum settings.'),
349                     'lastpost' => new external_value(PARAM_INT, 'The id of the last post in the discussion'),
350                     'lastuserid' => new external_value(PARAM_INT, 'The id of the user who made the last post'),
351                     'lastuserfullname' => new external_value(PARAM_TEXT, 'The last person to posts fullname'),
352                     'lastuserimagealt' => new external_value(PARAM_TEXT, 'The last person to posts image alt'),
353                     'lastuserpicture' => new external_value(PARAM_INT, 'The last person to posts profile picture'),
354                     'lastuseremail' => new external_value(PARAM_TEXT, 'The last person to posts email'),
355                 ), 'discussion'
356             )
357         );
358     }
360     /**
361      * Describes the parameters for get_forum_discussion_posts.
362      *
363      * @return external_external_function_parameters
364      * @since Moodle 2.7
365      */
366     public static function get_forum_discussion_posts_parameters() {
367         return new external_function_parameters (
368             array(
369                 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED),
370                 'sortby' => new external_value(PARAM_ALPHA,
371                     'sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
372                 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
373             )
374         );
375     }
377     /**
378      * Returns a list of forum posts for a discussion
379      *
380      * @param int $discussionid the post ids
381      * @param string $sortby sort by this element (id, created or modified)
382      * @param string $sortdirection sort direction: ASC or DESC
383      *
384      * @return array the forum post details
385      * @since Moodle 2.7
386      */
387     public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") {
388         global $CFG, $DB, $USER, $PAGE;
390         $posts = array();
391         $warnings = array();
393         // Validate the parameter.
394         $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(),
395             array(
396                 'discussionid' => $discussionid,
397                 'sortby' => $sortby,
398                 'sortdirection' => $sortdirection));
400         // Compact/extract functions are not recommended.
401         $discussionid   = $params['discussionid'];
402         $sortby         = $params['sortby'];
403         $sortdirection  = $params['sortdirection'];
405         $sortallowedvalues = array('id', 'created', 'modified');
406         if (!in_array($sortby, $sortallowedvalues)) {
407             throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
408                 'allowed values are: ' . implode(',', $sortallowedvalues));
409         }
411         $sortdirection = strtoupper($sortdirection);
412         $directionallowedvalues = array('ASC', 'DESC');
413         if (!in_array($sortdirection, $directionallowedvalues)) {
414             throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
415                 'allowed values are: ' . implode(',', $directionallowedvalues));
416         }
418         $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST);
419         $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
420         $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
421         $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
423         // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
424         $modcontext = context_module::instance($cm->id);
425         self::validate_context($modcontext);
427         // This require must be here, see mod/forum/discuss.php.
428         require_once($CFG->dirroot . "/mod/forum/lib.php");
430         // Check they have the view forum capability.
431         require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
433         if (! $post = forum_get_post_full($discussion->firstpost)) {
434             throw new moodle_exception('notexists', 'forum');
435         }
437         // This function check groups, qanda, timed discussions, etc.
438         if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
439             throw new moodle_exception('noviewdiscussionspermission', 'forum');
440         }
442         $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
444         // We will add this field in the response.
445         $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
447         $forumtracked = forum_tp_is_tracked($forum);
449         $sort = 'p.' . $sortby . ' ' . $sortdirection;
450         $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
452         foreach ($allposts as $post) {
454             if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
455                 $warning = array();
456                 $warning['item'] = 'post';
457                 $warning['itemid'] = $post->id;
458                 $warning['warningcode'] = '1';
459                 $warning['message'] = 'You can\'t see this post';
460                 $warnings[] = $warning;
461                 continue;
462             }
464             // Function forum_get_all_discussion_posts adds postread field.
465             // Note that the value returned can be a boolean or an integer. The WS expects a boolean.
466             if (empty($post->postread)) {
467                 $post->postread = false;
468             } else {
469                 $post->postread = true;
470             }
472             $post->canreply = $canreply;
473             if (!empty($post->children)) {
474                 $post->children = array_keys($post->children);
475             } else {
476                 $post->children = array();
477             }
479             $userpicture = new user_picture($post);
480             $userpicture->size = 1; // Size f1.
481             $post->userpictureurl = $userpicture->get_url($PAGE)->out(false);
483             $user = new stdclass();
484             $user->id = $post->userid;
485             $user = username_load_fields_from_object($user, $post);
486             $post->userfullname = fullname($user, $canviewfullname);
488             // Rewrite embedded images URLs.
489             list($post->message, $post->messageformat) =
490                 external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_forum', 'post', $post->id);
492             // List attachments.
493             if (!empty($post->attachment)) {
494                 $post->attachments = array();
496                 $fs = get_file_storage();
497                 if ($files = $fs->get_area_files($modcontext->id, 'mod_forum', 'attachment', $post->id, "filename", false)) {
498                     foreach ($files as $file) {
499                         $filename = $file->get_filename();
500                         $fileurl = moodle_url::make_webservice_pluginfile_url(
501                                         $modcontext->id, 'mod_forum', 'attachment', $post->id, '/', $filename);
503                         $post->attachments[] = array(
504                             'filename' => $filename,
505                             'mimetype' => $file->get_mimetype(),
506                             'fileurl'  => $fileurl->out(false)
507                         );
508                     }
509                 }
510             }
512             $posts[] = $post;
513         }
515         $result = array();
516         $result['posts'] = $posts;
517         $result['warnings'] = $warnings;
518         return $result;
519     }
521     /**
522      * Describes the get_forum_discussion_posts return value.
523      *
524      * @return external_single_structure
525      * @since Moodle 2.7
526      */
527     public static function get_forum_discussion_posts_returns() {
528         return new external_single_structure(
529             array(
530                 'posts' => new external_multiple_structure(
531                         new external_single_structure(
532                             array(
533                                 'id' => new external_value(PARAM_INT, 'Post id'),
534                                 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
535                                 'parent' => new external_value(PARAM_INT, 'Parent id'),
536                                 'userid' => new external_value(PARAM_INT, 'User id'),
537                                 'created' => new external_value(PARAM_INT, 'Creation time'),
538                                 'modified' => new external_value(PARAM_INT, 'Time modified'),
539                                 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
540                                 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
541                                 'message' => new external_value(PARAM_RAW, 'The post message'),
542                                 'messageformat' => new external_format_value('message'),
543                                 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
544                                 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
545                                 'attachments' => new external_multiple_structure(
546                                     new external_single_structure(
547                                         array (
548                                             'filename' => new external_value(PARAM_FILE, 'file name'),
549                                             'mimetype' => new external_value(PARAM_RAW, 'mime type'),
550                                             'fileurl'  => new external_value(PARAM_URL, 'file download url')
551                                         )
552                                     ), 'attachments', VALUE_OPTIONAL
553                                 ),
554                                 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
555                                 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
556                                 'children' => new external_multiple_structure(new external_value(PARAM_INT, 'children post id')),
557                                 'canreply' => new external_value(PARAM_BOOL, 'The user can reply to posts?'),
558                                 'postread' => new external_value(PARAM_BOOL, 'The post was read'),
559                                 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
560                                 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL)
561                             ), 'post'
562                         )
563                     ),
564                 'warnings' => new external_warnings()
565             )
566         );
567     }
569     /**
570      * Describes the parameters for get_forum_discussions_paginated.
571      *
572      * @return external_external_function_parameters
573      * @since Moodle 2.8
574      */
575     public static function get_forum_discussions_paginated_parameters() {
576         return new external_function_parameters (
577             array(
578                 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
579                 'sortby' => new external_value(PARAM_ALPHA,
580                     'sort by this element: id, timemodified, timestart or timeend', VALUE_DEFAULT, 'timemodified'),
581                 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
582                 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
583                 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
584             )
585         );
586     }
588     /**
589      * Returns a list of forum discussions optionally sorted and paginated.
590      *
591      * @param int $forumid the forum instance id
592      * @param string $sortby sort by this element (id, timemodified, timestart or timeend)
593      * @param string $sortdirection sort direction: ASC or DESC
594      * @param int $page page number
595      * @param int $perpage items per page
596      *
597      * @return array the forum discussion details including warnings
598      * @since Moodle 2.8
599      */
600     public static function get_forum_discussions_paginated($forumid, $sortby = 'timemodified', $sortdirection = 'DESC',
601                                                     $page = -1, $perpage = 0) {
602         global $CFG, $DB, $USER, $PAGE;
604         require_once($CFG->dirroot . "/mod/forum/lib.php");
606         $warnings = array();
607         $discussions = array();
609         $params = self::validate_parameters(self::get_forum_discussions_paginated_parameters(),
610             array(
611                 'forumid' => $forumid,
612                 'sortby' => $sortby,
613                 'sortdirection' => $sortdirection,
614                 'page' => $page,
615                 'perpage' => $perpage
616             )
617         );
619         // Compact/extract functions are not recommended.
620         $forumid        = $params['forumid'];
621         $sortby         = $params['sortby'];
622         $sortdirection  = $params['sortdirection'];
623         $page           = $params['page'];
624         $perpage        = $params['perpage'];
626         $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend');
627         if (!in_array($sortby, $sortallowedvalues)) {
628             throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
629                 'allowed values are: ' . implode(',', $sortallowedvalues));
630         }
632         $sortdirection = strtoupper($sortdirection);
633         $directionallowedvalues = array('ASC', 'DESC');
634         if (!in_array($sortdirection, $directionallowedvalues)) {
635             throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
636                 'allowed values are: ' . implode(',', $directionallowedvalues));
637         }
639         $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
640         $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
641         $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
643         // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
644         $modcontext = context_module::instance($cm->id);
645         self::validate_context($modcontext);
647         // Check they have the view forum capability.
648         require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
650         $sort = 'd.' . $sortby . ' ' . $sortdirection;
651         $alldiscussions = forum_get_discussions($cm, $sort, true, -1, -1, true, $page, $perpage, FORUM_POSTS_ALL_USER_GROUPS);
653         if ($alldiscussions) {
654             $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
656             // Get the unreads array, this takes a forum id and returns data for all discussions.
657             $unreads = array();
658             if ($cantrack = forum_tp_can_track_forums($forum)) {
659                 if ($forumtracked = forum_tp_is_tracked($forum)) {
660                     $unreads = forum_get_discussions_unread($cm);
661                 }
662             }
663             // The forum function returns the replies for all the discussions in a given forum.
664             $replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage);
666             foreach ($alldiscussions as $discussion) {
668                 // This function checks for qanda forums.
669                 // Note that the forum_get_discussions returns as id the post id, not the discussion id so we need to do this.
670                 $discussionrec = clone $discussion;
671                 $discussionrec->id = $discussion->discussion;
672                 if (!forum_user_can_see_discussion($forum, $discussionrec, $modcontext)) {
673                     $warning = array();
674                     // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
675                     $warning['item'] = 'post';
676                     $warning['itemid'] = $discussion->id;
677                     $warning['warningcode'] = '1';
678                     $warning['message'] = 'You can\'t see this discussion';
679                     $warnings[] = $warning;
680                     continue;
681                 }
683                 $discussion->numunread = 0;
684                 if ($cantrack && $forumtracked) {
685                     if (isset($unreads[$discussion->discussion])) {
686                         $discussion->numunread = (int) $unreads[$discussion->discussion];
687                     }
688                 }
690                 $discussion->numreplies = 0;
691                 if (!empty($replies[$discussion->discussion])) {
692                     $discussion->numreplies = (int) $replies[$discussion->discussion]->replies;
693                 }
695                 $picturefields = explode(',', user_picture::fields());
697                 // Load user objects from the results of the query.
698                 $user = new stdclass();
699                 $user->id = $discussion->userid;
700                 $user = username_load_fields_from_object($user, $discussion, null, $picturefields);
701                 // Preserve the id, it can be modified by username_load_fields_from_object.
702                 $user->id = $discussion->userid;
703                 $discussion->userfullname = fullname($user, $canviewfullname);
705                 $userpicture = new user_picture($user);
706                 $userpicture->size = 1; // Size f1.
707                 $discussion->userpictureurl = $userpicture->get_url($PAGE)->out(false);
709                 $usermodified = new stdclass();
710                 $usermodified->id = $discussion->usermodified;
711                 $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um', $picturefields);
712                 // Preserve the id (it can be overwritten due to the prefixed $picturefields).
713                 $usermodified->id = $discussion->usermodified;
714                 $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname);
716                 $userpicture = new user_picture($usermodified);
717                 $userpicture->size = 1; // Size f1.
718                 $discussion->usermodifiedpictureurl = $userpicture->get_url($PAGE)->out(false);
720                 // Rewrite embedded images URLs.
721                 list($discussion->message, $discussion->messageformat) =
722                     external_format_text($discussion->message, $discussion->messageformat,
723                                             $modcontext->id, 'mod_forum', 'post', $discussion->id);
725                 // List attachments.
726                 if (!empty($discussion->attachment)) {
727                     $discussion->attachments = array();
729                     $fs = get_file_storage();
730                     if ($files = $fs->get_area_files($modcontext->id, 'mod_forum', 'attachment',
731                                                         $discussion->id, "filename", false)) {
732                         foreach ($files as $file) {
733                             $filename = $file->get_filename();
735                             $discussion->attachments[] = array(
736                                 'filename' => $filename,
737                                 'mimetype' => $file->get_mimetype(),
738                                 'fileurl'  => file_encode_url($CFG->wwwroot.'/webservice/pluginfile.php',
739                                                 '/'.$modcontext->id.'/mod_forum/attachment/'.$discussion->id.'/'.$filename)
740                             );
741                         }
742                     }
743                 }
745                 $discussions[] = $discussion;
746             }
747         }
749         $result = array();
750         $result['discussions'] = $discussions;
751         $result['warnings'] = $warnings;
752         return $result;
754     }
756     /**
757      * Describes the get_forum_discussions_paginated return value.
758      *
759      * @return external_single_structure
760      * @since Moodle 2.8
761      */
762     public static function get_forum_discussions_paginated_returns() {
763         return new external_single_structure(
764             array(
765                 'discussions' => new external_multiple_structure(
766                         new external_single_structure(
767                             array(
768                                 'id' => new external_value(PARAM_INT, 'Post id'),
769                                 'name' => new external_value(PARAM_TEXT, 'Discussion name'),
770                                 'groupid' => new external_value(PARAM_INT, 'Group id'),
771                                 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
772                                 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
773                                 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
774                                 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
775                                 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
776                                 'parent' => new external_value(PARAM_INT, 'Parent id'),
777                                 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
778                                 'created' => new external_value(PARAM_INT, 'Creation time'),
779                                 'modified' => new external_value(PARAM_INT, 'Time modified'),
780                                 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
781                                 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
782                                 'message' => new external_value(PARAM_RAW, 'The post message'),
783                                 'messageformat' => new external_format_value('message'),
784                                 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
785                                 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
786                                 'attachments' => new external_multiple_structure(
787                                     new external_single_structure(
788                                         array (
789                                             'filename' => new external_value(PARAM_FILE, 'file name'),
790                                             'mimetype' => new external_value(PARAM_RAW, 'mime type'),
791                                             'fileurl'  => new external_value(PARAM_URL, 'file download url')
792                                         )
793                                     ), 'attachments', VALUE_OPTIONAL
794                                 ),
795                                 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
796                                 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
797                                 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
798                                 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
799                                 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
800                                 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
801                                 'numreplies' => new external_value(PARAM_TEXT, 'The number of replies in the discussion'),
802                                 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.')
803                             ), 'post'
804                         )
805                     ),
806                 'warnings' => new external_warnings()
807             )
808         );
809     }
811     /**
812      * Returns description of method parameters
813      *
814      * @return external_function_parameters
815      * @since Moodle 2.9
816      */
817     public static function view_forum_parameters() {
818         return new external_function_parameters(
819             array(
820                 'forumid' => new external_value(PARAM_INT, 'forum instance id')
821             )
822         );
823     }
825     /**
826      * Trigger the course module viewed event and update the module completion status.
827      *
828      * @param int $forumid the forum instance id
829      * @return array of warnings and status result
830      * @since Moodle 2.9
831      * @throws moodle_exception
832      */
833     public static function view_forum($forumid) {
834         global $DB, $CFG;
835         require_once($CFG->dirroot . "/mod/forum/lib.php");
837         $params = self::validate_parameters(self::view_forum_parameters(),
838                                             array(
839                                                 'forumid' => $forumid
840                                             ));
841         $warnings = array();
843         // Request and permission validation.
844         $forum = $DB->get_record('forum', array('id' => $params['forumid']), 'id', MUST_EXIST);
845         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
847         $context = context_module::instance($cm->id);
848         self::validate_context($context);
850         require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum');
852         // Call the forum/lib API.
853         forum_view($forum, $course, $cm, $context);
855         $result = array();
856         $result['status'] = true;
857         $result['warnings'] = $warnings;
858         return $result;
859     }
861     /**
862      * Returns description of method result value
863      *
864      * @return external_description
865      * @since Moodle 2.9
866      */
867     public static function view_forum_returns() {
868         return new external_single_structure(
869             array(
870                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
871                 'warnings' => new external_warnings()
872             )
873         );
874     }
876     /**
877      * Returns description of method parameters
878      *
879      * @return external_function_parameters
880      * @since Moodle 2.9
881      */
882     public static function view_forum_discussion_parameters() {
883         return new external_function_parameters(
884             array(
885                 'discussionid' => new external_value(PARAM_INT, 'discussion id')
886             )
887         );
888     }
890     /**
891      * Trigger the discussion viewed event.
892      *
893      * @param int $discussionid the discussion id
894      * @return array of warnings and status result
895      * @since Moodle 2.9
896      * @throws moodle_exception
897      */
898     public static function view_forum_discussion($discussionid) {
899         global $DB, $CFG;
900         require_once($CFG->dirroot . "/mod/forum/lib.php");
902         $params = self::validate_parameters(self::view_forum_discussion_parameters(),
903                                             array(
904                                                 'discussionid' => $discussionid
905                                             ));
906         $warnings = array();
908         $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST);
909         $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
910         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
912         // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
913         $modcontext = context_module::instance($cm->id);
914         self::validate_context($modcontext);
916         require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
918         // Call the forum/lib API.
919         forum_discussion_view($modcontext, $forum, $discussion);
921         $result = array();
922         $result['status'] = true;
923         $result['warnings'] = $warnings;
924         return $result;
925     }
927     /**
928      * Returns description of method result value
929      *
930      * @return external_description
931      * @since Moodle 2.9
932      */
933     public static function view_forum_discussion_returns() {
934         return new external_single_structure(
935             array(
936                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
937                 'warnings' => new external_warnings()
938             )
939         );
940     }
942     /**
943      * Returns description of method parameters
944      *
945      * @return external_function_parameters
946      * @since Moodle 3.0
947      */
948     public static function add_discussion_post_parameters() {
949         return new external_function_parameters(
950             array(
951                 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to
952                                                 (can be the initial discussion post'),
953                 'subject' => new external_value(PARAM_TEXT, 'new post subject'),
954                 'message' => new external_value(PARAM_RAW, 'new post message (only html format allowed)'),
955                 'options' => new external_multiple_structure (
956                     new external_single_structure(
957                         array(
958                             'name' => new external_value(PARAM_ALPHANUM,
959                                         'The allowed keys (value format) are:
960                                         discussionsubscribe (bool); subscribe to the discussion?, default to true
961                             '),
962                             'value' => new external_value(PARAM_RAW, 'the value of the option,
963                                                             this param is validated in the external function.'
964                         )
965                     )
966                 ), 'Options', VALUE_DEFAULT, array())
967             )
968         );
969     }
971     /**
972      * Create new posts into an existing discussion.
973      *
974      * @param int $postid the post id we are going to reply to
975      * @param string $subject new post subject
976      * @param string $message new post message (only html format allowed)
977      * @param array $options optional settings
978      * @return array of warnings and the new post id
979      * @since Moodle 3.0
980      * @throws moodle_exception
981      */
982     public static function add_discussion_post($postid, $subject, $message, $options = array()) {
983         global $DB, $CFG, $USER;
984         require_once($CFG->dirroot . "/mod/forum/lib.php");
986         $params = self::validate_parameters(self::add_discussion_post_parameters(),
987                                             array(
988                                                 'postid' => $postid,
989                                                 'subject' => $subject,
990                                                 'message' => $message,
991                                                 'options' => $options
992                                             ));
993         // Validate options.
994         $options = array(
995             'discussionsubscribe' => true
996         );
997         foreach ($params['options'] as $option) {
998             $name = trim($option['name']);
999             switch ($name) {
1000                 case 'discussionsubscribe':
1001                     $value = clean_param($option['value'], PARAM_BOOL);
1002                     break;
1003                 default:
1004                     throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1005             }
1006             $options[$name] = $value;
1007         }
1009         $warnings = array();
1011         if (!$parent = forum_get_post_full($params['postid'])) {
1012             throw new moodle_exception('invalidparentpostid', 'forum');
1013         }
1015         if (!$discussion = $DB->get_record("forum_discussions", array("id" => $parent->discussion))) {
1016             throw new moodle_exception('notpartofdiscussion', 'forum');
1017         }
1019         // Request and permission validation.
1020         $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
1021         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1023         $context = context_module::instance($cm->id);
1024         self::validate_context($context);
1026         if (!forum_user_can_post($forum, $discussion, $USER, $cm, $course, $context)) {
1027             throw new moodle_exception('nopostforum', 'forum');
1028         }
1030         $thresholdwarning = forum_check_throttling($forum, $cm);
1031         forum_check_blocking_threshold($thresholdwarning);
1033         // Create the post.
1034         $post = new stdClass();
1035         $post->discussion = $discussion->id;
1036         $post->parent = $parent->id;
1037         $post->subject = $params['subject'];
1038         $post->message = $params['message'];
1039         $post->messageformat = FORMAT_HTML;   // Force formatting for now.
1040         $post->messagetrust = trusttext_trusted($context);
1041         $post->itemid = 0;
1043         if ($postid = forum_add_new_post($post, null)) {
1045             $post->id = $postid;
1047             // Trigger events and completion.
1048             $params = array(
1049                 'context' => $context,
1050                 'objectid' => $post->id,
1051                 'other' => array(
1052                     'discussionid' => $discussion->id,
1053                     'forumid' => $forum->id,
1054                     'forumtype' => $forum->type,
1055                 )
1056             );
1057             $event = \mod_forum\event\post_created::create($params);
1058             $event->add_record_snapshot('forum_posts', $post);
1059             $event->add_record_snapshot('forum_discussions', $discussion);
1060             $event->trigger();
1062             // Update completion state.
1063             $completion = new completion_info($course);
1064             if ($completion->is_enabled($cm) &&
1065                     ($forum->completionreplies || $forum->completionposts)) {
1066                 $completion->update_state($cm, COMPLETION_COMPLETE);
1067             }
1069             $settings = new stdClass();
1070             $settings->discussionsubscribe = $options['discussionsubscribe'];
1071             forum_post_subscription($settings, $forum, $discussion);
1072         } else {
1073             throw new moodle_exception('couldnotadd', 'forum');
1074         }
1076         $result = array();
1077         $result['postid'] = $postid;
1078         $result['warnings'] = $warnings;
1079         return $result;
1080     }
1082     /**
1083      * Returns description of method result value
1084      *
1085      * @return external_description
1086      * @since Moodle 3.0
1087      */
1088     public static function add_discussion_post_returns() {
1089         return new external_single_structure(
1090             array(
1091                 'postid' => new external_value(PARAM_INT, 'new post id'),
1092                 'warnings' => new external_warnings()
1093             )
1094         );
1095     }
1097     /**
1098      * Returns description of method parameters
1099      *
1100      * @return external_function_parameters
1101      * @since Moodle 3.0
1102      */
1103     public static function add_discussion_parameters() {
1104         return new external_function_parameters(
1105             array(
1106                 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1107                 'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'),
1108                 'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'),
1109                 'groupid' => new external_value(PARAM_INT, 'The group, default to -1', VALUE_DEFAULT, -1),
1110                 'options' => new external_multiple_structure (
1111                     new external_single_structure(
1112                         array(
1113                             'name' => new external_value(PARAM_ALPHANUM,
1114                                         'The allowed keys (value format) are:
1115                                         discussionsubscribe (bool); subscribe to the discussion?, default to true
1116                             '),
1117                             'value' => new external_value(PARAM_RAW, 'The value of the option,
1118                                                             This param is validated in the external function.'
1119                         )
1120                     )
1121                 ), 'Options', VALUE_DEFAULT, array())
1122             )
1123         );
1124     }
1126     /**
1127      * Add a new discussion into an existing forum.
1128      *
1129      * @param int $forumid the forum instance id
1130      * @param string $subject new discussion subject
1131      * @param string $message new discussion message (only html format allowed)
1132      * @param int $groupid the user course group
1133      * @param array $options optional settings
1134      * @return array of warnings and the new discussion id
1135      * @since Moodle 3.0
1136      * @throws moodle_exception
1137      */
1138     public static function add_discussion($forumid, $subject, $message, $groupid = -1, $options = array()) {
1139         global $DB, $CFG;
1140         require_once($CFG->dirroot . "/mod/forum/lib.php");
1142         $params = self::validate_parameters(self::add_discussion_parameters(),
1143                                             array(
1144                                                 'forumid' => $forumid,
1145                                                 'subject' => $subject,
1146                                                 'message' => $message,
1147                                                 'groupid' => $groupid,
1148                                                 'options' => $options
1149                                             ));
1150         // Validate options.
1151         $options = array(
1152             'discussionsubscribe' => true
1153         );
1154         foreach ($params['options'] as $option) {
1155             $name = trim($option['name']);
1156             switch ($name) {
1157                 case 'discussionsubscribe':
1158                     $value = clean_param($option['value'], PARAM_BOOL);
1159                     break;
1160                 default:
1161                     throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1162             }
1163             $options[$name] = $value;
1164         }
1166         $warnings = array();
1168         // Request and permission validation.
1169         $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1170         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1172         $context = context_module::instance($cm->id);
1173         self::validate_context($context);
1175         // Normalize group.
1176         if (!groups_get_activity_groupmode($cm)) {
1177             // Groups not supported, force to -1.
1178             $groupid = -1;
1179         } else {
1180             // Check if we receive the default or and empty value for groupid,
1181             // in this case, get the group for the user in the activity.
1182             if ($groupid === -1 or empty($params['groupid'])) {
1183                 $groupid = groups_get_activity_group($cm);
1184             } else {
1185                 // Here we rely in the group passed, forum_user_can_post_discussion will validate the group.
1186                 $groupid = $params['groupid'];
1187             }
1188         }
1190         if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) {
1191             throw new moodle_exception('cannotcreatediscussion', 'forum');
1192         }
1194         $thresholdwarning = forum_check_throttling($forum, $cm);
1195         forum_check_blocking_threshold($thresholdwarning);
1197         // Create the discussion.
1198         $discussion = new stdClass();
1199         $discussion->course = $course->id;
1200         $discussion->forum = $forum->id;
1201         $discussion->message = $params['message'];
1202         $discussion->messageformat = FORMAT_HTML;   // Force formatting for now.
1203         $discussion->messagetrust = trusttext_trusted($context);
1204         $discussion->itemid = 0;
1205         $discussion->groupid = $groupid;
1206         $discussion->mailnow = 0;
1207         $discussion->subject = $params['subject'];
1208         $discussion->name = $discussion->subject;
1209         $discussion->timestart = 0;
1210         $discussion->timeend = 0;
1212         if ($discussionid = forum_add_discussion($discussion)) {
1214             $discussion->id = $discussionid;
1216             // Trigger events and completion.
1218             $params = array(
1219                 'context' => $context,
1220                 'objectid' => $discussion->id,
1221                 'other' => array(
1222                     'forumid' => $forum->id,
1223                 )
1224             );
1225             $event = \mod_forum\event\discussion_created::create($params);
1226             $event->add_record_snapshot('forum_discussions', $discussion);
1227             $event->trigger();
1229             $completion = new completion_info($course);
1230             if ($completion->is_enabled($cm) &&
1231                     ($forum->completiondiscussions || $forum->completionposts)) {
1232                 $completion->update_state($cm, COMPLETION_COMPLETE);
1233             }
1235             $settings = new stdClass();
1236             $settings->discussionsubscribe = $options['discussionsubscribe'];
1237             forum_post_subscription($settings, $forum, $discussion);
1238         } else {
1239             throw new moodle_exception('couldnotadd', 'forum');
1240         }
1242         $result = array();
1243         $result['discussionid'] = $discussionid;
1244         $result['warnings'] = $warnings;
1245         return $result;
1246     }
1248     /**
1249      * Returns description of method result value
1250      *
1251      * @return external_description
1252      * @since Moodle 3.0
1253      */
1254     public static function add_discussion_returns() {
1255         return new external_single_structure(
1256             array(
1257                 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'),
1258                 'warnings' => new external_warnings()
1259             )
1260         );
1261     }