MDL-31355 mod_forum: WS should return duedate and cutoffdate.
[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 34 *
9db43c73 35 * @return external_function_parameters
2b9fe87d
MN
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);
4669389d 100 $forum->istracked = forum_tp_is_tracked($forum);
2256bb74
JL
101 if ($forum->istracked) {
102 $forum->unreadpostscount = forum_tp_count_forum_unread_posts($cm, $course);
103 }
ea5b910b
JL
104
105 // Add the forum to the array to return.
106 $arrforums[$forum->id] = $forum;
2b9fe87d
MN
107 }
108 }
109
110 return $arrforums;
111 }
112
113 /**
a9a0cb69 114 * Describes the get_forum return value.
2b9fe87d
MN
115 *
116 * @return external_single_structure
117 * @since Moodle 2.5
118 */
0f3bbfd4 119 public static function get_forums_by_courses_returns() {
2b9fe87d
MN
120 return new external_multiple_structure(
121 new external_single_structure(
122 array(
123 'id' => new external_value(PARAM_INT, 'Forum id'),
5b587c75 124 'course' => new external_value(PARAM_INT, 'Course id'),
2b9fe87d 125 'type' => new external_value(PARAM_TEXT, 'The forum type'),
5b587c75 126 'name' => new external_value(PARAM_RAW, 'Forum name'),
2b9fe87d
MN
127 'intro' => new external_value(PARAM_RAW, 'The forum intro'),
128 'introformat' => new external_format_value('intro'),
7ef49bd3 129 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
99bcb318
SR
130 'duedate' => new external_value(PARAM_INT, 'duedate for the user', VALUE_OPTIONAL),
131 'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user', VALUE_OPTIONAL),
2b9fe87d
MN
132 'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
133 'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
134 'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
135 'scale' => new external_value(PARAM_INT, 'Scale'),
136 'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'),
137 'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'),
138 'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'),
139 'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'),
140 'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'),
141 'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'),
142 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
143 'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'),
144 'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'),
145 'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'),
146 'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'),
147 'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'),
148 'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'),
7ea6ada3 149 'cmid' => new external_value(PARAM_INT, 'Course module id'),
ea5b910b
JL
150 'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL),
151 'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL),
0f3bbfd4 152 'lockdiscussionafter' => new external_value(PARAM_INT, 'After what period a discussion is locked', VALUE_OPTIONAL),
4669389d 153 'istracked' => new external_value(PARAM_BOOL, 'If the user is tracking the forum', VALUE_OPTIONAL),
2256bb74
JL
154 'unreadpostscount' => new external_value(PARAM_INT, 'The number of unread posts for tracked forums',
155 VALUE_OPTIONAL),
2b9fe87d
MN
156 ), 'forum'
157 )
158 );
159 }
a9a0cb69 160
bc4c7337
AN
161 /**
162 * Get the forum posts in the specified discussion.
163 *
164 * @param int $discussionid
165 * @param string $sortby
166 * @param string $sortdirection
167 * @return array
168 */
169 public static function get_discussion_posts(int $discussionid, ?string $sortby, ?string $sortdirection) {
170 global $USER;
171 // Validate the parameter.
172 $params = self::validate_parameters(self::get_discussion_posts_parameters(), [
173 'discussionid' => $discussionid,
174 'sortby' => $sortby,
175 'sortdirection' => $sortdirection,
176 ]);
177 $warnings = [];
178
179 $vaultfactory = mod_forum\local\container::get_vault_factory();
180
181 $discussionvault = $vaultfactory->get_discussion_vault();
182 $discussion = $discussionvault->get_from_id($params['discussionid']);
183
184 $forumvault = $vaultfactory->get_forum_vault();
185 $forum = $forumvault->get_from_id($discussion->get_forum_id());
186
187 $sortby = $params['sortby'];
188 $sortdirection = $params['sortdirection'];
189 $sortallowedvalues = ['id', 'created', 'modified'];
190 $directionallowedvalues = ['ASC', 'DESC'];
191
192 if (!in_array(strtolower($sortby), $sortallowedvalues)) {
193 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
194 'allowed values are: ' . implode(', ', $sortallowedvalues));
195 }
196
197 $sortdirection = strtoupper($sortdirection);
198 if (!in_array($sortdirection, $directionallowedvalues)) {
199 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
200 'allowed values are: ' . implode(',', $directionallowedvalues));
201 }
202
203 $managerfactory = mod_forum\local\container::get_manager_factory();
204 $capabilitymanager = $managerfactory->get_capability_manager($forum);
205
206 $postvault = $vaultfactory->get_post_vault();
207 $posts = $postvault->get_from_discussion_id(
208 $USER,
209 $discussion->get_id(),
210 $capabilitymanager->can_view_any_private_reply($USER),
211 "{$sortby} {$sortdirection}"
212 );
213
214 $builderfactory = mod_forum\local\container::get_builder_factory();
215 $postbuilder = $builderfactory->get_exported_posts_builder();
216
217 $legacydatamapper = mod_forum\local\container::get_legacy_data_mapper_factory();
218
219 return [
220 'posts' => $postbuilder->build($USER, [$forum], [$discussion], $posts),
221 'ratinginfo' => \core_rating\external\util::get_rating_info(
222 $legacydatamapper->get_forum_data_mapper()->to_legacy_object($forum),
223 $forum->get_context(),
224 'mod_forum',
225 'post',
226 $legacydatamapper->get_post_data_mapper()->to_legacy_objects($posts)
227 ),
228 'warnings' => $warnings,
229 ];
230 }
231
232 /**
233 * Describe the post parameters.
234 *
235 * @return external_function_parameters
236 */
237 public static function get_discussion_posts_parameters() {
238 return new external_function_parameters ([
239 'discussionid' => new external_value(PARAM_INT, 'The ID of the discussion from which to fetch posts.', VALUE_REQUIRED),
240 'sortby' => new external_value(PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
241 'sortdirection' => new external_value(PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
242 ]);
243 }
244
245 /**
246 * Describe the post return format.
247 *
248 * @return external_single_structure
249 */
250 public static function get_discussion_posts_returns() {
251 return new external_single_structure([
252 'posts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
253 'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
254 'warnings' => new external_warnings()
255 ]);
256 }
257
e2ede426
JL
258 /**
259 * Describes the parameters for get_forum_discussion_posts.
260 *
9db43c73 261 * @return external_function_parameters
e2ede426
JL
262 * @since Moodle 2.7
263 */
264 public static function get_forum_discussion_posts_parameters() {
265 return new external_function_parameters (
266 array(
267 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED),
268 'sortby' => new external_value(PARAM_ALPHA,
269 'sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
270 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
271 )
272 );
273 }
274
275 /**
276 * Returns a list of forum posts for a discussion
277 *
278 * @param int $discussionid the post ids
279 * @param string $sortby sort by this element (id, created or modified)
280 * @param string $sortdirection sort direction: ASC or DESC
281 *
282 * @return array the forum post details
283 * @since Moodle 2.7
8245daba 284 * @todo MDL-65252 This will be removed in Moodle 4.1
e2ede426 285 */
fb8840d2 286 public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") {
d85bedf7 287 global $CFG, $DB, $USER, $PAGE;
e2ede426 288
b1aa7dfa 289 $posts = array();
e2ede426
JL
290 $warnings = array();
291
292 // Validate the parameter.
293 $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(),
294 array(
295 'discussionid' => $discussionid,
296 'sortby' => $sortby,
297 'sortdirection' => $sortdirection));
298
299 // Compact/extract functions are not recommended.
300 $discussionid = $params['discussionid'];
301 $sortby = $params['sortby'];
302 $sortdirection = $params['sortdirection'];
303
304 $sortallowedvalues = array('id', 'created', 'modified');
305 if (!in_array($sortby, $sortallowedvalues)) {
306 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
307 'allowed values are: ' . implode(',', $sortallowedvalues));
308 }
309
310 $sortdirection = strtoupper($sortdirection);
311 $directionallowedvalues = array('ASC', 'DESC');
312 if (!in_array($sortdirection, $directionallowedvalues)) {
313 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
314 'allowed values are: ' . implode(',', $directionallowedvalues));
315 }
316
317 $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST);
318 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
319 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
320 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
321
322 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
323 $modcontext = context_module::instance($cm->id);
324 self::validate_context($modcontext);
325
326 // This require must be here, see mod/forum/discuss.php.
327 require_once($CFG->dirroot . "/mod/forum/lib.php");
328
329 // Check they have the view forum capability.
330 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
331
332 if (! $post = forum_get_post_full($discussion->firstpost)) {
333 throw new moodle_exception('notexists', 'forum');
334 }
335
336 // This function check groups, qanda, timed discussions, etc.
337 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
338 throw new moodle_exception('noviewdiscussionspermission', 'forum');
339 }
340
341 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
342
343 // We will add this field in the response.
344 $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
345
346 $forumtracked = forum_tp_is_tracked($forum);
347
348 $sort = 'p.' . $sortby . ' ' . $sortdirection;
b1aa7dfa 349 $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
e2ede426 350
b1aa7dfa 351 foreach ($allposts as $post) {
3e95e09b 352 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) {
e2ede426
JL
353 $warning = array();
354 $warning['item'] = 'post';
355 $warning['itemid'] = $post->id;
356 $warning['warningcode'] = '1';
357 $warning['message'] = 'You can\'t see this post';
358 $warnings[] = $warning;
359 continue;
360 }
361
362 // Function forum_get_all_discussion_posts adds postread field.
f47eeafd
JL
363 // Note that the value returned can be a boolean or an integer. The WS expects a boolean.
364 if (empty($post->postread)) {
b1aa7dfa 365 $post->postread = false;
f47eeafd 366 } else {
b1aa7dfa 367 $post->postread = true;
e2ede426 368 }
f47eeafd 369
bc4c7337
AN
370 $post->isprivatereply = !empty($post->privatereplyto);
371
b1aa7dfa
JL
372 $post->canreply = $canreply;
373 if (!empty($post->children)) {
374 $post->children = array_keys($post->children);
fb8840d2 375 } else {
b1aa7dfa 376 $post->children = array();
e2ede426
JL
377 }
378
3e95e09b
AN
379 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
380 // The post is available, but has been marked as deleted.
381 // It will still be available but filled with a placeholder.
382 $post->userid = null;
383 $post->userfullname = null;
384 $post->userpictureurl = null;
385
386 $post->subject = get_string('privacy:request:delete:post:subject', 'mod_forum');
387 $post->message = get_string('privacy:request:delete:post:message', 'mod_forum');
388
389 $post->deleted = true;
390 $posts[] = $post;
391
392 continue;
393 }
394 $post->deleted = false;
395
30861fbd
JL
396 if (forum_is_author_hidden($post, $forum)) {
397 $post->userid = null;
398 $post->userfullname = null;
399 $post->userpictureurl = null;
400 } else {
401 $user = new stdclass();
402 $user->id = $post->userid;
403 $user = username_load_fields_from_object($user, $post, null, array('picture', 'imagealt', 'email'));
404 $post->userfullname = fullname($user, $canviewfullname);
81f810dc 405
30861fbd
JL
406 $userpicture = new user_picture($user);
407 $userpicture->size = 1; // Size f1.
408 $post->userpictureurl = $userpicture->get_url($PAGE)->out(false);
409 }
14ebc396 410
55bb8189 411 $post->subject = external_format_string($post->subject, $modcontext->id);
48fb0250
JL
412 // Rewrite embedded images URLs.
413 list($post->message, $post->messageformat) =
414 external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_forum', 'post', $post->id);
415
416 // List attachments.
417 if (!empty($post->attachment)) {
14590070 418 $post->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment', $post->id);
48fb0250 419 }
c8743f62
JL
420 $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post', $post->id);
421 if (!empty($messageinlinefiles)) {
422 $post->messageinlinefiles = $messageinlinefiles;
423 }
e2ede426 424
b1aa7dfa 425 $posts[] = $post;
e2ede426
JL
426 }
427
428 $result = array();
429 $result['posts'] = $posts;
b7ce46df 430 $result['ratinginfo'] = \core_rating\external\util::get_rating_info($forum, $modcontext, 'mod_forum', 'post', $posts);
e2ede426
JL
431 $result['warnings'] = $warnings;
432 return $result;
433 }
434
435 /**
436 * Describes the get_forum_discussion_posts return value.
437 *
438 * @return external_single_structure
439 * @since Moodle 2.7
440 */
441 public static function get_forum_discussion_posts_returns() {
442 return new external_single_structure(
443 array(
444 'posts' => new external_multiple_structure(
445 new external_single_structure(
446 array(
447 'id' => new external_value(PARAM_INT, 'Post id'),
448 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
449 'parent' => new external_value(PARAM_INT, 'Parent id'),
450 'userid' => new external_value(PARAM_INT, 'User id'),
451 'created' => new external_value(PARAM_INT, 'Creation time'),
452 'modified' => new external_value(PARAM_INT, 'Time modified'),
453 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
454 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
455 'message' => new external_value(PARAM_RAW, 'The post message'),
48fb0250 456 'messageformat' => new external_format_value('message'),
e2ede426 457 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
c8743f62 458 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL),
48fb0250 459 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
14590070 460 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
e2ede426
JL
461 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
462 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
463 'children' => new external_multiple_structure(new external_value(PARAM_INT, 'children post id')),
464 'canreply' => new external_value(PARAM_BOOL, 'The user can reply to posts?'),
465 'postread' => new external_value(PARAM_BOOL, 'The post was read'),
694bf0c7 466 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
3e95e09b
AN
467 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL),
468 'deleted' => new external_value(PARAM_BOOL, 'This post has been removed.'),
bc4c7337 469 'isprivatereply' => new external_value(PARAM_BOOL, 'The post is a private reply'),
e2ede426
JL
470 ), 'post'
471 )
472 ),
b7ce46df 473 'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
e2ede426
JL
474 'warnings' => new external_warnings()
475 )
476 );
477 }
478
bc4c7337
AN
479 /**
480 * Mark the get_forum_discussion_posts web service as deprecated.
481 *
482 * @return bool
483 */
484 public static function get_forum_discussion_posts_is_deprecated() {
485 return true;
486 }
487
7c51b40a
JL
488 /**
489 * Describes the parameters for get_forum_discussions_paginated.
490 *
9db43c73 491 * @return external_function_parameters
7c51b40a
JL
492 * @since Moodle 2.8
493 */
494 public static function get_forum_discussions_paginated_parameters() {
495 return new external_function_parameters (
496 array(
497 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
498 'sortby' => new external_value(PARAM_ALPHA,
499 'sort by this element: id, timemodified, timestart or timeend', VALUE_DEFAULT, 'timemodified'),
500 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
501 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
502 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
503 )
504 );
505 }
506
507 /**
508 * Returns a list of forum discussions optionally sorted and paginated.
509 *
510 * @param int $forumid the forum instance id
511 * @param string $sortby sort by this element (id, timemodified, timestart or timeend)
512 * @param string $sortdirection sort direction: ASC or DESC
513 * @param int $page page number
514 * @param int $perpage items per page
515 *
516 * @return array the forum discussion details including warnings
517 * @since Moodle 2.8
518 */
519 public static function get_forum_discussions_paginated($forumid, $sortby = 'timemodified', $sortdirection = 'DESC',
520 $page = -1, $perpage = 0) {
d85bedf7 521 global $CFG, $DB, $USER, $PAGE;
7c51b40a
JL
522
523 require_once($CFG->dirroot . "/mod/forum/lib.php");
524
525 $warnings = array();
039c81f0 526 $discussions = array();
7c51b40a
JL
527
528 $params = self::validate_parameters(self::get_forum_discussions_paginated_parameters(),
529 array(
530 'forumid' => $forumid,
531 'sortby' => $sortby,
532 'sortdirection' => $sortdirection,
533 'page' => $page,
534 'perpage' => $perpage
535 )
536 );
537
538 // Compact/extract functions are not recommended.
539 $forumid = $params['forumid'];
540 $sortby = $params['sortby'];
541 $sortdirection = $params['sortdirection'];
542 $page = $params['page'];
543 $perpage = $params['perpage'];
544
545 $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend');
546 if (!in_array($sortby, $sortallowedvalues)) {
547 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
548 'allowed values are: ' . implode(',', $sortallowedvalues));
549 }
550
551 $sortdirection = strtoupper($sortdirection);
552 $directionallowedvalues = array('ASC', 'DESC');
553 if (!in_array($sortdirection, $directionallowedvalues)) {
554 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
555 'allowed values are: ' . implode(',', $directionallowedvalues));
556 }
557
558 $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
559 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
560 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
561
562 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
563 $modcontext = context_module::instance($cm->id);
564 self::validate_context($modcontext);
565
566 // Check they have the view forum capability.
567 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
568
5f219cf1 569 $sort = 'd.pinned DESC, d.' . $sortby . ' ' . $sortdirection;
4f3a2d21 570 $alldiscussions = forum_get_discussions($cm, $sort, true, -1, -1, true, $page, $perpage, FORUM_POSTS_ALL_USER_GROUPS);
7c51b40a 571
039c81f0 572 if ($alldiscussions) {
7c51b40a
JL
573 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
574
575 // Get the unreads array, this takes a forum id and returns data for all discussions.
576 $unreads = array();
577 if ($cantrack = forum_tp_can_track_forums($forum)) {
578 if ($forumtracked = forum_tp_is_tracked($forum)) {
579 $unreads = forum_get_discussions_unread($cm);
580 }
581 }
582 // The forum function returns the replies for all the discussions in a given forum.
bc4c7337
AN
583 $canseeprivatereplies = has_capability('mod/forum:readprivatereplies', $modcontext);
584 $replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage, $canseeprivatereplies);
7c51b40a 585
039c81f0
JL
586 foreach ($alldiscussions as $discussion) {
587
7c51b40a 588 // This function checks for qanda forums.
039c81f0
JL
589 // Note that the forum_get_discussions returns as id the post id, not the discussion id so we need to do this.
590 $discussionrec = clone $discussion;
591 $discussionrec->id = $discussion->discussion;
592 if (!forum_user_can_see_discussion($forum, $discussionrec, $modcontext)) {
7c51b40a
JL
593 $warning = array();
594 // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
595 $warning['item'] = 'post';
596 $warning['itemid'] = $discussion->id;
597 $warning['warningcode'] = '1';
598 $warning['message'] = 'You can\'t see this discussion';
599 $warnings[] = $warning;
600 continue;
601 }
602
603 $discussion->numunread = 0;
604 if ($cantrack && $forumtracked) {
605 if (isset($unreads[$discussion->discussion])) {
606 $discussion->numunread = (int) $unreads[$discussion->discussion];
607 }
608 }
609
610 $discussion->numreplies = 0;
611 if (!empty($replies[$discussion->discussion])) {
612 $discussion->numreplies = (int) $replies[$discussion->discussion]->replies;
613 }
614
55bb8189
JL
615 $discussion->name = external_format_string($discussion->name, $modcontext->id);
616 $discussion->subject = external_format_string($discussion->subject, $modcontext->id);
7c51b40a
JL
617 // Rewrite embedded images URLs.
618 list($discussion->message, $discussion->messageformat) =
619 external_format_text($discussion->message, $discussion->messageformat,
620 $modcontext->id, 'mod_forum', 'post', $discussion->id);
621
622 // List attachments.
623 if (!empty($discussion->attachment)) {
14590070
JL
624 $discussion->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment',
625 $discussion->id);
7c51b40a 626 }
c8743f62
JL
627 $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post', $discussion->id);
628 if (!empty($messageinlinefiles)) {
629 $discussion->messageinlinefiles = $messageinlinefiles;
630 }
7c51b40a 631
0f3bbfd4
AN
632 $discussion->locked = forum_discussion_is_locked($forum, $discussion);
633 $discussion->canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
634
30861fbd
JL
635 if (forum_is_author_hidden($discussion, $forum)) {
636 $discussion->userid = null;
637 $discussion->userfullname = null;
638 $discussion->userpictureurl = null;
639
640 $discussion->usermodified = null;
641 $discussion->usermodifiedfullname = null;
642 $discussion->usermodifiedpictureurl = null;
643 } else {
644 $picturefields = explode(',', user_picture::fields());
645
646 // Load user objects from the results of the query.
647 $user = new stdclass();
648 $user->id = $discussion->userid;
649 $user = username_load_fields_from_object($user, $discussion, null, $picturefields);
650 // Preserve the id, it can be modified by username_load_fields_from_object.
651 $user->id = $discussion->userid;
652 $discussion->userfullname = fullname($user, $canviewfullname);
653
654 $userpicture = new user_picture($user);
655 $userpicture->size = 1; // Size f1.
656 $discussion->userpictureurl = $userpicture->get_url($PAGE)->out(false);
657
658 $usermodified = new stdclass();
659 $usermodified->id = $discussion->usermodified;
660 $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um', $picturefields);
661 // Preserve the id (it can be overwritten due to the prefixed $picturefields).
662 $usermodified->id = $discussion->usermodified;
663 $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname);
664
665 $userpicture = new user_picture($usermodified);
666 $userpicture->size = 1; // Size f1.
667 $discussion->usermodifiedpictureurl = $userpicture->get_url($PAGE)->out(false);
668 }
669
039c81f0 670 $discussions[] = $discussion;
7c51b40a
JL
671 }
672 }
673
674 $result = array();
675 $result['discussions'] = $discussions;
676 $result['warnings'] = $warnings;
677 return $result;
678
679 }
680
681 /**
682 * Describes the get_forum_discussions_paginated return value.
683 *
684 * @return external_single_structure
685 * @since Moodle 2.8
686 */
687 public static function get_forum_discussions_paginated_returns() {
688 return new external_single_structure(
689 array(
690 'discussions' => new external_multiple_structure(
691 new external_single_structure(
692 array(
693 'id' => new external_value(PARAM_INT, 'Post id'),
694 'name' => new external_value(PARAM_TEXT, 'Discussion name'),
695 'groupid' => new external_value(PARAM_INT, 'Group id'),
696 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
697 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
698 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
699 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
700 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
701 'parent' => new external_value(PARAM_INT, 'Parent id'),
702 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
703 'created' => new external_value(PARAM_INT, 'Creation time'),
704 'modified' => new external_value(PARAM_INT, 'Time modified'),
705 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
706 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
707 'message' => new external_value(PARAM_RAW, 'The post message'),
708 'messageformat' => new external_format_value('message'),
709 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
c8743f62 710 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL),
7c51b40a 711 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
14590070 712 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
7c51b40a
JL
713 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
714 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
715 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
716 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
717 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
718 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
719 'numreplies' => new external_value(PARAM_TEXT, 'The number of replies in the discussion'),
5f219cf1 720 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
0f3bbfd4
AN
721 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'),
722 'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'),
723 'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'),
7c51b40a
JL
724 ), 'post'
725 )
726 ),
727 'warnings' => new external_warnings()
728 )
729 );
730 }
731
4a1e44a1
JL
732 /**
733 * Returns description of method parameters
734 *
735 * @return external_function_parameters
736 * @since Moodle 2.9
737 */
738 public static function view_forum_parameters() {
739 return new external_function_parameters(
740 array(
741 'forumid' => new external_value(PARAM_INT, 'forum instance id')
742 )
743 );
744 }
745
746 /**
1c2b7882 747 * Trigger the course module viewed event and update the module completion status.
4a1e44a1
JL
748 *
749 * @param int $forumid the forum instance id
750 * @return array of warnings and status result
751 * @since Moodle 2.9
752 * @throws moodle_exception
753 */
754 public static function view_forum($forumid) {
755 global $DB, $CFG;
756 require_once($CFG->dirroot . "/mod/forum/lib.php");
757
758 $params = self::validate_parameters(self::view_forum_parameters(),
759 array(
760 'forumid' => $forumid
761 ));
762 $warnings = array();
763
764 // Request and permission validation.
ca7e2fc2 765 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
4a1e44a1
JL
766 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
767
768 $context = context_module::instance($cm->id);
769 self::validate_context($context);
770
ea2fa324
JL
771 require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum');
772
4a1e44a1
JL
773 // Call the forum/lib API.
774 forum_view($forum, $course, $cm, $context);
775
776 $result = array();
777 $result['status'] = true;
778 $result['warnings'] = $warnings;
779 return $result;
780 }
781
782 /**
783 * Returns description of method result value
784 *
785 * @return external_description
786 * @since Moodle 2.9
787 */
788 public static function view_forum_returns() {
789 return new external_single_structure(
790 array(
791 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
792 'warnings' => new external_warnings()
793 )
794 );
795 }
796
a3c315dd
JL
797 /**
798 * Returns description of method parameters
799 *
800 * @return external_function_parameters
801 * @since Moodle 2.9
802 */
803 public static function view_forum_discussion_parameters() {
804 return new external_function_parameters(
805 array(
806 'discussionid' => new external_value(PARAM_INT, 'discussion id')
807 )
808 );
809 }
810
811 /**
1c2b7882 812 * Trigger the discussion viewed event.
a3c315dd
JL
813 *
814 * @param int $discussionid the discussion id
815 * @return array of warnings and status result
816 * @since Moodle 2.9
817 * @throws moodle_exception
818 */
819 public static function view_forum_discussion($discussionid) {
db3c9ff8 820 global $DB, $CFG, $USER;
a3c315dd
JL
821 require_once($CFG->dirroot . "/mod/forum/lib.php");
822
823 $params = self::validate_parameters(self::view_forum_discussion_parameters(),
824 array(
825 'discussionid' => $discussionid
826 ));
827 $warnings = array();
828
829 $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST);
830 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
831 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
832
833 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
834 $modcontext = context_module::instance($cm->id);
835 self::validate_context($modcontext);
836
ea2fa324
JL
837 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
838
a3c315dd
JL
839 // Call the forum/lib API.
840 forum_discussion_view($modcontext, $forum, $discussion);
841
db3c9ff8
PFO
842 // Mark as read if required.
843 if (!$CFG->forum_usermarksread && forum_tp_is_tracked($forum)) {
844 forum_tp_mark_discussion_read($USER, $discussion->id);
845 }
846
a3c315dd
JL
847 $result = array();
848 $result['status'] = true;
849 $result['warnings'] = $warnings;
850 return $result;
851 }
852
853 /**
854 * Returns description of method result value
855 *
856 * @return external_description
857 * @since Moodle 2.9
858 */
859 public static function view_forum_discussion_returns() {
860 return new external_single_structure(
861 array(
862 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
863 'warnings' => new external_warnings()
864 )
865 );
866 }
867
50a20317
JL
868 /**
869 * Returns description of method parameters
870 *
871 * @return external_function_parameters
872 * @since Moodle 3.0
873 */
874 public static function add_discussion_post_parameters() {
875 return new external_function_parameters(
876 array(
877 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to
878 (can be the initial discussion post'),
879 'subject' => new external_value(PARAM_TEXT, 'new post subject'),
880 'message' => new external_value(PARAM_RAW, 'new post message (only html format allowed)'),
881 'options' => new external_multiple_structure (
882 new external_single_structure(
883 array(
884 'name' => new external_value(PARAM_ALPHANUM,
885 'The allowed keys (value format) are:
886 discussionsubscribe (bool); subscribe to the discussion?, default to true
bc4c7337 887 private (bool); make this reply private to the author of the parent post, default to false.
48143990 888 inlineattachmentsid (int); the draft file area id for inline attachments
e881c4f5 889 attachmentsid (int); the draft file area id for attachments
50a20317
JL
890 '),
891 'value' => new external_value(PARAM_RAW, 'the value of the option,
892 this param is validated in the external function.'
893 )
894 )
895 ), 'Options', VALUE_DEFAULT, array())
896 )
897 );
898 }
899
900 /**
901 * Create new posts into an existing discussion.
902 *
903 * @param int $postid the post id we are going to reply to
904 * @param string $subject new post subject
905 * @param string $message new post message (only html format allowed)
906 * @param array $options optional settings
907 * @return array of warnings and the new post id
908 * @since Moodle 3.0
909 * @throws moodle_exception
910 */
911 public static function add_discussion_post($postid, $subject, $message, $options = array()) {
912 global $DB, $CFG, $USER;
913 require_once($CFG->dirroot . "/mod/forum/lib.php");
914
915 $params = self::validate_parameters(self::add_discussion_post_parameters(),
609a1073
JL
916 array(
917 'postid' => $postid,
918 'subject' => $subject,
919 'message' => $message,
920 'options' => $options
921 )
922 );
bc4c7337 923
609a1073
JL
924 $warnings = array();
925
926 if (!$parent = forum_get_post_full($params['postid'])) {
927 throw new moodle_exception('invalidparentpostid', 'forum');
928 }
929
930 if (!$discussion = $DB->get_record("forum_discussions", array("id" => $parent->discussion))) {
931 throw new moodle_exception('notpartofdiscussion', 'forum');
932 }
933
934 // Request and permission validation.
935 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
936 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
937
938 $context = context_module::instance($cm->id);
939 self::validate_context($context);
940
50a20317
JL
941 // Validate options.
942 $options = array(
41182118 943 'discussionsubscribe' => true,
bc4c7337 944 'private' => false,
48143990 945 'inlineattachmentsid' => 0,
e881c4f5 946 'attachmentsid' => null
50a20317
JL
947 );
948 foreach ($params['options'] as $option) {
949 $name = trim($option['name']);
950 switch ($name) {
951 case 'discussionsubscribe':
952 $value = clean_param($option['value'], PARAM_BOOL);
953 break;
bc4c7337
AN
954 case 'private':
955 $value = clean_param($option['value'], PARAM_BOOL);
956 break;
48143990 957 case 'inlineattachmentsid':
41182118
BK
958 $value = clean_param($option['value'], PARAM_INT);
959 break;
e881c4f5
BK
960 case 'attachmentsid':
961 $value = clean_param($option['value'], PARAM_INT);
609a1073
JL
962 // Ensure that the user has permissions to create attachments.
963 if (!has_capability('mod/forum:createattachment', $context)) {
964 $value = 0;
965 }
e881c4f5 966 break;
50a20317
JL
967 default:
968 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
969 }
970 $options[$name] = $value;
971 }
972
50a20317
JL
973 if (!forum_user_can_post($forum, $discussion, $USER, $cm, $course, $context)) {
974 throw new moodle_exception('nopostforum', 'forum');
975 }
976
977 $thresholdwarning = forum_check_throttling($forum, $cm);
978 forum_check_blocking_threshold($thresholdwarning);
979
980 // Create the post.
981 $post = new stdClass();
982 $post->discussion = $discussion->id;
983 $post->parent = $parent->id;
984 $post->subject = $params['subject'];
985 $post->message = $params['message'];
986 $post->messageformat = FORMAT_HTML; // Force formatting for now.
987 $post->messagetrust = trusttext_trusted($context);
48143990 988 $post->itemid = $options['inlineattachmentsid'];
3e95e09b 989 $post->attachments = $options['attachmentsid'];
bc4c7337 990 $post->isprivatereply = $options['private'];
3e95e09b 991 $post->deleted = 0;
e881c4f5
BK
992 $fakemform = $post->attachments;
993 if ($postid = forum_add_new_post($post, $fakemform)) {
50a20317
JL
994
995 $post->id = $postid;
996
997 // Trigger events and completion.
998 $params = array(
999 'context' => $context,
1000 'objectid' => $post->id,
1001 'other' => array(
1002 'discussionid' => $discussion->id,
1003 'forumid' => $forum->id,
1004 'forumtype' => $forum->type,
1005 )
1006 );
1007 $event = \mod_forum\event\post_created::create($params);
1008 $event->add_record_snapshot('forum_posts', $post);
1009 $event->add_record_snapshot('forum_discussions', $discussion);
1010 $event->trigger();
1011
1012 // Update completion state.
1013 $completion = new completion_info($course);
1014 if ($completion->is_enabled($cm) &&
1015 ($forum->completionreplies || $forum->completionposts)) {
1016 $completion->update_state($cm, COMPLETION_COMPLETE);
1017 }
1018
1019 $settings = new stdClass();
1020 $settings->discussionsubscribe = $options['discussionsubscribe'];
1021 forum_post_subscription($settings, $forum, $discussion);
1022 } else {
1023 throw new moodle_exception('couldnotadd', 'forum');
1024 }
1025
1026 $result = array();
1027 $result['postid'] = $postid;
1028 $result['warnings'] = $warnings;
1029 return $result;
1030 }
1031
1032 /**
1033 * Returns description of method result value
1034 *
1035 * @return external_description
1036 * @since Moodle 3.0
1037 */
1038 public static function add_discussion_post_returns() {
1039 return new external_single_structure(
1040 array(
1041 'postid' => new external_value(PARAM_INT, 'new post id'),
1042 'warnings' => new external_warnings()
1043 )
1044 );
1045 }
1046
7ab43ac8
JL
1047 /**
1048 * Returns description of method parameters
1049 *
1050 * @return external_function_parameters
1051 * @since Moodle 3.0
1052 */
1053 public static function add_discussion_parameters() {
1054 return new external_function_parameters(
1055 array(
7267daa8
AN
1056 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1057 'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'),
1058 'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'),
aa9059b9 1059 'groupid' => new external_value(PARAM_INT, 'The group, default to 0', VALUE_DEFAULT, 0),
7ab43ac8
JL
1060 'options' => new external_multiple_structure (
1061 new external_single_structure(
1062 array(
1063 'name' => new external_value(PARAM_ALPHANUM,
1064 'The allowed keys (value format) are:
1065 discussionsubscribe (bool); subscribe to the discussion?, default to true
5f219cf1 1066 discussionpinned (bool); is the discussion pinned, default to false
48143990 1067 inlineattachmentsid (int); the draft file area id for inline attachments
e881c4f5 1068 attachmentsid (int); the draft file area id for attachments
7ab43ac8 1069 '),
7267daa8
AN
1070 'value' => new external_value(PARAM_RAW, 'The value of the option,
1071 This param is validated in the external function.'
7ab43ac8
JL
1072 )
1073 )
1074 ), 'Options', VALUE_DEFAULT, array())
1075 )
1076 );
1077 }
1078
1079 /**
1080 * Add a new discussion into an existing forum.
1081 *
1082 * @param int $forumid the forum instance id
1083 * @param string $subject new discussion subject
1084 * @param string $message new discussion message (only html format allowed)
1085 * @param int $groupid the user course group
1086 * @param array $options optional settings
1087 * @return array of warnings and the new discussion id
1088 * @since Moodle 3.0
1089 * @throws moodle_exception
1090 */
aa9059b9 1091 public static function add_discussion($forumid, $subject, $message, $groupid = 0, $options = array()) {
7ab43ac8
JL
1092 global $DB, $CFG;
1093 require_once($CFG->dirroot . "/mod/forum/lib.php");
1094
1095 $params = self::validate_parameters(self::add_discussion_parameters(),
1096 array(
1097 'forumid' => $forumid,
1098 'subject' => $subject,
1099 'message' => $message,
1100 'groupid' => $groupid,
1101 'options' => $options
1102 ));
609a1073
JL
1103
1104 $warnings = array();
1105
1106 // Request and permission validation.
1107 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1108 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1109
1110 $context = context_module::instance($cm->id);
1111 self::validate_context($context);
1112
7ab43ac8
JL
1113 // Validate options.
1114 $options = array(
5f219cf1 1115 'discussionsubscribe' => true,
41182118 1116 'discussionpinned' => false,
48143990 1117 'inlineattachmentsid' => 0,
e881c4f5 1118 'attachmentsid' => null
7ab43ac8
JL
1119 );
1120 foreach ($params['options'] as $option) {
1121 $name = trim($option['name']);
1122 switch ($name) {
1123 case 'discussionsubscribe':
1124 $value = clean_param($option['value'], PARAM_BOOL);
1125 break;
5f219cf1
BK
1126 case 'discussionpinned':
1127 $value = clean_param($option['value'], PARAM_BOOL);
1128 break;
48143990 1129 case 'inlineattachmentsid':
41182118
BK
1130 $value = clean_param($option['value'], PARAM_INT);
1131 break;
e881c4f5
BK
1132 case 'attachmentsid':
1133 $value = clean_param($option['value'], PARAM_INT);
609a1073
JL
1134 // Ensure that the user has permissions to create attachments.
1135 if (!has_capability('mod/forum:createattachment', $context)) {
1136 $value = 0;
1137 }
e881c4f5 1138 break;
7ab43ac8
JL
1139 default:
1140 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1141 }
1142 $options[$name] = $value;
1143 }
1144
7ab43ac8
JL
1145 // Normalize group.
1146 if (!groups_get_activity_groupmode($cm)) {
1147 // Groups not supported, force to -1.
1148 $groupid = -1;
1149 } else {
1150 // Check if we receive the default or and empty value for groupid,
1151 // in this case, get the group for the user in the activity.
aa9059b9 1152 if (empty($params['groupid'])) {
7ab43ac8
JL
1153 $groupid = groups_get_activity_group($cm);
1154 } else {
1155 // Here we rely in the group passed, forum_user_can_post_discussion will validate the group.
1156 $groupid = $params['groupid'];
1157 }
1158 }
1159
1160 if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) {
1161 throw new moodle_exception('cannotcreatediscussion', 'forum');
1162 }
1163
1164 $thresholdwarning = forum_check_throttling($forum, $cm);
1165 forum_check_blocking_threshold($thresholdwarning);
1166
1167 // Create the discussion.
1168 $discussion = new stdClass();
1169 $discussion->course = $course->id;
1170 $discussion->forum = $forum->id;
1171 $discussion->message = $params['message'];
1172 $discussion->messageformat = FORMAT_HTML; // Force formatting for now.
1173 $discussion->messagetrust = trusttext_trusted($context);
48143990 1174 $discussion->itemid = $options['inlineattachmentsid'];
7ab43ac8
JL
1175 $discussion->groupid = $groupid;
1176 $discussion->mailnow = 0;
1177 $discussion->subject = $params['subject'];
1178 $discussion->name = $discussion->subject;
1179 $discussion->timestart = 0;
1180 $discussion->timeend = 0;
e881c4f5
BK
1181 $discussion->attachments = $options['attachmentsid'];
1182
5f219cf1
BK
1183 if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) {
1184 $discussion->pinned = FORUM_DISCUSSION_PINNED;
1185 } else {
1186 $discussion->pinned = FORUM_DISCUSSION_UNPINNED;
1187 }
e881c4f5
BK
1188 $fakemform = $options['attachmentsid'];
1189 if ($discussionid = forum_add_discussion($discussion, $fakemform)) {
7ab43ac8
JL
1190
1191 $discussion->id = $discussionid;
1192
1193 // Trigger events and completion.
1194
1195 $params = array(
1196 'context' => $context,
1197 'objectid' => $discussion->id,
1198 'other' => array(
1199 'forumid' => $forum->id,
1200 )
1201 );
1202 $event = \mod_forum\event\discussion_created::create($params);
1203 $event->add_record_snapshot('forum_discussions', $discussion);
1204 $event->trigger();
1205
1206 $completion = new completion_info($course);
1207 if ($completion->is_enabled($cm) &&
1208 ($forum->completiondiscussions || $forum->completionposts)) {
1209 $completion->update_state($cm, COMPLETION_COMPLETE);
1210 }
1211
1212 $settings = new stdClass();
1213 $settings->discussionsubscribe = $options['discussionsubscribe'];
1214 forum_post_subscription($settings, $forum, $discussion);
1215 } else {
1216 throw new moodle_exception('couldnotadd', 'forum');
1217 }
1218
1219 $result = array();
1220 $result['discussionid'] = $discussionid;
1221 $result['warnings'] = $warnings;
1222 return $result;
1223 }
1224
1225 /**
1226 * Returns description of method result value
1227 *
1228 * @return external_description
1229 * @since Moodle 3.0
1230 */
1231 public static function add_discussion_returns() {
1232 return new external_single_structure(
1233 array(
7267daa8 1234 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'),
7ab43ac8
JL
1235 'warnings' => new external_warnings()
1236 )
1237 );
1238 }
1239
04cd8ae3
JL
1240 /**
1241 * Returns description of method parameters
1242 *
1243 * @return external_function_parameters
1244 * @since Moodle 3.1
1245 */
1246 public static function can_add_discussion_parameters() {
1247 return new external_function_parameters(
1248 array(
1249 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1250 'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group.
1251 Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null)
1252 )
1253 );
1254 }
1255
1256 /**
1257 * Check if the current user can add discussions in the given forum (and optionally for the given group).
1258 *
1259 * @param int $forumid the forum instance id
1260 * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups.
1261 * @return array of warnings and the status (true if the user can add discussions)
1262 * @since Moodle 3.1
1263 * @throws moodle_exception
1264 */
1265 public static function can_add_discussion($forumid, $groupid = null) {
1266 global $DB, $CFG;
1267 require_once($CFG->dirroot . "/mod/forum/lib.php");
1268
1269 $params = self::validate_parameters(self::can_add_discussion_parameters(),
1270 array(
1271 'forumid' => $forumid,
1272 'groupid' => $groupid,
1273 ));
1274 $warnings = array();
1275
1276 // Request and permission validation.
1277 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1278 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1279
1280 $context = context_module::instance($cm->id);
1281 self::validate_context($context);
1282
1283 $status = forum_user_can_post_discussion($forum, $params['groupid'], -1, $cm, $context);
1284
1285 $result = array();
1286 $result['status'] = $status;
581e75bf
JL
1287 $result['canpindiscussions'] = has_capability('mod/forum:pindiscussions', $context);
1288 $result['cancreateattachment'] = forum_can_create_attachment($forum, $context);
04cd8ae3
JL
1289 $result['warnings'] = $warnings;
1290 return $result;
1291 }
1292
1293 /**
1294 * Returns description of method result value
1295 *
1296 * @return external_description
1297 * @since Moodle 3.1
1298 */
1299 public static function can_add_discussion_returns() {
1300 return new external_single_structure(
1301 array(
1302 'status' => new external_value(PARAM_BOOL, 'True if the user can add discussions, false otherwise.'),
581e75bf
JL
1303 'canpindiscussions' => new external_value(PARAM_BOOL, 'True if the user can pin discussions, false otherwise.',
1304 VALUE_OPTIONAL),
1305 'cancreateattachment' => new external_value(PARAM_BOOL, 'True if the user can add attachments, false otherwise.',
1306 VALUE_OPTIONAL),
04cd8ae3
JL
1307 'warnings' => new external_warnings()
1308 )
1309 );
1310 }
1311
4daa0d08
JL
1312 /**
1313 * Describes the parameters for get_forum_access_information.
1314 *
1315 * @return external_external_function_parameters
1316 * @since Moodle 3.7
1317 */
1318 public static function get_forum_access_information_parameters() {
1319 return new external_function_parameters (
1320 array(
1321 'forumid' => new external_value(PARAM_INT, 'Forum instance id.')
1322 )
1323 );
1324 }
1325
1326 /**
1327 * Return access information for a given forum.
1328 *
1329 * @param int $forumid forum instance id
1330 * @return array of warnings and the access information
1331 * @since Moodle 3.7
1332 * @throws moodle_exception
1333 */
1334 public static function get_forum_access_information($forumid) {
1335 global $DB;
1336
1337 $params = self::validate_parameters(self::get_forum_access_information_parameters(), array('forumid' => $forumid));
1338
1339 // Request and permission validation.
1340 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1341 $cm = get_coursemodule_from_instance('forum', $forum->id);
1342
1343 $context = context_module::instance($cm->id);
1344 self::validate_context($context);
1345
1346 $result = array();
1347 // Return all the available capabilities.
1348 $capabilities = load_capability_def('mod_forum');
1349 foreach ($capabilities as $capname => $capdata) {
1350 // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
1351 $field = 'can' . str_replace('mod/forum:', '', $capname);
1352 $result[$field] = has_capability($capname, $context);
1353 }
1354
1355 $result['warnings'] = array();
1356 return $result;
1357 }
1358
1359 /**
1360 * Describes the get_forum_access_information return value.
1361 *
1362 * @return external_single_structure
1363 * @since Moodle 3.7
1364 */
1365 public static function get_forum_access_information_returns() {
1366
1367 $structure = array(
1368 'warnings' => new external_warnings()
1369 );
1370
1371 $capabilities = load_capability_def('mod_forum');
1372 foreach ($capabilities as $capname => $capdata) {
1373 // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
1374 $field = 'can' . str_replace('mod/forum:', '', $capname);
1375 $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.',
1376 VALUE_OPTIONAL);
1377 }
1378
1379 return new external_single_structure($structure);
1380 }
2646e9d6
RW
1381
1382 /**
1383 * Set the subscription state.
1384 *
1385 * @param int $forumid
1386 * @param int $discussionid
1387 * @param bool $targetstate
1388 * @return \stdClass
1389 */
1390 public static function set_subscription_state($forumid, $discussionid, $targetstate) {
f30f46db 1391 global $PAGE, $USER;
2646e9d6
RW
1392
1393 $params = self::validate_parameters(self::set_subscription_state_parameters(), [
1394 'forumid' => $forumid,
1395 'discussionid' => $discussionid,
1396 'targetstate' => $targetstate
1397 ]);
1398
1399 $vaultfactory = mod_forum\local\container::get_vault_factory();
1400 $forumvault = $vaultfactory->get_forum_vault();
1401 $forum = $forumvault->get_from_id($params['forumid']);
2646e9d6 1402 $coursemodule = $forum->get_course_module_record();
96a49734 1403 $context = $forum->get_context();
2646e9d6 1404
96a49734 1405 self::validate_context($context);
2646e9d6 1406
2646e9d6
RW
1407 $discussionvault = $vaultfactory->get_discussion_vault();
1408 $discussion = $discussionvault->get_from_id($params['discussionid']);
1409 $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
1410
1411 $forumrecord = $legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
1412 $discussionrecord = $legacydatamapperfactory->get_discussion_data_mapper()->to_legacy_object($discussion);
1413
1414 if (!\mod_forum\subscriptions::is_subscribable($forumrecord)) {
1415 // Nothing to do. We won't actually output any content here though.
1416 throw new \moodle_exception('cannotsubscribe', 'mod_forum');
1417 }
1418
1419 $issubscribed = \mod_forum\subscriptions::is_subscribed(
1420 $USER->id,
1421 $forumrecord,
1422 $discussion->get_id(),
1423 $coursemodule
1424 );
1425
1426 // If the current state doesn't equal the desired state then update the current
1427 // state to the desired state.
1428 if ($issubscribed != (bool) $params['targetstate']) {
1429 if ($params['targetstate']) {
1430 \mod_forum\subscriptions::subscribe_user_to_discussion($USER->id, $discussionrecord, $context);
1431 } else {
1432 \mod_forum\subscriptions::unsubscribe_user_from_discussion($USER->id, $discussionrecord, $context);
1433 }
1434 }
1435
1436 $exporterfactory = mod_forum\local\container::get_exporter_factory();
1437 $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1438 return $exporter->export($PAGE->get_renderer('mod_forum'));
1439 }
1440
1441 /**
1442 * Returns description of method parameters.
1443 *
1444 * @return external_function_parameters
1445 */
1446 public static function set_subscription_state_parameters() {
1447 return new external_function_parameters(
1448 [
1449 'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'),
1450 'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'),
1451 'targetstate' => new external_value(PARAM_BOOL, 'The target state')
1452 ]
1453 );
1454 }
1455
1456 /**
1457 * Returns description of method result value.
1458 *
1459 * @return external_description
1460 */
1461 public static function set_subscription_state_returns() {
1462 return \mod_forum\local\exporters\discussion::get_read_structure();
1463 }
2b9fe87d 1464}