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