Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
use mod_forum\local\exporters\post as post_exporter;
19
use mod_forum\local\exporters\discussion as discussion_exporter;
20
use core_external\external_api;
21
use core_external\external_files;
22
use core_external\external_format_value;
23
use core_external\external_function_parameters;
24
use core_external\external_multiple_structure;
25
use core_external\external_single_structure;
26
use core_external\external_value;
27
use core_external\external_warnings;
28
use core_external\util as external_util;
29
 
30
/**
31
 * External forum API
32
 *
33
 * @package    mod_forum
34
 * @copyright  2012 Mark Nelson <markn@moodle.com>
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
class mod_forum_external extends external_api {
38
 
39
    /**
40
     * Describes the parameters for get_forum.
41
     *
42
     * @return external_function_parameters
43
     * @since Moodle 2.5
44
     */
45
    public static function get_forums_by_courses_parameters() {
46
        return new external_function_parameters (
47
            array(
48
                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID',
49
                        VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Course IDs', VALUE_DEFAULT, array()),
50
            )
51
        );
52
    }
53
 
54
    /**
55
     * Returns a list of forums in a provided list of courses,
56
     * if no list is provided all forums that the user can view
57
     * will be returned.
58
     *
59
     * @param array $courseids the course ids
60
     * @return array the forum details
61
     * @since Moodle 2.5
62
     */
63
    public static function get_forums_by_courses($courseids = array()) {
64
        global $CFG;
65
 
66
        require_once($CFG->dirroot . "/mod/forum/lib.php");
67
 
68
        $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids));
69
 
70
        $courses = array();
71
        if (empty($params['courseids'])) {
72
            $courses = enrol_get_my_courses();
73
            $params['courseids'] = array_keys($courses);
74
        }
75
 
76
        // Array to store the forums to return.
77
        $arrforums = array();
78
        $warnings = array();
79
 
80
        // Ensure there are courseids to loop through.
81
        if (!empty($params['courseids'])) {
82
 
83
            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
84
 
85
            // Get the forums in this course. This function checks users visibility permissions.
86
            $forums = get_all_instances_in_courses("forum", $courses);
87
            foreach ($forums as $forum) {
88
 
89
                $course = $courses[$forum->course];
90
                $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
91
                $context = context_module::instance($cm->id);
92
 
93
                // Skip forums we are not allowed to see discussions.
94
                if (!has_capability('mod/forum:viewdiscussion', $context)) {
95
                    continue;
96
                }
97
 
98
                $forum->name = \core_external\util::format_string($forum->name, $context);
99
                // Format the intro before being returning using the format setting.
100
                $options = array('noclean' => true);
101
                [$forum->intro, $forum->introformat] = \core_external\util::format_text(
102
                    $forum->intro,
103
                    $forum->introformat,
104
                    $context,
105
                    'mod_forum',
106
                    'intro',
107
                    null,
108
                    $options
109
                );
110
                $forum->introfiles = external_util::get_area_files($context->id, 'mod_forum', 'intro', false, false);
111
                $forum->lang = clean_param($forum->lang, PARAM_LANG);
112
 
113
                // Discussions count. This function does static request cache.
114
                $forum->numdiscussions = forum_count_discussions($forum, $cm, $course);
115
                $forum->cmid = $forum->coursemodule;
116
                $forum->cancreatediscussions = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
117
                $forum->istracked = forum_tp_is_tracked($forum);
118
                if ($forum->istracked) {
119
                    $forum->unreadpostscount = forum_tp_count_forum_unread_posts($cm, $course);
120
                }
121
 
122
                // Add the forum to the array to return.
123
                $arrforums[$forum->id] = $forum;
124
            }
125
        }
126
 
127
        return $arrforums;
128
    }
129
 
130
    /**
131
     * Describes the get_forum return value.
132
     *
133
     * @return external_single_structure
134
     * @since Moodle 2.5
135
     */
136
    public static function get_forums_by_courses_returns() {
137
        // This should be using helper_for_get_mods_by_courses::standard_coursemodule_elements_returns, but it is so horribly
138
        // inconsistent with all similar web serviecs in other modules that we just can't.
139
        // Also, the return type declaration is wrong, but I am not changing it now because I don't want ot break things.
140
        return new external_multiple_structure(
141
            new external_single_structure(
142
                array(
143
                    'id' => new external_value(PARAM_INT, 'Forum id'),
144
                    'course' => new external_value(PARAM_INT, 'Course id'),
145
                    'type' => new external_value(PARAM_TEXT, 'The forum type'),
146
                    'name' => new external_value(PARAM_RAW, 'Forum name'),
147
                    'intro' => new external_value(PARAM_RAW, 'The forum intro'),
148
                    'introformat' => new external_format_value('intro'),
149
                    'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
150
                    'lang' => new external_value(PARAM_SAFEDIR, 'Forced activity language', VALUE_OPTIONAL),
151
                    'duedate' => new external_value(PARAM_INT, 'duedate for the user', VALUE_OPTIONAL),
152
                    'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user', VALUE_OPTIONAL),
153
                    'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
154
                    'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
155
                    'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
156
                    'scale' => new external_value(PARAM_INT, 'Scale'),
157
                    'grade_forum' => new external_value(PARAM_INT, 'Whole forum grade'),
158
                    'grade_forum_notify' => new external_value(PARAM_INT, 'Whether to send notifications to students upon grading by default'),
159
                    'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'),
160
                    'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'),
161
                    'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'),
162
                    'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'),
163
                    'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'),
164
                    'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'),
165
                    'timemodified' => new external_value(PARAM_INT, 'Time modified'),
166
                    'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'),
167
                    'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'),
168
                    'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'),
169
                    'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'),
170
                    'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'),
171
                    'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'),
172
                    'cmid' => new external_value(PARAM_INT, 'Course module id'),
173
                    'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL),
174
                    'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL),
175
                    'lockdiscussionafter' => new external_value(PARAM_INT, 'After what period a discussion is locked', VALUE_OPTIONAL),
176
                    'istracked' => new external_value(PARAM_BOOL, 'If the user is tracking the forum', VALUE_OPTIONAL),
177
                    'unreadpostscount' => new external_value(PARAM_INT, 'The number of unread posts for tracked forums',
178
                        VALUE_OPTIONAL),
179
                ), 'forum'
180
            )
181
        );
182
    }
183
 
184
    /**
185
     * Get the forum posts in the specified discussion.
186
     *
187
     * @param   int $discussionid
188
     * @param   string $sortby
189
     * @param   string $sortdirection
190
     * @param   bool $includeinlineattachments Whether inline attachments should be included or not.
191
     * @return  array
192
     */
193
    public static function get_discussion_posts(int $discussionid, ?string $sortby, ?string $sortdirection, bool $includeinlineattachments = false) {
194
        global $USER;
195
        // Validate the parameter.
196
        $params = self::validate_parameters(self::get_discussion_posts_parameters(), [
197
                'discussionid' => $discussionid,
198
                'sortby' => $sortby,
199
                'sortdirection' => $sortdirection,
200
            ]);
201
        $warnings = [];
202
 
203
        $vaultfactory = mod_forum\local\container::get_vault_factory();
204
 
205
        $discussionvault = $vaultfactory->get_discussion_vault();
206
        $discussion = $discussionvault->get_from_id($params['discussionid']);
207
 
208
        $forumvault = $vaultfactory->get_forum_vault();
209
        $forum = $forumvault->get_from_id($discussion->get_forum_id());
210
        $context = $forum->get_context();
211
        self::validate_context($context);
212
 
213
        $sortby = $params['sortby'];
214
        $sortdirection = $params['sortdirection'];
215
        $sortallowedvalues = ['id', 'created', 'modified'];
216
        $directionallowedvalues = ['ASC', 'DESC'];
217
 
218
        if (!in_array(strtolower($sortby), $sortallowedvalues)) {
219
            throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
220
                'allowed values are: ' . implode(', ', $sortallowedvalues));
221
        }
222
 
223
        $sortdirection = strtoupper($sortdirection);
224
        if (!in_array($sortdirection, $directionallowedvalues)) {
225
            throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
226
                'allowed values are: ' . implode(',', $directionallowedvalues));
227
        }
228
 
1441 ariadna 229
        $orderbysql = "{$sortby} {$sortdirection}";
230
        if (!empty($sortby) && $sortby != 'id') {
231
            $orderbysql .= ", id {$sortdirection}";
232
        }
233
 
1 efrain 234
        $managerfactory = mod_forum\local\container::get_manager_factory();
235
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
236
 
237
        $postvault = $vaultfactory->get_post_vault();
238
        $posts = $postvault->get_from_discussion_id(
239
                $USER,
240
                $discussion->get_id(),
241
                $capabilitymanager->can_view_any_private_reply($USER),
1441 ariadna 242
                $orderbysql
1 efrain 243
            );
244
 
245
        $builderfactory = mod_forum\local\container::get_builder_factory();
246
        $postbuilder = $builderfactory->get_exported_posts_builder();
247
 
248
        $legacydatamapper = mod_forum\local\container::get_legacy_data_mapper_factory();
249
 
250
        return [
251
            'posts' => $postbuilder->build($USER, [$forum], [$discussion], $posts, $includeinlineattachments),
252
            'forumid' => $discussion->get_forum_id(),
253
            'courseid' => $discussion->get_course_id(),
254
            'ratinginfo' => \core_rating\external\util::get_rating_info(
255
                $legacydatamapper->get_forum_data_mapper()->to_legacy_object($forum),
256
                $forum->get_context(),
257
                'mod_forum',
258
                'post',
259
                $legacydatamapper->get_post_data_mapper()->to_legacy_objects($posts)
260
            ),
261
            'warnings' => $warnings,
262
        ];
263
    }
264
 
265
    /**
266
     * Describe the post parameters.
267
     *
268
     * @return external_function_parameters
269
     */
270
    public static function get_discussion_posts_parameters() {
271
        return new external_function_parameters ([
272
            'discussionid' => new external_value(PARAM_INT, 'The ID of the discussion from which to fetch posts.', VALUE_REQUIRED),
273
            'sortby' => new external_value(PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
274
            'sortdirection' => new external_value(PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
275
            'includeinlineattachments' => new external_value(PARAM_BOOL, 'Whether inline attachments should be included or not', VALUE_DEFAULT,
276
                false),
277
        ]);
278
    }
279
 
280
    /**
281
     * Describe the post return format.
282
     *
283
     * @return external_single_structure
284
     */
285
    public static function get_discussion_posts_returns() {
286
        return new external_single_structure([
287
            'posts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
288
            'forumid' => new external_value(PARAM_INT, 'The forum id'),
289
            'courseid' => new external_value(PARAM_INT, 'The forum course id'),
290
            'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
291
            'warnings' => new external_warnings()
292
        ]);
293
    }
294
 
295
    /**
296
     * Describes the parameters for get_forum_discussions.
297
     *
298
     * @return external_function_parameters
299
     * @since Moodle 3.7
300
     */
301
    public static function get_forum_discussions_parameters() {
302
        return new external_function_parameters (
303
            array(
304
                'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED),
305
                'sortorder' => new external_value(PARAM_INT,
306
                    'sort by this element: numreplies, , created or timemodified', VALUE_DEFAULT, -1),
307
                'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1),
308
                'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
309
                'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0),
310
            )
311
        );
312
    }
313
 
314
    /**
315
     * Returns a list of forum discussions optionally sorted and paginated.
316
     *
317
     * @param int $forumid the forum instance id
318
     * @param int $sortorder The sort order
319
     * @param int $page page number
320
     * @param int $perpage items per page
321
     * @param int $groupid the user course group
322
     *
323
     *
324
     * @return array the forum discussion details including warnings
325
     * @since Moodle 3.7
326
     */
327
    public static function get_forum_discussions(int $forumid, ?int $sortorder = -1, ?int $page = -1,
328
            ?int $perpage = 0, ?int $groupid = 0) {
329
 
330
        global $CFG, $DB, $USER;
331
 
332
        require_once($CFG->dirroot . "/mod/forum/lib.php");
333
 
334
        $warnings = array();
335
        $discussions = array();
336
 
337
        $params = self::validate_parameters(self::get_forum_discussions_parameters(),
338
            array(
339
                'forumid' => $forumid,
340
                'sortorder' => $sortorder,
341
                'page' => $page,
342
                'perpage' => $perpage,
343
                'groupid' => $groupid
344
            )
345
        );
346
 
347
        // Compact/extract functions are not recommended.
348
        $forumid        = $params['forumid'];
349
        $sortorder      = $params['sortorder'];
350
        $page           = $params['page'];
351
        $perpage        = $params['perpage'];
352
        $groupid        = $params['groupid'];
353
 
354
        $vaultfactory = \mod_forum\local\container::get_vault_factory();
355
        $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault();
356
 
357
        $sortallowedvalues = array(
358
            $discussionlistvault::SORTORDER_LASTPOST_DESC,
359
            $discussionlistvault::SORTORDER_LASTPOST_ASC,
360
            $discussionlistvault::SORTORDER_CREATED_DESC,
361
            $discussionlistvault::SORTORDER_CREATED_ASC,
362
            $discussionlistvault::SORTORDER_REPLIES_DESC,
363
            $discussionlistvault::SORTORDER_REPLIES_ASC
364
        );
365
 
366
        // If sortorder not defined set a default one.
367
        if ($sortorder == -1) {
368
            $sortorder = $discussionlistvault::SORTORDER_LASTPOST_DESC;
369
        }
370
 
371
        if (!in_array($sortorder, $sortallowedvalues)) {
372
            throw new invalid_parameter_exception('Invalid value for sortorder parameter (value: ' . $sortorder . '),' .
373
                ' allowed values are: ' . implode(',', $sortallowedvalues));
374
        }
375
 
376
        $managerfactory = \mod_forum\local\container::get_manager_factory();
377
        $urlfactory = \mod_forum\local\container::get_url_factory();
378
        $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
379
 
380
        $forumvault = $vaultfactory->get_forum_vault();
381
        $forum = $forumvault->get_from_id($forumid);
382
        if (!$forum) {
383
            throw new \moodle_exception("Unable to find forum with id {$forumid}");
384
        }
385
        $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper();
386
        $forumrecord = $forumdatamapper->to_legacy_object($forum);
387
 
388
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
389
 
390
        $course = $DB->get_record('course', array('id' => $forum->get_course_id()), '*', MUST_EXIST);
391
        $cm = get_coursemodule_from_instance('forum', $forum->get_id(), $course->id, false, MUST_EXIST);
392
 
393
        // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
394
        $modcontext = context_module::instance($cm->id);
395
        self::validate_context($modcontext);
396
 
397
        $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($USER);
398
 
399
        // Check they have the view forum capability.
400
        if (!$capabilitymanager->can_view_discussions($USER)) {
401
            throw new moodle_exception('noviewdiscussionspermission', 'forum');
402
        }
403
 
404
        $alldiscussions = mod_forum_get_discussion_summaries($forum, $USER, $groupid, $sortorder, $page, $perpage);
405
 
406
        if ($alldiscussions) {
407
            $discussionids = array_keys($alldiscussions);
408
 
409
            $postvault = $vaultfactory->get_post_vault();
410
            $postdatamapper = $legacydatamapperfactory->get_post_data_mapper();
411
            // Return the reply count for each discussion in a given forum.
412
            $replies = $postvault->get_reply_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply);
413
            // Return the first post for each discussion in a given forum.
414
            $firstposts = $postvault->get_first_post_for_discussion_ids($discussionids);
415
 
416
            // Get the unreads array, this takes a forum id and returns data for all discussions.
417
            $unreads = array();
418
            if ($cantrack = forum_tp_can_track_forums($forumrecord)) {
419
                if ($forumtracked = forum_tp_is_tracked($forumrecord)) {
420
                    $unreads = $postvault->get_unread_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply);
421
                }
422
            }
423
 
424
            $canlock = $capabilitymanager->can_manage_forum($USER);
425
 
426
            $usercontext = context_user::instance($USER->id);
427
            $ufservice = core_favourites\service_factory::get_service_for_user_context($usercontext);
428
 
429
            $canfavourite = has_capability('mod/forum:cantogglefavourite', $modcontext, $USER);
430
 
431
            foreach ($alldiscussions as $discussionsummary) {
432
                $discussion = $discussionsummary->get_discussion();
433
                $firstpostauthor = $discussionsummary->get_first_post_author();
434
                $latestpostauthor = $discussionsummary->get_latest_post_author();
435
 
436
                // This function checks for qanda forums.
437
                $canviewdiscussion = $capabilitymanager->can_view_discussion($USER, $discussion);
438
                if (!$canviewdiscussion) {
439
                    $warning = array();
440
                    // Function forum_get_discussions returns forum_posts ids not forum_discussions ones.
441
                    $warning['item'] = 'post';
442
                    $warning['itemid'] = $discussion->get_id();
443
                    $warning['warningcode'] = '1';
444
                    $warning['message'] = 'You can\'t see this discussion';
445
                    $warnings[] = $warning;
446
                    continue;
447
                }
448
 
449
                $firstpost = $firstposts[$discussion->get_first_post_id()];
450
                $discussionobject = $postdatamapper->to_legacy_object($firstpost);
451
                // Fix up the types for these properties.
452
                $discussionobject->mailed = $discussionobject->mailed ? 1 : 0;
453
                $discussionobject->messagetrust = $discussionobject->messagetrust ? 1 : 0;
454
                $discussionobject->mailnow = $discussionobject->mailnow ? 1 : 0;
455
                $discussionobject->groupid = $discussion->get_group_id();
456
                $discussionobject->timemodified = $discussion->get_time_modified();
457
                $discussionobject->usermodified = $discussion->get_user_modified();
458
                $discussionobject->timestart = $discussion->get_time_start();
459
                $discussionobject->timeend = $discussion->get_time_end();
460
                $discussionobject->pinned = $discussion->is_pinned();
461
 
462
                $discussionobject->numunread = 0;
463
                if ($cantrack && $forumtracked) {
464
                    if (isset($unreads[$discussion->get_id()])) {
465
                        $discussionobject->numunread = (int) $unreads[$discussion->get_id()];
466
                    }
467
                }
468
 
469
                $discussionobject->numreplies = 0;
470
                if (!empty($replies[$discussion->get_id()])) {
471
                    $discussionobject->numreplies = (int) $replies[$discussion->get_id()];
472
                }
473
 
474
                $discussionobject->name = \core_external\util::format_string($discussion->get_name(), $modcontext);
475
                $discussionobject->subject = \core_external\util::format_string($discussionobject->subject, $modcontext);
476
                // Rewrite embedded images URLs.
477
                $options = array('trusted' => $discussionobject->messagetrust);
478
                list($discussionobject->message, $discussionobject->messageformat) =
479
                    \core_external\util::format_text($discussionobject->message, $discussionobject->messageformat,
480
                        $modcontext, 'mod_forum', 'post', $discussionobject->id, $options);
481
 
482
                // List attachments.
483
                if (!empty($discussionobject->attachment)) {
484
                    $discussionobject->attachments = external_util::get_area_files($modcontext->id, 'mod_forum',
485
                        'attachment', $discussionobject->id);
486
                }
487
                $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post',
488
                    $discussionobject->id);
489
                if (!empty($messageinlinefiles)) {
490
                    $discussionobject->messageinlinefiles = $messageinlinefiles;
491
                }
492
 
493
                $discussionobject->locked = $forum->is_discussion_locked($discussion);
494
                $discussionobject->canlock = $canlock;
495
                $discussionobject->starred = !empty($ufservice) ? $ufservice->favourite_exists('mod_forum', 'discussions',
496
                    $discussion->get_id(), $modcontext) : false;
497
                $discussionobject->canreply = $capabilitymanager->can_post_in_discussion($USER, $discussion);
498
                $discussionobject->canfavourite = $canfavourite;
499
 
500
                if (forum_is_author_hidden($discussionobject, $forumrecord)) {
501
                    $discussionobject->userid = null;
502
                    $discussionobject->userfullname = null;
503
                    $discussionobject->userpictureurl = null;
504
 
505
                    $discussionobject->usermodified = null;
506
                    $discussionobject->usermodifiedfullname = null;
507
                    $discussionobject->usermodifiedpictureurl = null;
508
 
509
                } else {
510
                    $discussionobject->userfullname = $firstpostauthor->get_full_name();
511
                    $discussionobject->userpictureurl = $urlfactory->get_author_profile_image_url($firstpostauthor, null, 2)
512
                        ->out(false);
513
 
514
                    $discussionobject->usermodifiedfullname = $latestpostauthor->get_full_name();
515
                    $discussionobject->usermodifiedpictureurl = $urlfactory->get_author_profile_image_url(
516
                        $latestpostauthor, null, 2)->out(false);
517
                }
518
 
519
                $discussions[] = (array) $discussionobject;
520
            }
521
        }
522
        $result = array();
523
        $result['discussions'] = $discussions;
524
        $result['warnings'] = $warnings;
525
 
526
        return $result;
527
    }
528
 
529
    /**
530
     * Describes the get_forum_discussions return value.
531
     *
532
     * @return external_single_structure
533
     * @since Moodle 3.7
534
     */
535
    public static function get_forum_discussions_returns() {
536
        return new external_single_structure(
537
            array(
538
                'discussions' => new external_multiple_structure(
539
                    new external_single_structure(
540
                        array(
541
                            'id' => new external_value(PARAM_INT, 'Post id'),
542
                            'name' => new external_value(PARAM_RAW, 'Discussion name'),
543
                            'groupid' => new external_value(PARAM_INT, 'Group id'),
544
                            'timemodified' => new external_value(PARAM_INT, 'Time modified'),
545
                            'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'),
546
                            'timestart' => new external_value(PARAM_INT, 'Time discussion can start'),
547
                            'timeend' => new external_value(PARAM_INT, 'Time discussion ends'),
548
                            'discussion' => new external_value(PARAM_INT, 'Discussion id'),
549
                            'parent' => new external_value(PARAM_INT, 'Parent id'),
550
                            'userid' => new external_value(PARAM_INT, 'User who started the discussion id'),
551
                            'created' => new external_value(PARAM_INT, 'Creation time'),
552
                            'modified' => new external_value(PARAM_INT, 'Time modified'),
553
                            'mailed' => new external_value(PARAM_INT, 'Mailed?'),
554
                            'subject' => new external_value(PARAM_RAW, 'The post subject'),
555
                            'message' => new external_value(PARAM_RAW, 'The post message'),
556
                            'messageformat' => new external_format_value('message'),
557
                            'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'),
558
                            'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL),
559
                            'attachment' => new external_value(PARAM_RAW, 'Has attachments?'),
560
                            'attachments' => new external_files('attachments', VALUE_OPTIONAL),
561
                            'totalscore' => new external_value(PARAM_INT, 'The post message total score'),
562
                            'mailnow' => new external_value(PARAM_INT, 'Mail now?'),
563
                            'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'),
564
                            'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'),
565
                            'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'),
566
                            'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'),
567
                            'numreplies' => new external_value(PARAM_INT, 'The number of replies in the discussion'),
568
                            'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
569
                            'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'),
570
                            'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'),
571
                            'starred' => new external_value(PARAM_BOOL, 'Is the discussion starred'),
572
                            'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'),
573
                            'canlock' => new external_value(PARAM_BOOL, 'Can the user lock the discussion'),
574
                            'canfavourite' => new external_value(PARAM_BOOL, 'Can the user star the discussion'),
575
                        ), 'post'
576
                    )
577
                ),
578
                'warnings' => new external_warnings()
579
            )
580
        );
581
    }
582
 
583
    /**
584
     * Returns description of method parameters
585
     *
586
     * @return external_function_parameters
587
     * @since Moodle 2.9
588
     */
589
    public static function view_forum_parameters() {
590
        return new external_function_parameters(
591
            array(
592
                'forumid' => new external_value(PARAM_INT, 'forum instance id')
593
            )
594
        );
595
    }
596
 
597
    /**
598
     * Trigger the course module viewed event and update the module completion status.
599
     *
600
     * @param int $forumid the forum instance id
601
     * @return array of warnings and status result
602
     * @since Moodle 2.9
603
     * @throws moodle_exception
604
     */
605
    public static function view_forum($forumid) {
606
        global $DB, $CFG;
607
        require_once($CFG->dirroot . "/mod/forum/lib.php");
608
 
609
        $params = self::validate_parameters(self::view_forum_parameters(),
610
                                            array(
611
                                                'forumid' => $forumid
612
                                            ));
613
        $warnings = array();
614
 
615
        // Request and permission validation.
616
        $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
617
        list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
618
 
619
        $context = context_module::instance($cm->id);
620
        self::validate_context($context);
621
 
622
        require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum');
623
 
624
        // Call the forum/lib API.
625
        forum_view($forum, $course, $cm, $context);
626
 
627
        $result = array();
628
        $result['status'] = true;
629
        $result['warnings'] = $warnings;
630
        return $result;
631
    }
632
 
633
    /**
634
     * Returns description of method result value
635
     *
636
     * @return \core_external\external_description
637
     * @since Moodle 2.9
638
     */
639
    public static function view_forum_returns() {
640
        return new external_single_structure(
641
            array(
642
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
643
                'warnings' => new external_warnings()
644
            )
645
        );
646
    }
647
 
648
    /**
649
     * Returns description of method parameters
650
     *
651
     * @return external_function_parameters
652
     * @since Moodle 2.9
653
     */
654
    public static function view_forum_discussion_parameters() {
655
        return new external_function_parameters(
656
            array(
657
                'discussionid' => new external_value(PARAM_INT, 'discussion id')
658
            )
659
        );
660
    }
661
 
662
    /**
663
     * Trigger the discussion viewed event.
664
     *
665
     * @param int $discussionid the discussion id
666
     * @return array of warnings and status result
667
     * @since Moodle 2.9
668
     * @throws moodle_exception
669
     */
670
    public static function view_forum_discussion($discussionid) {
671
        global $DB, $CFG, $USER;
672
        require_once($CFG->dirroot . "/mod/forum/lib.php");
673
 
674
        $params = self::validate_parameters(self::view_forum_discussion_parameters(),
675
                                            array(
676
                                                'discussionid' => $discussionid
677
                                            ));
678
        $warnings = array();
679
 
680
        $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST);
681
        $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
682
        list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
683
 
684
        // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
685
        $modcontext = context_module::instance($cm->id);
686
        self::validate_context($modcontext);
687
 
688
        require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum');
689
 
690
        // Call the forum/lib API.
691
        forum_discussion_view($modcontext, $forum, $discussion);
692
 
693
        // Mark as read if required.
694
        if (!$CFG->forum_usermarksread && forum_tp_is_tracked($forum)) {
695
            forum_tp_mark_discussion_read($USER, $discussion->id);
696
        }
697
 
698
        $result = array();
699
        $result['status'] = true;
700
        $result['warnings'] = $warnings;
701
        return $result;
702
    }
703
 
704
    /**
705
     * Returns description of method result value
706
     *
707
     * @return \core_external\external_description
708
     * @since Moodle 2.9
709
     */
710
    public static function view_forum_discussion_returns() {
711
        return new external_single_structure(
712
            array(
713
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
714
                'warnings' => new external_warnings()
715
            )
716
        );
717
    }
718
 
719
    /**
720
     * Returns description of method parameters
721
     *
722
     * @return external_function_parameters
723
     * @since Moodle 3.0
724
     */
725
    public static function add_discussion_post_parameters() {
726
        return new external_function_parameters(
727
            array(
728
                'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to
729
                                                (can be the initial discussion post'),
730
                'subject' => new external_value(PARAM_TEXT, 'new post subject'),
731
                'message' => new external_value(PARAM_RAW, 'new post message (html assumed if messageformat is not provided)'),
732
                'options' => new external_multiple_structure (
733
                    new external_single_structure(
734
                        array(
735
                            'name' => new external_value(PARAM_ALPHANUM,
736
                                        'The allowed keys (value format) are:
737
                                        discussionsubscribe (bool); subscribe to the discussion?, default to true
738
                                        private (bool); make this reply private to the author of the parent post, default to false.
739
                                        inlineattachmentsid              (int); the draft file area id for inline attachments
740
                                        attachmentsid       (int); the draft file area id for attachments
741
                                        topreferredformat (bool); convert the message & messageformat to FORMAT_HTML, defaults to false
742
                            '),
743
                            'value' => new external_value(PARAM_RAW, 'the value of the option,
744
                                                            this param is validated in the external function.'
745
                        )
746
                    )
747
                ), 'Options', VALUE_DEFAULT, array()),
748
                'messageformat' => new external_format_value('message', VALUE_DEFAULT)
749
            )
750
        );
751
    }
752
 
753
    /**
754
     * Create new posts into an existing discussion.
755
     *
756
     * @param int $postid the post id we are going to reply to
757
     * @param string $subject new post subject
758
     * @param string $message new post message (html assumed if messageformat is not provided)
759
     * @param array $options optional settings
760
     * @param string $messageformat The format of the message, defaults to FORMAT_HTML for BC
761
     * @return array of warnings and the new post id
762
     * @since Moodle 3.0
763
     * @throws moodle_exception
764
     */
765
    public static function add_discussion_post($postid, $subject, $message, $options = array(), $messageformat = FORMAT_HTML) {
766
        global $CFG, $USER;
767
        require_once($CFG->dirroot . "/mod/forum/lib.php");
768
 
769
        // Get all the factories that are required.
770
        $vaultfactory = mod_forum\local\container::get_vault_factory();
771
        $entityfactory = mod_forum\local\container::get_entity_factory();
772
        $datamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
773
        $managerfactory = mod_forum\local\container::get_manager_factory();
774
        $discussionvault = $vaultfactory->get_discussion_vault();
775
        $forumvault = $vaultfactory->get_forum_vault();
776
        $discussiondatamapper = $datamapperfactory->get_discussion_data_mapper();
777
        $forumdatamapper = $datamapperfactory->get_forum_data_mapper();
778
 
779
        $params = self::validate_parameters(self::add_discussion_post_parameters(),
780
            array(
781
                'postid' => $postid,
782
                'subject' => $subject,
783
                'message' => $message,
784
                'options' => $options,
785
                'messageformat' => $messageformat,
786
            )
787
        );
788
 
789
        $warnings = array();
790
 
791
        if (!$parent = forum_get_post_full($params['postid'])) {
792
            throw new moodle_exception('invalidparentpostid', 'forum');
793
        }
794
 
795
        if (!$discussion = $discussionvault->get_from_id($parent->discussion)) {
796
            throw new moodle_exception('notpartofdiscussion', 'forum');
797
        }
798
 
799
        // Request and permission validation.
800
        $forum = $forumvault->get_from_id($discussion->get_forum_id());
801
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
802
        $course = $forum->get_course_record();
803
        $cm = $forum->get_course_module_record();
804
 
805
        $discussionrecord = $discussiondatamapper->to_legacy_object($discussion);
806
        $forumrecord = $forumdatamapper->to_legacy_object($forum);
807
        $context = context_module::instance($cm->id);
808
        self::validate_context($context);
809
 
810
        $coursecontext = \context_course::instance($forum->get_course_id());
811
        $discussionsubscribe = \mod_forum\subscriptions::get_user_default_subscription($forumrecord, $coursecontext,
812
            $cm, null);
813
 
814
        // Validate options.
815
        $options = array(
816
            'discussionsubscribe' => $discussionsubscribe,
817
            'private'             => false,
818
            'inlineattachmentsid' => 0,
819
            'attachmentsid' => null,
820
            'topreferredformat'   => false
821
        );
822
        foreach ($params['options'] as $option) {
823
            $name = trim($option['name']);
824
            switch ($name) {
825
                case 'discussionsubscribe':
826
                    $value = clean_param($option['value'], PARAM_BOOL);
827
                    break;
828
                case 'private':
829
                    $value = clean_param($option['value'], PARAM_BOOL);
830
                    break;
831
                case 'inlineattachmentsid':
832
                    $value = clean_param($option['value'], PARAM_INT);
833
                    break;
834
                case 'attachmentsid':
835
                    $value = clean_param($option['value'], PARAM_INT);
836
                    // Ensure that the user has permissions to create attachments.
837
                    if (!has_capability('mod/forum:createattachment', $context)) {
838
                        $value = 0;
839
                    }
840
                    break;
841
                case 'topreferredformat':
842
                    $value = clean_param($option['value'], PARAM_BOOL);
843
                    break;
844
                default:
845
                    throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
846
            }
847
            $options[$name] = $value;
848
        }
849
 
850
        if (!$capabilitymanager->can_post_in_discussion($USER, $discussion)) {
851
            throw new moodle_exception('nopostforum', 'forum');
852
        }
853
 
854
        $thresholdwarning = forum_check_throttling($forumrecord, $cm);
855
        forum_check_blocking_threshold($thresholdwarning);
856
 
857
        // If we want to force a conversion to the preferred format, let's do it now.
858
        if ($options['topreferredformat']) {
859
            // We always are going to honor the preferred format. We are creating a new post.
860
            $preferredformat = editors_get_preferred_format();
861
            // If the post is not HTML and the preferred format is HTML, convert to it.
862
            if ($params['messageformat'] != FORMAT_HTML and $preferredformat == FORMAT_HTML) {
863
                $params['message'] = format_text($params['message'], $params['messageformat'], ['filter' => false]);
864
            }
865
            $params['messageformat'] = $preferredformat;
866
        }
867
 
868
        // Create the post.
869
        $post = new stdClass();
870
        $post->discussion = $discussion->get_id();
871
        $post->parent = $parent->id;
872
        $post->subject = $params['subject'];
873
        $post->message = $params['message'];
874
        $post->messageformat = $params['messageformat'];
875
        $post->messagetrust = trusttext_trusted($context);
876
        $post->itemid = $options['inlineattachmentsid'];
877
        $post->attachments = $options['attachmentsid'];
878
        $post->isprivatereply = $options['private'];
879
        $post->deleted = 0;
880
        $fakemform = $post->attachments;
881
        if ($postid = forum_add_new_post($post, $fakemform)) {
882
 
883
            $post->id = $postid;
884
 
885
            // Trigger events and completion.
886
            $params = array(
887
                'context' => $context,
888
                'objectid' => $post->id,
889
                'other' => array(
890
                    'discussionid' => $discussion->get_id(),
891
                    'forumid' => $forum->get_id(),
892
                    'forumtype' => $forum->get_type(),
893
                )
894
            );
895
            $event = \mod_forum\event\post_created::create($params);
896
            $event->add_record_snapshot('forum_posts', $post);
897
            $event->add_record_snapshot('forum_discussions', $discussionrecord);
898
            $event->trigger();
899
 
900
            // Update completion state.
901
            $completion = new completion_info($course);
902
            if ($completion->is_enabled($cm) &&
903
                    ($forum->get_completion_replies() || $forum->get_completion_posts())) {
904
                $completion->update_state($cm, COMPLETION_COMPLETE);
905
            }
906
 
907
            if ($options['discussionsubscribe']) {
908
                $settings = new stdClass();
909
                $settings->discussionsubscribe = $options['discussionsubscribe'];
910
                forum_post_subscription($settings, $forumrecord, $discussionrecord);
911
            }
912
        } else {
913
            throw new moodle_exception('couldnotadd', 'forum');
914
        }
915
 
916
        $builderfactory = \mod_forum\local\container::get_builder_factory();
917
        $exportedpostsbuilder = $builderfactory->get_exported_posts_builder();
918
        $postentity = $entityfactory->get_post_from_stdClass($post);
919
        $exportedposts = $exportedpostsbuilder->build($USER, [$forum], [$discussion], [$postentity]);
920
        $exportedpost = $exportedposts[0];
921
 
922
        $message = [];
923
        $message[] = [
924
            'type' => 'success',
925
            'message' => get_string("postaddedsuccess", "forum")
926
        ];
927
 
928
        $message[] = [
929
            'type' => 'success',
930
            'message' => get_string("postaddedtimeleft", "forum", format_time($CFG->maxeditingtime))
931
        ];
932
 
933
        $result = array();
934
        $result['postid'] = $postid;
935
        $result['warnings'] = $warnings;
936
        $result['post'] = $exportedpost;
937
        $result['messages'] = $message;
938
        return $result;
939
    }
940
 
941
    /**
942
     * Returns description of method result value
943
     *
944
     * @return \core_external\external_description
945
     * @since Moodle 3.0
946
     */
947
    public static function add_discussion_post_returns() {
948
        return new external_single_structure(
949
            array(
950
                'postid' => new external_value(PARAM_INT, 'new post id'),
951
                'warnings' => new external_warnings(),
952
                'post' => post_exporter::get_read_structure(),
953
                'messages' => new external_multiple_structure(
954
                    new external_single_structure(
955
                        array(
956
                            'type' => new external_value(PARAM_TEXT, "The classification to be used in the client side", VALUE_REQUIRED),
957
                            'message' => new external_value(PARAM_TEXT,'untranslated english message to explain the warning', VALUE_REQUIRED)
958
                        ), 'Messages'), 'list of warnings', VALUE_OPTIONAL
959
                ),
960
                //'alertmessage' => new external_value(PARAM_RAW, 'Success message to be displayed to the user.'),
961
            )
962
        );
963
    }
964
 
965
    /**
966
     * Toggle the favouriting value for the discussion provided
967
     *
968
     * @param int $discussionid The discussion we need to favourite
969
     * @param bool $targetstate The state of the favourite value
970
     * @return array The exported discussion
971
     */
972
    public static function toggle_favourite_state($discussionid, $targetstate) {
973
        global $DB, $PAGE, $USER;
974
 
975
        $params = self::validate_parameters(self::toggle_favourite_state_parameters(), [
976
            'discussionid' => $discussionid,
977
            'targetstate' => $targetstate
978
        ]);
979
 
980
        $vaultfactory = mod_forum\local\container::get_vault_factory();
981
        // Get the discussion vault and the corresponding discussion entity.
982
        $discussionvault = $vaultfactory->get_discussion_vault();
983
        $discussion = $discussionvault->get_from_id($params['discussionid']);
984
 
985
        $forumvault = $vaultfactory->get_forum_vault();
986
        $forum = $forumvault->get_from_id($discussion->get_forum_id());
987
        $forumcontext = $forum->get_context();
988
        self::validate_context($forumcontext);
989
 
990
        $managerfactory = mod_forum\local\container::get_manager_factory();
991
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
992
 
993
        // Does the user have the ability to favourite the discussion?
994
        if (!$capabilitymanager->can_favourite_discussion($USER)) {
995
            throw new moodle_exception('cannotfavourite', 'forum');
996
        }
997
        $usercontext = context_user::instance($USER->id);
998
        $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
999
        $isfavourited = $ufservice->favourite_exists('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
1000
 
1001
        $favouritefunction = $targetstate ? 'create_favourite' : 'delete_favourite';
1002
        if ($isfavourited != (bool) $params['targetstate']) {
1003
            $ufservice->{$favouritefunction}('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
1004
        }
1005
 
1006
        $exporterfactory = mod_forum\local\container::get_exporter_factory();
1007
        $builder = mod_forum\local\container::get_builder_factory()->get_exported_discussion_builder();
1008
        $favourited = ($builder->is_favourited($discussion, $forumcontext, $USER) ? [$discussion->get_id()] : []);
1009
        $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion, [], $favourited);
1010
        return $exporter->export($PAGE->get_renderer('mod_forum'));
1011
    }
1012
 
1013
    /**
1014
     * Returns description of method result value
1015
     *
1016
     * @return \core_external\external_description
1017
     * @since Moodle 3.0
1018
     */
1019
    public static function toggle_favourite_state_returns() {
1020
        return discussion_exporter::get_read_structure();
1021
    }
1022
 
1023
    /**
1024
     * Defines the parameters for the toggle_favourite_state method
1025
     *
1026
     * @return external_function_parameters
1027
     */
1028
    public static function toggle_favourite_state_parameters() {
1029
        return new external_function_parameters(
1030
            [
1031
                'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'),
1032
                'targetstate' => new external_value(PARAM_BOOL, 'The target state')
1033
            ]
1034
        );
1035
    }
1036
 
1037
    /**
1038
     * Returns description of method parameters
1039
     *
1040
     * @return external_function_parameters
1041
     * @since Moodle 3.0
1042
     */
1043
    public static function add_discussion_parameters() {
1044
        return new external_function_parameters(
1045
            array(
1046
                'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1047
                'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'),
1048
                'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'),
1049
                'groupid' => new external_value(PARAM_INT, 'The group, default to 0', VALUE_DEFAULT, 0),
1050
                'options' => new external_multiple_structure (
1051
                    new external_single_structure(
1052
                        array(
1053
                            'name' => new external_value(PARAM_ALPHANUM,
1054
                                        'The allowed keys (value format) are:
1055
                                        discussionsubscribe (bool); subscribe to the discussion?, default to true
1056
                                        discussionpinned    (bool); is the discussion pinned, default to false
1057
                                        inlineattachmentsid              (int); the draft file area id for inline attachments
1058
                                        attachmentsid       (int); the draft file area id for attachments
1059
                            '),
1060
                            'value' => new external_value(PARAM_RAW, 'The value of the option,
1061
                                                            This param is validated in the external function.'
1062
                        )
1063
                    )
1064
                ), 'Options', VALUE_DEFAULT, array())
1065
            )
1066
        );
1067
    }
1068
 
1069
    /**
1070
     * Add a new discussion into an existing forum.
1071
     *
1072
     * @param int $forumid the forum instance id
1073
     * @param string $subject new discussion subject
1074
     * @param string $message new discussion message (only html format allowed)
1075
     * @param int $groupid the user course group
1076
     * @param array $options optional settings
1077
     * @return array of warnings and the new discussion id
1078
     * @since Moodle 3.0
1079
     * @throws moodle_exception
1080
     */
1081
    public static function add_discussion($forumid, $subject, $message, $groupid = 0, $options = array()) {
1082
        global $DB, $CFG;
1083
        require_once($CFG->dirroot . "/mod/forum/lib.php");
1084
 
1085
        $params = self::validate_parameters(self::add_discussion_parameters(),
1086
                                            array(
1087
                                                'forumid' => $forumid,
1088
                                                'subject' => $subject,
1089
                                                'message' => $message,
1090
                                                'groupid' => $groupid,
1091
                                                'options' => $options
1092
                                            ));
1093
 
1094
        $warnings = array();
1095
 
1096
        // Request and permission validation.
1097
        $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1098
        list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1099
 
1100
        $context = context_module::instance($cm->id);
1101
        self::validate_context($context);
1102
 
1103
        // Validate options.
1104
        $options = array(
1105
            'discussionsubscribe' => true,
1106
            'discussionpinned' => false,
1107
            'inlineattachmentsid' => 0,
1108
            'attachmentsid' => null
1109
        );
1110
        foreach ($params['options'] as $option) {
1111
            $name = trim($option['name']);
1112
            switch ($name) {
1113
                case 'discussionsubscribe':
1114
                    $value = clean_param($option['value'], PARAM_BOOL);
1115
                    break;
1116
                case 'discussionpinned':
1117
                    $value = clean_param($option['value'], PARAM_BOOL);
1118
                    break;
1119
                case 'inlineattachmentsid':
1120
                    $value = clean_param($option['value'], PARAM_INT);
1121
                    break;
1122
                case 'attachmentsid':
1123
                    $value = clean_param($option['value'], PARAM_INT);
1124
                    // Ensure that the user has permissions to create attachments.
1125
                    if (!has_capability('mod/forum:createattachment', $context)) {
1126
                        $value = 0;
1127
                    }
1128
                    break;
1129
                default:
1130
                    throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
1131
            }
1132
            $options[$name] = $value;
1133
        }
1134
 
1135
        // Normalize group.
1136
        if (!groups_get_activity_groupmode($cm)) {
1137
            // Groups not supported, force to -1.
1138
            $groupid = -1;
1139
        } else {
1140
            // Check if we receive the default or and empty value for groupid,
1141
            // in this case, get the group for the user in the activity.
1142
            if (empty($params['groupid'])) {
1143
                $groupid = groups_get_activity_group($cm);
1144
            } else {
1145
                // Here we rely in the group passed, forum_user_can_post_discussion will validate the group.
1146
                $groupid = $params['groupid'];
1147
            }
1148
        }
1149
 
1150
        if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) {
1151
            throw new moodle_exception('cannotcreatediscussion', 'forum');
1152
        }
1153
 
1154
        $thresholdwarning = forum_check_throttling($forum, $cm);
1155
        forum_check_blocking_threshold($thresholdwarning);
1156
 
1157
        // Create the discussion.
1158
        $discussion = new stdClass();
1159
        $discussion->course = $course->id;
1160
        $discussion->forum = $forum->id;
1161
        $discussion->message = $params['message'];
1162
        $discussion->messageformat = FORMAT_HTML;   // Force formatting for now.
1163
        $discussion->messagetrust = trusttext_trusted($context);
1164
        $discussion->itemid = $options['inlineattachmentsid'];
1165
        $discussion->groupid = $groupid;
1166
        $discussion->mailnow = 0;
1167
        $discussion->subject = $params['subject'];
1168
        $discussion->name = $discussion->subject;
1169
        $discussion->timestart = 0;
1170
        $discussion->timeend = 0;
1171
        $discussion->timelocked = 0;
1172
        $discussion->attachments = $options['attachmentsid'];
1173
 
1174
        if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) {
1175
            $discussion->pinned = FORUM_DISCUSSION_PINNED;
1176
        } else {
1177
            $discussion->pinned = FORUM_DISCUSSION_UNPINNED;
1178
        }
1179
        $fakemform = $options['attachmentsid'];
1180
        if ($discussionid = forum_add_discussion($discussion, $fakemform)) {
1181
 
1182
            $discussion->id = $discussionid;
1183
 
1184
            // Trigger events and completion.
1185
 
1186
            $params = array(
1187
                'context' => $context,
1188
                'objectid' => $discussion->id,
1189
                'other' => array(
1190
                    'forumid' => $forum->id,
1191
                )
1192
            );
1193
            $event = \mod_forum\event\discussion_created::create($params);
1194
            $event->add_record_snapshot('forum_discussions', $discussion);
1195
            $event->trigger();
1196
 
1197
            $completion = new completion_info($course);
1198
            if ($completion->is_enabled($cm) &&
1199
                    ($forum->completiondiscussions || $forum->completionposts)) {
1200
                $completion->update_state($cm, COMPLETION_COMPLETE);
1201
            }
1202
 
1203
            $settings = new stdClass();
1204
            $settings->discussionsubscribe = $options['discussionsubscribe'];
1205
            forum_post_subscription($settings, $forum, $discussion);
1206
        } else {
1207
            throw new moodle_exception('couldnotadd', 'forum');
1208
        }
1209
 
1210
        $result = array();
1211
        $result['discussionid'] = $discussionid;
1212
        $result['warnings'] = $warnings;
1213
        return $result;
1214
    }
1215
 
1216
    /**
1217
     * Returns description of method result value
1218
     *
1219
     * @return \core_external\external_description
1220
     * @since Moodle 3.0
1221
     */
1222
    public static function add_discussion_returns() {
1223
        return new external_single_structure(
1224
            array(
1225
                'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'),
1226
                'warnings' => new external_warnings()
1227
            )
1228
        );
1229
    }
1230
 
1231
    /**
1232
     * Returns description of method parameters
1233
     *
1234
     * @return external_function_parameters
1235
     * @since Moodle 3.1
1236
     */
1237
    public static function can_add_discussion_parameters() {
1238
        return new external_function_parameters(
1239
            array(
1240
                'forumid' => new external_value(PARAM_INT, 'Forum instance ID'),
1241
                'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group.
1242
                                                Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null)
1243
            )
1244
        );
1245
    }
1246
 
1247
    /**
1248
     * Check if the current user can add discussions in the given forum (and optionally for the given group).
1249
     *
1250
     * @param int $forumid the forum instance id
1251
     * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups.
1252
     * @return array of warnings and the status (true if the user can add discussions)
1253
     * @since Moodle 3.1
1254
     * @throws moodle_exception
1255
     */
1256
    public static function can_add_discussion($forumid, $groupid = null) {
1257
        global $DB, $CFG;
1258
        require_once($CFG->dirroot . "/mod/forum/lib.php");
1259
 
1260
        $params = self::validate_parameters(self::can_add_discussion_parameters(),
1261
                                            array(
1262
                                                'forumid' => $forumid,
1263
                                                'groupid' => $groupid,
1264
                                            ));
1265
        $warnings = array();
1266
 
1267
        // Request and permission validation.
1268
        $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1269
        list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
1270
 
1271
        $context = context_module::instance($cm->id);
1272
        self::validate_context($context);
1273
 
1274
        $status = forum_user_can_post_discussion($forum, $params['groupid'], -1, $cm, $context);
1275
 
1276
        $result = array();
1277
        $result['status'] = $status;
1278
        $result['canpindiscussions'] = has_capability('mod/forum:pindiscussions', $context);
1279
        $result['cancreateattachment'] = forum_can_create_attachment($forum, $context);
1280
        $result['warnings'] = $warnings;
1281
        return $result;
1282
    }
1283
 
1284
    /**
1285
     * Returns description of method result value
1286
     *
1287
     * @return \core_external\external_description
1288
     * @since Moodle 3.1
1289
     */
1290
    public static function can_add_discussion_returns() {
1291
        return new external_single_structure(
1292
            array(
1293
                'status' => new external_value(PARAM_BOOL, 'True if the user can add discussions, false otherwise.'),
1294
                'canpindiscussions' => new external_value(PARAM_BOOL, 'True if the user can pin discussions, false otherwise.',
1295
                    VALUE_OPTIONAL),
1296
                'cancreateattachment' => new external_value(PARAM_BOOL, 'True if the user can add attachments, false otherwise.',
1297
                    VALUE_OPTIONAL),
1298
                'warnings' => new external_warnings()
1299
            )
1300
        );
1301
    }
1302
 
1303
    /**
1304
     * Describes the parameters for get_forum_access_information.
1305
     *
1306
     * @return external_external_function_parameters
1307
     * @since Moodle 3.7
1308
     */
1309
    public static function get_forum_access_information_parameters() {
1310
        return new external_function_parameters (
1311
            array(
1312
                'forumid' => new external_value(PARAM_INT, 'Forum instance id.')
1313
            )
1314
        );
1315
    }
1316
 
1317
    /**
1318
     * Return access information for a given forum.
1319
     *
1320
     * @param int $forumid forum instance id
1321
     * @return array of warnings and the access information
1322
     * @since Moodle 3.7
1323
     * @throws  moodle_exception
1324
     */
1325
    public static function get_forum_access_information($forumid) {
1326
        global $DB;
1327
 
1328
        $params = self::validate_parameters(self::get_forum_access_information_parameters(), array('forumid' => $forumid));
1329
 
1330
        // Request and permission validation.
1331
        $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST);
1332
        $cm = get_coursemodule_from_instance('forum', $forum->id);
1333
 
1334
        $context = context_module::instance($cm->id);
1335
        self::validate_context($context);
1336
 
1337
        $result = array();
1338
        // Return all the available capabilities.
1339
        $capabilities = load_capability_def('mod_forum');
1340
        foreach ($capabilities as $capname => $capdata) {
1341
            // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
1342
            $field = 'can' . str_replace('mod/forum:', '', $capname);
1343
            $result[$field] = has_capability($capname, $context);
1344
        }
1345
 
1346
        $result['warnings'] = array();
1347
        return $result;
1348
    }
1349
 
1350
    /**
1351
     * Describes the get_forum_access_information return value.
1352
     *
1353
     * @return external_single_structure
1354
     * @since Moodle 3.7
1355
     */
1356
    public static function get_forum_access_information_returns() {
1357
 
1358
        $structure = array(
1359
            'warnings' => new external_warnings()
1360
        );
1361
 
1362
        $capabilities = load_capability_def('mod_forum');
1363
        foreach ($capabilities as $capname => $capdata) {
1364
            // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules.
1365
            $field = 'can' . str_replace('mod/forum:', '', $capname);
1366
            $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.',
1367
                VALUE_OPTIONAL);
1368
        }
1369
 
1370
        return new external_single_structure($structure);
1371
    }
1372
 
1373
    /**
1374
     * Set the subscription state.
1375
     *
1376
     * @param   int     $forumid
1377
     * @param   int     $discussionid
1378
     * @param   bool    $targetstate
1379
     * @return  \stdClass
1380
     */
1381
    public static function set_subscription_state($forumid, $discussionid, $targetstate) {
1382
        global $PAGE, $USER;
1383
 
1384
        $params = self::validate_parameters(self::set_subscription_state_parameters(), [
1385
            'forumid' => $forumid,
1386
            'discussionid' => $discussionid,
1387
            'targetstate' => $targetstate
1388
        ]);
1389
 
1390
        $vaultfactory = mod_forum\local\container::get_vault_factory();
1391
        $forumvault = $vaultfactory->get_forum_vault();
1392
        $forum = $forumvault->get_from_id($params['forumid']);
1393
        $coursemodule = $forum->get_course_module_record();
1394
        $context = $forum->get_context();
1395
 
1396
        self::validate_context($context);
1397
 
1398
        $discussionvault = $vaultfactory->get_discussion_vault();
1399
        $discussion = $discussionvault->get_from_id($params['discussionid']);
1400
        $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
1401
 
1402
        $forumrecord = $legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
1403
        $discussionrecord = $legacydatamapperfactory->get_discussion_data_mapper()->to_legacy_object($discussion);
1404
 
1405
        if (!\mod_forum\subscriptions::is_subscribable($forumrecord)) {
1406
            // Nothing to do. We won't actually output any content here though.
1407
            throw new \moodle_exception('cannotsubscribe', 'mod_forum');
1408
        }
1409
 
1410
        $issubscribed = \mod_forum\subscriptions::is_subscribed(
1411
            $USER->id,
1412
            $forumrecord,
1413
            $discussion->get_id(),
1414
            $coursemodule
1415
        );
1416
 
1417
        // If the current state doesn't equal the desired state then update the current
1418
        // state to the desired state.
1419
        if ($issubscribed != (bool) $params['targetstate']) {
1420
            if ($params['targetstate']) {
1421
                \mod_forum\subscriptions::subscribe_user_to_discussion($USER->id, $discussionrecord, $context);
1422
            } else {
1423
                \mod_forum\subscriptions::unsubscribe_user_from_discussion($USER->id, $discussionrecord, $context);
1424
            }
1425
        }
1426
 
1427
        $exporterfactory = mod_forum\local\container::get_exporter_factory();
1428
        $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1429
        return $exporter->export($PAGE->get_renderer('mod_forum'));
1430
    }
1431
 
1432
    /**
1433
     * Returns description of method parameters.
1434
     *
1435
     * @return external_function_parameters
1436
     */
1437
    public static function set_subscription_state_parameters() {
1438
        return new external_function_parameters(
1439
            [
1440
                'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'),
1441
                'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'),
1442
                'targetstate' => new external_value(PARAM_BOOL, 'The target state')
1443
            ]
1444
        );
1445
    }
1446
 
1447
    /**
1448
     * Returns description of method result value.
1449
     *
1450
     * @return \core_external\external_description
1451
     */
1452
    public static function set_subscription_state_returns() {
1453
        return discussion_exporter::get_read_structure();
1454
    }
1455
 
1456
    /**
1457
     * Set the lock state.
1458
     *
1459
     * @param   int     $forumid
1460
     * @param   int     $discussionid
1461
     * @param   string    $targetstate
1462
     * @return  \stdClass
1463
     */
1464
    public static function set_lock_state($forumid, $discussionid, $targetstate) {
1465
        global $DB, $PAGE, $USER;
1466
 
1467
        $params = self::validate_parameters(self::set_lock_state_parameters(), [
1468
            'forumid' => $forumid,
1469
            'discussionid' => $discussionid,
1470
            'targetstate' => $targetstate
1471
        ]);
1472
 
1473
        $vaultfactory = mod_forum\local\container::get_vault_factory();
1474
        $forumvault = $vaultfactory->get_forum_vault();
1475
        $forum = $forumvault->get_from_id($params['forumid']);
1476
 
1477
        $managerfactory = mod_forum\local\container::get_manager_factory();
1478
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
1479
        if (!$capabilitymanager->can_manage_forum($USER)) {
1480
            throw new moodle_exception('errorcannotlock', 'forum');
1481
        }
1482
 
1483
        // If the targetstate(currentstate) is not 0 then it should be set to the current time.
1484
        $lockedvalue = $targetstate ? 0 : time();
1485
        self::validate_context($forum->get_context());
1486
 
1487
        $discussionvault = $vaultfactory->get_discussion_vault();
1488
        $discussion = $discussionvault->get_from_id($params['discussionid']);
1489
 
1490
        // If the current state doesn't equal the desired state then update the current.
1491
        // state to the desired state.
1492
        $discussion->toggle_locked_state($lockedvalue);
1493
        $response = $discussionvault->update_discussion($discussion);
1494
        $discussion = !$response ? $response : $discussion;
1495
 
1496
        // Trigger an event.
1497
        $params = [
1498
            'context' => $forum->get_context(),
1499
            'objectid' => $discussion->get_id(),
1500
            'other' => ['forumid' => $forum->get_id(), 'status' => $lockedvalue ? 'locked' : 'unlocked'],
1501
        ];
1502
        $event = \mod_forum\event\discussion_lock_updated::create($params);
1503
        $discussionrecord = $DB->get_record('forum_discussions', ['id' => $discussion->get_id()]);
1504
        $event->add_record_snapshot('forum_discussions', $discussionrecord);
1505
        $event->trigger();
1506
 
1507
        $exporterfactory = mod_forum\local\container::get_exporter_factory();
1508
        $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1509
        return $exporter->export($PAGE->get_renderer('mod_forum'));
1510
    }
1511
 
1512
    /**
1513
     * Returns description of method parameters.
1514
     *
1515
     * @return external_function_parameters
1516
     */
1517
    public static function set_lock_state_parameters() {
1518
        return new external_function_parameters(
1519
            [
1520
                'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'),
1521
                'discussionid' => new external_value(PARAM_INT, 'The discussion to lock / unlock'),
1522
                'targetstate' => new external_value(PARAM_INT, 'The timestamp for the lock state')
1523
            ]
1524
        );
1525
    }
1526
 
1527
    /**
1528
     * Returns description of method result value.
1529
     *
1530
     * @return \core_external\external_description
1531
     */
1532
    public static function set_lock_state_returns() {
1533
        return new external_single_structure([
1534
            'id' => new external_value(PARAM_INT, 'The discussion we are locking.'),
1535
            'locked' => new external_value(PARAM_BOOL, 'The locked state of the discussion.'),
1536
            'times' => new external_single_structure([
1537
                'locked' => new external_value(PARAM_INT, 'The locked time of the discussion.'),
1538
            ])
1539
        ]);
1540
    }
1541
 
1542
    /**
1543
     * Set the pin state.
1544
     *
1545
     * @param   int     $discussionid
1546
     * @param   bool    $targetstate
1547
     * @return  \stdClass
1548
     */
1549
    public static function set_pin_state($discussionid, $targetstate) {
1550
        global $PAGE, $USER;
1551
        $params = self::validate_parameters(self::set_pin_state_parameters(), [
1552
            'discussionid' => $discussionid,
1553
            'targetstate' => $targetstate,
1554
        ]);
1555
        $vaultfactory = mod_forum\local\container::get_vault_factory();
1556
        $managerfactory = mod_forum\local\container::get_manager_factory();
1557
        $forumvault = $vaultfactory->get_forum_vault();
1558
        $discussionvault = $vaultfactory->get_discussion_vault();
1559
        $discussion = $discussionvault->get_from_id($params['discussionid']);
1560
        $forum = $forumvault->get_from_id($discussion->get_forum_id());
1561
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
1562
 
1563
        self::validate_context($forum->get_context());
1564
 
1565
        $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
1566
        if (!$capabilitymanager->can_pin_discussions($USER)) {
1567
            // Nothing to do. We won't actually output any content here though.
1568
            throw new \moodle_exception('cannotpindiscussions', 'mod_forum');
1569
        }
1570
 
1571
        $discussion->set_pinned($targetstate);
1572
        $discussionvault->update_discussion($discussion);
1573
 
1574
        $exporterfactory = mod_forum\local\container::get_exporter_factory();
1575
        $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
1576
        return $exporter->export($PAGE->get_renderer('mod_forum'));
1577
    }
1578
 
1579
    /**
1580
     * Returns description of method parameters.
1581
     *
1582
     * @return external_function_parameters
1583
     */
1584
    public static function set_pin_state_parameters() {
1585
        return new external_function_parameters(
1586
            [
1587
                'discussionid' => new external_value(PARAM_INT, 'The discussion to pin or unpin', VALUE_REQUIRED,
1588
                    null, NULL_NOT_ALLOWED),
1589
                'targetstate' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED,
1590
                    null, NULL_NOT_ALLOWED),
1591
            ]
1592
        );
1593
    }
1594
 
1595
    /**
1596
     * Returns description of method result value.
1597
     *
1598
     * @return external_single_structure
1599
     */
1600
    public static function set_pin_state_returns() {
1601
        return discussion_exporter::get_read_structure();
1602
    }
1603
 
1604
    /**
1605
     * Returns description of method parameters
1606
     *
1607
     * @return external_function_parameters
1608
     * @since Moodle 3.8
1609
     */
1610
    public static function delete_post_parameters() {
1611
        return new external_function_parameters(
1612
            array(
1613
                'postid' => new external_value(PARAM_INT, 'Post to be deleted. It can be a discussion topic post.'),
1614
            )
1615
        );
1616
    }
1617
 
1618
    /**
1619
     * Deletes a post or a discussion completely when the post is the discussion topic.
1620
     *
1621
     * @param int $postid post to be deleted, it can be a discussion topic post.
1622
     * @return array of warnings and the status (true if the post/discussion was deleted)
1623
     * @since Moodle 3.8
1624
     * @throws moodle_exception
1625
     */
1626
    public static function delete_post($postid) {
1627
        global $USER, $CFG;
1628
        require_once($CFG->dirroot . "/mod/forum/lib.php");
1629
 
1630
        $params = self::validate_parameters(self::delete_post_parameters(),
1631
            array(
1632
                'postid' => $postid,
1633
            )
1634
        );
1635
        $warnings = array();
1636
        $vaultfactory = mod_forum\local\container::get_vault_factory();
1637
        $forumvault = $vaultfactory->get_forum_vault();
1638
        $discussionvault = $vaultfactory->get_discussion_vault();
1639
        $postvault = $vaultfactory->get_post_vault();
1640
        $postentity = $postvault->get_from_id($params['postid']);
1641
 
1642
        if (empty($postentity)) {
1643
            throw new moodle_exception('invalidpostid', 'forum');
1644
        }
1645
 
1646
        $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id());
1647
 
1648
        if (empty($discussionentity)) {
1649
            throw new moodle_exception('notpartofdiscussion', 'forum');
1650
        }
1651
 
1652
        $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id());
1653
        if (empty($forumentity)) {
1654
            throw new moodle_exception('invalidforumid', 'forum');
1655
        }
1656
 
1657
        $context = $forumentity->get_context();
1658
 
1659
        self::validate_context($context);
1660
 
1661
        $managerfactory = mod_forum\local\container::get_manager_factory();
1662
        $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
1663
        $capabilitymanager = $managerfactory->get_capability_manager($forumentity);
1664
        $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper();
1665
        $discussiondatamapper = $legacydatamapperfactory->get_discussion_data_mapper();
1666
        $postdatamapper = $legacydatamapperfactory->get_post_data_mapper();
1667
 
1668
        $replycount = $postvault->get_reply_count_for_post_id_in_discussion_id($USER, $postentity->get_id(),
1669
            $discussionentity->get_id(), true);
1670
        $hasreplies = $replycount > 0;
1671
 
1672
        $capabilitymanager->validate_delete_post($USER, $discussionentity, $postentity, $hasreplies);
1673
 
1674
        if (!$postentity->has_parent()) {
1675
            $status = forum_delete_discussion(
1676
                $discussiondatamapper->to_legacy_object($discussionentity),
1677
                false,
1678
                $forumentity->get_course_record(),
1679
                $forumentity->get_course_module_record(),
1680
                $forumdatamapper->to_legacy_object($forumentity)
1681
            );
1682
        } else {
1683
            $status = forum_delete_post(
1684
                $postdatamapper->to_legacy_object($postentity),
1685
                has_capability('mod/forum:deleteanypost', $context),
1686
                $forumentity->get_course_record(),
1687
                $forumentity->get_course_module_record(),
1688
                $forumdatamapper->to_legacy_object($forumentity)
1689
            );
1690
        }
1691
 
1692
        $result = array();
1693
        $result['status'] = $status;
1694
        $result['warnings'] = $warnings;
1695
        return $result;
1696
    }
1697
 
1698
    /**
1699
     * Returns description of method result value
1700
     *
1701
     * @return \core_external\external_description
1702
     * @since Moodle 3.8
1703
     */
1704
    public static function delete_post_returns() {
1705
        return new external_single_structure(
1706
            array(
1707
                'status' => new external_value(PARAM_BOOL, 'True if the post/discussion was deleted, false otherwise.'),
1708
                'warnings' => new external_warnings()
1709
            )
1710
        );
1711
    }
1712
 
1713
    /**
1714
     * Get the forum posts in the specified forum instance.
1715
     *
1716
     * @param   int $userid
1717
     * @param   int $cmid
1718
     * @param   string $sortby
1719
     * @param   string $sortdirection
1720
     * @return  array
1721
     */
1722
    public static function get_discussion_posts_by_userid(int $userid, int $cmid, ?string $sortby, ?string $sortdirection) {
1723
        global $USER, $DB;
1724
        // Validate the parameter.
1725
        $params = self::validate_parameters(self::get_discussion_posts_by_userid_parameters(), [
1726
                'userid' => $userid,
1727
                'cmid' => $cmid,
1728
                'sortby' => $sortby,
1729
                'sortdirection' => $sortdirection,
1730
        ]);
1731
        $warnings = [];
1732
 
1733
        $user = core_user::get_user($params['userid']);
1734
 
1735
        $vaultfactory = mod_forum\local\container::get_vault_factory();
1736
 
1737
        $forumvault = $vaultfactory->get_forum_vault();
1738
        $forum = $forumvault->get_from_course_module_id($params['cmid']);
1739
 
1740
        // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..).
1741
        self::validate_context($forum->get_context());
1742
 
1743
        $sortby = $params['sortby'];
1744
        $sortdirection = $params['sortdirection'];
1745
        $sortallowedvalues = ['id', 'created', 'modified'];
1746
        $directionallowedvalues = ['ASC', 'DESC'];
1747
 
1748
        if (!in_array(strtolower($sortby), $sortallowedvalues)) {
1749
            throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' .
1750
                    'allowed values are: ' . implode(', ', $sortallowedvalues));
1751
        }
1752
 
1753
        $sortdirection = strtoupper($sortdirection);
1754
        if (!in_array($sortdirection, $directionallowedvalues)) {
1755
            throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
1756
                    'allowed values are: ' . implode(',', $directionallowedvalues));
1757
        }
1758
 
1759
        $managerfactory = mod_forum\local\container::get_manager_factory();
1760
        $capabilitymanager = $managerfactory->get_capability_manager($forum);
1761
 
1762
        $discussionsummariesvault = $vaultfactory->get_discussions_in_forum_vault();
1763
        $discussionsummaries = $discussionsummariesvault->get_from_forum_id(
1764
            $forum->get_id(),
1765
            true,
1766
            null,
1767
            $discussionsummariesvault::SORTORDER_CREATED_ASC,
1768
            0,
1769
 
1770
        );
1771
 
1772
        $postvault = $vaultfactory->get_post_vault();
1773
 
1774
        $builderfactory = mod_forum\local\container::get_builder_factory();
1775
        $postbuilder = $builderfactory->get_exported_posts_builder();
1776
 
1777
        $builtdiscussions = [];
1778
        foreach ($discussionsummaries as $discussionsummary) {
1779
            $discussion = $discussionsummary->get_discussion();
1780
            if (!$capabilitymanager->can_view_discussion($USER, $discussion)) {
1781
                continue;
1782
            }
1783
            $posts = $postvault->get_posts_in_discussion_for_user_id(
1784
                    $discussion->get_id(),
1785
                    $user->id,
1786
                    $capabilitymanager->can_view_any_private_reply($USER),
1787
                    "{$sortby} {$sortdirection}"
1788
            );
1789
            if (empty($posts)) {
1790
                continue;
1791
            }
1792
 
1793
            $parentids = array_filter(array_map(function($post) {
1794
                return $post->has_parent() ? $post->get_parent_id() : null;
1795
            }, $posts));
1796
 
1797
            $parentposts = [];
1798
            if ($parentids) {
1799
                $parentposts = $postbuilder->build(
1800
                    $USER,
1801
                    [$forum],
1802
                    [$discussion],
1803
                    $postvault->get_from_ids(array_values($parentids))
1804
                );
1805
            }
1806
 
1807
            $discussionauthor = $discussionsummary->get_first_post_author();
1808
            $firstpost = $discussionsummary->get_first_post();
1809
 
1810
            $builtdiscussions[] = [
1811
                'name' => $discussion->get_name(),
1812
                'id' => $discussion->get_id(),
1813
                'timecreated' => $firstpost->get_time_created(),
1814
                'authorfullname' => $discussionauthor->get_full_name(),
1815
                'posts' => [
1816
                    'userposts' => $postbuilder->build($USER, [$forum], [$discussion], $posts),
1817
                    'parentposts' => $parentposts,
1818
                ],
1819
            ];
1820
        }
1821
 
1822
        return [
1823
                'discussions' => $builtdiscussions,
1824
                'warnings' => $warnings,
1825
        ];
1826
    }
1827
 
1828
    /**
1829
     * Describe the post parameters.
1830
     *
1831
     * @return external_function_parameters
1832
     */
1833
    public static function get_discussion_posts_by_userid_parameters() {
1834
        return new external_function_parameters ([
1835
                'userid' => new external_value(
1836
                        PARAM_INT, 'The ID of the user of whom to fetch posts.', VALUE_REQUIRED),
1837
                'cmid' => new external_value(
1838
                        PARAM_INT, 'The ID of the module of which to fetch items.', VALUE_REQUIRED),
1839
                'sortby' => new external_value(
1840
                        PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'),
1841
                'sortdirection' => new external_value(
1842
                        PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC')
1843
        ]);
1844
    }
1845
 
1846
    /**
1847
     * Describe the post return format.
1848
     *
1849
     * @return external_single_structure
1850
     */
1851
    public static function get_discussion_posts_by_userid_returns() {
1852
        return new external_single_structure([
1853
                'discussions' => new external_multiple_structure(
1854
                    new external_single_structure([
1855
                        'name' => new external_value(PARAM_RAW, 'Name of the discussion'),
1856
                        'id' => new external_value(PARAM_INT, 'ID of the discussion'),
1857
                        'timecreated' => new external_value(PARAM_INT, 'Timestamp of the discussion start'),
1858
                        'authorfullname' => new external_value(PARAM_RAW, 'Full name of the user that started the discussion'),
1859
                        'posts' => new external_single_structure([
1860
                            'userposts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
1861
                            'parentposts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()),
1862
                        ]),
1863
                    ])),
1864
                'warnings' => new external_warnings(),
1865
        ]);
1866
    }
1867
 
1868
    /**
1869
     * Returns description of method parameters
1870
     *
1871
     * @return external_function_parameters
1872
     * @since Moodle 3.8
1873
     */
1874
    public static function get_discussion_post_parameters() {
1875
        return new external_function_parameters(
1876
            array(
1877
                'postid' => new external_value(PARAM_INT, 'Post to fetch.'),
1878
            )
1879
        );
1880
    }
1881
 
1882
    /**
1883
     * Get a particular discussion post.
1884
     *
1885
     * @param int $postid post to fetch
1886
     * @return array of post and warnings (if any)
1887
     * @since Moodle 3.8
1888
     * @throws moodle_exception
1889
     */
1890
    public static function get_discussion_post($postid) {
1891
        global $USER, $CFG;
1892
 
1893
        $params = self::validate_parameters(self::get_discussion_post_parameters(),
1894
                                            array(
1895
                                                'postid' => $postid,
1896
                                            ));
1897
        $warnings = array();
1898
        $vaultfactory = mod_forum\local\container::get_vault_factory();
1899
        $forumvault = $vaultfactory->get_forum_vault();
1900
        $discussionvault = $vaultfactory->get_discussion_vault();
1901
        $postvault = $vaultfactory->get_post_vault();
1902
 
1903
        $postentity = $postvault->get_from_id($params['postid']);
1904
        if (empty($postentity)) {
1905
            throw new moodle_exception('invalidpostid', 'forum');
1906
        }
1907
        $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id());
1908
        if (empty($discussionentity)) {
1909
            throw new moodle_exception('notpartofdiscussion', 'forum');
1910
        }
1911
        $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id());
1912
        if (empty($forumentity)) {
1913
            throw new moodle_exception('invalidforumid', 'forum');
1914
        }
1915
        self::validate_context($forumentity->get_context());
1916
 
1917
        $managerfactory = mod_forum\local\container::get_manager_factory();
1918
        $capabilitymanager = $managerfactory->get_capability_manager($forumentity);
1919
 
1920
        if (!$capabilitymanager->can_view_post($USER, $discussionentity, $postentity)) {
1921
            throw new moodle_exception('noviewdiscussionspermission', 'forum');
1922
        }
1923
 
1924
        $builderfactory = mod_forum\local\container::get_builder_factory();
1925
        $postbuilder = $builderfactory->get_exported_posts_builder();
1926
        $posts = $postbuilder->build($USER, [$forumentity], [$discussionentity], [$postentity]);
1927
        $post = empty($posts) ? array() : reset($posts);
1928
 
1929
        $result = array();
1930
        $result['post'] = $post;
1931
        $result['warnings'] = $warnings;
1932
        return $result;
1933
    }
1934
 
1935
    /**
1936
     * Returns description of method result value
1937
     *
1938
     * @return \core_external\external_description
1939
     * @since Moodle 3.8
1940
     */
1941
    public static function get_discussion_post_returns() {
1942
        return new external_single_structure(
1943
            array(
1944
                'post' => \mod_forum\local\exporters\post::get_read_structure(),
1945
                'warnings' => new external_warnings(),
1946
            )
1947
        );
1948
    }
1949
 
1950
    /**
1951
     * Returns description of method parameters
1952
     *
1953
     * @return external_function_parameters
1954
     * @since Moodle 3.8
1955
     */
1956
    public static function prepare_draft_area_for_post_parameters() {
1957
        return new external_function_parameters(
1958
            array(
1959
                'postid' => new external_value(PARAM_INT, 'Post to prepare the draft area for.'),
1960
                'area' => new external_value(PARAM_ALPHA, 'Area to prepare: attachment or post.'),
1961
                'draftitemid' => new external_value(PARAM_INT, 'The draft item id to use. 0 to generate one.',
1962
                    VALUE_DEFAULT, 0),
1963
                'filestokeep' => new external_multiple_structure(
1964
                    new external_single_structure(
1965
                        array(
1966
                            'filename' => new external_value(PARAM_FILE, 'File name.'),
1967
                            'filepath' => new external_value(PARAM_PATH, 'File path.'),
1968
                        )
1969
                    ), 'Only keep these files in the draft file area. Empty for keeping all.', VALUE_DEFAULT, []
1970
                ),
1971
            )
1972
        );
1973
    }
1974
 
1975
    /**
1976
     * Prepares a draft area for editing a post.
1977
     *
1978
     * @param int $postid post to prepare the draft area for
1979
     * @param string $area area to prepare attachment or post
1980
     * @param int $draftitemid the draft item id to use. 0 to generate a new one.
1981
     * @param array $filestokeep only keep these files in the draft file area. Empty for keeping all.
1982
     * @return array of files in the area, the area options and the draft item id
1983
     * @since Moodle 3.8
1984
     * @throws moodle_exception
1985
     */
1986
    public static function prepare_draft_area_for_post($postid, $area, $draftitemid = 0, $filestokeep = []) {
1987
        global $USER;
1988
 
1989
        $params = self::validate_parameters(
1990
            self::prepare_draft_area_for_post_parameters(),
1991
            array(
1992
                'postid' => $postid,
1993
                'area' => $area,
1994
                'draftitemid' => $draftitemid,
1995
                'filestokeep' => $filestokeep,
1996
            )
1997
        );
1998
        $directionallowedvalues = ['ASC', 'DESC'];
1999
 
2000
        $allowedareas = ['attachment', 'post'];
2001
        if (!in_array($params['area'], $allowedareas)) {
2002
            throw new invalid_parameter_exception('Invalid value for area parameter
2003
                (value: ' . $params['area'] . '),' . 'allowed values are: ' . implode(', ', $allowedareas));
2004
        }
2005
 
2006
        $warnings = array();
2007
        $vaultfactory = mod_forum\local\container::get_vault_factory();
2008
        $forumvault = $vaultfactory->get_forum_vault();
2009
        $discussionvault = $vaultfactory->get_discussion_vault();
2010
        $postvault = $vaultfactory->get_post_vault();
2011
 
2012
        $postentity = $postvault->get_from_id($params['postid']);
2013
        if (empty($postentity)) {
2014
            throw new moodle_exception('invalidpostid', 'forum');
2015
        }
2016
        $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id());
2017
        if (empty($discussionentity)) {
2018
            throw new moodle_exception('notpartofdiscussion', 'forum');
2019
        }
2020
        $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id());
2021
        if (empty($forumentity)) {
2022
            throw new moodle_exception('invalidforumid', 'forum');
2023
        }
2024
 
2025
        $context = $forumentity->get_context();
2026
        self::validate_context($context);
2027
 
2028
        $managerfactory = mod_forum\local\container::get_manager_factory();
2029
        $capabilitymanager = $managerfactory->get_capability_manager($forumentity);
2030
 
2031
        if (!$capabilitymanager->can_edit_post($USER, $discussionentity, $postentity)) {
2032
            throw new moodle_exception('noviewdiscussionspermission', 'forum');
2033
        }
2034
 
2035
        if ($params['area'] == 'attachment') {
2036
            $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
2037
            $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper();
2038
            $forum = $forumdatamapper->to_legacy_object($forumentity);
2039
 
2040
            $areaoptions = mod_forum_post_form::attachment_options($forum);
2041
            $messagetext = null;
2042
        } else {
2043
            $areaoptions = mod_forum_post_form::editor_options($context, $postentity->get_id());
2044
            $messagetext = $postentity->get_message();
2045
        }
2046
 
2047
        $draftitemid = empty($params['draftitemid']) ? 0 : $params['draftitemid'];
2048
        $messagetext = file_prepare_draft_area($draftitemid, $context->id, 'mod_forum', $params['area'],
2049
            $postentity->get_id(), $areaoptions, $messagetext);
2050
 
2051
        // Just get a structure compatible with external API.
2052
        array_walk($areaoptions, function(&$item, $key) {
2053
            $item = ['name' => $key, 'value' => $item];
2054
        });
2055
 
2056
        // Do we need to keep only the given files?
2057
        $usercontext = context_user::instance($USER->id);
2058
        if (!empty($params['filestokeep'])) {
2059
            $fs = get_file_storage();
2060
 
2061
            if ($areafiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid)) {
2062
                $filestokeep = [];
2063
                foreach ($params['filestokeep'] as $ftokeep) {
2064
                    $filestokeep[$ftokeep['filepath']][$ftokeep['filename']] = $ftokeep;
2065
                }
2066
 
2067
                foreach ($areafiles as $file) {
2068
                    if ($file->is_directory()) {
2069
                        continue;
2070
                    }
2071
                    if (!isset($filestokeep[$file->get_filepath()][$file->get_filename()])) {
2072
                        $file->delete();    // Not in the list to be kept.
2073
                    }
2074
                }
2075
            }
2076
        }
2077
 
2078
        $result = array(
2079
            'draftitemid' => $draftitemid,
2080
            'files' => external_util::get_area_files($usercontext->id, 'user', 'draft',
2081
                $draftitemid),
2082
            'areaoptions' => $areaoptions,
2083
            'messagetext' => $messagetext,
2084
            'warnings' => $warnings,
2085
        );
2086
        return $result;
2087
    }
2088
 
2089
    /**
2090
     * Returns description of method result value
2091
     *
2092
     * @return \core_external\external_description
2093
     * @since Moodle 3.8
2094
     */
2095
    public static function prepare_draft_area_for_post_returns() {
2096
        return new external_single_structure(
2097
            array(
2098
                'draftitemid' => new external_value(PARAM_INT, 'Draft item id for the file area.'),
2099
                'files' => new external_files('Draft area files.', VALUE_OPTIONAL),
2100
                'areaoptions' => new external_multiple_structure(
2101
                    new external_single_structure(
2102
                        array(
2103
                            'name' => new external_value(PARAM_RAW, 'Name of option.'),
2104
                            'value' => new external_value(PARAM_RAW, 'Value of option.'),
2105
                        )
2106
                    ), 'Draft file area options.'
2107
                ),
2108
                'messagetext' => new external_value(PARAM_RAW, 'Message text with URLs rewritten.'),
2109
                'warnings' => new external_warnings(),
2110
            )
2111
        );
2112
    }
2113
 
2114
    /**
2115
     * Returns description of method parameters
2116
     *
2117
     * @return external_function_parameters
2118
     * @since Moodle 3.8
2119
     */
2120
    public static function update_discussion_post_parameters() {
2121
        return new external_function_parameters(
2122
            [
2123
                'postid' => new external_value(PARAM_INT, 'Post to be updated. It can be a discussion topic post.'),
2124
                'subject' => new external_value(PARAM_TEXT, 'Updated post subject', VALUE_DEFAULT, ''),
2125
                'message' => new external_value(PARAM_RAW, 'Updated post message (HTML assumed if messageformat is not provided)',
2126
                    VALUE_DEFAULT, ''),
2127
                'messageformat' => new external_format_value('message', VALUE_DEFAULT),
2128
                'options' => new external_multiple_structure (
2129
                    new external_single_structure(
2130
                        [
2131
                            'name' => new external_value(
2132
                                PARAM_ALPHANUM,
2133
                                'The allowed keys (value format) are:
2134
                                pinned (bool); (only for discussions) whether to pin this discussion or not
2135
                                discussionsubscribe (bool); whether to subscribe to the post or not
2136
                                inlineattachmentsid (int); the draft file area id for inline attachments in the text
2137
                                attachmentsid (int); the draft file area id for attachments'
2138
                            ),
2139
                            'value' => new external_value(PARAM_RAW, 'The value of the option.')
2140
                        ]
2141
                    ),
2142
                    'Configuration options for the post.',
2143
                    VALUE_DEFAULT,
2144
                    []
2145
                ),
2146
            ]
2147
        );
2148
    }
2149
 
2150
    /**
2151
     * Updates a post or a discussion post topic.
2152
     *
2153
     * @param int $postid post to be updated, it can be a discussion topic post.
2154
     * @param string $subject updated post subject
2155
     * @param string $message updated post message (HTML assumed if messageformat is not provided)
2156
     * @param int $messageformat The format of the message, defaults to FORMAT_HTML
2157
     * @param array $options different configuration options for the post to be updated.
2158
     * @return array of warnings and the status (true if the post/discussion was deleted)
2159
     * @since Moodle 3.8
2160
     * @throws moodle_exception
2161
     * @todo support more options: timed posts, groups change and tags.
2162
     */
2163
    public static function update_discussion_post($postid, $subject = '', $message = '', $messageformat = FORMAT_HTML,
2164
            $options = []) {
2165
        global $CFG, $USER;
2166
        require_once($CFG->dirroot . "/mod/forum/lib.php");
2167
 
2168
        $params = self::validate_parameters(self::add_discussion_post_parameters(),
2169
            [
2170
                'postid' => $postid,
2171
                'subject' => $subject,
2172
                'message' => $message,
2173
                'options' => $options,
2174
                'messageformat' => $messageformat,
2175
            ]
2176
        );
2177
        $warnings = [];
2178
 
2179
        // Validate options.
2180
        $options = [];
2181
        foreach ($params['options'] as $option) {
2182
            $name = trim($option['name']);
2183
            switch ($name) {
2184
                case 'pinned':
2185
                    $value = clean_param($option['value'], PARAM_BOOL);
2186
                    break;
2187
                case 'discussionsubscribe':
2188
                    $value = clean_param($option['value'], PARAM_BOOL);
2189
                    break;
2190
                case 'inlineattachmentsid':
2191
                    $value = clean_param($option['value'], PARAM_INT);
2192
                    break;
2193
                case 'attachmentsid':
2194
                    $value = clean_param($option['value'], PARAM_INT);
2195
                    break;
2196
                default:
2197
                    throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
2198
            }
2199
            $options[$name] = $value;
2200
        }
2201
 
2202
        $managerfactory = mod_forum\local\container::get_manager_factory();
2203
        $vaultfactory = mod_forum\local\container::get_vault_factory();
2204
        $forumvault = $vaultfactory->get_forum_vault();
2205
        $discussionvault = $vaultfactory->get_discussion_vault();
2206
        $postvault = $vaultfactory->get_post_vault();
2207
        $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
2208
        $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper();
2209
        $discussiondatamapper = $legacydatamapperfactory->get_discussion_data_mapper();
2210
        $postdatamapper = $legacydatamapperfactory->get_post_data_mapper();
2211
 
2212
        $postentity = $postvault->get_from_id($params['postid']);
2213
        if (empty($postentity)) {
2214
            throw new moodle_exception('invalidpostid', 'forum');
2215
        }
2216
        $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id());
2217
        if (empty($discussionentity)) {
2218
            throw new moodle_exception('notpartofdiscussion', 'forum');
2219
        }
2220
        $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id());
2221
        if (empty($forumentity)) {
2222
            throw new moodle_exception('invalidforumid', 'forum');
2223
        }
2224
        $forum = $forumdatamapper->to_legacy_object($forumentity);
2225
        $capabilitymanager = $managerfactory->get_capability_manager($forumentity);
2226
 
2227
        $modcontext = $forumentity->get_context();
2228
        self::validate_context($modcontext);
2229
 
2230
        if (!$capabilitymanager->can_edit_post($USER, $discussionentity, $postentity)) {
2231
            throw new moodle_exception('cannotupdatepost', 'forum');
2232
        }
2233
 
2234
        // Get the original post.
2235
        $updatepost = $postdatamapper->to_legacy_object($postentity);
2236
        $updatepost->itemid = IGNORE_FILE_MERGE;
2237
        $updatepost->attachments = IGNORE_FILE_MERGE;
2238
 
2239
        // Prepare the post to be updated.
2240
        if ($params['subject'] !== '') {
2241
            $updatepost->subject = $params['subject'];
2242
        }
2243
 
2244
        if ($params['message'] !== '' && isset($params['messageformat'])) {
2245
            $updatepost->message       = $params['message'];
2246
            $updatepost->messageformat = $params['messageformat'];
2247
            $updatepost->messagetrust  = trusttext_trusted($modcontext);
2248
            // Clean message text.
2249
            $updatepost = trusttext_pre_edit($updatepost, 'message', $modcontext);
2250
        }
2251
 
2252
        if (isset($options['discussionsubscribe'])) {
2253
            // No need to validate anything here, forum_post_subscription will do.
2254
            $updatepost->discussionsubscribe = $options['discussionsubscribe'];
2255
        }
2256
 
2257
        // When editing first post/discussion.
2258
        if (!$postentity->has_parent()) {
2259
            // Defaults for discussion topic posts.
2260
            $updatepost->name = $discussionentity->get_name();
2261
            $updatepost->timestart = $discussionentity->get_time_start();
2262
            $updatepost->timeend = $discussionentity->get_time_end();
2263
 
2264
            if (isset($options['pinned'])) {
2265
                if ($capabilitymanager->can_pin_discussions($USER)) {
2266
                    // Can change pinned if we have capability.
2267
                    $updatepost->pinned = !empty($options['pinned']) ? FORUM_DISCUSSION_PINNED : FORUM_DISCUSSION_UNPINNED;
2268
                }
2269
            }
2270
        }
2271
 
2272
        if (isset($options['inlineattachmentsid'])) {
2273
            $updatepost->itemid = $options['inlineattachmentsid'];
2274
        }
2275
 
2276
        if (isset($options['attachmentsid']) && forum_can_create_attachment($forum, $modcontext)) {
2277
            $updatepost->attachments = $options['attachmentsid'];
2278
        }
2279
 
2280
        // Update the post.
2281
        $fakemform = $updatepost->id;
2282
        if (forum_update_post($updatepost, $fakemform)) {
2283
            $discussion = $discussiondatamapper->to_legacy_object($discussionentity);
2284
 
2285
            forum_trigger_post_updated_event($updatepost, $discussion, $modcontext, $forum);
2286
 
2287
            forum_post_subscription(
2288
                $updatepost,
2289
                $forum,
2290
                $discussion
2291
            );
2292
            $status = true;
2293
        } else {
2294
            $status = false;
2295
        }
2296
 
2297
        return [
2298
            'status' => $status,
2299
            'warnings' => $warnings,
2300
        ];
2301
    }
2302
 
2303
    /**
2304
     * Returns description of method result value
2305
     *
2306
     * @return \core_external\external_description
2307
     * @since Moodle 3.8
2308
     */
2309
    public static function update_discussion_post_returns() {
2310
        return new external_single_structure(
2311
            [
2312
                'status' => new external_value(PARAM_BOOL, 'True if the post/discussion was updated, false otherwise.'),
2313
                'warnings' => new external_warnings()
2314
            ]
2315
        );
2316
    }
2317
}