MDL-49612 forum: Fix some external_value creation in params declaration
[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         $courses = array();
64         if (empty($params['courseids'])) {
65             $courses = enrol_get_my_courses();
66             $params['courseids'] = array_keys($courses);
67         }
69         // Array to store the forums to return.
70         $arrforums = array();
71         $warnings = array();
73         // Ensure there are courseids to loop through.
74         if (!empty($params['courseids'])) {
76             list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
78             // Get the forums in this course. This function checks users visibility permissions.
79             $forums = get_all_instances_in_courses("forum", $courses);
80             foreach ($forums as $forum) {
82                 $course = $courses[$forum->course];
83                 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
84                 $context = context_module::instance($cm->id);
86                 // Skip forums we are not allowed to see discussions.
87                 if (!has_capability('mod/forum:viewdiscussion', $context)) {
88                     continue;
89                 }
91                 $forum->name = external_format_string($forum->name, $context->id);
92                 // Format the intro before being returning using the format setting.
93                 list($forum->intro, $forum->introformat) = external_format_text($forum->intro, $forum->introformat,
94                                                                                 $context->id, 'mod_forum', 'intro', 0);
95                 // Discussions count. This function does static request cache.
96                 $forum->numdiscussions = forum_count_discussions($forum, $cm, $course);
97                 $forum->cmid = $forum->coursemodule;
98                 $forum->cancreatediscussions = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
100                 // Add the forum to the array to return.
101                 $arrforums[$forum->id] = $forum;
102             }
103         }
105         return $arrforums;
106     }
108     /**
109      * Describes the get_forum return value.
110      *
111      * @return external_single_structure
112      * @since Moodle 2.5
113      */
114      public static function get_forums_by_courses_returns() {
115         return new external_multiple_structure(
116             new external_single_structure(
117                 array(
118                     'id' => new external_value(PARAM_INT, 'Forum id'),
119                     'course' => new external_value(PARAM_INT, 'Course id'),
120                     'type' => new external_value(PARAM_TEXT, 'The forum type'),
121                     'name' => new external_value(PARAM_RAW, 'Forum name'),
122                     'intro' => new external_value(PARAM_RAW, 'The forum intro'),
123                     'introformat' => new external_format_value('intro'),
124                     'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
125                     'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
126                     'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
127                     'scale' => new external_value(PARAM_INT, 'Scale'),
128                     'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'),
129                     'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'),
130                     'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'),
131                     'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'),
132                     'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'),
133                     'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'),
134                     'timemodified' => new external_value(PARAM_INT, 'Time modified'),
135                     'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'),
136                     'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'),
137                     'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'),
138                     'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'),
139                     'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'),
140                     'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'),
141                     'cmid' => new external_value(PARAM_INT, 'Course module id'),
142                     'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL),
143                     'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL),
144                 ), 'forum'
145             )
146         );
147     }
149     /**
150      * Describes the parameters for get_forum_discussions.
151      *
152      * @return external_external_function_parameters
153      * @since Moodle 2.5
154      * @deprecated Moodle 2.8 MDL-46458 - Please do not call this function any more.
155      * @see get_forum_discussions_paginated
156      */
157     public static function get_forum_discussions_parameters() {
158         return new external_function_parameters (
159             array(
160                 'forumids' => new external_multiple_structure(new external_value(PARAM_INT, 'forum ID',
161                         VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Forum IDs', VALUE_REQUIRED),
162                 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
163                 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
164             )
165         );
166     }
168     /**
169      * Returns a list of forum discussions as well as a summary of the discussion
170      * in a provided list of forums.
171      *
172      * @param array $forumids the forum ids
173      * @param int $limitfrom limit from SQL data
174      * @param int $limitnum limit number SQL data
175      *
176      * @return array the forum discussion details
177      * @since Moodle 2.5
178      * @deprecated Moodle 2.8 MDL-46458 - Please do not call this function any more.
179      * @see get_forum_discussions_paginated
180      */
181     public static function get_forum_discussions($forumids, $limitfrom = 0, $limitnum = 0) {
182         global $CFG, $DB, $USER;
184         require_once($CFG->dirroot . "/mod/forum/lib.php");
186         // Validate the parameter.
187         $params = self::validate_parameters(self::get_forum_discussions_parameters(),
188             array(
189                 'forumids'  => $forumids,
190                 'limitfrom' => $limitfrom,
191                 'limitnum'  => $limitnum,
192             ));
193         $forumids  = $params['forumids'];
194         $limitfrom = $params['limitfrom'];
195         $limitnum  = $params['limitnum'];
197         // Array to store the forum discussions to return.
198         $arrdiscussions = array();
199         // Keep track of the users we have looked up in the DB.
200         $arrusers = array();
202         // Loop through them.
203         foreach ($forumids as $id) {
204             // Get the forum object.
205             $forum = $DB->get_record('forum', array('id' => $id), '*', MUST_EXIST);
206             $course = get_course($forum->course);
208             $modinfo = get_fast_modinfo($course);
209             $forums  = $modinfo->get_instances_of('forum');
210             $cm = $forums[$forum->id];
212             // Get the module context.
213             $modcontext = context_module::instance($cm->id);
215             // Validate the context.
216             self::validate_context($modcontext);
218             require_capability('mod/forum:viewdiscussion', $modcontext);
220             // Get the discussions for this forum.
221             $params = array();
223             $groupselect = "";
224             $groupmode = groups_get_activity_groupmode($cm, $course);
226             if ($groupmode and $groupmode != VISIBLEGROUPS and !has_capability('moodle/site:accessallgroups', $modcontext)) {
227                 // Get all the discussions from all the groups this user belongs to.
228                 $usergroups = groups_get_user_groups($course->id);
229                 if (!empty($usergroups['0'])) {
230                     list($sql, $params) = $DB->get_in_or_equal($usergroups['0']);
231                     $groupselect = "AND (groupid $sql OR groupid = -1)";
232                 }
233             }
234             array_unshift($params, $id);
235             $select = "forum = ? $groupselect";
237             if ($discussions = $DB->get_records_select('forum_discussions', $select, $params, 'timemodified DESC', '*',
238                                                             $limitfrom, $limitnum)) {
240                 // Check if they can view full names.
241                 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
242                 // Get the unreads array, this takes a forum id and returns data for all discussions.
243                 $unreads = array();
244                 if ($cantrack = forum_tp_can_track_forums($forum)) {
245                     if ($forumtracked = forum_tp_is_tracked($forum)) {
246                         $unreads = forum_get_discussions_unread($cm);
247                     }
248                 }
249                 // The forum function returns the replies for all the discussions in a given forum.
250                 $replies = forum_count_discussion_replies($id);
252                 foreach ($discussions as $discussion) {
253                     // This function checks capabilities, timed discussions, groups and qanda forums posting.
254                     if (!forum_user_can_see_discussion($forum, $discussion, $modcontext)) {
255                         continue;
256                     }
258                     $usernamefields = user_picture::fields();
259                     // If we don't have the users details then perform DB call.
260                     if (empty($arrusers[$discussion->userid])) {
261                         $arrusers[$discussion->userid] = $DB->get_record('user', array('id' => $discussion->userid),
262                                 $usernamefields, MUST_EXIST);
263                     }
264                     // Get the subject.
265                     $subject = $DB->get_field('forum_posts', 'subject', array('id' => $discussion->firstpost), MUST_EXIST);
266                     // Create object to return.
267                     $return = new stdClass();
268                     $return->id = (int) $discussion->id;
269                     $return->course = $discussion->course;
270                     $return->forum = $discussion->forum;
271                     $return->name = $discussion->name;
272                     $return->userid = $discussion->userid;
273                     $return->groupid = $discussion->groupid;
274                     $return->assessed = $discussion->assessed;
275                     $return->timemodified = (int) $discussion->timemodified;
276                     $return->usermodified = $discussion->usermodified;
277                     $return->timestart = $discussion->timestart;
278                     $return->timeend = $discussion->timeend;
279                     $return->firstpost = (int) $discussion->firstpost;
280                     $return->firstuserfullname = fullname($arrusers[$discussion->userid], $canviewfullname);
281                     $return->firstuserimagealt = $arrusers[$discussion->userid]->imagealt;
282                     $return->firstuserpicture = $arrusers[$discussion->userid]->picture;
283                     $return->firstuseremail = $arrusers[$discussion->userid]->email;
284                     $return->subject = $subject;
285                     $return->numunread = '';
286                     if ($cantrack && $forumtracked) {
287                         if (isset($unreads[$discussion->id])) {
288                             $return->numunread = (int) $unreads[$discussion->id];
289                         }
290                     }
291                     // Check if there are any replies to this discussion.
292                     if (!empty($replies[$discussion->id])) {
293                          $return->numreplies = (int) $replies[$discussion->id]->replies;
294                          $return->lastpost = (int) $replies[$discussion->id]->lastpostid;
295                     } else { // No replies, so the last post will be the first post.
296                         $return->numreplies = 0;
297                         $return->lastpost = (int) $discussion->firstpost;
298                     }
299                     // Get the last post as well as the user who made it.
300                     $lastpost = $DB->get_record('forum_posts', array('id' => $return->lastpost), '*', MUST_EXIST);
301                     if (empty($arrusers[$lastpost->userid])) {
302                         $arrusers[$lastpost->userid] = $DB->get_record('user', array('id' => $lastpost->userid),
303                                 $usernamefields, MUST_EXIST);
304                     }
305                     $return->lastuserid = $lastpost->userid;
306                     $return->lastuserfullname = fullname($arrusers[$lastpost->userid], $canviewfullname);
307                     $return->lastuserimagealt = $arrusers[$lastpost->userid]->imagealt;
308                     $return->lastuserpicture = $arrusers[$lastpost->userid]->picture;
309                     $return->lastuseremail = $arrusers[$lastpost->userid]->email;
310                     // Add the discussion statistics to the array to return.
311                     $arrdiscussions[$return->id] = (array) $return;
312                 }
313             }
314         }
316         return $arrdiscussions;
317     }
319     /**
320      * Describes the get_forum_discussions return value.
321      *
322      * @return external_single_structure
323      * @since Moodle 2.5
324      * @deprecated Moodle 2.8 MDL-46458 - Please do not call this function any more.
325      * @see get_forum_discussions_paginated
326      */
327      public static function get_forum_discussions_returns() {
328         return new external_multiple_structure(
329             new external_single_structure(
330                 array(
331                     'id' => new external_value(PARAM_INT, 'Forum id'),
332                     'course' => new external_value(PARAM_INT, 'Course id'),
333                     'forum' => new external_value(PARAM_INT, 'The forum id'),
334                     'name' => new external_value(PARAM_TEXT, 'Discussion name'),
335                     'userid' => new external_value(PARAM_INT, 'User id'),
336                     'groupid' => new external_value(PARAM_INT, 'Group id'),
337                     'assessed' => new external_value(PARAM_INT, 'Is this assessed?'),
338                     'timemodified' => new external_value(PARAM_INT, 'Time modified'),
339                     'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
340                     'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
341                     'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
342                     'firstpost' => new external_value(PARAM_INT, 'The first post in the discussion'),
343                     'firstuserfullname' => new external_value(PARAM_TEXT, 'The discussion creators fullname'),
344                     'firstuserimagealt' => new external_value(PARAM_TEXT, 'The discussion creators image alt'),
345                     'firstuserpicture' => new external_value(PARAM_INT, 'The discussion creators profile picture'),
346                     'firstuseremail' => new external_value(PARAM_TEXT, 'The discussion creators email'),
347                     'subject' => new external_value(PARAM_TEXT, 'The discussion subject'),
348                     'numreplies' => new external_value(PARAM_TEXT, 'The number of replies in the discussion'),
349                     'numunread' => new external_value(PARAM_TEXT, 'The number of unread posts, blank if this value is
350                         not available due to forum settings.'),
351                     'lastpost' => new external_value(PARAM_INT, 'The id of the last post in the discussion'),
352                     'lastuserid' => new external_value(PARAM_INT, 'The id of the user who made the last post'),
353                     'lastuserfullname' => new external_value(PARAM_TEXT, 'The last person to posts fullname'),
354                     'lastuserimagealt' => new external_value(PARAM_TEXT, 'The last person to posts image alt'),
355                     'lastuserpicture' => new external_value(PARAM_INT, 'The last person to posts profile picture'),
356                     'lastuseremail' => new external_value(PARAM_TEXT, 'The last person to posts email'),
357                 ), 'discussion'
358             )
359         );
360     }
362     /**
363      * Describes the parameters for get_forum_discussion_posts.
364      *
365      * @return external_external_function_parameters
366      * @since Moodle 2.7
367      */
368     public static function get_forum_discussion_posts_parameters() {
369         return new external_function_parameters (
370             array(
371                 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED),
372                 'sortby' => new external_value(PARAM_ALPHA,
373                     'sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
374                 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
375             )
376         );
377     }
379     /**
380      * Returns a list of forum posts for a discussion
381      *
382      * @param int $discussionid the post ids
383      * @param string $sortby sort by this element (id, created or modified)
384      * @param string $sortdirection sort direction: ASC or DESC
385      *
386      * @return array the forum post details
387      * @since Moodle 2.7
388      */
389     public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") {
390         global $CFG, $DB, $USER, $PAGE;
392         $posts = array();
393         $warnings = array();
395         // Validate the parameter.
396         $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(),
397             array(
398                 'discussionid' => $discussionid,
399                 'sortby' => $sortby,
400                 'sortdirection' => $sortdirection));
402         // Compact/extract functions are not recommended.
403         $discussionid   = $params['discussionid'];
404         $sortby         = $params['sortby'];
405         $sortdirection  = $params['sortdirection'];
407         $sortallowedvalues = array('id', 'created', 'modified');
408         if (!in_array($sortby, $sortallowedvalues)) {
409             throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
410                 'allowed values are: ' . implode(',', $sortallowedvalues));
411         }
413         $sortdirection = strtoupper($sortdirection);
414         $directionallowedvalues = array('ASC', 'DESC');
415         if (!in_array($sortdirection, $directionallowedvalues)) {
416             throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
417                 'allowed values are: ' . implode(',', $directionallowedvalues));
418         }
420         $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST);
421         $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
422         $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
423         $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
425         // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
426         $modcontext = context_module::instance($cm->id);
427         self::validate_context($modcontext);
429         // This require must be here, see mod/forum/discuss.php.
430         require_once($CFG->dirroot . "/mod/forum/lib.php");
432         // Check they have the view forum capability.
433         require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
435         if (! $post = forum_get_post_full($discussion->firstpost)) {
436             throw new moodle_exception('notexists', 'forum');
437         }
439         // This function check groups, qanda, timed discussions, etc.
440         if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
441             throw new moodle_exception('noviewdiscussionspermission', 'forum');
442         }
444         $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
446         // We will add this field in the response.
447         $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
449         $forumtracked = forum_tp_is_tracked($forum);
451         $sort = 'p.' . $sortby . ' ' . $sortdirection;
452         $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
454         foreach ($allposts as $post) {
456             if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
457                 $warning = array();
458                 $warning['item'] = 'post';
459                 $warning['itemid'] = $post->id;
460                 $warning['warningcode'] = '1';
461                 $warning['message'] = 'You can\'t see this post';
462                 $warnings[] = $warning;
463                 continue;
464             }
466             // Function forum_get_all_discussion_posts adds postread field.
467             // Note that the value returned can be a boolean or an integer. The WS expects a boolean.
468             if (empty($post->postread)) {
469                 $post->postread = false;
470             } else {
471                 $post->postread = true;
472             }
474             $post->canreply = $canreply;
475             if (!empty($post->children)) {
476                 $post->children = array_keys($post->children);
477             } else {
478                 $post->children = array();
479             }
481             $user = new stdclass();
482             $user->id = $post->userid;
483             $user = username_load_fields_from_object($user, $post, null, array('picture', 'imagealt', 'email'));
484             $post->userfullname = fullname($user, $canviewfullname);
486             $userpicture = new user_picture($user);
487             $userpicture->size = 1; // Size f1.
488             $post->userpictureurl = $userpicture->get_url($PAGE)->out(false);
490             // Rewrite embedded images URLs.
491             list($post->message, $post->messageformat) =
492                 external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_forum', 'post', $post->id);
494             // List attachments.
495             if (!empty($post->attachment)) {
496                 $post->attachments = array();
498                 $fs = get_file_storage();
499                 if ($files = $fs->get_area_files($modcontext->id, 'mod_forum', 'attachment', $post->id, "filename", false)) {
500                     foreach ($files as $file) {
501                         $filename = $file->get_filename();
502                         $fileurl = moodle_url::make_webservice_pluginfile_url(
503                                         $modcontext->id, 'mod_forum', 'attachment', $post->id, '/', $filename);
505                         $post->attachments[] = array(
506                             'filename' => $filename,
507                             'mimetype' => $file->get_mimetype(),
508                             'fileurl'  => $fileurl->out(false)
509                         );
510                     }
511                 }
512             }
514             $posts[] = $post;
515         }
517         $result = array();
518         $result['posts'] = $posts;
519         $result['warnings'] = $warnings;
520         return $result;
521     }
523     /**
524      * Describes the get_forum_discussion_posts return value.
525      *
526      * @return external_single_structure
527      * @since Moodle 2.7
528      */
529     public static function get_forum_discussion_posts_returns() {
530         return new external_single_structure(
531             array(
532                 'posts' => new external_multiple_structure(
533                         new external_single_structure(
534                             array(
535                                 'id' => new external_value(PARAM_INT, 'Post id'),
536                                 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
537                                 'parent' => new external_value(PARAM_INT, 'Parent id'),
538                                 'userid' => new external_value(PARAM_INT, 'User id'),
539                                 'created' => new external_value(PARAM_INT, 'Creation time'),
540                                 'modified' => new external_value(PARAM_INT, 'Time modified'),
541                                 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
542                                 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
543                                 'message' => new external_value(PARAM_RAW, 'The post message'),
544                                 'messageformat' => new external_format_value('message'),
545                                 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
546                                 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
547                                 'attachments' => new external_multiple_structure(
548                                     new external_single_structure(
549                                         array (
550                                             'filename' => new external_value(PARAM_FILE, 'file name'),
551                                             'mimetype' => new external_value(PARAM_RAW, 'mime type'),
552                                             'fileurl'  => new external_value(PARAM_URL, 'file download url')
553                                         )
554                                     ), 'attachments', VALUE_OPTIONAL
555                                 ),
556                                 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
557                                 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
558                                 'children' => new external_multiple_structure(new external_value(PARAM_INT, 'children post id')),
559                                 'canreply' => new external_value(PARAM_BOOL, 'The user can reply to posts?'),
560                                 'postread' => new external_value(PARAM_BOOL, 'The post was read'),
561                                 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
562                                 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL)
563                             ), 'post'
564                         )
565                     ),
566                 'warnings' => new external_warnings()
567             )
568         );
569     }
571     /**
572      * Describes the parameters for get_forum_discussions_paginated.
573      *
574      * @return external_external_function_parameters
575      * @since Moodle 2.8
576      */
577     public static function get_forum_discussions_paginated_parameters() {
578         return new external_function_parameters (
579             array(
580                 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
581                 'sortby' => new external_value(PARAM_ALPHA,
582                     'sort by this element: id, timemodified, timestart or timeend', VALUE_DEFAULT, 'timemodified'),
583                 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
584                 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
585                 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
586             )
587         );
588     }
590     /**
591      * Returns a list of forum discussions optionally sorted and paginated.
592      *
593      * @param int $forumid the forum instance id
594      * @param string $sortby sort by this element (id, timemodified, timestart or timeend)
595      * @param string $sortdirection sort direction: ASC or DESC
596      * @param int $page page number
597      * @param int $perpage items per page
598      *
599      * @return array the forum discussion details including warnings
600      * @since Moodle 2.8
601      */
602     public static function get_forum_discussions_paginated($forumid, $sortby = 'timemodified', $sortdirection = 'DESC',
603                                                     $page = -1, $perpage = 0) {
604         global $CFG, $DB, $USER, $PAGE;
606         require_once($CFG->dirroot . "/mod/forum/lib.php");
608         $warnings = array();
609         $discussions = array();
611         $params = self::validate_parameters(self::get_forum_discussions_paginated_parameters(),
612             array(
613                 'forumid' => $forumid,
614                 'sortby' => $sortby,
615                 'sortdirection' => $sortdirection,
616                 'page' => $page,
617                 'perpage' => $perpage
618             )
619         );
621         // Compact/extract functions are not recommended.
622         $forumid        = $params['forumid'];
623         $sortby         = $params['sortby'];
624         $sortdirection  = $params['sortdirection'];
625         $page           = $params['page'];
626         $perpage        = $params['perpage'];
628         $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend');
629         if (!in_array($sortby, $sortallowedvalues)) {
630             throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
631                 'allowed values are: ' . implode(',', $sortallowedvalues));
632         }
634         $sortdirection = strtoupper($sortdirection);
635         $directionallowedvalues = array('ASC', 'DESC');
636         if (!in_array($sortdirection, $directionallowedvalues)) {
637             throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
638                 'allowed values are: ' . implode(',', $directionallowedvalues));
639         }
641         $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
642         $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
643         $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
645         // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
646         $modcontext = context_module::instance($cm->id);
647         self::validate_context($modcontext);
649         // Check they have the view forum capability.
650         require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
652         $sort = 'd.pinned DESC, d.' . $sortby . ' ' . $sortdirection;
653         $alldiscussions = forum_get_discussions($cm, $sort, true, -1, -1, true, $page, $perpage, FORUM_POSTS_ALL_USER_GROUPS);
655         if ($alldiscussions) {
656             $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
658             // Get the unreads array, this takes a forum id and returns data for all discussions.
659             $unreads = array();
660             if ($cantrack = forum_tp_can_track_forums($forum)) {
661                 if ($forumtracked = forum_tp_is_tracked($forum)) {
662                     $unreads = forum_get_discussions_unread($cm);
663                 }
664             }
665             // The forum function returns the replies for all the discussions in a given forum.
666             $replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage);
668             foreach ($alldiscussions as $discussion) {
670                 // This function checks for qanda forums.
671                 // Note that the forum_get_discussions returns as id the post id, not the discussion id so we need to do this.
672                 $discussionrec = clone $discussion;
673                 $discussionrec->id = $discussion->discussion;
674                 if (!forum_user_can_see_discussion($forum, $discussionrec, $modcontext)) {
675                     $warning = array();
676                     // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
677                     $warning['item'] = 'post';
678                     $warning['itemid'] = $discussion->id;
679                     $warning['warningcode'] = '1';
680                     $warning['message'] = 'You can\'t see this discussion';
681                     $warnings[] = $warning;
682                     continue;
683                 }
685                 $discussion->numunread = 0;
686                 if ($cantrack && $forumtracked) {
687                     if (isset($unreads[$discussion->discussion])) {
688                         $discussion->numunread = (int) $unreads[$discussion->discussion];
689                     }
690                 }
692                 $discussion->numreplies = 0;
693                 if (!empty($replies[$discussion->discussion])) {
694                     $discussion->numreplies = (int) $replies[$discussion->discussion]->replies;
695                 }
697                 $picturefields = explode(',', user_picture::fields());
699                 // Load user objects from the results of the query.
700                 $user = new stdclass();
701                 $user->id = $discussion->userid;
702                 $user = username_load_fields_from_object($user, $discussion, null, $picturefields);
703                 // Preserve the id, it can be modified by username_load_fields_from_object.
704                 $user->id = $discussion->userid;
705                 $discussion->userfullname = fullname($user, $canviewfullname);
707                 $userpicture = new user_picture($user);
708                 $userpicture->size = 1; // Size f1.
709                 $discussion->userpictureurl = $userpicture->get_url($PAGE)->out(false);
711                 $usermodified = new stdclass();
712                 $usermodified->id = $discussion->usermodified;
713                 $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um', $picturefields);
714                 // Preserve the id (it can be overwritten due to the prefixed $picturefields).
715                 $usermodified->id = $discussion->usermodified;
716                 $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname);
718                 $userpicture = new user_picture($usermodified);
719                 $userpicture->size = 1; // Size f1.
720                 $discussion->usermodifiedpictureurl = $userpicture->get_url($PAGE)->out(false);
722                 // Rewrite embedded images URLs.
723                 list($discussion->message, $discussion->messageformat) =
724                     external_format_text($discussion->message, $discussion->messageformat,
725                                             $modcontext->id, 'mod_forum', 'post', $discussion->id);
727                 // List attachments.
728                 if (!empty($discussion->attachment)) {
729                     $discussion->attachments = array();
731                     $fs = get_file_storage();
732                     if ($files = $fs->get_area_files($modcontext->id, 'mod_forum', 'attachment',
733                                                         $discussion->id, "filename", false)) {
734                         foreach ($files as $file) {
735                             $filename = $file->get_filename();
737                             $discussion->attachments[] = array(
738                                 'filename' => $filename,
739                                 'mimetype' => $file->get_mimetype(),
740                                 'fileurl'  => file_encode_url($CFG->wwwroot.'/webservice/pluginfile.php',
741                                                 '/'.$modcontext->id.'/mod_forum/attachment/'.$discussion->id.'/'.$filename)
742                             );
743                         }
744                     }
745                 }
747                 $discussions[] = $discussion;
748             }
749         }
751         $result = array();
752         $result['discussions'] = $discussions;
753         $result['warnings'] = $warnings;
754         return $result;
756     }
758     /**
759      * Describes the get_forum_discussions_paginated return value.
760      *
761      * @return external_single_structure
762      * @since Moodle 2.8
763      */
764     public static function get_forum_discussions_paginated_returns() {
765         return new external_single_structure(
766             array(
767                 'discussions' => new external_multiple_structure(
768                         new external_single_structure(
769                             array(
770                                 'id' => new external_value(PARAM_INT, 'Post id'),
771                                 'name' => new external_value(PARAM_TEXT, 'Discussion name'),
772                                 'groupid' => new external_value(PARAM_INT, 'Group id'),
773                                 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
774                                 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
775                                 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
776                                 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
777                                 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
778                                 'parent' => new external_value(PARAM_INT, 'Parent id'),
779                                 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
780                                 'created' => new external_value(PARAM_INT, 'Creation time'),
781                                 'modified' => new external_value(PARAM_INT, 'Time modified'),
782                                 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
783                                 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
784                                 'message' => new external_value(PARAM_RAW, 'The post message'),
785                                 'messageformat' => new external_format_value('message'),
786                                 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
787                                 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
788                                 'attachments' => new external_multiple_structure(
789                                     new external_single_structure(
790                                         array (
791                                             'filename' => new external_value(PARAM_FILE, 'file name'),
792                                             'mimetype' => new external_value(PARAM_RAW, 'mime type'),
793                                             'fileurl'  => new external_value(PARAM_URL, 'file download url')
794                                         )
795                                     ), 'attachments', VALUE_OPTIONAL
796                                 ),
797                                 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
798                                 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
799                                 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
800                                 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
801                                 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
802                                 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
803                                 'numreplies' => new external_value(PARAM_TEXT, 'The number of replies in the discussion'),
804                                 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
805                                 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned')
806                             ), 'post'
807                         )
808                     ),
809                 'warnings' => new external_warnings()
810             )
811         );
812     }
814     /**
815      * Returns description of method parameters
816      *
817      * @return external_function_parameters
818      * @since Moodle 2.9
819      */
820     public static function view_forum_parameters() {
821         return new external_function_parameters(
822             array(
823                 'forumid' => new external_value(PARAM_INT, 'forum instance id')
824             )
825         );
826     }
828     /**
829      * Trigger the course module viewed event and update the module completion status.
830      *
831      * @param int $forumid the forum instance id
832      * @return array of warnings and status result
833      * @since Moodle 2.9
834      * @throws moodle_exception
835      */
836     public static function view_forum($forumid) {
837         global $DB, $CFG;
838         require_once($CFG->dirroot . "/mod/forum/lib.php");
840         $params = self::validate_parameters(self::view_forum_parameters(),
841                                             array(
842                                                 'forumid' => $forumid
843                                             ));
844         $warnings = array();
846         // Request and permission validation.
847         $forum = $DB->get_record('forum', array('id' => $params['forumid']), 'id', MUST_EXIST);
848         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
850         $context = context_module::instance($cm->id);
851         self::validate_context($context);
853         require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum');
855         // Call the forum/lib API.
856         forum_view($forum, $course, $cm, $context);
858         $result = array();
859         $result['status'] = true;
860         $result['warnings'] = $warnings;
861         return $result;
862     }
864     /**
865      * Returns description of method result value
866      *
867      * @return external_description
868      * @since Moodle 2.9
869      */
870     public static function view_forum_returns() {
871         return new external_single_structure(
872             array(
873                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
874                 'warnings' => new external_warnings()
875             )
876         );
877     }
879     /**
880      * Returns description of method parameters
881      *
882      * @return external_function_parameters
883      * @since Moodle 2.9
884      */
885     public static function view_forum_discussion_parameters() {
886         return new external_function_parameters(
887             array(
888                 'discussionid' => new external_value(PARAM_INT, 'discussion id')
889             )
890         );
891     }
893     /**
894      * Trigger the discussion viewed event.
895      *
896      * @param int $discussionid the discussion id
897      * @return array of warnings and status result
898      * @since Moodle 2.9
899      * @throws moodle_exception
900      */
901     public static function view_forum_discussion($discussionid) {
902         global $DB, $CFG;
903         require_once($CFG->dirroot . "/mod/forum/lib.php");
905         $params = self::validate_parameters(self::view_forum_discussion_parameters(),
906                                             array(
907                                                 'discussionid' => $discussionid
908                                             ));
909         $warnings = array();
911         $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST);
912         $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
913         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
915         // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
916         $modcontext = context_module::instance($cm->id);
917         self::validate_context($modcontext);
919         require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
921         // Call the forum/lib API.
922         forum_discussion_view($modcontext, $forum, $discussion);
924         $result = array();
925         $result['status'] = true;
926         $result['warnings'] = $warnings;
927         return $result;
928     }
930     /**
931      * Returns description of method result value
932      *
933      * @return external_description
934      * @since Moodle 2.9
935      */
936     public static function view_forum_discussion_returns() {
937         return new external_single_structure(
938             array(
939                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
940                 'warnings' => new external_warnings()
941             )
942         );
943     }
945     /**
946      * Returns description of method parameters
947      *
948      * @return external_function_parameters
949      * @since Moodle 3.0
950      */
951     public static function add_discussion_post_parameters() {
952         return new external_function_parameters(
953             array(
954                 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to
955                                                 (can be the initial discussion post'),
956                 'subject' => new external_value(PARAM_TEXT, 'new post subject'),
957                 'message' => new external_value(PARAM_RAW, 'new post message (only html format allowed)'),
958                 'options' => new external_multiple_structure (
959                     new external_single_structure(
960                         array(
961                             'name' => new external_value(PARAM_ALPHANUM,
962                                         'The allowed keys (value format) are:
963                                         discussionsubscribe (bool); subscribe to the discussion?, default to true
964                             '),
965                             'value' => new external_value(PARAM_RAW, 'the value of the option,
966                                                             this param is validated in the external function.'
967                         )
968                     )
969                 ), 'Options', VALUE_DEFAULT, array())
970             )
971         );
972     }
974     /**
975      * Create new posts into an existing discussion.
976      *
977      * @param int $postid the post id we are going to reply to
978      * @param string $subject new post subject
979      * @param string $message new post message (only html format allowed)
980      * @param array $options optional settings
981      * @return array of warnings and the new post id
982      * @since Moodle 3.0
983      * @throws moodle_exception
984      */
985     public static function add_discussion_post($postid, $subject, $message, $options = array()) {
986         global $DB, $CFG, $USER;
987         require_once($CFG->dirroot . "/mod/forum/lib.php");
989         $params = self::validate_parameters(self::add_discussion_post_parameters(),
990                                             array(
991                                                 'postid' => $postid,
992                                                 'subject' => $subject,
993                                                 'message' => $message,
994                                                 'options' => $options
995                                             ));
996         // Validate options.
997         $options = array(
998             'discussionsubscribe' => true
999         );
1000         foreach ($params['options'] as $option) {
1001             $name = trim($option['name']);
1002             switch ($name) {
1003                 case 'discussionsubscribe':
1004                     $value = clean_param($option['value'], PARAM_BOOL);
1005                     break;
1006                 default:
1007                     throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1008             }
1009             $options[$name] = $value;
1010         }
1012         $warnings = array();
1014         if (!$parent = forum_get_post_full($params['postid'])) {
1015             throw new moodle_exception('invalidparentpostid', 'forum');
1016         }
1018         if (!$discussion = $DB->get_record("forum_discussions", array("id" => $parent->discussion))) {
1019             throw new moodle_exception('notpartofdiscussion', 'forum');
1020         }
1022         // Request and permission validation.
1023         $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
1024         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1026         $context = context_module::instance($cm->id);
1027         self::validate_context($context);
1029         if (!forum_user_can_post($forum, $discussion, $USER, $cm, $course, $context)) {
1030             throw new moodle_exception('nopostforum', 'forum');
1031         }
1033         $thresholdwarning = forum_check_throttling($forum, $cm);
1034         forum_check_blocking_threshold($thresholdwarning);
1036         // Create the post.
1037         $post = new stdClass();
1038         $post->discussion = $discussion->id;
1039         $post->parent = $parent->id;
1040         $post->subject = $params['subject'];
1041         $post->message = $params['message'];
1042         $post->messageformat = FORMAT_HTML;   // Force formatting for now.
1043         $post->messagetrust = trusttext_trusted($context);
1044         $post->itemid = 0;
1046         if ($postid = forum_add_new_post($post, null)) {
1048             $post->id = $postid;
1050             // Trigger events and completion.
1051             $params = array(
1052                 'context' => $context,
1053                 'objectid' => $post->id,
1054                 'other' => array(
1055                     'discussionid' => $discussion->id,
1056                     'forumid' => $forum->id,
1057                     'forumtype' => $forum->type,
1058                 )
1059             );
1060             $event = \mod_forum\event\post_created::create($params);
1061             $event->add_record_snapshot('forum_posts', $post);
1062             $event->add_record_snapshot('forum_discussions', $discussion);
1063             $event->trigger();
1065             // Update completion state.
1066             $completion = new completion_info($course);
1067             if ($completion->is_enabled($cm) &&
1068                     ($forum->completionreplies || $forum->completionposts)) {
1069                 $completion->update_state($cm, COMPLETION_COMPLETE);
1070             }
1072             $settings = new stdClass();
1073             $settings->discussionsubscribe = $options['discussionsubscribe'];
1074             forum_post_subscription($settings, $forum, $discussion);
1075         } else {
1076             throw new moodle_exception('couldnotadd', 'forum');
1077         }
1079         $result = array();
1080         $result['postid'] = $postid;
1081         $result['warnings'] = $warnings;
1082         return $result;
1083     }
1085     /**
1086      * Returns description of method result value
1087      *
1088      * @return external_description
1089      * @since Moodle 3.0
1090      */
1091     public static function add_discussion_post_returns() {
1092         return new external_single_structure(
1093             array(
1094                 'postid' => new external_value(PARAM_INT, 'new post id'),
1095                 'warnings' => new external_warnings()
1096             )
1097         );
1098     }
1100     /**
1101      * Returns description of method parameters
1102      *
1103      * @return external_function_parameters
1104      * @since Moodle 3.0
1105      */
1106     public static function add_discussion_parameters() {
1107         return new external_function_parameters(
1108             array(
1109                 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1110                 'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'),
1111                 'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'),
1112                 'groupid' => new external_value(PARAM_INT, 'The group, default to -1', VALUE_DEFAULT, -1),
1113                 'options' => new external_multiple_structure (
1114                     new external_single_structure(
1115                         array(
1116                             'name' => new external_value(PARAM_ALPHANUM,
1117                                         'The allowed keys (value format) are:
1118                                         discussionsubscribe (bool); subscribe to the discussion?, default to true
1119                                         discussionpinned    (bool); is the discussion pinned, default to false
1120                             '),
1121                             'value' => new external_value(PARAM_RAW, 'The value of the option,
1122                                                             This param is validated in the external function.'
1123                         )
1124                     )
1125                 ), 'Options', VALUE_DEFAULT, array())
1126             )
1127         );
1128     }
1130     /**
1131      * Add a new discussion into an existing forum.
1132      *
1133      * @param int $forumid the forum instance id
1134      * @param string $subject new discussion subject
1135      * @param string $message new discussion message (only html format allowed)
1136      * @param int $groupid the user course group
1137      * @param array $options optional settings
1138      * @return array of warnings and the new discussion id
1139      * @since Moodle 3.0
1140      * @throws moodle_exception
1141      */
1142     public static function add_discussion($forumid, $subject, $message, $groupid = -1, $options = array()) {
1143         global $DB, $CFG;
1144         require_once($CFG->dirroot . "/mod/forum/lib.php");
1146         $params = self::validate_parameters(self::add_discussion_parameters(),
1147                                             array(
1148                                                 'forumid' => $forumid,
1149                                                 'subject' => $subject,
1150                                                 'message' => $message,
1151                                                 'groupid' => $groupid,
1152                                                 'options' => $options
1153                                             ));
1154         // Validate options.
1155         $options = array(
1156             'discussionsubscribe' => true,
1157             'discussionpinned' => false
1158         );
1159         foreach ($params['options'] as $option) {
1160             $name = trim($option['name']);
1161             switch ($name) {
1162                 case 'discussionsubscribe':
1163                     $value = clean_param($option['value'], PARAM_BOOL);
1164                     break;
1165                 case 'discussionpinned':
1166                     $value = clean_param($option['value'], PARAM_BOOL);
1167                     break;
1168                 default:
1169                     throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1170             }
1171             $options[$name] = $value;
1172         }
1174         $warnings = array();
1176         // Request and permission validation.
1177         $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1178         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1180         $context = context_module::instance($cm->id);
1181         self::validate_context($context);
1183         // Normalize group.
1184         if (!groups_get_activity_groupmode($cm)) {
1185             // Groups not supported, force to -1.
1186             $groupid = -1;
1187         } else {
1188             // Check if we receive the default or and empty value for groupid,
1189             // in this case, get the group for the user in the activity.
1190             if ($groupid === -1 or empty($params['groupid'])) {
1191                 $groupid = groups_get_activity_group($cm);
1192             } else {
1193                 // Here we rely in the group passed, forum_user_can_post_discussion will validate the group.
1194                 $groupid = $params['groupid'];
1195             }
1196         }
1198         if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) {
1199             throw new moodle_exception('cannotcreatediscussion', 'forum');
1200         }
1202         $thresholdwarning = forum_check_throttling($forum, $cm);
1203         forum_check_blocking_threshold($thresholdwarning);
1205         // Create the discussion.
1206         $discussion = new stdClass();
1207         $discussion->course = $course->id;
1208         $discussion->forum = $forum->id;
1209         $discussion->message = $params['message'];
1210         $discussion->messageformat = FORMAT_HTML;   // Force formatting for now.
1211         $discussion->messagetrust = trusttext_trusted($context);
1212         $discussion->itemid = 0;
1213         $discussion->groupid = $groupid;
1214         $discussion->mailnow = 0;
1215         $discussion->subject = $params['subject'];
1216         $discussion->name = $discussion->subject;
1217         $discussion->timestart = 0;
1218         $discussion->timeend = 0;
1219         if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) {
1220             $discussion->pinned = FORUM_DISCUSSION_PINNED;
1221         } else {
1222             $discussion->pinned = FORUM_DISCUSSION_UNPINNED;
1223         }
1225         if ($discussionid = forum_add_discussion($discussion)) {
1227             $discussion->id = $discussionid;
1229             // Trigger events and completion.
1231             $params = array(
1232                 'context' => $context,
1233                 'objectid' => $discussion->id,
1234                 'other' => array(
1235                     'forumid' => $forum->id,
1236                 )
1237             );
1238             $event = \mod_forum\event\discussion_created::create($params);
1239             $event->add_record_snapshot('forum_discussions', $discussion);
1240             $event->trigger();
1242             $completion = new completion_info($course);
1243             if ($completion->is_enabled($cm) &&
1244                     ($forum->completiondiscussions || $forum->completionposts)) {
1245                 $completion->update_state($cm, COMPLETION_COMPLETE);
1246             }
1248             $settings = new stdClass();
1249             $settings->discussionsubscribe = $options['discussionsubscribe'];
1250             forum_post_subscription($settings, $forum, $discussion);
1251         } else {
1252             throw new moodle_exception('couldnotadd', 'forum');
1253         }
1255         $result = array();
1256         $result['discussionid'] = $discussionid;
1257         $result['warnings'] = $warnings;
1258         return $result;
1259     }
1261     /**
1262      * Returns description of method result value
1263      *
1264      * @return external_description
1265      * @since Moodle 3.0
1266      */
1267     public static function add_discussion_returns() {
1268         return new external_single_structure(
1269             array(
1270                 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'),
1271                 'warnings' => new external_warnings()
1272             )
1273         );
1274     }
1276     /**
1277      * Returns description of method parameters
1278      *
1279      * @return external_function_parameters
1280      * @since Moodle 3.1
1281      */
1282     public static function can_add_discussion_parameters() {
1283         return new external_function_parameters(
1284             array(
1285                 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1286                 'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group.
1287                                                 Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null)
1288             )
1289         );
1290     }
1292     /**
1293      * Check if the current user can add discussions in the given forum (and optionally for the given group).
1294      *
1295      * @param int $forumid the forum instance id
1296      * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups.
1297      * @return array of warnings and the status (true if the user can add discussions)
1298      * @since Moodle 3.1
1299      * @throws moodle_exception
1300      */
1301     public static function can_add_discussion($forumid, $groupid = null) {
1302         global $DB, $CFG;
1303         require_once($CFG->dirroot . "/mod/forum/lib.php");
1305         $params = self::validate_parameters(self::can_add_discussion_parameters(),
1306                                             array(
1307                                                 'forumid' => $forumid,
1308                                                 'groupid' => $groupid,
1309                                             ));
1310         $warnings = array();
1312         // Request and permission validation.
1313         $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1314         list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1316         $context = context_module::instance($cm->id);
1317         self::validate_context($context);
1319         $status = forum_user_can_post_discussion($forum, $params['groupid'], -1, $cm, $context);
1321         $result = array();
1322         $result['status'] = $status;
1323         $result['warnings'] = $warnings;
1324         return $result;
1325     }
1327     /**
1328      * Returns description of method result value
1329      *
1330      * @return external_description
1331      * @since Moodle 3.1
1332      */
1333     public static function can_add_discussion_returns() {
1334         return new external_single_structure(
1335             array(
1336                 'status' => new external_value(PARAM_BOOL, 'True if the user can add discussions, false otherwise.'),
1337                 'warnings' => new external_warnings()
1338             )
1339         );
1340     }