MDL-56019 ws: Use itemid null when getting modules intro
[moodle.git] / mod / forum / externallib.php
CommitLineData
2b9fe87d
MN
1<?php
2
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/>.
17
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 */
25
26defined('MOODLE_INTERNAL') || die;
27
28require_once("$CFG->libdir/externallib.php");
29
30class mod_forum_external extends external_api {
31
32 /**
a9a0cb69 33 * Describes the parameters for get_forum.
2b9fe87d
MN
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',
a69c9abd 42 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Course IDs', VALUE_DEFAULT, array()),
2b9fe87d
MN
43 )
44 );
45 }
46
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()) {
c8f1d8a0 57 global $CFG;
2b9fe87d
MN
58
59 require_once($CFG->dirroot . "/mod/forum/lib.php");
60
a9a0cb69
MN
61 $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids));
62
583b02e4 63 $courses = array();
a9a0cb69 64 if (empty($params['courseids'])) {
583b02e4
FM
65 $courses = enrol_get_my_courses();
66 $params['courseids'] = array_keys($courses);
2b9fe87d
MN
67 }
68
69 // Array to store the forums to return.
70 $arrforums = array();
ea5b910b 71 $warnings = array();
2b9fe87d 72
0c246ae5 73 // Ensure there are courseids to loop through.
ea5b910b
JL
74 if (!empty($params['courseids'])) {
75
583b02e4 76 list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
c8f1d8a0
JL
77
78 // Get the forums in this course. This function checks users visibility permissions.
ea5b910b
JL
79 $forums = get_all_instances_in_courses("forum", $courses);
80 foreach ($forums as $forum) {
81
82 $course = $courses[$forum->course];
83 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
84 $context = context_module::instance($cm->id);
85
86 // Skip forums we are not allowed to see discussions.
87 if (!has_capability('mod/forum:viewdiscussion', $context)) {
88 continue;
2b9fe87d 89 }
ea5b910b 90
5b587c75 91 $forum->name = external_format_string($forum->name, $context->id);
ea5b910b
JL
92 // Format the intro before being returning using the format setting.
93 list($forum->intro, $forum->introformat) = external_format_text($forum->intro, $forum->introformat,
d33c67bc 94 $context->id, 'mod_forum', 'intro', null);
7ef49bd3 95 $forum->introfiles = external_util::get_area_files($context->id, 'mod_forum', 'intro', false, false);
ea5b910b
JL
96 // Discussions count. This function does static request cache.
97 $forum->numdiscussions = forum_count_discussions($forum, $cm, $course);
98 $forum->cmid = $forum->coursemodule;
99 $forum->cancreatediscussions = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
100
101 // Add the forum to the array to return.
102 $arrforums[$forum->id] = $forum;
2b9fe87d
MN
103 }
104 }
105
106 return $arrforums;
107 }
108
109 /**
a9a0cb69 110 * Describes the get_forum return value.
2b9fe87d
MN
111 *
112 * @return external_single_structure
113 * @since Moodle 2.5
114 */
115 public static function get_forums_by_courses_returns() {
116 return new external_multiple_structure(
117 new external_single_structure(
118 array(
119 'id' => new external_value(PARAM_INT, 'Forum id'),
5b587c75 120 'course' => new external_value(PARAM_INT, 'Course id'),
2b9fe87d 121 'type' => new external_value(PARAM_TEXT, 'The forum type'),
5b587c75 122 'name' => new external_value(PARAM_RAW, 'Forum name'),
2b9fe87d
MN
123 'intro' => new external_value(PARAM_RAW, 'The forum intro'),
124 'introformat' => new external_format_value('intro'),
7ef49bd3 125 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
2b9fe87d
MN
126 'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
127 'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
128 'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
129 'scale' => new external_value(PARAM_INT, 'Scale'),
130 'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'),
131 'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'),
132 'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'),
133 'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'),
134 'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'),
135 'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'),
136 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
137 'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'),
138 'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'),
139 'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'),
140 'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'),
141 'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'),
142 'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'),
7ea6ada3 143 'cmid' => new external_value(PARAM_INT, 'Course module id'),
ea5b910b
JL
144 'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL),
145 'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL),
2b9fe87d
MN
146 ), 'forum'
147 )
148 );
149 }
a9a0cb69 150
e2ede426
JL
151 /**
152 * Describes the parameters for get_forum_discussion_posts.
153 *
154 * @return external_external_function_parameters
155 * @since Moodle 2.7
156 */
157 public static function get_forum_discussion_posts_parameters() {
158 return new external_function_parameters (
159 array(
160 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED),
161 'sortby' => new external_value(PARAM_ALPHA,
162 'sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
163 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
164 )
165 );
166 }
167
168 /**
169 * Returns a list of forum posts for a discussion
170 *
171 * @param int $discussionid the post ids
172 * @param string $sortby sort by this element (id, created or modified)
173 * @param string $sortdirection sort direction: ASC or DESC
174 *
175 * @return array the forum post details
176 * @since Moodle 2.7
177 */
fb8840d2 178 public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") {
d85bedf7 179 global $CFG, $DB, $USER, $PAGE;
e2ede426 180
b1aa7dfa 181 $posts = array();
e2ede426
JL
182 $warnings = array();
183
184 // Validate the parameter.
185 $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(),
186 array(
187 'discussionid' => $discussionid,
188 'sortby' => $sortby,
189 'sortdirection' => $sortdirection));
190
191 // Compact/extract functions are not recommended.
192 $discussionid = $params['discussionid'];
193 $sortby = $params['sortby'];
194 $sortdirection = $params['sortdirection'];
195
196 $sortallowedvalues = array('id', 'created', 'modified');
197 if (!in_array($sortby, $sortallowedvalues)) {
198 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
199 'allowed values are: ' . implode(',', $sortallowedvalues));
200 }
201
202 $sortdirection = strtoupper($sortdirection);
203 $directionallowedvalues = array('ASC', 'DESC');
204 if (!in_array($sortdirection, $directionallowedvalues)) {
205 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
206 'allowed values are: ' . implode(',', $directionallowedvalues));
207 }
208
209 $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST);
210 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
211 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
212 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
213
214 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
215 $modcontext = context_module::instance($cm->id);
216 self::validate_context($modcontext);
217
218 // This require must be here, see mod/forum/discuss.php.
219 require_once($CFG->dirroot . "/mod/forum/lib.php");
220
221 // Check they have the view forum capability.
222 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
223
224 if (! $post = forum_get_post_full($discussion->firstpost)) {
225 throw new moodle_exception('notexists', 'forum');
226 }
227
228 // This function check groups, qanda, timed discussions, etc.
229 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
230 throw new moodle_exception('noviewdiscussionspermission', 'forum');
231 }
232
233 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
234
235 // We will add this field in the response.
236 $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
237
238 $forumtracked = forum_tp_is_tracked($forum);
239
240 $sort = 'p.' . $sortby . ' ' . $sortdirection;
b1aa7dfa 241 $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
e2ede426 242
b1aa7dfa 243 foreach ($allposts as $post) {
e2ede426
JL
244
245 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
246 $warning = array();
247 $warning['item'] = 'post';
248 $warning['itemid'] = $post->id;
249 $warning['warningcode'] = '1';
250 $warning['message'] = 'You can\'t see this post';
251 $warnings[] = $warning;
252 continue;
253 }
254
255 // Function forum_get_all_discussion_posts adds postread field.
f47eeafd
JL
256 // Note that the value returned can be a boolean or an integer. The WS expects a boolean.
257 if (empty($post->postread)) {
b1aa7dfa 258 $post->postread = false;
f47eeafd 259 } else {
b1aa7dfa 260 $post->postread = true;
e2ede426 261 }
f47eeafd 262
b1aa7dfa
JL
263 $post->canreply = $canreply;
264 if (!empty($post->children)) {
265 $post->children = array_keys($post->children);
fb8840d2 266 } else {
b1aa7dfa 267 $post->children = array();
e2ede426
JL
268 }
269
270 $user = new stdclass();
694bf0c7 271 $user->id = $post->userid;
14ebc396 272 $user = username_load_fields_from_object($user, $post, null, array('picture', 'imagealt', 'email'));
694bf0c7 273 $post->userfullname = fullname($user, $canviewfullname);
81f810dc 274
14ebc396
BK
275 $userpicture = new user_picture($user);
276 $userpicture->size = 1; // Size f1.
277 $post->userpictureurl = $userpicture->get_url($PAGE)->out(false);
278
55bb8189 279 $post->subject = external_format_string($post->subject, $modcontext->id);
48fb0250
JL
280 // Rewrite embedded images URLs.
281 list($post->message, $post->messageformat) =
282 external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_forum', 'post', $post->id);
283
284 // List attachments.
285 if (!empty($post->attachment)) {
14590070 286 $post->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment', $post->id);
48fb0250 287 }
e2ede426 288
b1aa7dfa 289 $posts[] = $post;
e2ede426
JL
290 }
291
292 $result = array();
293 $result['posts'] = $posts;
294 $result['warnings'] = $warnings;
295 return $result;
296 }
297
298 /**
299 * Describes the get_forum_discussion_posts return value.
300 *
301 * @return external_single_structure
302 * @since Moodle 2.7
303 */
304 public static function get_forum_discussion_posts_returns() {
305 return new external_single_structure(
306 array(
307 'posts' => new external_multiple_structure(
308 new external_single_structure(
309 array(
310 'id' => new external_value(PARAM_INT, 'Post id'),
311 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
312 'parent' => new external_value(PARAM_INT, 'Parent id'),
313 'userid' => new external_value(PARAM_INT, 'User id'),
314 'created' => new external_value(PARAM_INT, 'Creation time'),
315 'modified' => new external_value(PARAM_INT, 'Time modified'),
316 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
317 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
318 'message' => new external_value(PARAM_RAW, 'The post message'),
48fb0250 319 'messageformat' => new external_format_value('message'),
e2ede426 320 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
48fb0250 321 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
14590070 322 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
e2ede426
JL
323 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
324 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
325 'children' => new external_multiple_structure(new external_value(PARAM_INT, 'children post id')),
326 'canreply' => new external_value(PARAM_BOOL, 'The user can reply to posts?'),
327 'postread' => new external_value(PARAM_BOOL, 'The post was read'),
694bf0c7
JL
328 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
329 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL)
e2ede426
JL
330 ), 'post'
331 )
332 ),
333 'warnings' => new external_warnings()
334 )
335 );
336 }
337
7c51b40a
JL
338 /**
339 * Describes the parameters for get_forum_discussions_paginated.
340 *
341 * @return external_external_function_parameters
342 * @since Moodle 2.8
343 */
344 public static function get_forum_discussions_paginated_parameters() {
345 return new external_function_parameters (
346 array(
347 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
348 'sortby' => new external_value(PARAM_ALPHA,
349 'sort by this element: id, timemodified, timestart or timeend', VALUE_DEFAULT, 'timemodified'),
350 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
351 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
352 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
353 )
354 );
355 }
356
357 /**
358 * Returns a list of forum discussions optionally sorted and paginated.
359 *
360 * @param int $forumid the forum instance id
361 * @param string $sortby sort by this element (id, timemodified, timestart or timeend)
362 * @param string $sortdirection sort direction: ASC or DESC
363 * @param int $page page number
364 * @param int $perpage items per page
365 *
366 * @return array the forum discussion details including warnings
367 * @since Moodle 2.8
368 */
369 public static function get_forum_discussions_paginated($forumid, $sortby = 'timemodified', $sortdirection = 'DESC',
370 $page = -1, $perpage = 0) {
d85bedf7 371 global $CFG, $DB, $USER, $PAGE;
7c51b40a
JL
372
373 require_once($CFG->dirroot . "/mod/forum/lib.php");
374
375 $warnings = array();
039c81f0 376 $discussions = array();
7c51b40a
JL
377
378 $params = self::validate_parameters(self::get_forum_discussions_paginated_parameters(),
379 array(
380 'forumid' => $forumid,
381 'sortby' => $sortby,
382 'sortdirection' => $sortdirection,
383 'page' => $page,
384 'perpage' => $perpage
385 )
386 );
387
388 // Compact/extract functions are not recommended.
389 $forumid = $params['forumid'];
390 $sortby = $params['sortby'];
391 $sortdirection = $params['sortdirection'];
392 $page = $params['page'];
393 $perpage = $params['perpage'];
394
395 $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend');
396 if (!in_array($sortby, $sortallowedvalues)) {
397 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
398 'allowed values are: ' . implode(',', $sortallowedvalues));
399 }
400
401 $sortdirection = strtoupper($sortdirection);
402 $directionallowedvalues = array('ASC', 'DESC');
403 if (!in_array($sortdirection, $directionallowedvalues)) {
404 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
405 'allowed values are: ' . implode(',', $directionallowedvalues));
406 }
407
408 $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
409 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
410 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
411
412 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
413 $modcontext = context_module::instance($cm->id);
414 self::validate_context($modcontext);
415
416 // Check they have the view forum capability.
417 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
418
5f219cf1 419 $sort = 'd.pinned DESC, d.' . $sortby . ' ' . $sortdirection;
4f3a2d21 420 $alldiscussions = forum_get_discussions($cm, $sort, true, -1, -1, true, $page, $perpage, FORUM_POSTS_ALL_USER_GROUPS);
7c51b40a 421
039c81f0 422 if ($alldiscussions) {
7c51b40a
JL
423 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
424
425 // Get the unreads array, this takes a forum id and returns data for all discussions.
426 $unreads = array();
427 if ($cantrack = forum_tp_can_track_forums($forum)) {
428 if ($forumtracked = forum_tp_is_tracked($forum)) {
429 $unreads = forum_get_discussions_unread($cm);
430 }
431 }
432 // The forum function returns the replies for all the discussions in a given forum.
433 $replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage);
434
039c81f0
JL
435 foreach ($alldiscussions as $discussion) {
436
7c51b40a 437 // This function checks for qanda forums.
039c81f0
JL
438 // Note that the forum_get_discussions returns as id the post id, not the discussion id so we need to do this.
439 $discussionrec = clone $discussion;
440 $discussionrec->id = $discussion->discussion;
441 if (!forum_user_can_see_discussion($forum, $discussionrec, $modcontext)) {
7c51b40a
JL
442 $warning = array();
443 // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
444 $warning['item'] = 'post';
445 $warning['itemid'] = $discussion->id;
446 $warning['warningcode'] = '1';
447 $warning['message'] = 'You can\'t see this discussion';
448 $warnings[] = $warning;
449 continue;
450 }
451
452 $discussion->numunread = 0;
453 if ($cantrack && $forumtracked) {
454 if (isset($unreads[$discussion->discussion])) {
455 $discussion->numunread = (int) $unreads[$discussion->discussion];
456 }
457 }
458
459 $discussion->numreplies = 0;
460 if (!empty($replies[$discussion->discussion])) {
461 $discussion->numreplies = (int) $replies[$discussion->discussion]->replies;
462 }
463
d85bedf7
JL
464 $picturefields = explode(',', user_picture::fields());
465
7c51b40a
JL
466 // Load user objects from the results of the query.
467 $user = new stdclass();
468 $user->id = $discussion->userid;
d85bedf7
JL
469 $user = username_load_fields_from_object($user, $discussion, null, $picturefields);
470 // Preserve the id, it can be modified by username_load_fields_from_object.
471 $user->id = $discussion->userid;
7c51b40a 472 $discussion->userfullname = fullname($user, $canviewfullname);
81f810dc 473
d85bedf7
JL
474 $userpicture = new user_picture($user);
475 $userpicture->size = 1; // Size f1.
476 $discussion->userpictureurl = $userpicture->get_url($PAGE)->out(false);
7c51b40a
JL
477
478 $usermodified = new stdclass();
479 $usermodified->id = $discussion->usermodified;
d85bedf7
JL
480 $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um', $picturefields);
481 // Preserve the id (it can be overwritten due to the prefixed $picturefields).
482 $usermodified->id = $discussion->usermodified;
7c51b40a 483 $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname);
81f810dc 484
d85bedf7
JL
485 $userpicture = new user_picture($usermodified);
486 $userpicture->size = 1; // Size f1.
487 $discussion->usermodifiedpictureurl = $userpicture->get_url($PAGE)->out(false);
7c51b40a 488
55bb8189
JL
489 $discussion->name = external_format_string($discussion->name, $modcontext->id);
490 $discussion->subject = external_format_string($discussion->subject, $modcontext->id);
7c51b40a
JL
491 // Rewrite embedded images URLs.
492 list($discussion->message, $discussion->messageformat) =
493 external_format_text($discussion->message, $discussion->messageformat,
494 $modcontext->id, 'mod_forum', 'post', $discussion->id);
495
496 // List attachments.
497 if (!empty($discussion->attachment)) {
14590070
JL
498 $discussion->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment',
499 $discussion->id);
7c51b40a
JL
500 }
501
039c81f0 502 $discussions[] = $discussion;
7c51b40a
JL
503 }
504 }
505
506 $result = array();
507 $result['discussions'] = $discussions;
508 $result['warnings'] = $warnings;
509 return $result;
510
511 }
512
513 /**
514 * Describes the get_forum_discussions_paginated return value.
515 *
516 * @return external_single_structure
517 * @since Moodle 2.8
518 */
519 public static function get_forum_discussions_paginated_returns() {
520 return new external_single_structure(
521 array(
522 'discussions' => new external_multiple_structure(
523 new external_single_structure(
524 array(
525 'id' => new external_value(PARAM_INT, 'Post id'),
526 'name' => new external_value(PARAM_TEXT, 'Discussion name'),
527 'groupid' => new external_value(PARAM_INT, 'Group id'),
528 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
529 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
530 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
531 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
532 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
533 'parent' => new external_value(PARAM_INT, 'Parent id'),
534 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
535 'created' => new external_value(PARAM_INT, 'Creation time'),
536 'modified' => new external_value(PARAM_INT, 'Time modified'),
537 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
538 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
539 'message' => new external_value(PARAM_RAW, 'The post message'),
540 'messageformat' => new external_format_value('message'),
541 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
542 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
14590070 543 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
7c51b40a
JL
544 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
545 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
546 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
547 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
548 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
549 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
550 'numreplies' => new external_value(PARAM_TEXT, 'The number of replies in the discussion'),
5f219cf1
BK
551 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
552 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned')
7c51b40a
JL
553 ), 'post'
554 )
555 ),
556 'warnings' => new external_warnings()
557 )
558 );
559 }
560
4a1e44a1
JL
561 /**
562 * Returns description of method parameters
563 *
564 * @return external_function_parameters
565 * @since Moodle 2.9
566 */
567 public static function view_forum_parameters() {
568 return new external_function_parameters(
569 array(
570 'forumid' => new external_value(PARAM_INT, 'forum instance id')
571 )
572 );
573 }
574
575 /**
1c2b7882 576 * Trigger the course module viewed event and update the module completion status.
4a1e44a1
JL
577 *
578 * @param int $forumid the forum instance id
579 * @return array of warnings and status result
580 * @since Moodle 2.9
581 * @throws moodle_exception
582 */
583 public static function view_forum($forumid) {
584 global $DB, $CFG;
585 require_once($CFG->dirroot . "/mod/forum/lib.php");
586
587 $params = self::validate_parameters(self::view_forum_parameters(),
588 array(
589 'forumid' => $forumid
590 ));
591 $warnings = array();
592
593 // Request and permission validation.
ca7e2fc2 594 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
4a1e44a1
JL
595 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
596
597 $context = context_module::instance($cm->id);
598 self::validate_context($context);
599
ea2fa324
JL
600 require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum');
601
4a1e44a1
JL
602 // Call the forum/lib API.
603 forum_view($forum, $course, $cm, $context);
604
605 $result = array();
606 $result['status'] = true;
607 $result['warnings'] = $warnings;
608 return $result;
609 }
610
611 /**
612 * Returns description of method result value
613 *
614 * @return external_description
615 * @since Moodle 2.9
616 */
617 public static function view_forum_returns() {
618 return new external_single_structure(
619 array(
620 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
621 'warnings' => new external_warnings()
622 )
623 );
624 }
625
a3c315dd
JL
626 /**
627 * Returns description of method parameters
628 *
629 * @return external_function_parameters
630 * @since Moodle 2.9
631 */
632 public static function view_forum_discussion_parameters() {
633 return new external_function_parameters(
634 array(
635 'discussionid' => new external_value(PARAM_INT, 'discussion id')
636 )
637 );
638 }
639
640 /**
1c2b7882 641 * Trigger the discussion viewed event.
a3c315dd
JL
642 *
643 * @param int $discussionid the discussion id
644 * @return array of warnings and status result
645 * @since Moodle 2.9
646 * @throws moodle_exception
647 */
648 public static function view_forum_discussion($discussionid) {
649 global $DB, $CFG;
650 require_once($CFG->dirroot . "/mod/forum/lib.php");
651
652 $params = self::validate_parameters(self::view_forum_discussion_parameters(),
653 array(
654 'discussionid' => $discussionid
655 ));
656 $warnings = array();
657
658 $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST);
659 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
660 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
661
662 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
663 $modcontext = context_module::instance($cm->id);
664 self::validate_context($modcontext);
665
ea2fa324
JL
666 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
667
a3c315dd
JL
668 // Call the forum/lib API.
669 forum_discussion_view($modcontext, $forum, $discussion);
670
671 $result = array();
672 $result['status'] = true;
673 $result['warnings'] = $warnings;
674 return $result;
675 }
676
677 /**
678 * Returns description of method result value
679 *
680 * @return external_description
681 * @since Moodle 2.9
682 */
683 public static function view_forum_discussion_returns() {
684 return new external_single_structure(
685 array(
686 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
687 'warnings' => new external_warnings()
688 )
689 );
690 }
691
50a20317
JL
692 /**
693 * Returns description of method parameters
694 *
695 * @return external_function_parameters
696 * @since Moodle 3.0
697 */
698 public static function add_discussion_post_parameters() {
699 return new external_function_parameters(
700 array(
701 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to
702 (can be the initial discussion post'),
703 'subject' => new external_value(PARAM_TEXT, 'new post subject'),
704 'message' => new external_value(PARAM_RAW, 'new post message (only html format allowed)'),
705 'options' => new external_multiple_structure (
706 new external_single_structure(
707 array(
708 'name' => new external_value(PARAM_ALPHANUM,
709 'The allowed keys (value format) are:
710 discussionsubscribe (bool); subscribe to the discussion?, default to true
48143990 711 inlineattachmentsid (int); the draft file area id for inline attachments
e881c4f5 712 attachmentsid (int); the draft file area id for attachments
50a20317
JL
713 '),
714 'value' => new external_value(PARAM_RAW, 'the value of the option,
715 this param is validated in the external function.'
716 )
717 )
718 ), 'Options', VALUE_DEFAULT, array())
719 )
720 );
721 }
722
723 /**
724 * Create new posts into an existing discussion.
725 *
726 * @param int $postid the post id we are going to reply to
727 * @param string $subject new post subject
728 * @param string $message new post message (only html format allowed)
729 * @param array $options optional settings
730 * @return array of warnings and the new post id
731 * @since Moodle 3.0
732 * @throws moodle_exception
733 */
734 public static function add_discussion_post($postid, $subject, $message, $options = array()) {
735 global $DB, $CFG, $USER;
736 require_once($CFG->dirroot . "/mod/forum/lib.php");
737
738 $params = self::validate_parameters(self::add_discussion_post_parameters(),
739 array(
740 'postid' => $postid,
741 'subject' => $subject,
742 'message' => $message,
743 'options' => $options
744 ));
745 // Validate options.
746 $options = array(
41182118 747 'discussionsubscribe' => true,
48143990 748 'inlineattachmentsid' => 0,
e881c4f5 749 'attachmentsid' => null
50a20317
JL
750 );
751 foreach ($params['options'] as $option) {
752 $name = trim($option['name']);
753 switch ($name) {
754 case 'discussionsubscribe':
755 $value = clean_param($option['value'], PARAM_BOOL);
756 break;
48143990 757 case 'inlineattachmentsid':
41182118
BK
758 $value = clean_param($option['value'], PARAM_INT);
759 break;
e881c4f5
BK
760 case 'attachmentsid':
761 $value = clean_param($option['value'], PARAM_INT);
762 break;
50a20317
JL
763 default:
764 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
765 }
766 $options[$name] = $value;
767 }
768
769 $warnings = array();
770
d3d8f81c 771 if (!$parent = forum_get_post_full($params['postid'])) {
50a20317
JL
772 throw new moodle_exception('invalidparentpostid', 'forum');
773 }
774
d3d8f81c 775 if (!$discussion = $DB->get_record("forum_discussions", array("id" => $parent->discussion))) {
50a20317
JL
776 throw new moodle_exception('notpartofdiscussion', 'forum');
777 }
778
779 // Request and permission validation.
780 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
781 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
782
783 $context = context_module::instance($cm->id);
784 self::validate_context($context);
785
786 if (!forum_user_can_post($forum, $discussion, $USER, $cm, $course, $context)) {
787 throw new moodle_exception('nopostforum', 'forum');
788 }
789
790 $thresholdwarning = forum_check_throttling($forum, $cm);
791 forum_check_blocking_threshold($thresholdwarning);
792
793 // Create the post.
794 $post = new stdClass();
795 $post->discussion = $discussion->id;
796 $post->parent = $parent->id;
797 $post->subject = $params['subject'];
798 $post->message = $params['message'];
799 $post->messageformat = FORMAT_HTML; // Force formatting for now.
800 $post->messagetrust = trusttext_trusted($context);
48143990 801 $post->itemid = $options['inlineattachmentsid'];
e881c4f5
BK
802 $post->attachments = $options['attachmentsid'];
803 $fakemform = $post->attachments;
804 if ($postid = forum_add_new_post($post, $fakemform)) {
50a20317
JL
805
806 $post->id = $postid;
807
808 // Trigger events and completion.
809 $params = array(
810 'context' => $context,
811 'objectid' => $post->id,
812 'other' => array(
813 'discussionid' => $discussion->id,
814 'forumid' => $forum->id,
815 'forumtype' => $forum->type,
816 )
817 );
818 $event = \mod_forum\event\post_created::create($params);
819 $event->add_record_snapshot('forum_posts', $post);
820 $event->add_record_snapshot('forum_discussions', $discussion);
821 $event->trigger();
822
823 // Update completion state.
824 $completion = new completion_info($course);
825 if ($completion->is_enabled($cm) &&
826 ($forum->completionreplies || $forum->completionposts)) {
827 $completion->update_state($cm, COMPLETION_COMPLETE);
828 }
829
830 $settings = new stdClass();
831 $settings->discussionsubscribe = $options['discussionsubscribe'];
832 forum_post_subscription($settings, $forum, $discussion);
833 } else {
834 throw new moodle_exception('couldnotadd', 'forum');
835 }
836
837 $result = array();
838 $result['postid'] = $postid;
839 $result['warnings'] = $warnings;
840 return $result;
841 }
842
843 /**
844 * Returns description of method result value
845 *
846 * @return external_description
847 * @since Moodle 3.0
848 */
849 public static function add_discussion_post_returns() {
850 return new external_single_structure(
851 array(
852 'postid' => new external_value(PARAM_INT, 'new post id'),
853 'warnings' => new external_warnings()
854 )
855 );
856 }
857
7ab43ac8
JL
858 /**
859 * Returns description of method parameters
860 *
861 * @return external_function_parameters
862 * @since Moodle 3.0
863 */
864 public static function add_discussion_parameters() {
865 return new external_function_parameters(
866 array(
7267daa8
AN
867 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
868 'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'),
869 'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'),
870 'groupid' => new external_value(PARAM_INT, 'The group, default to -1', VALUE_DEFAULT, -1),
7ab43ac8
JL
871 'options' => new external_multiple_structure (
872 new external_single_structure(
873 array(
874 'name' => new external_value(PARAM_ALPHANUM,
875 'The allowed keys (value format) are:
876 discussionsubscribe (bool); subscribe to the discussion?, default to true
5f219cf1 877 discussionpinned (bool); is the discussion pinned, default to false
48143990 878 inlineattachmentsid (int); the draft file area id for inline attachments
e881c4f5 879 attachmentsid (int); the draft file area id for attachments
7ab43ac8 880 '),
7267daa8
AN
881 'value' => new external_value(PARAM_RAW, 'The value of the option,
882 This param is validated in the external function.'
7ab43ac8
JL
883 )
884 )
885 ), 'Options', VALUE_DEFAULT, array())
886 )
887 );
888 }
889
890 /**
891 * Add a new discussion into an existing forum.
892 *
893 * @param int $forumid the forum instance id
894 * @param string $subject new discussion subject
895 * @param string $message new discussion message (only html format allowed)
896 * @param int $groupid the user course group
897 * @param array $options optional settings
898 * @return array of warnings and the new discussion id
899 * @since Moodle 3.0
900 * @throws moodle_exception
901 */
902 public static function add_discussion($forumid, $subject, $message, $groupid = -1, $options = array()) {
903 global $DB, $CFG;
904 require_once($CFG->dirroot . "/mod/forum/lib.php");
905
906 $params = self::validate_parameters(self::add_discussion_parameters(),
907 array(
908 'forumid' => $forumid,
909 'subject' => $subject,
910 'message' => $message,
911 'groupid' => $groupid,
912 'options' => $options
913 ));
914 // Validate options.
915 $options = array(
5f219cf1 916 'discussionsubscribe' => true,
41182118 917 'discussionpinned' => false,
48143990 918 'inlineattachmentsid' => 0,
e881c4f5 919 'attachmentsid' => null
7ab43ac8
JL
920 );
921 foreach ($params['options'] as $option) {
922 $name = trim($option['name']);
923 switch ($name) {
924 case 'discussionsubscribe':
925 $value = clean_param($option['value'], PARAM_BOOL);
926 break;
5f219cf1
BK
927 case 'discussionpinned':
928 $value = clean_param($option['value'], PARAM_BOOL);
929 break;
48143990 930 case 'inlineattachmentsid':
41182118
BK
931 $value = clean_param($option['value'], PARAM_INT);
932 break;
e881c4f5
BK
933 case 'attachmentsid':
934 $value = clean_param($option['value'], PARAM_INT);
935 break;
7ab43ac8
JL
936 default:
937 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
938 }
939 $options[$name] = $value;
940 }
941
942 $warnings = array();
943
944 // Request and permission validation.
945 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
946 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
947
948 $context = context_module::instance($cm->id);
949 self::validate_context($context);
950
951 // Normalize group.
952 if (!groups_get_activity_groupmode($cm)) {
953 // Groups not supported, force to -1.
954 $groupid = -1;
955 } else {
956 // Check if we receive the default or and empty value for groupid,
957 // in this case, get the group for the user in the activity.
958 if ($groupid === -1 or empty($params['groupid'])) {
959 $groupid = groups_get_activity_group($cm);
960 } else {
961 // Here we rely in the group passed, forum_user_can_post_discussion will validate the group.
962 $groupid = $params['groupid'];
963 }
964 }
965
966 if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) {
967 throw new moodle_exception('cannotcreatediscussion', 'forum');
968 }
969
970 $thresholdwarning = forum_check_throttling($forum, $cm);
971 forum_check_blocking_threshold($thresholdwarning);
972
973 // Create the discussion.
974 $discussion = new stdClass();
975 $discussion->course = $course->id;
976 $discussion->forum = $forum->id;
977 $discussion->message = $params['message'];
978 $discussion->messageformat = FORMAT_HTML; // Force formatting for now.
979 $discussion->messagetrust = trusttext_trusted($context);
48143990 980 $discussion->itemid = $options['inlineattachmentsid'];
7ab43ac8
JL
981 $discussion->groupid = $groupid;
982 $discussion->mailnow = 0;
983 $discussion->subject = $params['subject'];
984 $discussion->name = $discussion->subject;
985 $discussion->timestart = 0;
986 $discussion->timeend = 0;
e881c4f5
BK
987 $discussion->attachments = $options['attachmentsid'];
988
5f219cf1
BK
989 if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) {
990 $discussion->pinned = FORUM_DISCUSSION_PINNED;
991 } else {
992 $discussion->pinned = FORUM_DISCUSSION_UNPINNED;
993 }
e881c4f5
BK
994 $fakemform = $options['attachmentsid'];
995 if ($discussionid = forum_add_discussion($discussion, $fakemform)) {
7ab43ac8
JL
996
997 $discussion->id = $discussionid;
998
999 // Trigger events and completion.
1000
1001 $params = array(
1002 'context' => $context,
1003 'objectid' => $discussion->id,
1004 'other' => array(
1005 'forumid' => $forum->id,
1006 )
1007 );
1008 $event = \mod_forum\event\discussion_created::create($params);
1009 $event->add_record_snapshot('forum_discussions', $discussion);
1010 $event->trigger();
1011
1012 $completion = new completion_info($course);
1013 if ($completion->is_enabled($cm) &&
1014 ($forum->completiondiscussions || $forum->completionposts)) {
1015 $completion->update_state($cm, COMPLETION_COMPLETE);
1016 }
1017
1018 $settings = new stdClass();
1019 $settings->discussionsubscribe = $options['discussionsubscribe'];
1020 forum_post_subscription($settings, $forum, $discussion);
1021 } else {
1022 throw new moodle_exception('couldnotadd', 'forum');
1023 }
1024
1025 $result = array();
1026 $result['discussionid'] = $discussionid;
1027 $result['warnings'] = $warnings;
1028 return $result;
1029 }
1030
1031 /**
1032 * Returns description of method result value
1033 *
1034 * @return external_description
1035 * @since Moodle 3.0
1036 */
1037 public static function add_discussion_returns() {
1038 return new external_single_structure(
1039 array(
7267daa8 1040 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'),
7ab43ac8
JL
1041 'warnings' => new external_warnings()
1042 )
1043 );
1044 }
1045
04cd8ae3
JL
1046 /**
1047 * Returns description of method parameters
1048 *
1049 * @return external_function_parameters
1050 * @since Moodle 3.1
1051 */
1052 public static function can_add_discussion_parameters() {
1053 return new external_function_parameters(
1054 array(
1055 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1056 'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group.
1057 Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null)
1058 )
1059 );
1060 }
1061
1062 /**
1063 * Check if the current user can add discussions in the given forum (and optionally for the given group).
1064 *
1065 * @param int $forumid the forum instance id
1066 * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups.
1067 * @return array of warnings and the status (true if the user can add discussions)
1068 * @since Moodle 3.1
1069 * @throws moodle_exception
1070 */
1071 public static function can_add_discussion($forumid, $groupid = null) {
1072 global $DB, $CFG;
1073 require_once($CFG->dirroot . "/mod/forum/lib.php");
1074
1075 $params = self::validate_parameters(self::can_add_discussion_parameters(),
1076 array(
1077 'forumid' => $forumid,
1078 'groupid' => $groupid,
1079 ));
1080 $warnings = array();
1081
1082 // Request and permission validation.
1083 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1084 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1085
1086 $context = context_module::instance($cm->id);
1087 self::validate_context($context);
1088
1089 $status = forum_user_can_post_discussion($forum, $params['groupid'], -1, $cm, $context);
1090
1091 $result = array();
1092 $result['status'] = $status;
1093 $result['warnings'] = $warnings;
1094 return $result;
1095 }
1096
1097 /**
1098 * Returns description of method result value
1099 *
1100 * @return external_description
1101 * @since Moodle 3.1
1102 */
1103 public static function can_add_discussion_returns() {
1104 return new external_single_structure(
1105 array(
1106 'status' => new external_value(PARAM_BOOL, 'True if the user can add discussions, false otherwise.'),
1107 'warnings' => new external_warnings()
1108 )
1109 );
1110 }
1111
2b9fe87d 1112}