MDL-65557 forum: increase default image size of author profile url
[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
9b4f09ba 30use mod_forum\local\exporters\post as post_exporter;
99bda8a7 31use mod_forum\local\exporters\discussion as discussion_exporter;
9b4f09ba 32
2b9fe87d
MN
33class mod_forum_external extends external_api {
34
35 /**
a9a0cb69 36 * Describes the parameters for get_forum.
2b9fe87d 37 *
9db43c73 38 * @return external_function_parameters
2b9fe87d
MN
39 * @since Moodle 2.5
40 */
41 public static function get_forums_by_courses_parameters() {
42 return new external_function_parameters (
43 array(
44 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID',
a69c9abd 45 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Course IDs', VALUE_DEFAULT, array()),
2b9fe87d
MN
46 )
47 );
48 }
49
50 /**
51 * Returns a list of forums in a provided list of courses,
52 * if no list is provided all forums that the user can view
53 * will be returned.
54 *
55 * @param array $courseids the course ids
56 * @return array the forum details
57 * @since Moodle 2.5
58 */
59 public static function get_forums_by_courses($courseids = array()) {
c8f1d8a0 60 global $CFG;
2b9fe87d
MN
61
62 require_once($CFG->dirroot . "/mod/forum/lib.php");
63
a9a0cb69
MN
64 $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids));
65
583b02e4 66 $courses = array();
a9a0cb69 67 if (empty($params['courseids'])) {
583b02e4
FM
68 $courses = enrol_get_my_courses();
69 $params['courseids'] = array_keys($courses);
2b9fe87d
MN
70 }
71
72 // Array to store the forums to return.
73 $arrforums = array();
ea5b910b 74 $warnings = array();
2b9fe87d 75
0c246ae5 76 // Ensure there are courseids to loop through.
ea5b910b
JL
77 if (!empty($params['courseids'])) {
78
583b02e4 79 list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
c8f1d8a0
JL
80
81 // Get the forums in this course. This function checks users visibility permissions.
ea5b910b
JL
82 $forums = get_all_instances_in_courses("forum", $courses);
83 foreach ($forums as $forum) {
84
85 $course = $courses[$forum->course];
86 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
87 $context = context_module::instance($cm->id);
88
89 // Skip forums we are not allowed to see discussions.
90 if (!has_capability('mod/forum:viewdiscussion', $context)) {
91 continue;
2b9fe87d 92 }
ea5b910b 93
5b587c75 94 $forum->name = external_format_string($forum->name, $context->id);
ea5b910b
JL
95 // Format the intro before being returning using the format setting.
96 list($forum->intro, $forum->introformat) = external_format_text($forum->intro, $forum->introformat,
d33c67bc 97 $context->id, 'mod_forum', 'intro', null);
7ef49bd3 98 $forum->introfiles = external_util::get_area_files($context->id, 'mod_forum', 'intro', false, false);
ea5b910b
JL
99 // Discussions count. This function does static request cache.
100 $forum->numdiscussions = forum_count_discussions($forum, $cm, $course);
101 $forum->cmid = $forum->coursemodule;
102 $forum->cancreatediscussions = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
4669389d 103 $forum->istracked = forum_tp_is_tracked($forum);
2256bb74
JL
104 if ($forum->istracked) {
105 $forum->unreadpostscount = forum_tp_count_forum_unread_posts($cm, $course);
106 }
ea5b910b
JL
107
108 // Add the forum to the array to return.
109 $arrforums[$forum->id] = $forum;
2b9fe87d
MN
110 }
111 }
112
113 return $arrforums;
114 }
115
116 /**
a9a0cb69 117 * Describes the get_forum return value.
2b9fe87d
MN
118 *
119 * @return external_single_structure
120 * @since Moodle 2.5
121 */
0f3bbfd4 122 public static function get_forums_by_courses_returns() {
2b9fe87d
MN
123 return new external_multiple_structure(
124 new external_single_structure(
125 array(
126 'id' => new external_value(PARAM_INT, 'Forum id'),
5b587c75 127 'course' => new external_value(PARAM_INT, 'Course id'),
2b9fe87d 128 'type' => new external_value(PARAM_TEXT, 'The forum type'),
5b587c75 129 'name' => new external_value(PARAM_RAW, 'Forum name'),
2b9fe87d
MN
130 'intro' => new external_value(PARAM_RAW, 'The forum intro'),
131 'introformat' => new external_format_value('intro'),
7ef49bd3 132 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
99bcb318
SR
133 'duedate' => new external_value(PARAM_INT, 'duedate for the user', VALUE_OPTIONAL),
134 'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user', VALUE_OPTIONAL),
2b9fe87d
MN
135 'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
136 'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
137 'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
138 'scale' => new external_value(PARAM_INT, 'Scale'),
139 'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'),
140 'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'),
141 'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'),
142 'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'),
143 'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'),
144 'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'),
145 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
146 'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'),
147 'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'),
148 'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'),
149 'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'),
150 'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'),
151 'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'),
7ea6ada3 152 'cmid' => new external_value(PARAM_INT, 'Course module id'),
ea5b910b
JL
153 'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL),
154 'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL),
0f3bbfd4 155 'lockdiscussionafter' => new external_value(PARAM_INT, 'After what period a discussion is locked', VALUE_OPTIONAL),
4669389d 156 'istracked' => new external_value(PARAM_BOOL, 'If the user is tracking the forum', VALUE_OPTIONAL),
2256bb74
JL
157 'unreadpostscount' => new external_value(PARAM_INT, 'The number of unread posts for tracked forums',
158 VALUE_OPTIONAL),
2b9fe87d
MN
159 ), 'forum'
160 )
161 );
162 }
a9a0cb69 163
bc4c7337
AN
164 /**
165 * Get the forum posts in the specified discussion.
166 *
167 * @param int $discussionid
168 * @param string $sortby
169 * @param string $sortdirection
170 * @return array
171 */
172 public static function get_discussion_posts(int $discussionid, ?string $sortby, ?string $sortdirection) {
173 global $USER;
174 // Validate the parameter.
175 $params = self::validate_parameters(self::get_discussion_posts_parameters(), [
176 'discussionid' => $discussionid,
177 'sortby' => $sortby,
178 'sortdirection' => $sortdirection,
179 ]);
180 $warnings = [];
181
182 $vaultfactory = mod_forum\local\container::get_vault_factory();
183
184 $discussionvault = $vaultfactory->get_discussion_vault();
185 $discussion = $discussionvault->get_from_id($params['discussionid']);
186
187 $forumvault = $vaultfactory->get_forum_vault();
188 $forum = $forumvault->get_from_id($discussion->get_forum_id());
189
190 $sortby = $params['sortby'];
191 $sortdirection = $params['sortdirection'];
192 $sortallowedvalues = ['id', 'created', 'modified'];
193 $directionallowedvalues = ['ASC', 'DESC'];
194
195 if (!in_array(strtolower($sortby), $sortallowedvalues)) {
196 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
197 'allowed values are: ' . implode(', ', $sortallowedvalues));
198 }
199
200 $sortdirection = strtoupper($sortdirection);
201 if (!in_array($sortdirection, $directionallowedvalues)) {
202 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
203 'allowed values are: ' . implode(',', $directionallowedvalues));
204 }
205
206 $managerfactory = mod_forum\local\container::get_manager_factory();
207 $capabilitymanager = $managerfactory->get_capability_manager($forum);
208
209 $postvault = $vaultfactory->get_post_vault();
210 $posts = $postvault->get_from_discussion_id(
211 $USER,
212 $discussion->get_id(),
213 $capabilitymanager->can_view_any_private_reply($USER),
214 "{$sortby} {$sortdirection}"
215 );
216
217 $builderfactory = mod_forum\local\container::get_builder_factory();
218 $postbuilder = $builderfactory->get_exported_posts_builder();
219
220 $legacydatamapper = mod_forum\local\container::get_legacy_data_mapper_factory();
221
222 return [
223 'posts' => $postbuilder->build($USER, [$forum], [$discussion], $posts),
224 'ratinginfo' => \core_rating\external\util::get_rating_info(
225 $legacydatamapper->get_forum_data_mapper()->to_legacy_object($forum),
226 $forum->get_context(),
227 'mod_forum',
228 'post',
229 $legacydatamapper->get_post_data_mapper()->to_legacy_objects($posts)
230 ),
231 'warnings' => $warnings,
232 ];
233 }
234
235 /**
236 * Describe the post parameters.
237 *
238 * @return external_function_parameters
239 */
240 public static function get_discussion_posts_parameters() {
241 return new external_function_parameters ([
242 'discussionid' => new external_value(PARAM_INT, 'The ID of the discussion from which to fetch posts.', VALUE_REQUIRED),
243 'sortby' => new external_value(PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
244 'sortdirection' => new external_value(PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
245 ]);
246 }
247
248 /**
249 * Describe the post return format.
250 *
251 * @return external_single_structure
252 */
253 public static function get_discussion_posts_returns() {
254 return new external_single_structure([
255 'posts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
256 'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
257 'warnings' => new external_warnings()
258 ]);
259 }
260
e2ede426
JL
261 /**
262 * Describes the parameters for get_forum_discussion_posts.
263 *
9db43c73 264 * @return external_function_parameters
e2ede426
JL
265 * @since Moodle 2.7
266 */
267 public static function get_forum_discussion_posts_parameters() {
268 return new external_function_parameters (
269 array(
270 'discussionid' => new external_value(PARAM_INT, 'discussion ID', VALUE_REQUIRED),
271 'sortby' => new external_value(PARAM_ALPHA,
272 'sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
273 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
274 )
275 );
276 }
277
278 /**
279 * Returns a list of forum posts for a discussion
280 *
281 * @param int $discussionid the post ids
282 * @param string $sortby sort by this element (id, created or modified)
283 * @param string $sortdirection sort direction: ASC or DESC
284 *
285 * @return array the forum post details
286 * @since Moodle 2.7
8245daba 287 * @todo MDL-65252 This will be removed in Moodle 4.1
e2ede426 288 */
fb8840d2 289 public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") {
d85bedf7 290 global $CFG, $DB, $USER, $PAGE;
e2ede426 291
b1aa7dfa 292 $posts = array();
e2ede426
JL
293 $warnings = array();
294
295 // Validate the parameter.
296 $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(),
297 array(
298 'discussionid' => $discussionid,
299 'sortby' => $sortby,
300 'sortdirection' => $sortdirection));
301
302 // Compact/extract functions are not recommended.
303 $discussionid = $params['discussionid'];
304 $sortby = $params['sortby'];
305 $sortdirection = $params['sortdirection'];
306
307 $sortallowedvalues = array('id', 'created', 'modified');
308 if (!in_array($sortby, $sortallowedvalues)) {
309 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
310 'allowed values are: ' . implode(',', $sortallowedvalues));
311 }
312
313 $sortdirection = strtoupper($sortdirection);
314 $directionallowedvalues = array('ASC', 'DESC');
315 if (!in_array($sortdirection, $directionallowedvalues)) {
316 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
317 'allowed values are: ' . implode(',', $directionallowedvalues));
318 }
319
320 $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST);
321 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
322 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
323 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
324
325 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
326 $modcontext = context_module::instance($cm->id);
327 self::validate_context($modcontext);
328
329 // This require must be here, see mod/forum/discuss.php.
330 require_once($CFG->dirroot . "/mod/forum/lib.php");
331
332 // Check they have the view forum capability.
333 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
334
335 if (! $post = forum_get_post_full($discussion->firstpost)) {
336 throw new moodle_exception('notexists', 'forum');
337 }
338
339 // This function check groups, qanda, timed discussions, etc.
340 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
341 throw new moodle_exception('noviewdiscussionspermission', 'forum');
342 }
343
344 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
345
346 // We will add this field in the response.
347 $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
348
349 $forumtracked = forum_tp_is_tracked($forum);
350
351 $sort = 'p.' . $sortby . ' ' . $sortdirection;
b1aa7dfa 352 $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
e2ede426 353
b1aa7dfa 354 foreach ($allposts as $post) {
3e95e09b 355 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) {
e2ede426
JL
356 $warning = array();
357 $warning['item'] = 'post';
358 $warning['itemid'] = $post->id;
359 $warning['warningcode'] = '1';
360 $warning['message'] = 'You can\'t see this post';
361 $warnings[] = $warning;
362 continue;
363 }
364
365 // Function forum_get_all_discussion_posts adds postread field.
f47eeafd
JL
366 // Note that the value returned can be a boolean or an integer. The WS expects a boolean.
367 if (empty($post->postread)) {
b1aa7dfa 368 $post->postread = false;
f47eeafd 369 } else {
b1aa7dfa 370 $post->postread = true;
e2ede426 371 }
f47eeafd 372
bc4c7337
AN
373 $post->isprivatereply = !empty($post->privatereplyto);
374
b1aa7dfa
JL
375 $post->canreply = $canreply;
376 if (!empty($post->children)) {
377 $post->children = array_keys($post->children);
fb8840d2 378 } else {
b1aa7dfa 379 $post->children = array();
e2ede426
JL
380 }
381
3e95e09b
AN
382 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
383 // The post is available, but has been marked as deleted.
384 // It will still be available but filled with a placeholder.
385 $post->userid = null;
386 $post->userfullname = null;
387 $post->userpictureurl = null;
388
389 $post->subject = get_string('privacy:request:delete:post:subject', 'mod_forum');
390 $post->message = get_string('privacy:request:delete:post:message', 'mod_forum');
391
392 $post->deleted = true;
393 $posts[] = $post;
394
395 continue;
396 }
397 $post->deleted = false;
398
30861fbd
JL
399 if (forum_is_author_hidden($post, $forum)) {
400 $post->userid = null;
401 $post->userfullname = null;
402 $post->userpictureurl = null;
403 } else {
404 $user = new stdclass();
405 $user->id = $post->userid;
406 $user = username_load_fields_from_object($user, $post, null, array('picture', 'imagealt', 'email'));
407 $post->userfullname = fullname($user, $canviewfullname);
81f810dc 408
30861fbd
JL
409 $userpicture = new user_picture($user);
410 $userpicture->size = 1; // Size f1.
411 $post->userpictureurl = $userpicture->get_url($PAGE)->out(false);
412 }
14ebc396 413
55bb8189 414 $post->subject = external_format_string($post->subject, $modcontext->id);
48fb0250
JL
415 // Rewrite embedded images URLs.
416 list($post->message, $post->messageformat) =
417 external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_forum', 'post', $post->id);
418
419 // List attachments.
420 if (!empty($post->attachment)) {
14590070 421 $post->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment', $post->id);
48fb0250 422 }
c8743f62
JL
423 $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post', $post->id);
424 if (!empty($messageinlinefiles)) {
425 $post->messageinlinefiles = $messageinlinefiles;
426 }
6c344ff2
JL
427 // Post tags.
428 $post->tags = \core_tag\external\util::get_item_tags('mod_forum', 'forum_posts', $post->id);
e2ede426 429
b1aa7dfa 430 $posts[] = $post;
e2ede426
JL
431 }
432
433 $result = array();
434 $result['posts'] = $posts;
b7ce46df 435 $result['ratinginfo'] = \core_rating\external\util::get_rating_info($forum, $modcontext, 'mod_forum', 'post', $posts);
e2ede426
JL
436 $result['warnings'] = $warnings;
437 return $result;
438 }
439
440 /**
441 * Describes the get_forum_discussion_posts return value.
442 *
443 * @return external_single_structure
444 * @since Moodle 2.7
445 */
446 public static function get_forum_discussion_posts_returns() {
447 return new external_single_structure(
448 array(
449 'posts' => new external_multiple_structure(
450 new external_single_structure(
451 array(
452 'id' => new external_value(PARAM_INT, 'Post id'),
453 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
454 'parent' => new external_value(PARAM_INT, 'Parent id'),
455 'userid' => new external_value(PARAM_INT, 'User id'),
456 'created' => new external_value(PARAM_INT, 'Creation time'),
457 'modified' => new external_value(PARAM_INT, 'Time modified'),
458 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
459 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
460 'message' => new external_value(PARAM_RAW, 'The post message'),
48fb0250 461 'messageformat' => new external_format_value('message'),
e2ede426 462 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
c8743f62 463 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL),
48fb0250 464 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
14590070 465 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
e2ede426
JL
466 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
467 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
468 'children' => new external_multiple_structure(new external_value(PARAM_INT, 'children post id')),
469 'canreply' => new external_value(PARAM_BOOL, 'The user can reply to posts?'),
470 'postread' => new external_value(PARAM_BOOL, 'The post was read'),
694bf0c7 471 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
3e95e09b
AN
472 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL),
473 'deleted' => new external_value(PARAM_BOOL, 'This post has been removed.'),
bc4c7337 474 'isprivatereply' => new external_value(PARAM_BOOL, 'The post is a private reply'),
6c344ff2
JL
475 'tags' => new external_multiple_structure(
476 \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL
477 ),
e2ede426
JL
478 ), 'post'
479 )
480 ),
b7ce46df 481 'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
e2ede426
JL
482 'warnings' => new external_warnings()
483 )
484 );
485 }
486
bc4c7337
AN
487 /**
488 * Mark the get_forum_discussion_posts web service as deprecated.
489 *
490 * @return bool
491 */
492 public static function get_forum_discussion_posts_is_deprecated() {
493 return true;
494 }
495
7c51b40a
JL
496 /**
497 * Describes the parameters for get_forum_discussions_paginated.
498 *
1a9c60e9 499 * @deprecated since 3.7
9db43c73 500 * @return external_function_parameters
7c51b40a
JL
501 * @since Moodle 2.8
502 */
503 public static function get_forum_discussions_paginated_parameters() {
504 return new external_function_parameters (
505 array(
506 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
507 'sortby' => new external_value(PARAM_ALPHA,
508 'sort by this element: id, timemodified, timestart or timeend', VALUE_DEFAULT, 'timemodified'),
509 'sortdirection' => new external_value(PARAM_ALPHA, 'sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
510 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
511 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
512 )
513 );
514 }
515
516 /**
517 * Returns a list of forum discussions optionally sorted and paginated.
518 *
1a9c60e9 519 * @deprecated since 3.7
7c51b40a
JL
520 * @param int $forumid the forum instance id
521 * @param string $sortby sort by this element (id, timemodified, timestart or timeend)
522 * @param string $sortdirection sort direction: ASC or DESC
523 * @param int $page page number
524 * @param int $perpage items per page
525 *
526 * @return array the forum discussion details including warnings
527 * @since Moodle 2.8
528 */
529 public static function get_forum_discussions_paginated($forumid, $sortby = 'timemodified', $sortdirection = 'DESC',
1a9c60e9 530 $page = -1, $perpage = 0) {
d85bedf7 531 global $CFG, $DB, $USER, $PAGE;
7c51b40a
JL
532
533 require_once($CFG->dirroot . "/mod/forum/lib.php");
534
535 $warnings = array();
039c81f0 536 $discussions = array();
7c51b40a
JL
537
538 $params = self::validate_parameters(self::get_forum_discussions_paginated_parameters(),
539 array(
540 'forumid' => $forumid,
541 'sortby' => $sortby,
542 'sortdirection' => $sortdirection,
543 'page' => $page,
544 'perpage' => $perpage
545 )
546 );
547
548 // Compact/extract functions are not recommended.
549 $forumid = $params['forumid'];
550 $sortby = $params['sortby'];
551 $sortdirection = $params['sortdirection'];
552 $page = $params['page'];
553 $perpage = $params['perpage'];
554
555 $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend');
556 if (!in_array($sortby, $sortallowedvalues)) {
557 throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
558 'allowed values are: ' . implode(',', $sortallowedvalues));
559 }
560
561 $sortdirection = strtoupper($sortdirection);
562 $directionallowedvalues = array('ASC', 'DESC');
563 if (!in_array($sortdirection, $directionallowedvalues)) {
564 throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
565 'allowed values are: ' . implode(',', $directionallowedvalues));
566 }
567
568 $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
569 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
570 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id, false, MUST_EXIST);
571
572 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
573 $modcontext = context_module::instance($cm->id);
574 self::validate_context($modcontext);
575
576 // Check they have the view forum capability.
577 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
578
5f219cf1 579 $sort = 'd.pinned DESC, d.' . $sortby . ' ' . $sortdirection;
4f3a2d21 580 $alldiscussions = forum_get_discussions($cm, $sort, true, -1, -1, true, $page, $perpage, FORUM_POSTS_ALL_USER_GROUPS);
7c51b40a 581
039c81f0 582 if ($alldiscussions) {
7c51b40a
JL
583 $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext);
584
585 // Get the unreads array, this takes a forum id and returns data for all discussions.
586 $unreads = array();
587 if ($cantrack = forum_tp_can_track_forums($forum)) {
588 if ($forumtracked = forum_tp_is_tracked($forum)) {
589 $unreads = forum_get_discussions_unread($cm);
590 }
591 }
592 // The forum function returns the replies for all the discussions in a given forum.
bc4c7337 593 $canseeprivatereplies = has_capability('mod/forum:readprivatereplies', $modcontext);
565cccfa 594 $canlock = has_capability('moodle/course:manageactivities', $modcontext, $USER);
bc4c7337 595 $replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage, $canseeprivatereplies);
7c51b40a 596
039c81f0
JL
597 foreach ($alldiscussions as $discussion) {
598
7c51b40a 599 // This function checks for qanda forums.
039c81f0
JL
600 // Note that the forum_get_discussions returns as id the post id, not the discussion id so we need to do this.
601 $discussionrec = clone $discussion;
602 $discussionrec->id = $discussion->discussion;
603 if (!forum_user_can_see_discussion($forum, $discussionrec, $modcontext)) {
7c51b40a
JL
604 $warning = array();
605 // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
606 $warning['item'] = 'post';
607 $warning['itemid'] = $discussion->id;
608 $warning['warningcode'] = '1';
609 $warning['message'] = 'You can\'t see this discussion';
610 $warnings[] = $warning;
611 continue;
612 }
613
614 $discussion->numunread = 0;
615 if ($cantrack && $forumtracked) {
616 if (isset($unreads[$discussion->discussion])) {
617 $discussion->numunread = (int) $unreads[$discussion->discussion];
618 }
619 }
620
621 $discussion->numreplies = 0;
622 if (!empty($replies[$discussion->discussion])) {
623 $discussion->numreplies = (int) $replies[$discussion->discussion]->replies;
624 }
625
55bb8189
JL
626 $discussion->name = external_format_string($discussion->name, $modcontext->id);
627 $discussion->subject = external_format_string($discussion->subject, $modcontext->id);
7c51b40a
JL
628 // Rewrite embedded images URLs.
629 list($discussion->message, $discussion->messageformat) =
630 external_format_text($discussion->message, $discussion->messageformat,
631 $modcontext->id, 'mod_forum', 'post', $discussion->id);
632
633 // List attachments.
634 if (!empty($discussion->attachment)) {
14590070
JL
635 $discussion->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment',
636 $discussion->id);
7c51b40a 637 }
c8743f62
JL
638 $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post', $discussion->id);
639 if (!empty($messageinlinefiles)) {
640 $discussion->messageinlinefiles = $messageinlinefiles;
641 }
7c51b40a 642
565cccfa
P
643 $discussion->locked = forum_discussion_is_locked($forum, $discussion);
644 $discussion->canlock = $canlock;
0f3bbfd4
AN
645 $discussion->canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
646
30861fbd
JL
647 if (forum_is_author_hidden($discussion, $forum)) {
648 $discussion->userid = null;
649 $discussion->userfullname = null;
650 $discussion->userpictureurl = null;
651
652 $discussion->usermodified = null;
653 $discussion->usermodifiedfullname = null;
654 $discussion->usermodifiedpictureurl = null;
655 } else {
656 $picturefields = explode(',', user_picture::fields());
657
658 // Load user objects from the results of the query.
659 $user = new stdclass();
660 $user->id = $discussion->userid;
661 $user = username_load_fields_from_object($user, $discussion, null, $picturefields);
662 // Preserve the id, it can be modified by username_load_fields_from_object.
663 $user->id = $discussion->userid;
664 $discussion->userfullname = fullname($user, $canviewfullname);
665
666 $userpicture = new user_picture($user);
667 $userpicture->size = 1; // Size f1.
668 $discussion->userpictureurl = $userpicture->get_url($PAGE)->out(false);
669
670 $usermodified = new stdclass();
671 $usermodified->id = $discussion->usermodified;
672 $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um', $picturefields);
673 // Preserve the id (it can be overwritten due to the prefixed $picturefields).
674 $usermodified->id = $discussion->usermodified;
675 $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname);
676
677 $userpicture = new user_picture($usermodified);
678 $userpicture->size = 1; // Size f1.
679 $discussion->usermodifiedpictureurl = $userpicture->get_url($PAGE)->out(false);
680 }
681
039c81f0 682 $discussions[] = $discussion;
7c51b40a
JL
683 }
684 }
685
686 $result = array();
687 $result['discussions'] = $discussions;
688 $result['warnings'] = $warnings;
689 return $result;
690
691 }
692
693 /**
694 * Describes the get_forum_discussions_paginated return value.
695 *
1a9c60e9 696 * @deprecated since 3.7
7c51b40a
JL
697 * @return external_single_structure
698 * @since Moodle 2.8
699 */
700 public static function get_forum_discussions_paginated_returns() {
701 return new external_single_structure(
702 array(
703 'discussions' => new external_multiple_structure(
704 new external_single_structure(
705 array(
706 'id' => new external_value(PARAM_INT, 'Post id'),
707 'name' => new external_value(PARAM_TEXT, 'Discussion name'),
708 'groupid' => new external_value(PARAM_INT, 'Group id'),
709 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
710 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
711 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
712 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
713 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
714 'parent' => new external_value(PARAM_INT, 'Parent id'),
715 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
716 'created' => new external_value(PARAM_INT, 'Creation time'),
717 'modified' => new external_value(PARAM_INT, 'Time modified'),
718 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
719 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
720 'message' => new external_value(PARAM_RAW, 'The post message'),
721 'messageformat' => new external_format_value('message'),
722 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
c8743f62 723 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL),
7c51b40a 724 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
14590070 725 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
7c51b40a
JL
726 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
727 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
728 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
729 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
730 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
731 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
28c2f2b2 732 'numreplies' => new external_value(PARAM_INT, 'The number of replies in the discussion'),
5f219cf1 733 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
0f3bbfd4
AN
734 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'),
735 'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'),
736 'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'),
565cccfa 737 'canlock' => new external_value(PARAM_BOOL, 'Can the user lock the discussion'),
7c51b40a
JL
738 ), 'post'
739 )
740 ),
741 'warnings' => new external_warnings()
742 )
743 );
744 }
745
1a9c60e9
MG
746 /**
747 * Describes the parameters for get_forum_discussions.
748 *
749 * @return external_function_parameters
750 * @since Moodle 3.7
751 */
752 public static function get_forum_discussions_parameters() {
753 return new external_function_parameters (
754 array(
755 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
756 'sortorder' => new external_value(PARAM_INT,
757 'sort by this element: numreplies, , created or timemodified', VALUE_DEFAULT, -1),
758 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
759 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
760 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0),
761 )
762 );
763 }
764
765 /**
766 * Returns a list of forum discussions optionally sorted and paginated.
767 *
768 * @param int $forumid the forum instance id
769 * @param int $sortorder The sort order
770 * @param int $page page number
771 * @param int $perpage items per page
772 * @param int $groupid the user course group
773 *
774 *
775 * @return array the forum discussion details including warnings
776 * @since Moodle 3.7
777 */
778 public static function get_forum_discussions(int $forumid, ?int $sortorder = -1, ?int $page = -1,
779 ?int $perpage = 0, ?int $groupid = 0) {
780
781 global $CFG, $DB, $USER;
782
783 require_once($CFG->dirroot . "/mod/forum/lib.php");
784
785 $warnings = array();
786 $discussions = array();
787
788 $params = self::validate_parameters(self::get_forum_discussions_parameters(),
789 array(
790 'forumid' => $forumid,
791 'sortorder' => $sortorder,
792 'page' => $page,
793 'perpage' => $perpage,
794 'groupid' => $groupid
795 )
796 );
797
798 // Compact/extract functions are not recommended.
799 $forumid = $params['forumid'];
800 $sortorder = $params['sortorder'];
801 $page = $params['page'];
802 $perpage = $params['perpage'];
803 $groupid = $params['groupid'];
804
805 $vaultfactory = \mod_forum\local\container::get_vault_factory();
806 $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault();
807
808 $sortallowedvalues = array(
809 $discussionlistvault::SORTORDER_LASTPOST_DESC,
810 $discussionlistvault::SORTORDER_LASTPOST_ASC,
811 $discussionlistvault::SORTORDER_CREATED_DESC,
812 $discussionlistvault::SORTORDER_CREATED_ASC,
813 $discussionlistvault::SORTORDER_REPLIES_DESC,
814 $discussionlistvault::SORTORDER_REPLIES_ASC
815 );
816
817 // If sortorder not defined set a default one.
818 if ($sortorder == -1) {
819 $sortorder = $discussionlistvault::SORTORDER_LASTPOST_DESC;
820 }
821
822 if (!in_array($sortorder, $sortallowedvalues)) {
823 throw new invalid_parameter_exception('Invalid value for sortorder parameter (value: ' . $sortorder . '),' .
824 ' allowed values are: ' . implode(',', $sortallowedvalues));
825 }
826
827 $managerfactory = \mod_forum\local\container::get_manager_factory();
828 $urlfactory = \mod_forum\local\container::get_url_factory();
829 $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
830
831 $forumvault = $vaultfactory->get_forum_vault();
832 $forum = $forumvault->get_from_id($forumid);
2a80ecb5
MG
833 if (!$forum) {
834 throw new \moodle_exception("Unable to find forum with id {$forumid}");
835 }
1a9c60e9
MG
836 $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper();
837 $forumrecord = $forumdatamapper->to_legacy_object($forum);
838
839 $capabilitymanager = $managerfactory->get_capability_manager($forum);
840
841 $course = $DB->get_record('course', array('id' => $forum->get_course_id()), '*', MUST_EXIST);
842 $cm = get_coursemodule_from_instance('forum', $forum->get_id(), $course->id, false, MUST_EXIST);
843
844 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
845 $modcontext = context_module::instance($cm->id);
846 self::validate_context($modcontext);
847
848 $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($USER);
849
850 // Check they have the view forum capability.
851 if (!$capabilitymanager->can_view_discussions($USER)) {
852 throw new moodle_exception('noviewdiscussionspermission', 'forum');
853 }
854
855 $alldiscussions = get_discussions($forum, $USER, $groupid, $sortorder, $page, $perpage);
856
857 if ($alldiscussions) {
858 $discussionids = array_keys($alldiscussions);
859
860 $postvault = $vaultfactory->get_post_vault();
861 // Return the reply count for each discussion in a given forum.
862 $replies = $postvault->get_reply_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply);
863 // Return the first post for each discussion in a given forum.
864 $firstposts = $postvault->get_first_post_for_discussion_ids($discussionids);
865
866 // Get the unreads array, this takes a forum id and returns data for all discussions.
867 $unreads = array();
868 if ($cantrack = forum_tp_can_track_forums($forumrecord)) {
869 if ($forumtracked = forum_tp_is_tracked($forumrecord)) {
870 $unreads = $postvault->get_unread_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply);
871 }
872 }
873
874 $canlock = $capabilitymanager->can_manage_forum($USER);
875
121ccf15
MG
876 $usercontext = context_user::instance($USER->id);
877 $ufservice = core_favourites\service_factory::get_service_for_user_context($usercontext);
878
879 $canfavourite = has_capability('mod/forum:cantogglefavourite', $modcontext, $USER);
880
1a9c60e9
MG
881 foreach ($alldiscussions as $discussionsummary) {
882 $discussion = $discussionsummary->get_discussion();
883 $firstpostauthor = $discussionsummary->get_first_post_author();
884 $latestpostauthor = $discussionsummary->get_latest_post_author();
885
886 // This function checks for qanda forums.
887 $canviewdiscussion = $capabilitymanager->can_view_discussion($USER, $discussion);
888 if (!$canviewdiscussion) {
889 $warning = array();
890 // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
891 $warning['item'] = 'post';
892 $warning['itemid'] = $discussion->get_id();
893 $warning['warningcode'] = '1';
894 $warning['message'] = 'You can\'t see this discussion';
895 $warnings[] = $warning;
896 continue;
897 }
898
899 $discussionobject = $firstposts[$discussion->get_first_post_id()];
900 $discussionobject->groupid = $discussion->get_group_id();
901 $discussionobject->timemodified = $discussion->get_time_modified();
902 $discussionobject->usermodified = $discussion->get_user_modified();
903 $discussionobject->timestart = $discussion->get_time_start();
904 $discussionobject->timeend = $discussion->get_time_end();
905 $discussionobject->pinned = $discussion->is_pinned();
906
907 $discussionobject->numunread = 0;
908 if ($cantrack && $forumtracked) {
909 if (isset($unreads[$discussion->get_id()])) {
910 $discussionobject->numunread = (int) $unreads[$discussion->get_id()];
911 }
912 }
913
914 $discussionobject->numreplies = 0;
915 if (!empty($replies[$discussion->get_id()])) {
916 $discussionobject->numreplies = (int) $replies[$discussion->get_id()];
917 }
918
919 $discussionobject->name = external_format_string($discussion->get_name(), $modcontext->id);
920 $discussionobject->subject = external_format_string($discussionobject->subject, $modcontext->id);
921 // Rewrite embedded images URLs.
922 list($discussionobject->message, $discussionobject->messageformat) =
923 external_format_text($discussionobject->message, $discussionobject->messageformat,
924 $modcontext->id, 'mod_forum', 'post', $discussionobject->id);
925
926 // List attachments.
927 if (!empty($discussionobject->attachment)) {
928 $discussionobject->attachments = external_util::get_area_files($modcontext->id, 'mod_forum',
929 'attachment', $discussionobject->id);
930 }
931 $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post',
932 $discussionobject->id);
933 if (!empty($messageinlinefiles)) {
934 $discussionobject->messageinlinefiles = $messageinlinefiles;
935 }
936
937 $discussionobject->locked = $forum->is_discussion_locked($discussion);
938 $discussionobject->canlock = $canlock;
121ccf15
MG
939 $discussionobject->starred = !empty($ufservice) ? $ufservice->favourite_exists('mod_forum', 'discussions',
940 $discussion->get_id(), $modcontext) : false;
1a9c60e9 941 $discussionobject->canreply = $capabilitymanager->can_post_in_discussion($USER, $discussion);
121ccf15 942 $discussionobject->canfavourite = $canfavourite;
1a9c60e9
MG
943
944 if (forum_is_author_hidden($discussionobject, $forumrecord)) {
945 $discussionobject->userid = null;
946 $discussionobject->userfullname = null;
947 $discussionobject->userpictureurl = null;
948
949 $discussionobject->usermodified = null;
950 $discussionobject->usermodifiedfullname = null;
951 $discussionobject->usermodifiedpictureurl = null;
952
953 } else {
954 $discussionobject->userfullname = $firstpostauthor->get_full_name();
6489aa2a 955 $discussionobject->userpictureurl = $urlfactory->get_author_profile_image_url($firstpostauthor, null, 2)
1a9c60e9
MG
956 ->out(false);
957
958 $discussionobject->usermodifiedfullname = $latestpostauthor->get_full_name();
6489aa2a
RW
959 $discussionobject->usermodifiedpictureurl = $urlfactory->get_author_profile_image_url(
960 $latestpostauthor, null, 2)->out(false);
1a9c60e9
MG
961 }
962
963 $discussions[] = (array) $discussionobject;
964 }
965 }
966 $result = array();
967 $result['discussions'] = $discussions;
968 $result['warnings'] = $warnings;
969
970 return $result;
971 }
972
973 /**
974 * Describes the get_forum_discussions return value.
975 *
976 * @return external_single_structure
977 * @since Moodle 3.7
978 */
979 public static function get_forum_discussions_returns() {
980 return new external_single_structure(
981 array(
982 'discussions' => new external_multiple_structure(
983 new external_single_structure(
984 array(
985 'id' => new external_value(PARAM_INT, 'Post id'),
986 'name' => new external_value(PARAM_TEXT, 'Discussion name'),
987 'groupid' => new external_value(PARAM_INT, 'Group id'),
988 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
989 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
990 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
991 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
992 'discussion' => new external_value(PARAM_INT, 'Discussion id'),
993 'parent' => new external_value(PARAM_INT, 'Parent id'),
994 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
995 'created' => new external_value(PARAM_INT, 'Creation time'),
996 'modified' => new external_value(PARAM_INT, 'Time modified'),
997 'mailed' => new external_value(PARAM_INT, 'Mailed?'),
998 'subject' => new external_value(PARAM_TEXT, 'The post subject'),
999 'message' => new external_value(PARAM_RAW, 'The post message'),
1000 'messageformat' => new external_format_value('message'),
1001 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
1002 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL),
1003 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
1004 'attachments' => new external_files('attachments', VALUE_OPTIONAL),
1005 'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
1006 'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
1007 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
1008 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
1009 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
1010 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
1011 'numreplies' => new external_value(PARAM_INT, 'The number of replies in the discussion'),
1012 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
1013 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'),
1014 'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'),
121ccf15 1015 'starred' => new external_value(PARAM_BOOL, 'Is the discussion starred'),
1a9c60e9
MG
1016 'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'),
1017 'canlock' => new external_value(PARAM_BOOL, 'Can the user lock the discussion'),
121ccf15 1018 'canfavourite' => new external_value(PARAM_BOOL, 'Can the user star the discussion'),
1a9c60e9
MG
1019 ), 'post'
1020 )
1021 ),
1022 'warnings' => new external_warnings()
1023 )
1024 );
1025 }
1026
4a1e44a1
JL
1027 /**
1028 * Returns description of method parameters
1029 *
1030 * @return external_function_parameters
1031 * @since Moodle 2.9
1032 */
1033 public static function view_forum_parameters() {
1034 return new external_function_parameters(
1035 array(
1036 'forumid' => new external_value(PARAM_INT, 'forum instance id')
1037 )
1038 );
1039 }
1040
1041 /**
1c2b7882 1042 * Trigger the course module viewed event and update the module completion status.
4a1e44a1
JL
1043 *
1044 * @param int $forumid the forum instance id
1045 * @return array of warnings and status result
1046 * @since Moodle 2.9
1047 * @throws moodle_exception
1048 */
1049 public static function view_forum($forumid) {
1050 global $DB, $CFG;
1051 require_once($CFG->dirroot . "/mod/forum/lib.php");
1052
1053 $params = self::validate_parameters(self::view_forum_parameters(),
1054 array(
1055 'forumid' => $forumid
1056 ));
1057 $warnings = array();
1058
1059 // Request and permission validation.
ca7e2fc2 1060 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
4a1e44a1
JL
1061 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1062
1063 $context = context_module::instance($cm->id);
1064 self::validate_context($context);
1065
ea2fa324
JL
1066 require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum');
1067
4a1e44a1
JL
1068 // Call the forum/lib API.
1069 forum_view($forum, $course, $cm, $context);
1070
1071 $result = array();
1072 $result['status'] = true;
1073 $result['warnings'] = $warnings;
1074 return $result;
1075 }
1076
1077 /**
1078 * Returns description of method result value
1079 *
1080 * @return external_description
1081 * @since Moodle 2.9
1082 */
1083 public static function view_forum_returns() {
1084 return new external_single_structure(
1085 array(
1086 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1087 'warnings' => new external_warnings()
1088 )
1089 );
1090 }
1091
a3c315dd
JL
1092 /**
1093 * Returns description of method parameters
1094 *
1095 * @return external_function_parameters
1096 * @since Moodle 2.9
1097 */
1098 public static function view_forum_discussion_parameters() {
1099 return new external_function_parameters(
1100 array(
1101 'discussionid' => new external_value(PARAM_INT, 'discussion id')
1102 )
1103 );
1104 }
1105
1106 /**
1c2b7882 1107 * Trigger the discussion viewed event.
a3c315dd
JL
1108 *
1109 * @param int $discussionid the discussion id
1110 * @return array of warnings and status result
1111 * @since Moodle 2.9
1112 * @throws moodle_exception
1113 */
1114 public static function view_forum_discussion($discussionid) {
db3c9ff8 1115 global $DB, $CFG, $USER;
a3c315dd
JL
1116 require_once($CFG->dirroot . "/mod/forum/lib.php");
1117
1118 $params = self::validate_parameters(self::view_forum_discussion_parameters(),
1119 array(
1120 'discussionid' => $discussionid
1121 ));
1122 $warnings = array();
1123
1124 $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST);
1125 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
1126 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1127
1128 // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
1129 $modcontext = context_module::instance($cm->id);
1130 self::validate_context($modcontext);
1131
ea2fa324
JL
1132 require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
1133
a3c315dd
JL
1134 // Call the forum/lib API.
1135 forum_discussion_view($modcontext, $forum, $discussion);
1136
db3c9ff8
PFO
1137 // Mark as read if required.
1138 if (!$CFG->forum_usermarksread && forum_tp_is_tracked($forum)) {
1139 forum_tp_mark_discussion_read($USER, $discussion->id);
1140 }
1141
a3c315dd
JL
1142 $result = array();
1143 $result['status'] = true;
1144 $result['warnings'] = $warnings;
1145 return $result;
1146 }
1147
1148 /**
1149 * Returns description of method result value
1150 *
1151 * @return external_description
1152 * @since Moodle 2.9
1153 */
1154 public static function view_forum_discussion_returns() {
1155 return new external_single_structure(
1156 array(
1157 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1158 'warnings' => new external_warnings()
1159 )
1160 );
1161 }
1162
50a20317
JL
1163 /**
1164 * Returns description of method parameters
1165 *
1166 * @return external_function_parameters
1167 * @since Moodle 3.0
1168 */
1169 public static function add_discussion_post_parameters() {
1170 return new external_function_parameters(
1171 array(
1172 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to
1173 (can be the initial discussion post'),
1174 'subject' => new external_value(PARAM_TEXT, 'new post subject'),
1175 'message' => new external_value(PARAM_RAW, 'new post message (only html format allowed)'),
1176 'options' => new external_multiple_structure (
1177 new external_single_structure(
1178 array(
1179 'name' => new external_value(PARAM_ALPHANUM,
1180 'The allowed keys (value format) are:
1181 discussionsubscribe (bool); subscribe to the discussion?, default to true
bc4c7337 1182 private (bool); make this reply private to the author of the parent post, default to false.
48143990 1183 inlineattachmentsid (int); the draft file area id for inline attachments
e881c4f5 1184 attachmentsid (int); the draft file area id for attachments
50a20317
JL
1185 '),
1186 'value' => new external_value(PARAM_RAW, 'the value of the option,
1187 this param is validated in the external function.'
1188 )
1189 )
1190 ), 'Options', VALUE_DEFAULT, array())
1191 )
1192 );
1193 }
1194
1195 /**
1196 * Create new posts into an existing discussion.
1197 *
1198 * @param int $postid the post id we are going to reply to
1199 * @param string $subject new post subject
1200 * @param string $message new post message (only html format allowed)
1201 * @param array $options optional settings
1202 * @return array of warnings and the new post id
1203 * @since Moodle 3.0
1204 * @throws moodle_exception
1205 */
1206 public static function add_discussion_post($postid, $subject, $message, $options = array()) {
a0c9b6af 1207 global $CFG, $USER;
50a20317
JL
1208 require_once($CFG->dirroot . "/mod/forum/lib.php");
1209
9b4f09ba
P
1210 // Get all the factories that are required.
1211 $vaultfactory = mod_forum\local\container::get_vault_factory();
1212 $entityfactory = mod_forum\local\container::get_entity_factory();
1213 $datamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
1214 $managerfactory = mod_forum\local\container::get_manager_factory();
1215 $discussionvault = $vaultfactory->get_discussion_vault();
1216 $forumvault = $vaultfactory->get_forum_vault();
1217 $discussiondatamapper = $datamapperfactory->get_discussion_data_mapper();
1218 $forumdatamapper = $datamapperfactory->get_forum_data_mapper();
1219
50a20317 1220 $params = self::validate_parameters(self::add_discussion_post_parameters(),
609a1073
JL
1221 array(
1222 'postid' => $postid,
1223 'subject' => $subject,
1224 'message' => $message,
1225 'options' => $options
1226 )
1227 );
bc4c7337 1228
609a1073
JL
1229 $warnings = array();
1230
1231 if (!$parent = forum_get_post_full($params['postid'])) {
1232 throw new moodle_exception('invalidparentpostid', 'forum');
1233 }
1234
9b4f09ba 1235 if (!$discussion = $discussionvault->get_from_id($parent->discussion)) {
609a1073
JL
1236 throw new moodle_exception('notpartofdiscussion', 'forum');
1237 }
1238
1239 // Request and permission validation.
9b4f09ba
P
1240 $forum = $forumvault->get_from_id($discussion->get_forum_id());
1241 $capabilitymanager = $managerfactory->get_capability_manager($forum);
1242 $course = $forum->get_course_record();
1243 $cm = $forum->get_course_module_record();
609a1073 1244
9b4f09ba
P
1245 $discussionrecord = $discussiondatamapper->to_legacy_object($discussion);
1246 $forumrecord = $forumdatamapper->to_legacy_object($forum);
609a1073
JL
1247 $context = context_module::instance($cm->id);
1248 self::validate_context($context);
1249
50a20317
JL
1250 // Validate options.
1251 $options = array(
41182118 1252 'discussionsubscribe' => true,
bc4c7337 1253 'private' => false,
48143990 1254 'inlineattachmentsid' => 0,
e881c4f5 1255 'attachmentsid' => null
50a20317
JL
1256 );
1257 foreach ($params['options'] as $option) {
1258 $name = trim($option['name']);
1259 switch ($name) {
1260 case 'discussionsubscribe':
1261 $value = clean_param($option['value'], PARAM_BOOL);
1262 break;
bc4c7337
AN
1263 case 'private':
1264 $value = clean_param($option['value'], PARAM_BOOL);
1265 break;
48143990 1266 case 'inlineattachmentsid':
41182118
BK
1267 $value = clean_param($option['value'], PARAM_INT);
1268 break;
e881c4f5
BK
1269 case 'attachmentsid':
1270 $value = clean_param($option['value'], PARAM_INT);
609a1073
JL
1271 // Ensure that the user has permissions to create attachments.
1272 if (!has_capability('mod/forum:createattachment', $context)) {
1273 $value = 0;
1274 }
e881c4f5 1275 break;
50a20317
JL
1276 default:
1277 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1278 }
1279 $options[$name] = $value;
1280 }
1281
9b4f09ba 1282 if (!$capabilitymanager->can_post_in_discussion($USER, $discussion)) {
50a20317
JL
1283 throw new moodle_exception('nopostforum', 'forum');
1284 }
1285
9b4f09ba 1286 $thresholdwarning = forum_check_throttling($forumrecord, $cm);
50a20317
JL
1287 forum_check_blocking_threshold($thresholdwarning);
1288
1289 // Create the post.
1290 $post = new stdClass();
9b4f09ba 1291 $post->discussion = $discussion->get_id();
50a20317
JL
1292 $post->parent = $parent->id;
1293 $post->subject = $params['subject'];
1294 $post->message = $params['message'];
1295 $post->messageformat = FORMAT_HTML; // Force formatting for now.
1296 $post->messagetrust = trusttext_trusted($context);
48143990 1297 $post->itemid = $options['inlineattachmentsid'];
3e95e09b 1298 $post->attachments = $options['attachmentsid'];
bc4c7337 1299 $post->isprivatereply = $options['private'];
3e95e09b 1300 $post->deleted = 0;
e881c4f5
BK
1301 $fakemform = $post->attachments;
1302 if ($postid = forum_add_new_post($post, $fakemform)) {
50a20317
JL
1303
1304 $post->id = $postid;
1305
1306 // Trigger events and completion.
1307 $params = array(
1308 'context' => $context,
1309 'objectid' => $post->id,
1310 'other' => array(
9b4f09ba
P
1311 'discussionid' => $discussion->get_id(),
1312 'forumid' => $forum->get_id(),
1313 'forumtype' => $forum->get_type(),
50a20317
JL
1314 )
1315 );
1316 $event = \mod_forum\event\post_created::create($params);
1317 $event->add_record_snapshot('forum_posts', $post);
9b4f09ba 1318 $event->add_record_snapshot('forum_discussions', $discussionrecord);
50a20317
JL
1319 $event->trigger();
1320
1321 // Update completion state.
1322 $completion = new completion_info($course);
1323 if ($completion->is_enabled($cm) &&
9b4f09ba 1324 ($forum->get_completion_replies() || $forum->get_completion_posts())) {
50a20317
JL
1325 $completion->update_state($cm, COMPLETION_COMPLETE);
1326 }
1327
1328 $settings = new stdClass();
1329 $settings->discussionsubscribe = $options['discussionsubscribe'];
9b4f09ba 1330 forum_post_subscription($settings, $forumrecord, $discussionrecord);
50a20317
JL
1331 } else {
1332 throw new moodle_exception('couldnotadd', 'forum');
1333 }
1334
9b4f09ba
P
1335 $builderfactory = \mod_forum\local\container::get_builder_factory();
1336 $exportedpostsbuilder = $builderfactory->get_exported_posts_builder();
1337 $postentity = $entityfactory->get_post_from_stdClass($post);
1338 $exportedposts = $exportedpostsbuilder->build($USER, [$forum], [$discussion], [$postentity]);
1339 $exportedpost = $exportedposts[0];
1340
a0c9b6af
P
1341 $message = [];
1342 $message[] = [
1343 'type' => 'success',
1344 'message' => get_string("postaddedsuccess", "forum")
1345 ];
1346
1347 $message[] = [
1348 'type' => 'success',
1349 'message' => get_string("postaddedtimeleft", "forum", format_time($CFG->maxeditingtime))
1350 ];
1351
50a20317
JL
1352 $result = array();
1353 $result['postid'] = $postid;
1354 $result['warnings'] = $warnings;
9b4f09ba 1355 $result['post'] = $exportedpost;
a0c9b6af 1356 $result['messages'] = $message;
50a20317
JL
1357 return $result;
1358 }
1359
1360 /**
1361 * Returns description of method result value
1362 *
1363 * @return external_description
1364 * @since Moodle 3.0
1365 */
1366 public static function add_discussion_post_returns() {
1367 return new external_single_structure(
1368 array(
1369 'postid' => new external_value(PARAM_INT, 'new post id'),
9b4f09ba 1370 'warnings' => new external_warnings(),
a0c9b6af
P
1371 'post' => post_exporter::get_read_structure(),
1372 'messages' => new external_multiple_structure(
1373 new external_single_structure(
1374 array(
1375 'type' => new external_value(PARAM_TEXT, "The classification to be used in the client side", VALUE_REQUIRED),
1376 'message' => new external_value(PARAM_TEXT,'untranslated english message to explain the warning', VALUE_REQUIRED)
1377 ), 'Messages'), 'list of warnings', VALUE_OPTIONAL
1378 ),
1379 //'alertmessage' => new external_value(PARAM_RAW, 'Success message to be displayed to the user.'),
50a20317
JL
1380 )
1381 );
1382 }
1383
309e8698
P
1384 /**
1385 * Toggle the favouriting value for the discussion provided
1386 *
309e8698
P
1387 * @param int $discussionid The discussion we need to favourite
1388 * @param bool $targetstate The state of the favourite value
1389 * @return array The exported discussion
1390 */
8885cd57 1391 public static function toggle_favourite_state($discussionid, $targetstate) {
99bda8a7
P
1392 global $DB, $PAGE, $USER;
1393
1394 $params = self::validate_parameters(self::toggle_favourite_state_parameters(), [
99bda8a7
P
1395 'discussionid' => $discussionid,
1396 'targetstate' => $targetstate
1397 ]);
1398
1399 $vaultfactory = mod_forum\local\container::get_vault_factory();
8885cd57
P
1400 // Get the discussion vault and the corresponding discussion entity.
1401 $discussionvault = $vaultfactory->get_discussion_vault();
1402 $discussion = $discussionvault->get_from_id($params['discussionid']);
1403
99bda8a7 1404 $forumvault = $vaultfactory->get_forum_vault();
8885cd57 1405 $forum = $forumvault->get_from_id($discussion->get_forum_id());
99bda8a7 1406 $forumcontext = $forum->get_context();
99bda8a7
P
1407 self::validate_context($forumcontext);
1408
1409 $managerfactory = mod_forum\local\container::get_manager_factory();
1410 $capabilitymanager = $managerfactory->get_capability_manager($forum);
1411
99bda8a7 1412 // Does the user have the ability to favourite the discussion?
b4a1bbbb 1413 if (!$capabilitymanager->can_favourite_discussion($USER)) {
99bda8a7
P
1414 throw new moodle_exception('cannotfavourite', 'forum');
1415 }
d3cac88d 1416 $usercontext = context_user::instance($USER->id);
99bda8a7
P
1417 $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
1418 $isfavourited = $ufservice->favourite_exists('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
1419
1420 $favouritefunction = $targetstate ? 'create_favourite' : 'delete_favourite';
1421 if ($isfavourited != (bool) $params['targetstate']) {
1422 $ufservice->{$favouritefunction}('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
1423 }
1424
1425 $exporterfactory = mod_forum\local\container::get_exporter_factory();
13cd05ac
P
1426 $builder = mod_forum\local\container::get_builder_factory()->get_exported_discussion_builder();
1427 $favourited = ($builder->is_favourited($discussion, $forumcontext, $USER) ? [$discussion->get_id()] : []);
1428 $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion, [], $favourited);
99bda8a7
P
1429 return $exporter->export($PAGE->get_renderer('mod_forum'));
1430 }
1431
1432 /**
1433 * Returns description of method result value
1434 *
1435 * @return external_description
1436 * @since Moodle 3.0
1437 */
1438 public static function toggle_favourite_state_returns() {
1439 return discussion_exporter::get_read_structure();
1440 }
1441
309e8698
P
1442 /**
1443 * Defines the parameters for the toggle_favourite_state method
1444 *
1445 * @return external_function_parameters
1446 */
897ac0de 1447 public static function toggle_favourite_state_parameters() {
99bda8a7
P
1448 return new external_function_parameters(
1449 [
99bda8a7
P
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
7ab43ac8
JL
1456 /**
1457 * Returns description of method parameters
1458 *
1459 * @return external_function_parameters
1460 * @since Moodle 3.0
1461 */
1462 public static function add_discussion_parameters() {
1463 return new external_function_parameters(
1464 array(
7267daa8
AN
1465 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1466 'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'),
1467 'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'),
aa9059b9 1468 'groupid' => new external_value(PARAM_INT, 'The group, default to 0', VALUE_DEFAULT, 0),
7ab43ac8
JL
1469 'options' => new external_multiple_structure (
1470 new external_single_structure(
1471 array(
1472 'name' => new external_value(PARAM_ALPHANUM,
1473 'The allowed keys (value format) are:
1474 discussionsubscribe (bool); subscribe to the discussion?, default to true
5f219cf1 1475 discussionpinned (bool); is the discussion pinned, default to false
48143990 1476 inlineattachmentsid (int); the draft file area id for inline attachments
e881c4f5 1477 attachmentsid (int); the draft file area id for attachments
7ab43ac8 1478 '),
7267daa8
AN
1479 'value' => new external_value(PARAM_RAW, 'The value of the option,
1480 This param is validated in the external function.'
7ab43ac8
JL
1481 )
1482 )
1483 ), 'Options', VALUE_DEFAULT, array())
1484 )
1485 );
1486 }
1487
1488 /**
1489 * Add a new discussion into an existing forum.
1490 *
1491 * @param int $forumid the forum instance id
1492 * @param string $subject new discussion subject
1493 * @param string $message new discussion message (only html format allowed)
1494 * @param int $groupid the user course group
1495 * @param array $options optional settings
1496 * @return array of warnings and the new discussion id
1497 * @since Moodle 3.0
1498 * @throws moodle_exception
1499 */
aa9059b9 1500 public static function add_discussion($forumid, $subject, $message, $groupid = 0, $options = array()) {
7ab43ac8
JL
1501 global $DB, $CFG;
1502 require_once($CFG->dirroot . "/mod/forum/lib.php");
1503
1504 $params = self::validate_parameters(self::add_discussion_parameters(),
1505 array(
1506 'forumid' => $forumid,
1507 'subject' => $subject,
1508 'message' => $message,
1509 'groupid' => $groupid,
1510 'options' => $options
1511 ));
609a1073
JL
1512
1513 $warnings = array();
1514
1515 // Request and permission validation.
1516 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1517 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1518
1519 $context = context_module::instance($cm->id);
1520 self::validate_context($context);
1521
7ab43ac8
JL
1522 // Validate options.
1523 $options = array(
5f219cf1 1524 'discussionsubscribe' => true,
41182118 1525 'discussionpinned' => false,
48143990 1526 'inlineattachmentsid' => 0,
e881c4f5 1527 'attachmentsid' => null
7ab43ac8
JL
1528 );
1529 foreach ($params['options'] as $option) {
1530 $name = trim($option['name']);
1531 switch ($name) {
1532 case 'discussionsubscribe':
1533 $value = clean_param($option['value'], PARAM_BOOL);
1534 break;
5f219cf1
BK
1535 case 'discussionpinned':
1536 $value = clean_param($option['value'], PARAM_BOOL);
1537 break;
48143990 1538 case 'inlineattachmentsid':
41182118
BK
1539 $value = clean_param($option['value'], PARAM_INT);
1540 break;
e881c4f5
BK
1541 case 'attachmentsid':
1542 $value = clean_param($option['value'], PARAM_INT);
609a1073
JL
1543 // Ensure that the user has permissions to create attachments.
1544 if (!has_capability('mod/forum:createattachment', $context)) {
1545 $value = 0;
1546 }
e881c4f5 1547 break;
7ab43ac8
JL
1548 default:
1549 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1550 }
1551 $options[$name] = $value;
1552 }
1553
7ab43ac8
JL
1554 // Normalize group.
1555 if (!groups_get_activity_groupmode($cm)) {
1556 // Groups not supported, force to -1.
1557 $groupid = -1;
1558 } else {
1559 // Check if we receive the default or and empty value for groupid,
1560 // in this case, get the group for the user in the activity.
aa9059b9 1561 if (empty($params['groupid'])) {
7ab43ac8
JL
1562 $groupid = groups_get_activity_group($cm);
1563 } else {
1564 // Here we rely in the group passed, forum_user_can_post_discussion will validate the group.
1565 $groupid = $params['groupid'];
1566 }
1567 }
1568
1569 if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) {
1570 throw new moodle_exception('cannotcreatediscussion', 'forum');
1571 }
1572
1573 $thresholdwarning = forum_check_throttling($forum, $cm);
1574 forum_check_blocking_threshold($thresholdwarning);
1575
1576 // Create the discussion.
1577 $discussion = new stdClass();
1578 $discussion->course = $course->id;
1579 $discussion->forum = $forum->id;
1580 $discussion->message = $params['message'];
1581 $discussion->messageformat = FORMAT_HTML; // Force formatting for now.
1582 $discussion->messagetrust = trusttext_trusted($context);
48143990 1583 $discussion->itemid = $options['inlineattachmentsid'];
7ab43ac8
JL
1584 $discussion->groupid = $groupid;
1585 $discussion->mailnow = 0;
1586 $discussion->subject = $params['subject'];
1587 $discussion->name = $discussion->subject;
1588 $discussion->timestart = 0;
1589 $discussion->timeend = 0;
2893812e 1590 $discussion->timelocked = 0;
e881c4f5
BK
1591 $discussion->attachments = $options['attachmentsid'];
1592
5f219cf1
BK
1593 if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) {
1594 $discussion->pinned = FORUM_DISCUSSION_PINNED;
1595 } else {
1596 $discussion->pinned = FORUM_DISCUSSION_UNPINNED;
1597 }
e881c4f5
BK
1598 $fakemform = $options['attachmentsid'];
1599 if ($discussionid = forum_add_discussion($discussion, $fakemform)) {
7ab43ac8
JL
1600
1601 $discussion->id = $discussionid;
1602
1603 // Trigger events and completion.
1604
1605 $params = array(
1606 'context' => $context,
1607 'objectid' => $discussion->id,
1608 'other' => array(
1609 'forumid' => $forum->id,
1610 )
1611 );
1612 $event = \mod_forum\event\discussion_created::create($params);
1613 $event->add_record_snapshot('forum_discussions', $discussion);
1614 $event->trigger();
1615
1616 $completion = new completion_info($course);
1617 if ($completion->is_enabled($cm) &&
1618 ($forum->completiondiscussions || $forum->completionposts)) {
1619 $completion->update_state($cm, COMPLETION_COMPLETE);
1620 }
1621
1622 $settings = new stdClass();
1623 $settings->discussionsubscribe = $options['discussionsubscribe'];
1624 forum_post_subscription($settings, $forum, $discussion);
1625 } else {
1626 throw new moodle_exception('couldnotadd', 'forum');
1627 }
1628
1629 $result = array();
1630 $result['discussionid'] = $discussionid;
1631 $result['warnings'] = $warnings;
1632 return $result;
1633 }
1634
1635 /**
1636 * Returns description of method result value
1637 *
1638 * @return external_description
1639 * @since Moodle 3.0
1640 */
1641 public static function add_discussion_returns() {
1642 return new external_single_structure(
1643 array(
7267daa8 1644 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'),
7ab43ac8
JL
1645 'warnings' => new external_warnings()
1646 )
1647 );
1648 }
1649
04cd8ae3
JL
1650 /**
1651 * Returns description of method parameters
1652 *
1653 * @return external_function_parameters
1654 * @since Moodle 3.1
1655 */
1656 public static function can_add_discussion_parameters() {
1657 return new external_function_parameters(
1658 array(
1659 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1660 'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group.
1661 Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null)
1662 )
1663 );
1664 }
1665
1666 /**
1667 * Check if the current user can add discussions in the given forum (and optionally for the given group).
1668 *
1669 * @param int $forumid the forum instance id
1670 * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups.
1671 * @return array of warnings and the status (true if the user can add discussions)
1672 * @since Moodle 3.1
1673 * @throws moodle_exception
1674 */
1675 public static function can_add_discussion($forumid, $groupid = null) {
1676 global $DB, $CFG;
1677 require_once($CFG->dirroot . "/mod/forum/lib.php");
1678
1679 $params = self::validate_parameters(self::can_add_discussion_parameters(),
1680 array(
1681 'forumid' => $forumid,
1682 'groupid' => $groupid,
1683 ));
1684 $warnings = array();
1685
1686 // Request and permission validation.
1687 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1688 list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1689
1690 $context = context_module::instance($cm->id);
1691 self::validate_context($context);
1692
1693 $status = forum_user_can_post_discussion($forum, $params['groupid'], -1, $cm, $context);
1694
1695 $result = array();
1696 $result['status'] = $status;
581e75bf
JL
1697 $result['canpindiscussions'] = has_capability('mod/forum:pindiscussions', $context);
1698 $result['cancreateattachment'] = forum_can_create_attachment($forum, $context);
04cd8ae3
JL
1699 $result['warnings'] = $warnings;
1700 return $result;
1701 }
1702
1703 /**
1704 * Returns description of method result value
1705 *
1706 * @return external_description
1707 * @since Moodle 3.1
1708 */
1709 public static function can_add_discussion_returns() {
1710 return new external_single_structure(
1711 array(
1712 'status' => new external_value(PARAM_BOOL, 'True if the user can add discussions, false otherwise.'),
581e75bf
JL
1713 'canpindiscussions' => new external_value(PARAM_BOOL, 'True if the user can pin discussions, false otherwise.',
1714 VALUE_OPTIONAL),
1715 'cancreateattachment' => new external_value(PARAM_BOOL, 'True if the user can add attachments, false otherwise.',
1716 VALUE_OPTIONAL),
04cd8ae3
JL
1717 'warnings' => new external_warnings()
1718 )
1719 );
1720 }
1721
4daa0d08
JL
1722 /**
1723 * Describes the parameters for get_forum_access_information.
1724 *
1725 * @return external_external_function_parameters
1726 * @since Moodle 3.7
1727 */
1728 public static function get_forum_access_information_parameters() {
1729 return new external_function_parameters (
1730 array(
1731 'forumid' => new external_value(PARAM_INT, 'Forum instance id.')
1732 )
1733 );
1734 }
1735
1736 /**
1737 * Return access information for a given forum.
1738 *
1739 * @param int $forumid forum instance id
1740 * @return array of warnings and the access information
1741 * @since Moodle 3.7
1742 * @throws moodle_exception
1743 */
1744 public static function get_forum_access_information($forumid) {
1745 global $DB;
1746
1747 $params = self::validate_parameters(self::get_forum_access_information_parameters(), array('forumid' => $forumid));
1748
1749 // Request and permission validation.
1750 $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1751 $cm = get_coursemodule_from_instance('forum', $forum->id);
1752
1753 $context = context_module::instance($cm->id);
1754 self::validate_context($context);
1755
1756 $result = array();
1757 // Return all the available capabilities.
1758 $capabilities = load_capability_def('mod_forum');
1759 foreach ($capabilities as $capname => $capdata) {
1760 // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
1761 $field = 'can' . str_replace('mod/forum:', '', $capname);
1762 $result[$field] = has_capability($capname, $context);
1763 }
1764
1765 $result['warnings'] = array();
1766 return $result;
1767 }
1768
1769 /**
1770 * Describes the get_forum_access_information return value.
1771 *
1772 * @return external_single_structure
1773 * @since Moodle 3.7
1774 */
1775 public static function get_forum_access_information_returns() {
1776
1777 $structure = array(
1778 'warnings' => new external_warnings()
1779 );
1780
1781 $capabilities = load_capability_def('mod_forum');
1782 foreach ($capabilities as $capname => $capdata) {
1783 // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
1784 $field = 'can' . str_replace('mod/forum:', '', $capname);
1785 $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.',
1786 VALUE_OPTIONAL);
1787 }
1788
1789 return new external_single_structure($structure);
1790 }
2646e9d6
RW
1791
1792 /**
1793 * Set the subscription state.
1794 *
1795 * @param int $forumid
1796 * @param int $discussionid
1797 * @param bool $targetstate
1798 * @return \stdClass
1799 */
1800 public static function set_subscription_state($forumid, $discussionid, $targetstate) {
f30f46db 1801 global $PAGE, $USER;
2646e9d6
RW
1802
1803 $params = self::validate_parameters(self::set_subscription_state_parameters(), [
1804 'forumid' => $forumid,
1805 'discussionid' => $discussionid,
1806 'targetstate' => $targetstate
1807 ]);
1808
1809 $vaultfactory = mod_forum\local\container::get_vault_factory();
1810 $forumvault = $vaultfactory->get_forum_vault();
1811 $forum = $forumvault->get_from_id($params['forumid']);
2646e9d6 1812 $coursemodule = $forum->get_course_module_record();
96a49734 1813 $context = $forum->get_context();
2646e9d6 1814
96a49734 1815 self::validate_context($context);
2646e9d6 1816
2646e9d6
RW
1817 $discussionvault = $vaultfactory->get_discussion_vault();
1818 $discussion = $discussionvault->get_from_id($params['discussionid']);
1819 $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
1820
1821 $forumrecord = $legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
1822 $discussionrecord = $legacydatamapperfactory->get_discussion_data_mapper()->to_legacy_object($discussion);
1823
1824 if (!\mod_forum\subscriptions::is_subscribable($forumrecord)) {
1825 // Nothing to do. We won't actually output any content here though.
1826 throw new \moodle_exception('cannotsubscribe', 'mod_forum');
1827 }
1828
1829 $issubscribed = \mod_forum\subscriptions::is_subscribed(
1830 $USER->id,
1831 $forumrecord,
1832 $discussion->get_id(),
1833 $coursemodule
1834 );
1835
1836 // If the current state doesn't equal the desired state then update the current
1837 // state to the desired state.
1838 if ($issubscribed != (bool) $params['targetstate']) {
1839 if ($params['targetstate']) {
1840 \mod_forum\subscriptions::subscribe_user_to_discussion($USER->id, $discussionrecord, $context);
1841 } else {
1842 \mod_forum\subscriptions::unsubscribe_user_from_discussion($USER->id, $discussionrecord, $context);
1843 }
1844 }
1845
1846 $exporterfactory = mod_forum\local\container::get_exporter_factory();
1847 $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1848 return $exporter->export($PAGE->get_renderer('mod_forum'));
1849 }
1850
1851 /**
1852 * Returns description of method parameters.
1853 *
1854 * @return external_function_parameters
1855 */
1856 public static function set_subscription_state_parameters() {
1857 return new external_function_parameters(
1858 [
1859 'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'),
1860 'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'),
1861 'targetstate' => new external_value(PARAM_BOOL, 'The target state')
1862 ]
1863 );
1864 }
1865
1866 /**
1867 * Returns description of method result value.
1868 *
1869 * @return external_description
1870 */
1871 public static function set_subscription_state_returns() {
8885cd57 1872 return discussion_exporter::get_read_structure();
2646e9d6 1873 }
2893812e 1874
2893812e
P
1875 /**
1876 * Set the lock state.
1877 *
1878 * @param int $forumid
1879 * @param int $discussionid
1880 * @param string $targetstate
1881 * @return \stdClass
1882 */
1883 public static function set_lock_state($forumid, $discussionid, $targetstate) {
1884 global $DB, $PAGE, $USER;
1885
1886 $params = self::validate_parameters(self::set_lock_state_parameters(), [
1887 'forumid' => $forumid,
1888 'discussionid' => $discussionid,
1889 'targetstate' => $targetstate
1890 ]);
1891
1892 $vaultfactory = mod_forum\local\container::get_vault_factory();
1893 $forumvault = $vaultfactory->get_forum_vault();
1894 $forum = $forumvault->get_from_id($params['forumid']);
2893812e
P
1895
1896 $managerfactory = mod_forum\local\container::get_manager_factory();
1897 $capabilitymanager = $managerfactory->get_capability_manager($forum);
bdb4a87d
P
1898 if (!$capabilitymanager->can_manage_forum($USER)) {
1899 throw new moodle_exception('errorcannotlock', 'forum');
1900 }
1901
1902 // If the targetstate(currentstate) is not 0 then it should be set to the current time.
1903 $lockedvalue = $targetstate ? 0 : time();
1904 self::validate_context($forum->get_context());
1905
2893812e
P
1906 $discussionvault = $vaultfactory->get_discussion_vault();
1907 $discussion = $discussionvault->get_from_id($params['discussionid']);
2893812e 1908
c475fe41 1909 // If the current state doesn't equal the desired state then update the current.
2893812e 1910 // state to the desired state.
bdb4a87d
P
1911 $discussion->toggle_locked_state($lockedvalue);
1912 $response = $discussionvault->update_discussion($discussion);
1913 $discussion = !$response ? $response : $discussion;
a7260536 1914 $exporterfactory = mod_forum\local\container::get_exporter_factory();
1915 $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1916 return $exporter->export($PAGE->get_renderer('mod_forum'));
1917 }
1918
cda9da99
P
1919 /**
1920 * Returns description of method parameters.
1921 *
1922 * @return external_function_parameters
1923 */
1924 public static function set_lock_state_parameters() {
1925 return new external_function_parameters(
1926 [
1927 'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'),
1928 'discussionid' => new external_value(PARAM_INT, 'The discussion to lock / unlock'),
1929 'targetstate' => new external_value(PARAM_INT, 'The timestamp for the lock state')
1930 ]
1931 );
1932 }
1933
1934 /**
1935 * Returns description of method result value.
1936 *
1937 * @return external_description
1938 */
1939 public static function set_lock_state_returns() {
1940 return new external_single_structure([
1941 'id' => new external_value(PARAM_INT, 'The discussion we are locking.'),
1942 'locked' => new external_value(PARAM_BOOL, 'The locked state of the discussion.'),
1943 'times' => new external_single_structure([
1944 'locked' => new external_value(PARAM_INT, 'The locked time of the discussion.'),
1945 ])
1946 ]);
1947 }
1948
a7260536 1949 /**
1950 * Set the pin state.
1951 *
a7260536 1952 * @param int $discussionid
1953 * @param bool $targetstate
1954 * @return \stdClass
1955 */
8885cd57 1956 public static function set_pin_state($discussionid, $targetstate) {
a7260536 1957 global $PAGE, $USER;
1958 $params = self::validate_parameters(self::set_pin_state_parameters(), [
a7260536 1959 'discussionid' => $discussionid,
1960 'targetstate' => $targetstate,
1961 ]);
1962 $vaultfactory = mod_forum\local\container::get_vault_factory();
309e8698 1963 $managerfactory = mod_forum\local\container::get_manager_factory();
a7260536 1964 $forumvault = $vaultfactory->get_forum_vault();
1965 $discussionvault = $vaultfactory->get_discussion_vault();
8885cd57
P
1966 $discussion = $discussionvault->get_from_id($params['discussionid']);
1967 $forum = $forumvault->get_from_id($discussion->get_forum_id());
309e8698 1968 $capabilitymanager = $managerfactory->get_capability_manager($forum);
a7260536 1969
1970 self::validate_context($forum->get_context());
24962ee1
P
1971
1972 $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
309e8698 1973 if (!$capabilitymanager->can_pin_discussions($USER)) {
a7260536 1974 // Nothing to do. We won't actually output any content here though.
309e8698 1975 throw new \moodle_exception('cannotpindiscussions', 'mod_forum');
a7260536 1976 }
1977
a7260536 1978 $discussion->set_pinned($targetstate);
cda9da99 1979 $discussionvault->update_discussion($discussion);
2893812e
P
1980
1981 $exporterfactory = mod_forum\local\container::get_exporter_factory();
1982 $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1983 return $exporter->export($PAGE->get_renderer('mod_forum'));
1984 }
1985
a7260536 1986 /**
1987 * Returns description of method parameters.
1988 *
1989 * @return external_function_parameters
1990 */
1991 public static function set_pin_state_parameters() {
1992 return new external_function_parameters(
1993 [
309e8698
P
1994 'discussionid' => new external_value(PARAM_INT, 'The discussion to pin or unpin', VALUE_REQUIRED,
1995 null, NULL_NOT_ALLOWED),
1996 'targetstate' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED,
1997 null, NULL_NOT_ALLOWED),
a7260536 1998 ]
1999 );
2000 }
2001
a7260536 2002 /**
2003 * Returns description of method result value.
2004 *
2005 * @return external_single_structure
2006 */
2007 public static function set_pin_state_returns() {
8885cd57 2008 return discussion_exporter::get_read_structure();
a7260536 2009 }
2b9fe87d 2010}