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