1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17
18/**
19 * External message API
20 *
21 * @package    core_message
22 * @category   external
23 * @copyright  2011 Jerome Mouneyrac
24 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27defined('MOODLE_INTERNAL') || die();
28
29require_once("$CFG->libdir/externallib.php");
30require_once($CFG->dirroot . "/message/lib.php");
31
32/**
33 * Message external functions
34 *
35 * @package    core_message
36 * @category   external
37 * @copyright  2011 Jerome Mouneyrac
38 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 * @since Moodle 2.2
40 */
41class core_message_external extends external_api {
42    /**
43     * Returns description of method parameters
44     *
45     * @return external_function_parameters
46     * @since Moodle 3.6
47     */
48    public static function send_messages_to_conversation_parameters() {
49        return new external_function_parameters(
50            array(
51                'conversationid' => new external_value(PARAM_INT, 'id of the conversation'),
52                'messages' => new external_multiple_structure(
53                    new external_single_structure(
54                        array(
55                            'text' => new external_value(PARAM_RAW, 'the text of the message'),
56                            'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
57                        )
58                    )
59                )
60            )
61        );
62    }
63
64    /**
65     * Send messages from the current USER to a conversation.
66     *
67     * This conversation may be any type of conversation, individual or group.
68     *
69     * @param int $conversationid the id of the conversation to which the messages will be sent.
70     * @param array $messages An array of message to send.
71     * @return array the array of messages which were sent (created).
72     * @since Moodle 3.6
73     */
74    public static function send_messages_to_conversation(int $conversationid, array $messages = []) {
75        global $CFG, $USER;
76
77        // Check if messaging is enabled.
78        if (empty($CFG->messaging)) {
79            throw new moodle_exception('disabled', 'message');
80        }
81
82        // Ensure the current user is allowed to run this function.
83        $context = context_system::instance();
84        self::validate_context($context);
85
86        $params = self::validate_parameters(self::send_messages_to_conversation_parameters(), [
87            'conversationid' => $conversationid,
88            'messages' => $messages
89        ]);
90
91        // Validate messages content before posting them.
92        foreach ($params['messages'] as $message) {
93            // Check message length.
94            if (strlen($message['text']) > \core_message\api::MESSAGE_MAX_LENGTH) {
95                throw new moodle_exception('errormessagetoolong', 'message');
96            }
97        }
98
99        $messages = [];
100        foreach ($params['messages'] as $message) {
101            $createdmessage = \core_message\api::send_message_to_conversation($USER->id, $params['conversationid'], $message['text'],
102                $message['textformat']);
103            $createdmessage->text = message_format_message_text((object) [
104                'smallmessage' => $createdmessage->text,
105                'fullmessageformat' => external_validate_format($message['textformat']),
106                'fullmessagetrust' => $createdmessage->fullmessagetrust
107            ]);
108            $messages[] = $createdmessage;
109        }
110
111        return $messages;
112    }
113
114    /**
115     * Returns description of method result value.
116     *
117     * @return external_description
118     * @since Moodle 3.6
119     */
120    public static function send_messages_to_conversation_returns() {
121        return new external_multiple_structure(
122            self::get_conversation_message_structure()
123        );
124    }
125
126
127    /**
128     * Returns description of method parameters
129     *
130     * @return external_function_parameters
131     * @since Moodle 2.2
132     */
133    public static function send_instant_messages_parameters() {
134        return new external_function_parameters(
135            array(
136                'messages' => new external_multiple_structure(
137                    new external_single_structure(
138                        array(
139                            'touserid' => new external_value(PARAM_INT, 'id of the user to send the private message'),
140                            'text' => new external_value(PARAM_RAW, 'the text of the message'),
141                            'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
142                            'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own client id for the message. If this id is provided, the fail message id will be returned to you', VALUE_OPTIONAL),
143                        )
144                    )
145                )
146            )
147        );
148    }
149
150    /**
151     * Send private messages from the current USER to other users
152     *
153     * @param array $messages An array of message to send.
154     * @return array
155     * @since Moodle 2.2
156     */
157    public static function send_instant_messages($messages = array()) {
158        global $CFG, $USER, $DB;
159
160        // Check if messaging is enabled.
161        if (empty($CFG->messaging)) {
162            throw new moodle_exception('disabled', 'message');
163        }
164
165        // Ensure the current user is allowed to run this function
166        $context = context_system::instance();
167        self::validate_context($context);
168        require_capability('moodle/site:sendmessage', $context);
169
170        // Ensure the current user is allowed to delete message for everyone.
171        $candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $context);
172
173        $params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
174
175        //retrieve all tousers of the messages
176        $receivers = array();
177        foreach($params['messages'] as $message) {
178            $receivers[] = $message['touserid'];
179        }
180        list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
181        $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
182
183        $resultmessages = array();
184        $messageids = array();
185        foreach ($params['messages'] as $message) {
186            $resultmsg = array(); //the infos about the success of the operation
187
188            // We are going to do some checking.
189            // Code should match /messages/index.php checks.
190            $success = true;
191
192            // Check the user exists.
193            if (empty($tousers[$message['touserid']])) {
194                $success = false;
195                $errormessage = get_string('touserdoesntexist', 'message', $message['touserid']);
196            }
197
198            // Check message length.
199            if ($success && strlen($message['text']) > \core_message\api::MESSAGE_MAX_LENGTH) {
200                $success = false;
201                $errormessage = get_string('errormessagetoolong', 'message');
202            }
203
204            // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead userid
205            // Check if the recipient can be messaged by the sender.
206            if ($success && !\core_message\api::can_send_message($tousers[$message['touserid']]->id, $USER->id)) {
207                $success = false;
208                $errormessage = get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message['touserid'])));
209            }
210
211            // Now we can send the message (at least try).
212            if ($success) {
213                // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead one touser object.
214                $success = message_post_message($USER, $tousers[$message['touserid']],
215                        $message['text'], external_validate_format($message['textformat']));
216            }
217
218            // Build the resultmsg.
219            if (isset($message['clientmsgid'])) {
220                $resultmsg['clientmsgid'] = $message['clientmsgid'];
221            }
222            if ($success) {
223                $resultmsg['msgid'] = $success;
224                $resultmsg['timecreated'] = time();
225                $resultmsg['candeletemessagesforallusers'] = $candeletemessagesforallusers;
226                $messageids[] = $success;
227            } else {
228                // WARNINGS: for backward compatibility we return this errormessage.
229                //          We should have thrown exceptions as these errors prevent results to be returned.
230                // See http://docs.moodle.org/dev/Errors_handling_in_web_services#When_to_send_a_warning_on_the_server_side .
231                $resultmsg['msgid'] = -1;
232                if (!isset($errormessage)) { // Nobody has set a message error or thrown an exception, let's set it.
233                    $errormessage = get_string('messageundeliveredbynotificationsettings', 'error');
234                }
235                $resultmsg['errormessage'] = $errormessage;
236            }
237
238            $resultmessages[] = $resultmsg;
239        }
240
241        if (!empty($messageids)) {
242            $messagerecords = $DB->get_records_list(
243                'messages',
244                'id',
245                $messageids,
246                '',
247                'id, conversationid, smallmessage, fullmessageformat, fullmessagetrust');
248            $resultmessages = array_map(function($resultmessage) use ($messagerecords, $USER) {
249                $id = $resultmessage['msgid'];
250                $resultmessage['conversationid'] = isset($messagerecords[$id]) ? $messagerecords[$id]->conversationid : null;
251                $resultmessage['useridfrom'] = $USER->id;
252                $resultmessage['text'] = message_format_message_text((object) [
253                    'smallmessage' => $messagerecords[$id]->smallmessage,
254                    'fullmessageformat' => external_validate_format($messagerecords[$id]->fullmessageformat),
255                    'fullmessagetrust' => $messagerecords[$id]->fullmessagetrust
256                ]);
257                return $resultmessage;
258            }, $resultmessages);
259        }
260
261        return $resultmessages;
262    }
263
264    /**
265     * Returns description of method result value
266     *
267     * @return external_description
268     * @since Moodle 2.2
269     */
270    public static function send_instant_messages_returns() {
271        return new external_multiple_structure(
272            new external_single_structure(
273                array(
274                    'msgid' => new external_value(PARAM_INT, 'test this to know if it succeeds:  id of the created message if it succeeded, -1 when failed'),
275                    'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
276                    'errormessage' => new external_value(PARAM_TEXT, 'error message - if it failed', VALUE_OPTIONAL),
277                    'text' => new external_value(PARAM_RAW, 'The text of the message', VALUE_OPTIONAL),
278                    'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message', VALUE_OPTIONAL),
279                    'conversationid' => new external_value(PARAM_INT, 'The conversation id for this message', VALUE_OPTIONAL),
280                    'useridfrom' => new external_value(PARAM_INT, 'The user id who sent the message', VALUE_OPTIONAL),
281                    'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
282                        'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
283                )
284            )
285        );
286    }
287
288    /**
289     * Create contacts parameters description.
290     *
291     * @deprecated since Moodle 3.6
292     * @return external_function_parameters
293     * @since Moodle 2.5
294     */
295    public static function create_contacts_parameters() {
296        return new external_function_parameters(
297            array(
298                'userids' => new external_multiple_structure(
299                    new external_value(PARAM_INT, 'User ID'),
300                    'List of user IDs'
301                ),
302                'userid' => new external_value(PARAM_INT, 'The id of the user we are creating the contacts for, 0 for the
303                    current user', VALUE_DEFAULT, 0)
304            )
305        );
306    }
307
308    /**
309     * Create contacts.
310     *
311     * @deprecated since Moodle 3.6
312     * @param array $userids array of user IDs.
313     * @param int $userid The id of the user we are creating the contacts for
314     * @return external_description
315     * @since Moodle 2.5
316     */
317    public static function create_contacts($userids, $userid = 0) {
318        global $CFG, $USER;
319
320        // Check if messaging is enabled.
321        if (empty($CFG->messaging)) {
322            throw new moodle_exception('disabled', 'message');
323        }
324
325        if (empty($userid)) {
326            $userid = $USER->id;
327        }
328
329        // Validate context.
330        $context = context_system::instance();
331        self::validate_context($context);
332
333        $params = array('userids' => $userids, 'userid' => $userid);
334        $params = self::validate_parameters(self::create_contacts_parameters(), $params);
335
336        $capability = 'moodle/site:manageallmessaging';
337        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
338            throw new required_capability_exception($context, $capability, 'nopermissions', '');
339        }
340
341        $warnings = array();
342        foreach ($params['userids'] as $id) {
343            if (!message_add_contact($id, 0, $params['userid'])) {
344                $warnings[] = array(
345                    'item' => 'user',
346                    'itemid' => $id,
347                    'warningcode' => 'contactnotcreated',
348                    'message' => 'The contact could not be created'
349                );
350            }
351        }
352        return $warnings;
353    }
354
355    /**
356     * Create contacts return description.
357     *
358     * @deprecated since Moodle 3.6
359     * @return external_description
360     * @since Moodle 2.5
361     */
362    public static function create_contacts_returns() {
363        return new external_warnings();
364    }
365
366    /**
367     * Marking the method as deprecated.
368     *
369     * @return bool
370     */
371    public static function create_contacts_is_deprecated() {
372        return true;
373    }
374
375    /**
376     * Delete contacts parameters description.
377     *
378     * @return external_function_parameters
379     * @since Moodle 2.5
380     */
381    public static function delete_contacts_parameters() {
382        return new external_function_parameters(
383            array(
384                'userids' => new external_multiple_structure(
385                    new external_value(PARAM_INT, 'User ID'),
386                    'List of user IDs'
387                ),
388                'userid' => new external_value(PARAM_INT, 'The id of the user we are deleting the contacts for, 0 for the
389                    current user', VALUE_DEFAULT, 0)
390            )
391        );
392    }
393
394    /**
395     * Delete contacts.
396     *
397     * @param array $userids array of user IDs.
398     * @param int $userid The id of the user we are deleting the contacts for
399     * @return null
400     * @since Moodle 2.5
401     */
402    public static function delete_contacts($userids, $userid = 0) {
403        global $CFG, $USER;
404
405        // Check if messaging is enabled.
406        if (empty($CFG->messaging)) {
407            throw new moodle_exception('disabled', 'message');
408        }
409
410        if (empty($userid)) {
411            $userid = $USER->id;
412        }
413
414        // Validate context.
415        $context = context_system::instance();
416        self::validate_context($context);
417
418        $params = array('userids' => $userids, 'userid' => $userid);
419        $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
420
421        $capability = 'moodle/site:manageallmessaging';
422        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
423            throw new required_capability_exception($context, $capability, 'nopermissions', '');
424        }
425
426        foreach ($params['userids'] as $id) {
427            \core_message\api::remove_contact($params['userid'], $id);
428        }
429
430        return null;
431    }
432
433    /**
434     * Delete contacts return description.
435     *
436     * @return external_description
437     * @since Moodle 2.5
438     */
439    public static function delete_contacts_returns() {
440        return null;
441    }
442
443    /**
444     * Mute conversations parameters description.
445     *
446     * @return external_function_parameters
447     */
448    public static function mute_conversations_parameters() {
449        return new external_function_parameters(
450            [
451                'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
452                'conversationids' => new external_multiple_structure(
453                    new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
454                ),
455            ]
456        );
457    }
458
459    /**
460     * Mutes conversations.
461     *
462     * @param int $userid The id of the user who is blocking
463     * @param array $conversationids The list of conversations being muted
464     * @return external_description
465     */
466    public static function mute_conversations(int $userid, array $conversationids) {
467        global $CFG, $USER;
468
469        // Check if messaging is enabled.
470        if (empty($CFG->messaging)) {
471            throw new moodle_exception('disabled', 'message');
472        }
473
474        // Validate context.
475        $context = context_system::instance();
476        self::validate_context($context);
477
478        $params = ['userid' => $userid, 'conversationids' => $conversationids];
479        $params = self::validate_parameters(self::mute_conversations_parameters(), $params);
480
481        $capability = 'moodle/site:manageallmessaging';
482        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
483            throw new required_capability_exception($context, $capability, 'nopermissions', '');
484        }
485
486        foreach ($params['conversationids'] as $conversationid) {
487            if (!\core_message\api::is_conversation_muted($params['userid'], $conversationid)) {
488                \core_message\api::mute_conversation($params['userid'], $conversationid);
489            }
490        }
491
492        return [];
493    }
494
495    /**
496     * Mute conversations return description.
497     *
498     * @return external_description
499     */
500    public static function mute_conversations_returns() {
501        return new external_warnings();
502    }
503
504    /**
505     * Unmute conversations parameters description.
506     *
507     * @return external_function_parameters
508     */
509    public static function unmute_conversations_parameters() {
510        return new external_function_parameters(
511            [
512                'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
513                'conversationids' => new external_multiple_structure(
514                    new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
515                ),
516            ]
517        );
518    }
519
520    /**
521     * Unmute conversations.
522     *
523     * @param int $userid The id of the user who is unblocking
524     * @param array $conversationids The list of conversations being muted
525     */
526    public static function unmute_conversations(int $userid, array $conversationids) {
527        global $CFG, $USER;
528
529        // Check if messaging is enabled.
530        if (empty($CFG->messaging)) {
531            throw new moodle_exception('disabled', 'message');
532        }
533
534        // Validate context.
535        $context = context_system::instance();
536        self::validate_context($context);
537
538        $params = ['userid' => $userid, 'conversationids' => $conversationids];
539        $params = self::validate_parameters(self::unmute_conversations_parameters(), $params);
540
541        $capability = 'moodle/site:manageallmessaging';
542        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
543            throw new required_capability_exception($context, $capability, 'nopermissions', '');
544        }
545
546        foreach ($params['conversationids'] as $conversationid) {
547            \core_message\api::unmute_conversation($params['userid'], $conversationid);
548        }
549
550        return [];
551    }
552
553    /**
554     * Unmute conversations return description.
555     *
556     * @return external_description
557     */
558    public static function unmute_conversations_returns() {
559        return new external_warnings();
560    }
561
562    /**
563     * Block user parameters description.
564     *
565     * @return external_function_parameters
566     */
567    public static function block_user_parameters() {
568        return new external_function_parameters(
569            [
570                'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
571                'blockeduserid' => new external_value(PARAM_INT, 'The id of the user being blocked'),
572            ]
573        );
574    }
575
576    /**
577     * Blocks a user.
578     *
579     * @param int $userid The id of the user who is blocking
580     * @param int $blockeduserid The id of the user being blocked
581     * @return external_description
582     */
583    public static function block_user(int $userid, int $blockeduserid) {
584        global $CFG, $USER;
585
586        // Check if messaging is enabled.
587        if (empty($CFG->messaging)) {
588            throw new moodle_exception('disabled', 'message');
589        }
590
591        // Validate context.
592        $context = context_system::instance();
593        self::validate_context($context);
594
595        $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
596        $params = self::validate_parameters(self::block_user_parameters(), $params);
597
598        $capability = 'moodle/site:manageallmessaging';
599        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
600            throw new required_capability_exception($context, $capability, 'nopermissions', '');
601        }
602
603        // If the blocking is going to be useless then don't do it.
604        if (\core_message\api::can_send_message($userid, $blockeduserid, true)) {
605            return [];
606        }
607
608        if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
609            \core_message\api::block_user($params['userid'], $params['blockeduserid']);
610        }
611
612        return [];
613    }
614
615    /**
616     * Block user return description.
617     *
618     * @return external_description
619     */
620    public static function block_user_returns() {
621        return new external_warnings();
622    }
623
624    /**
625     * Unblock user parameters description.
626     *
627     * @return external_function_parameters
628     */
629    public static function unblock_user_parameters() {
630        return new external_function_parameters(
631            [
632                'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
633                'unblockeduserid' => new external_value(PARAM_INT, 'The id of the user being unblocked'),
634            ]
635        );
636    }
637
638    /**
639     * Unblock user.
640     *
641     * @param int $userid The id of the user who is unblocking
642     * @param int $unblockeduserid The id of the user being unblocked
643     */
644    public static function unblock_user(int $userid, int $unblockeduserid) {
645        global $CFG, $USER;
646
647        // Check if messaging is enabled.
648        if (empty($CFG->messaging)) {
649            throw new moodle_exception('disabled', 'message');
650        }
651
652        // Validate context.
653        $context = context_system::instance();
654        self::validate_context($context);
655
656        $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
657        $params = self::validate_parameters(self::unblock_user_parameters(), $params);
658
659        $capability = 'moodle/site:manageallmessaging';
660        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
661            throw new required_capability_exception($context, $capability, 'nopermissions', '');
662        }
663
664        \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
665
666        return [];
667    }
668
669    /**
670     * Unblock user return description.
671     *
672     * @return external_description
673     */
674    public static function unblock_user_returns() {
675        return new external_warnings();
676    }
677
678    /**
679     * Block contacts parameters description.
680     *
681     * @deprecated since Moodle 3.6
682     * @return external_function_parameters
683     * @since Moodle 2.5
684     */
685    public static function block_contacts_parameters() {
686        return new external_function_parameters(
687            array(
688                'userids' => new external_multiple_structure(
689                    new external_value(PARAM_INT, 'User ID'),
690                    'List of user IDs'
691                ),
692                'userid' => new external_value(PARAM_INT, 'The id of the user we are blocking the contacts for, 0 for the
693                    current user', VALUE_DEFAULT, 0)
694            )
695        );
696    }
697
698    /**
699     * Block contacts.
700     *
701     * @deprecated since Moodle 3.6
702     * @param array $userids array of user IDs.
703     * @param int $userid The id of the user we are blocking the contacts for
704     * @return external_description
705     * @since Moodle 2.5
706     */
707    public static function block_contacts($userids, $userid = 0) {
708        global $CFG, $USER;
709
710        // Check if messaging is enabled.
711        if (empty($CFG->messaging)) {
712            throw new moodle_exception('disabled', 'message');
713        }
714
715        if (empty($userid)) {
716            $userid = $USER->id;
717        }
718
719        // Validate context.
720        $context = context_system::instance();
721        self::validate_context($context);
722
723        $params = array('userids' => $userids, 'userid' => $userid);
724        $params = self::validate_parameters(self::block_contacts_parameters(), $params);
725
726        $capability = 'moodle/site:manageallmessaging';
727        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
728            throw new required_capability_exception($context, $capability, 'nopermissions', '');
729        }
730
731        $warnings = array();
732        foreach ($params['userids'] as $id) {
733            if (!message_block_contact($id, $params['userid'])) {
734                $warnings[] = array(
735                    'item' => 'user',
736                    'itemid' => $id,
737                    'warningcode' => 'contactnotblocked',
738                    'message' => 'The contact could not be blocked'
739                );
740            }
741        }
742        return $warnings;
743    }
744
745    /**
746     * Block contacts return description.
747     *
748     * @deprecated since Moodle 3.6
749     * @return external_description
750     * @since Moodle 2.5
751     */
752    public static function block_contacts_returns() {
753        return new external_warnings();
754    }
755
756    /**
757     * Marking the method as deprecated.
758     *
759     * @return bool
760     */
761    public static function block_contacts_is_deprecated() {
762        return true;
763    }
764
765    /**
766     * Unblock contacts parameters description.
767     *
768     * @deprecated since Moodle 3.6
769     * @return external_function_parameters
770     * @since Moodle 2.5
771     */
772    public static function unblock_contacts_parameters() {
773        return new external_function_parameters(
774            array(
775                'userids' => new external_multiple_structure(
776                    new external_value(PARAM_INT, 'User ID'),
777                    'List of user IDs'
778                ),
779                'userid' => new external_value(PARAM_INT, 'The id of the user we are unblocking the contacts for, 0 for the
780                    current user', VALUE_DEFAULT, 0)
781            )
782        );
783    }
784
785    /**
786     * Unblock contacts.
787     *
788     * @param array $userids array of user IDs.
789     * @param int $userid The id of the user we are unblocking the contacts for
790     * @return null
791     * @since Moodle 2.5
792     */
793    public static function unblock_contacts($userids, $userid = 0) {
794        global $CFG, $USER;
795
796        // Check if messaging is enabled.
797        if (empty($CFG->messaging)) {
798            throw new moodle_exception('disabled', 'message');
799        }
800
801        if (empty($userid)) {
802            $userid = $USER->id;
803        }
804
805        // Validate context.
806        $context = context_system::instance();
807        self::validate_context($context);
808
809        $params = array('userids' => $userids, 'userid' => $userid);
810        $params = self::validate_parameters(self::unblock_contacts_parameters(), $params);
811
812        $capability = 'moodle/site:manageallmessaging';
813        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
814            throw new required_capability_exception($context, $capability, 'nopermissions', '');
815        }
816
817        foreach ($params['userids'] as $id) {
818            message_unblock_contact($id, $params['userid']);
819        }
820
821        return null;
822    }
823
824    /**
825     * Unblock contacts return description.
826     *
827     * @deprecated since Moodle 3.6
828     * @return external_description
829     * @since Moodle 2.5
830     */
831    public static function unblock_contacts_returns() {
832        return null;
833    }
834
835    /**
836     * Marking the method as deprecated.
837     *
838     * @return bool
839     */
840    public static function unblock_contacts_is_deprecated() {
841        return true;
842    }
843
844    /**
845     * Returns contact requests parameters description.
846     *
847     * @return external_function_parameters
848     */
849    public static function get_contact_requests_parameters() {
850        return new external_function_parameters(
851            [
852                'userid' => new external_value(PARAM_INT, 'The id of the user we want the requests for'),
853                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
854                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
855            ]
856        );
857    }
858
859    /**
860     * Handles returning the contact requests for a user.
861     *
862     * This also includes the user data necessary to display information
863     * about the user.
864     *
865     * It will not include blocked users.
866     *
867     * @param int $userid The id of the user we want to get the contact requests for
868     * @param int $limitfrom
869     * @param int $limitnum
870     */
871    public static function get_contact_requests(int $userid, int $limitfrom = 0, int $limitnum = 0) {
872        global $CFG, $USER;
873
874        // Check if messaging is enabled.
875        if (empty($CFG->messaging)) {
876            throw new moodle_exception('disabled', 'message');
877        }
878
879        // Validate context.
880        $context = context_system::instance();
881        self::validate_context($context);
882
883        $params = [
884            'userid' => $userid,
885            'limitfrom' => $limitfrom,
886            'limitnum' => $limitnum
887        ];
888        $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
889
890        $capability = 'moodle/site:manageallmessaging';
891        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
892            throw new required_capability_exception($context, $capability, 'nopermissions', '');
893        }
894
895        return \core_message\api::get_contact_requests($params['userid'], $params['limitfrom'], $params['limitnum']);
896    }
897
898    /**
899     * Returns the contact requests return description.
900     *
901     * @return external_description
902     */
903    public static function get_contact_requests_returns() {
904        return new external_multiple_structure(
905            self::get_conversation_member_structure()
906        );
907    }
908
909    /**
910     * Returns the number of contact requests the user has received parameters description.
911     *
912     * @return external_function_parameters
913     */
914    public static function get_received_contact_requests_count_parameters() {
915        return new external_function_parameters(
916            array(
917                'userid' => new external_value(PARAM_INT, 'The id of the user we want to return the number of ' .
918                    'received contact requests for', VALUE_REQUIRED),
919            )
920        );
921    }
922
923    /**
924     * Returns the number of contact requests the user has received.
925     *
926     * @param int $userid The ID of the user we want to return the number of received contact requests for
927     * @return external_value
928     */
929    public static function get_received_contact_requests_count(int $userid) {
930        global $CFG, $USER;
931
932        // Check if messaging is enabled.
933        if (empty($CFG->messaging)) {
934            throw new moodle_exception('disabled', 'message');
935        }
936
937        // Validate context.
938        $context = context_system::instance();
939        self::validate_context($context);
940
941        $params = [
942            'userid' => $userid,
943        ];
944        $params = self::validate_parameters(self::get_received_contact_requests_count_parameters(), $params);
945
946        $capability = 'moodle/site:manageallmessaging';
947        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
948            throw new required_capability_exception($context, $capability, 'nopermissions', '');
949        }
950
951        return \core_message\api::get_received_contact_requests_count($params['userid']);
952    }
953
954    /**
955     * Returns the number of contact requests the user has received return description.
956     *
957     * @return external_value
958     */
959    public static function get_received_contact_requests_count_returns() {
960        return new external_value(PARAM_INT, 'The number of received contact requests');
961    }
962
963    /**
964     * Returns get conversation members parameters description.
965     *
966     * @return external_function_parameters
967     */
968    public static function get_conversation_members_parameters() {
969        return new external_function_parameters(
970            [
971                'userid' => new external_value(PARAM_INT, 'The id of the user we are performing this action on behalf of'),
972                'conversationid' => new external_value(PARAM_INT, 'The id of the conversation'),
973                'includecontactrequests' => new external_value(PARAM_BOOL, 'Do we want to include contact requests?',
974                    VALUE_DEFAULT, false),
975                'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Do we want to include privacy info?',
976                    VALUE_DEFAULT, false),
977                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
978                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
979            ]
980        );
981    }
982
983    /**
984     * Returns a list of conversation members.
985     *
986     * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info.
987     * @param int $conversationid The id of the conversation
988     * @param bool $includecontactrequests Do we want to include contact requests with this data?
989     * @param bool $includeprivacyinfo Do we want to include privacy info?
990     * @param int $limitfrom
991     * @param int $limitnum
992     * @return array
993     */
994    public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false,
995                                                    bool $includeprivacyinfo = false, int $limitfrom = 0, int $limitnum = 0) {
996        global $CFG, $USER;
997
998        // Check if messaging is enabled.
999        if (empty($CFG->messaging)) {
1000            throw new moodle_exception('disabled', 'message');
1001        }
1002
1003        // Validate context.
1004        $context = context_system::instance();
1005        self::validate_context($context);
1006
1007        $params = [
1008            'userid' => $userid,
1009            'conversationid' => $conversationid,
1010            'includecontactrequests' => $includecontactrequests,
1011            'includeprivacyinfo' => $includeprivacyinfo,
1012            'limitfrom' => $limitfrom,
1013            'limitnum' => $limitnum
1014        ];
1015        $params = self::validate_parameters(self::get_conversation_members_parameters(), $params);
1016
1017        $capability = 'moodle/site:manageallmessaging';
1018        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
1019            throw new required_capability_exception($context, $capability, 'nopermissions', '');
1020        }
1021
1022        // The user needs to be a part of the conversation before querying who the members are.
1023        if (!\core_message\api::is_user_in_conversation($params['userid'], $params['conversationid'])) {
1024            throw new moodle_exception('You are not a member of this conversation.');
1025        }
1026
1027        return \core_message\api::get_conversation_members($params['userid'], $params['conversationid'], $params['includecontactrequests'],
1028            $params['includeprivacyinfo'], $params['limitfrom'], $params['limitnum']);
1029    }
1030
1031    /**
1032     * Returns the get conversation members return description.
1033     *
1034     * @return external_description
1035     */
1036    public static function get_conversation_members_returns() {
1037        return new external_multiple_structure(
1038            self::get_conversation_member_structure()
1039        );
1040    }
1041
1042    /**
1043     * Creates a contact request parameters description.
1044     *
1045     * @return external_function_parameters
1046     */
1047    public static function create_contact_request_parameters() {
1048        return new external_function_parameters(
1049            [
1050                'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
1051                'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
1052            ]
1053        );
1054    }
1055
1056    /**
1057     * Creates a contact request.
1058     *
1059     * @param int $userid The id of the user who is creating the contact request
1060     * @param int $requesteduserid The id of the user being requested
1061     */
1062    public static function create_contact_request(int $userid, int $requesteduserid) {
1063        global $CFG, $USER;
1064
1065        // Check if messaging is enabled.
1066        if (empty($CFG->messaging)) {
1067            throw new moodle_exception('disabled', 'message');
1068        }
1069
1070        // Validate context.
1071        $context = context_system::instance();
1072        self::validate_context($context);
1073
1074        $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
1075        $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
1076
1077        $capability = 'moodle/site:manageallmessaging';
1078        if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
1079            throw new required_capability_exception($context, $capability, 'nopermissions', '');
1080        }
1081
1082        $result = [
1083            'warnings' => []
1084        ];
1085
1086        if (!\core_message\api::can_create_contact($params['userid'], $params['requesteduserid'])) {
1087            $result['warnings'][] = [
1088                'item' => 'user',
1089                'itemid' => $params['requesteduserid'],
1090                'warningcode' => 'cannotcreatecontactrequest',
1091                'message' => 'You are unable to create a contact request for this user'
1092            ];
1093        } else {
1094            if ($requests = \core_message\api::get_contact_requests_between_users($params['userid'], $params['requesteduserid'])) {
1095                // There should only ever be one but just in case there are multiple then we can return the first.
1096                $result['request'] = array_shift($requests);
1097            } else {
1098                $result['request'] = \core_message\api::create_contact_request($params['userid'], $params['requesteduserid']);
1099            }
1100        }
1101
1102        return $result;
1103    }
1104
1105    /**
1106     * Creates a contact request return description.
1107     *
1108     * @return external_description
1109     */
1110    public static function create_contact_request_returns() {
1111        return new external_single_structure(
1112            array(
1113                'request' => new external_single_structure(
1114                    array(
1115                        'id' => new external_value(PARAM_INT, 'Message id'),
1116                        'userid' => new external_value(PARAM_INT, 'User from id'),
1117                        'requesteduserid' => new external_value(PARAM_INT, 'User to id'),
1118                        'timecreated' => new external_value(PARAM_INT, 'Time created'),
1119                    ),
1120                    'request record',
1121                    VALUE_OPTIONAL
1122                ),
1123                'warnings' => new external_warnings()
1124            )
1125        );
1126    }
1127
1128    /**
1129     * Confirm a contact request parameters description.
1130     *
1131     * @return external_function_parameters
1132     */
1133    public static function confirm_contact_request_parameters() {
1134        return new external_function_parameters(
1135            [
1136                'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
1137                'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
1138            ]
1139        );
1140    }
1141
1142    /**
1143     * Confirm a contact request.
1144     *
1145     * @param int $userid The id of the user who is creating the contact request
1146     * @param int $requesteduserid The id of the user being requested
1147     */
1148    public static function confirm_contact_request(int $userid, int $requesteduserid) {
1149        global $CFG, $USER;
1150
1151        // Check if messaging is enabled.
1152        if (empty($CFG->messaging)) {
1153            throw new moodle_exception('disabled', 'message');
1154        }
1155
1156        // Validate context.
1157        $context = context_system::instance();
1158        self::validate_context($context);
1159
1160        $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
1161        $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
1162
1163        $capability = 'moodle/site:manageallmessaging';
1164        if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
1165            throw new required_capability_exception($context, $capability, 'nopermissions', '');
1166        }
1167
1168        \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
1169
1170        return [];
1171    }
1172
1173    /**
1174     * Confirm a contact request return description.
1175     *
1176     * @return external_description
1177     */
1178    public static function confirm_contact_request_returns() {
1179        return new external_warnings();
1180    }
1181
1182    /**
1183     * Declines a contact request parameters description.
1184     *
1185     * @return external_function_parameters
1186     */
1187    public static function decline_contact_request_parameters() {
1188        return new external_function_parameters(
1189            [
1190                'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
1191                'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
1192            ]
1193        );
1194    }
1195
1196    /**
1197     * Declines a contact request.
1198     *
1199     * @param int $userid The id of the user who is creating the contact request
1200     * @param int $requesteduserid The id of the user being requested
1201     */
1202    public static function decline_contact_request(int $userid, int $requesteduserid) {
1203        global $CFG, $USER;
1204
1205        // Check if messaging is enabled.
1206        if (empty($CFG->messaging)) {
1207            throw new moodle_exception('disabled', 'message');
1208        }
1209
1210        // Validate context.
1211        $context = context_system::instance();
1212        self::validate_context($context);
1213
1214        $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
1215        $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
1216
1217        $capability = 'moodle/site:manageallmessaging';
1218        if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
1219            throw new required_capability_exception($context, $capability, 'nopermissions', '');
1220        }
1221
1222        \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
1223
1224        return [];
1225    }
1226
1227    /**
1228     * Declines a contact request return description.
1229     *
1230     * @return external_description
1231     */
1232    public static function decline_contact_request_returns() {
1233        return new external_warnings();
1234    }
1235
1236    /**
1237     * Return the structure of a message area contact.
1238     *
1239     * @return external_single_structure
1240     * @since Moodle 3.2
1241     */
1242    private static function get_messagearea_contact_structure() {
1243        return new external_single_structure(
1244            array(
1245                'userid' => new external_value(PARAM_INT, 'The user\'s id'),
1246                'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1247                'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1248                'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1249                'ismessaging' => new external_value(PARAM_BOOL, 'If we are messaging the user'),
1250                'sentfromcurrentuser' => new external_value(PARAM_BOOL, 'Was the last message sent from the current user?'),
1251                'lastmessage' => new external_value(PARAM_NOTAGS, 'The user\'s last message'),
1252                'lastmessagedate' => new external_value(PARAM_INT, 'Timestamp for last message', VALUE_DEFAULT, null),
1253                'messageid' => new external_value(PARAM_INT, 'The unique search message id', VALUE_DEFAULT, null),
1254                'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1255                'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1256                'isread' => new external_value(PARAM_BOOL, 'If the user has read the message'),
1257                'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1258                'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1259                    VALUE_DEFAULT, null),
1260                'conversationid' => new external_value(PARAM_INT, 'The id of the conversation', VALUE_DEFAULT, null),
1261            )
1262        );
1263    }
1264
1265    /**
1266     * Return the structure of a conversation.
1267     *
1268     * @return external_single_structure
1269     * @since Moodle 3.6
1270     */
1271    private static function get_conversation_structure() {
1272        return new external_single_structure(
1273            array(
1274                'id' => new external_value(PARAM_INT, 'The conversation id'),
1275                'name' => new external_value(PARAM_RAW, 'The conversation name, if set', VALUE_DEFAULT, null),
1276                'subname' => new external_value(PARAM_RAW, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null),
1277                'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null),
1278                'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group,3=self)'),
1279                'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'),
1280                'ismuted' => new external_value(PARAM_BOOL, 'If the user muted this conversation'),
1281                'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this conversation as a favourite'),
1282                'isread' => new external_value(PARAM_BOOL, 'If the user has read all messages in the conversation'),
1283                'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1284                    VALUE_DEFAULT, null),
1285                'members' => new external_multiple_structure(
1286                    self::get_conversation_member_structure()
1287                ),
1288                'messages' => new external_multiple_structure(
1289                    self::get_conversation_message_structure()
1290                ),
1291                'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
1292                    'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
1293            )
1294        );
1295    }
1296
1297    /**
1298     * Return the structure of a conversation member.
1299     *
1300     * @return external_single_structure
1301     * @since Moodle 3.6
1302     */
1303    private static function get_conversation_member_structure() {
1304        $result = [
1305            'id' => new external_value(PARAM_INT, 'The user id'),
1306            'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1307            'profileurl' => new external_value(PARAM_URL, 'The link to the user\'s profile page'),
1308            'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1309            'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1310            'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1311            'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1312            'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1313            'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'),
1314            'isdeleted' => new external_value(PARAM_BOOL, 'Is the user deleted?'),
1315            'canmessageevenifblocked' => new external_value(PARAM_BOOL,
1316                'If the user can still message even if they get blocked'),
1317            'canmessage' => new external_value(PARAM_BOOL, 'If the user can be messaged'),
1318            'requirescontact' => new external_value(PARAM_BOOL, 'If the user requires to be contacts'),
1319        ];
1320
1321        $result['contactrequests'] = new external_multiple_structure(
1322            new external_single_structure(
1323                [
1324                    'id' => new external_value(PARAM_INT, 'The id of the contact request'),
1325                    'userid' => new external_value(PARAM_INT, 'The id of the user who created the contact request'),
1326                    'requesteduserid' => new external_value(PARAM_INT, 'The id of the user confirming the request'),
1327                    'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the contact request'),
1328                ]
1329            ), 'The contact requests', VALUE_OPTIONAL
1330        );
1331
1332        $result['conversations'] = new external_multiple_structure(new external_single_structure(
1333            array(
1334                'id' => new external_value(PARAM_INT, 'Conversations id'),
1335                'type' => new external_value(PARAM_INT, 'Conversation type: private or public'),
1336                'name' => new external_value(PARAM_RAW, 'Multilang compatible conversation name'. VALUE_OPTIONAL),
1337                'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'),
1338            ), 'information about conversation', VALUE_OPTIONAL),
1339            'Conversations between users', VALUE_OPTIONAL
1340        );
1341
1342        return new external_single_structure(
1343            $result
1344        );
1345    }
1346
1347    /**
1348     * Return the structure of a message area message.
1349     *
1350     * @return external_single_structure
1351     * @since Moodle 3.6
1352     */
1353    private static function get_conversation_message_structure() {
1354        return new external_single_structure(
1355            array(
1356                'id' => new external_value(PARAM_INT, 'The id of the message'),
1357                'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1358                'text' => new external_value(PARAM_RAW, 'The text of the message'),
1359                'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1360            )
1361        );
1362    }
1363
1364    /**
1365     * Return the structure of a message area message.
1366     *
1367     * @return external_single_structure
1368     * @since Moodle 3.2
1369     */
1370    private static function get_messagearea_message_structure() {
1371        return new external_single_structure(
1372            array(
1373                'id' => new external_value(PARAM_INT, 'The id of the message'),
1374                'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1375                'useridto' => new external_value(PARAM_INT, 'The id of the user who received the message'),
1376                'text' => new external_value(PARAM_RAW, 'The text of the message'),
1377                'displayblocktime' => new external_value(PARAM_BOOL, 'Should we display the block time?'),
1378                'blocktime' => new external_value(PARAM_NOTAGS, 'The time to display above the message'),
1379                'position' => new external_value(PARAM_ALPHA, 'The position of the text'),
1380                'timesent' => new external_value(PARAM_NOTAGS, 'The time the message was sent'),
1381                'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1382                'isread' => new external_value(PARAM_INT, 'Determines if the message was read or not'),
1383            )
1384        );
1385    }
1386
1387    /**
1388     * Get messagearea search users in course parameters.
1389     *
1390     * @deprecated since 3.6
1391     *
1392     * @return external_function_parameters
1393     * @since 3.2
1394     */
1395    public static function data_for_messagearea_search_users_in_course_parameters() {
1396        return new external_function_parameters(
1397            array(
1398                'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1399                'courseid' => new external_value(PARAM_INT, 'The id of the course'),
1400                'search' => new external_value(PARAM_RAW, 'The string being searched'),
1401                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1402                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1403            )
1404        );
1405    }
1406
1407    /**
1408     * Get messagearea search users in course results.
1409     *
1410     * @deprecated since 3.6
1411     *
1412     * @param int $userid The id of the user who is performing the search
1413     * @param int $courseid The id of the course
1414     * @param string $search The string being searched
1415     * @param int $limitfrom
1416     * @param int $limitnum
1417     * @return stdClass
1418     * @throws moodle_exception
1419     * @since 3.2
1420     */
1421    public static function data_for_messagearea_search_users_in_course($userid, $courseid, $search, $limitfrom = 0,
1422                                                                       $limitnum = 0) {
1423        global $CFG, $PAGE, $USER;
1424
1425        // Check if messaging is enabled.
1426        if (empty($CFG->messaging)) {
1427            throw new moodle_exception('disabled', 'message');
1428        }
1429
1430        $systemcontext = context_system::instance();
1431
1432        $params = array(
1433            'userid' => $userid,
1434            'courseid' => $courseid,
1435            'search' => $search,
1436            'limitfrom' => $limitfrom,
1437            'limitnum' => $limitnum
1438        );
1439        $params = self::validate_parameters(self::data_for_messagearea_search_users_in_course_parameters(), $params);
1440        self::validate_context($systemcontext);
1441
1442        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1443            throw new moodle_exception('You do not have permission to perform this action.');
1444        }
1445
1446        $users = \core_message\api::search_users_in_course(
1447            $params['userid'],
1448            $params['courseid'],
1449            $params['search'],
1450            $params['limitfrom'],
1451            $params['limitnum']
1452        );
1453        $results = new \core_message\output\messagearea\user_search_results($users);
1454
1455        $renderer = $PAGE->get_renderer('core_message');
1456        return $results->export_for_template($renderer);
1457    }
1458
1459    /**
1460     * Get messagearea search users in course returns.
1461     *
1462     * @deprecated since 3.6
1463     *
1464     * @return external_single_structure
1465     * @since 3.2
1466     */
1467    public static function data_for_messagearea_search_users_in_course_returns() {
1468        return new external_single_structure(
1469            array(
1470                'contacts' => new external_multiple_structure(
1471                    self::get_messagearea_contact_structure()
1472                ),
1473            )
1474        );
1475    }
1476
1477    /**
1478     * Marking the method as deprecated.
1479     *
1480     * @return bool
1481     */
1482    public static function data_for_messagearea_search_users_in_course_is_deprecated() {
1483        return true;
1484    }
1485
1486    /**
1487     * Get messagearea search users parameters.
1488     *
1489     * @deprecated since 3.6
1490     *
1491     * @return external_function_parameters
1492     * @since 3.2
1493     */
1494    public static function data_for_messagearea_search_users_parameters() {
1495        return new external_function_parameters(
1496            array(
1497                'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1498                'search' => new external_value(PARAM_RAW, 'The string being searched'),
1499                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1500            )
1501        );
1502    }
1503
1504    /**
1505     * Get messagearea search users results.
1506     *
1507     * @deprecated since 3.6
1508     *
1509     * @param int $userid The id of the user who is performing the search
1510     * @param string $search The string being searched
1511     * @param int $limitnum
1512     * @return stdClass
1513     * @throws moodle_exception
1514     * @since 3.2
1515     */
1516    public static function data_for_messagearea_search_users($userid, $search, $limitnum = 0) {
1517        global $CFG, $PAGE, $USER;
1518
1519        // Check if messaging is enabled.
1520        if (empty($CFG->messaging)) {
1521            throw new moodle_exception('disabled', 'message');
1522        }
1523
1524        $systemcontext = context_system::instance();
1525
1526        $params = array(
1527            'userid' => $userid,
1528            'search' => $search,
1529            'limitnum' => $limitnum
1530        );
1531        $params = self::validate_parameters(self::data_for_messagearea_search_users_parameters(), $params);
1532        self::validate_context($systemcontext);
1533
1534        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1535            throw new moodle_exception('You do not have permission to perform this action.');
1536        }
1537
1538        list($contacts, $courses, $noncontacts) = \core_message\api::search_users(
1539            $params['userid'],
1540            $params['search'],
1541            $params['limitnum']
1542        );
1543
1544        $search = new \core_message\output\messagearea\user_search_results($contacts, $courses, $noncontacts);
1545
1546        $renderer = $PAGE->get_renderer('core_message');
1547        return $search->export_for_template($renderer);
1548    }
1549
1550    /**
1551     * Get messagearea search users returns.
1552     *
1553     * @deprecated since 3.6
1554     *
1555     * @return external_single_structure
1556     * @since 3.2
1557     */
1558    public static function data_for_messagearea_search_users_returns() {
1559        return new external_single_structure(
1560            array(
1561                'contacts' => new external_multiple_structure(
1562                    self::get_messagearea_contact_structure()
1563                ),
1564                'courses' => new external_multiple_structure(
1565                    new external_single_structure(
1566                        array(
1567                            'id' => new external_value(PARAM_INT, 'The course id'),
1568                            'shortname' => new external_value(PARAM_TEXT, 'The course shortname'),
1569                            'fullname' => new external_value(PARAM_TEXT, 'The course fullname'),
1570                        )
1571                    )
1572                ),
1573                'noncontacts' => new external_multiple_structure(
1574                    self::get_messagearea_contact_structure()
1575                )
1576            )
1577        );
1578    }
1579
1580    /**
1581     * Marking the method as deprecated.
1582     *
1583     * @return bool
1584     */
1585    public static function data_for_messagearea_search_users_is_deprecated() {
1586        return true;
1587    }
1588
1589    /**
1590     * Get messagearea message search users parameters.
1591     *
1592     * @return external_function_parameters
1593     * @since 3.6
1594     */
1595    public static function message_search_users_parameters() {
1596        return new external_function_parameters(
1597            array(
1598                'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1599                'search' => new external_value(PARAM_RAW, 'The string being searched'),
1600                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1601                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1602            )
1603        );
1604    }
1605
1606    /**
1607     * Get search users results.
1608     *
1609     * @param int $userid The id of the user who is performing the search
1610     * @param string $search The string being searched
1611     * @param int $limitfrom
1612     * @param int $limitnum
1613     * @return array
1614     * @throws moodle_exception
1615     * @since 3.6
1616     */
1617    public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) {
1618        global $USER;
1619
1620        $systemcontext = context_system::instance();
1621
1622        $params = array(
1623            'userid' => $userid,
1624            'search' => $search,
1625            'limitfrom' => $limitfrom,
1626            'limitnum' => $limitnum
1627        );
1628        $params = self::validate_parameters(self::message_search_users_parameters(), $params);
1629        self::validate_context($systemcontext);
1630
1631        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1632            throw new moodle_exception('You do not have permission to perform this action.');
1633        }
1634
1635        list($contacts, $noncontacts) = \core_message\api::message_search_users(
1636            $params['userid'],
1637            $params['search'],
1638            $params['limitfrom'],
1639            $params['limitnum']);
1640
1641        return array('contacts' => $contacts, 'noncontacts' => $noncontacts);
1642    }
1643
1644    /**
1645     * Get messagearea message search users returns.
1646     *
1647     * @return external_single_structure
1648     * @since 3.2
1649     */
1650    public static function message_search_users_returns() {
1651        return new external_single_structure(
1652            array(
1653                'contacts' => new external_multiple_structure(
1654                    self::get_conversation_member_structure()
1655                ),
1656                'noncontacts' => new external_multiple_structure(
1657                    self::get_conversation_member_structure()
1658                )
1659            )
1660        );
1661    }
1662
1663    /**
1664     * Get messagearea search messages parameters.
1665     *
1666     * @return external_function_parameters
1667     * @since 3.2
1668     */
1669    public static function data_for_messagearea_search_messages_parameters() {
1670        return new external_function_parameters(
1671            array(
1672                'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1673                'search' => new external_value(PARAM_RAW, 'The string being searched'),
1674                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1675                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1676            )
1677        );
1678    }
1679
1680    /**
1681     * Get messagearea search messages results.
1682     *
1683     * @param int $userid The id of the user who is performing the search
1684     * @param string $search The string being searched
1685     * @param int $limitfrom
1686     * @param int $limitnum
1687     * @return stdClass
1688     * @throws moodle_exception
1689     * @since 3.2
1690     */
1691    public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
1692        global $CFG, $USER;
1693
1694        // Check if messaging is enabled.
1695        if (empty($CFG->messaging)) {
1696            throw new moodle_exception('disabled', 'message');
1697        }
1698
1699        $systemcontext = context_system::instance();
1700
1701        $params = array(
1702            'userid' => $userid,
1703            'search' => $search,
1704            'limitfrom' => $limitfrom,
1705            'limitnum' => $limitnum
1706
1707        );
1708        $params = self::validate_parameters(self::data_for_messagearea_search_messages_parameters(), $params);
1709        self::validate_context($systemcontext);
1710
1711        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1712            throw new moodle_exception('You do not have permission to perform this action.');
1713        }
1714
1715        $messages = \core_message\api::search_messages(
1716            $params['userid'],
1717            $params['search'],
1718            $params['limitfrom'],
1719            $params['limitnum']
1720        );
1721
1722        $data = new \stdClass();
1723        $data->contacts = [];
1724        foreach ($messages as $message) {
1725            $contact = new \stdClass();
1726            $contact->userid = $message->userid;
1727            $contact->fullname = $message->fullname;
1728            $contact->profileimageurl = $message->profileimageurl;
1729            $contact->profileimageurlsmall = $message->profileimageurlsmall;
1730            $contact->messageid = $message->messageid;
1731            $contact->ismessaging = $message->ismessaging;
1732            $contact->sentfromcurrentuser = false;
1733            if ($message->lastmessage) {
1734                if ($message->userid !== $message->useridfrom) {
1735                    $contact->sentfromcurrentuser = true;
1736                }
1737                $contact->lastmessage = shorten_text($message->lastmessage, 60);
1738            } else {
1739                $contact->lastmessage = null;
1740            }
1741            $contact->lastmessagedate = $message->lastmessagedate;
1742            $contact->showonlinestatus = is_null($message->isonline) ? false : true;
1743            $contact->isonline = $message->isonline;
1744            $contact->isblocked = $message->isblocked;
1745            $contact->isread = $message->isread;
1746            $contact->unreadcount = $message->unreadcount;
1747            $contact->conversationid = $message->conversationid;
1748
1749            $data->contacts[] = $contact;
1750        }
1751
1752        return $data;
1753    }
1754
1755    /**
1756     * Get messagearea search messages returns.
1757     *
1758     * @return external_single_structure
1759     * @since 3.2
1760     */
1761    public static function data_for_messagearea_search_messages_returns() {
1762        return new external_single_structure(
1763            array(
1764                'contacts' => new external_multiple_structure(
1765                    self::get_messagearea_contact_structure()
1766                )
1767            )
1768        );
1769    }
1770
1771    /**
1772     * Get conversations parameters.
1773     *
1774     * @return external_function_parameters
1775     * @since 3.6
1776     */
1777    public static function get_conversations_parameters() {
1778        return new external_function_parameters(
1779            array(
1780                'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1781                'limitfrom' => new external_value(PARAM_INT, 'The offset to start at', VALUE_DEFAULT, 0),
1782                'limitnum' => new external_value(PARAM_INT, 'Limit number of conversations to this', VALUE_DEFAULT, 0),
1783                'type' => new external_value(PARAM_INT, 'Filter by type', VALUE_DEFAULT, null),
1784                'favourites' => new external_value(PARAM_BOOL, 'Whether to restrict the results to contain NO favourite
1785                conversations (false), ONLY favourite conversation (true), or ignore any restriction altogether (null)',
1786                    VALUE_DEFAULT, null),
1787                'mergeself' => new external_value(PARAM_BOOL, 'Whether to include self-conversations (true) or ONLY private
1788                    conversations (false) when private conversations are requested.',
1789                    VALUE_DEFAULT, false),
1790            )
1791        );
1792    }
1793
1794    /**
1795     * Get the list of conversations for the user.
1796     *
1797     * @param int $userid The id of the user who is performing the search
1798     * @param int $limitfrom
1799     * @param int $limitnum
1800     * @param int|null $type
1801     * @param bool|null $favourites
1802     * @param bool $mergeself whether to include self-conversations (true) or ONLY private conversations (false)
1803     *             when private conversations are requested.
1804     * @return stdClass
1805     * @throws \moodle_exception if the messaging feature is disabled on the site.
1806     * @since 3.2
1807     */
1808    public static function get_conversations($userid, $limitfrom = 0, $limitnum = 0, int $type = null, bool $favourites = null,
1809            bool $mergeself = false) {
1810        global $CFG, $USER;
1811
1812        // All the standard BL checks.
1813        if (empty($CFG->messaging)) {
1814            throw new moodle_exception('disabled', 'message');
1815        }
1816
1817        $params = array(
1818            'userid' => $userid,
1819            'limitfrom' => $limitfrom,
1820            'limitnum' => $limitnum,
1821            'type' => $type,
1822            'favourites' => $favourites,
1823            'mergeself' => $mergeself
1824        );
1825        $params = self::validate_parameters(self::get_conversations_parameters(), $params);
1826
1827        $systemcontext = context_system::instance();
1828        self::validate_context($systemcontext);
1829
1830        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1831            throw new moodle_exception('You do not have permission to perform this action.');
1832        }
1833
1834        $conversations = \core_message\api::get_conversations(
1835            $params['userid'],
1836            $params['limitfrom'],
1837            $params['limitnum'],
1838            $params['type'],
1839            $params['favourites'],
1840            $params['mergeself']
1841        );
1842
1843        return (object) ['conversations' => $conversations];
1844    }
1845
1846    /**
1847     * Get conversations returns.
1848     *
1849     * @return external_single_structure
1850     * @since 3.6
1851     */
1852    public static function get_conversations_returns() {
1853        return new external_single_structure(
1854            [
1855                'conversations' => new external_multiple_structure(
1856                    self::get_conversation_structure(true)
1857                )
1858            ]
1859        );
1860    }
1861
1862    /**
1863     * Get conversation parameters.
1864     *
1865     * @return external_function_parameters
1866     */
1867    public static function get_conversation_parameters() {
1868        return new external_function_parameters(
1869            array(
1870                'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1871                'conversationid' => new external_value(PARAM_INT, 'The id of the conversation to fetch'),
1872                'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1873                'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1874                'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1875                'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1876                'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1877                'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1878                'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1879            )
1880        );
1881    }
1882
1883    /**
1884     * Get a single conversation.
1885     *
1886     * @param int $userid The user id to get the conversation for
1887     * @param int $conversationid The id of the conversation to fetch
1888     * @param bool $includecontactrequests Should contact requests be included between members
1889     * @param bool $includeprivacyinfo Should privacy info be included between members
1890     * @param int $memberlimit Limit number of members to load
1891     * @param int $memberoffset Offset members by this amount
1892     * @param int $messagelimit Limit number of messages to load
1893     * @param int $messageoffset Offset the messages
1894     * @param bool $newestmessagesfirst Order messages by newest first
1895     * @return stdClass
1896     * @throws \moodle_exception if the messaging feature is disabled on the site.
1897     */
1898    public static function get_conversation(
1899        int $userid,
1900        int $conversationid,
1901        bool $includecontactrequests = false,
1902        bool $includeprivacyinfo = false,
1903        int $memberlimit = 0,
1904        int $memberoffset = 0,
1905        int $messagelimit = 0,
1906        int $messageoffset = 0,
1907        bool $newestmessagesfirst = true
1908    ) {
1909        global $CFG, $DB, $USER;
1910
1911        // All the standard BL checks.
1912        if (empty($CFG->messaging)) {
1913            throw new moodle_exception('disabled', 'message');
1914        }
1915
1916        $params = [
1917            'userid' => $userid,
1918            'conversationid' => $conversationid,
1919            'includecontactrequests' => $includecontactrequests,
1920            'includeprivacyinfo' => $includeprivacyinfo,
1921            'memberlimit' => $memberlimit,
1922            'memberoffset' => $memberoffset,
1923            'messagelimit' => $messagelimit,
1924            'messageoffset' => $messageoffset,
1925            'newestmessagesfirst' => $newestmessagesfirst
1926        ];
1927        self::validate_parameters(self::get_conversation_parameters(), $params);
1928
1929        $systemcontext = context_system::instance();
1930        self::validate_context($systemcontext);
1931
1932        $conversation = \core_message\api::get_conversation(
1933            $params['userid'],
1934            $params['conversationid'],
1935            $params['includecontactrequests'],
1936            $params['includeprivacyinfo'],
1937            $params['memberlimit'],
1938            $params['memberoffset'],
1939            $params['messagelimit'],
1940            $params['messageoffset'],
1941            $params['newestmessagesfirst']
1942        );
1943
1944        if ($conversation) {
1945            return $conversation;
1946        } else {
1947            // We have to throw an exception here because the external functions annoyingly
1948            // don't accept null to be returned for a single structure.
1949            throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1950        }
1951    }
1952
1953    /**
1954     * Get conversation returns.
1955     *
1956     * @return external_single_structure
1957     */
1958    public static function get_conversation_returns() {
1959        return self::get_conversation_structure();
1960    }
1961
1962    /**
1963     * Get conversation parameters.
1964     *
1965     * @return external_function_parameters
1966     */
1967    public static function get_conversation_between_users_parameters() {
1968        return new external_function_parameters(
1969            array(
1970                'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1971                'otheruserid' => new external_value(PARAM_INT, 'The other user id'),
1972                'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1973                'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1974                'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1975                'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1976                'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1977                'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1978                'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1979            )
1980        );
1981    }
1982
1983    /**
1984     * Get a single conversation between users.
1985     *
1986     * @param int $userid The user id to get the conversation for
1987     * @param int $otheruserid The other user id
1988     * @param bool $includecontactrequests Should contact requests be included between members
1989     * @param bool $includeprivacyinfo Should privacy info be included between members
1990     * @param int $memberlimit Limit number of members to load
1991     * @param int $memberoffset Offset members by this amount
1992     * @param int $messagelimit Limit number of messages to load
1993     * @param int $messageoffset Offset the messages
1994     * @param bool $newestmessagesfirst Order messages by newest first
1995     * @return stdClass
1996     * @throws \moodle_exception if the messaging feature is disabled on the site.
1997     */
1998    public static function get_conversation_between_users(
1999        int $userid,
2000        int $otheruserid,
2001        bool $includecontactrequests = false,
2002        bool $includeprivacyinfo = false,
2003        int $memberlimit = 0,
2004        int $memberoffset = 0,
2005        int $messagelimit = 0,
2006        int $messageoffset = 0,
2007        bool $newestmessagesfirst = true
2008    ) {
2009        global $CFG, $DB, $USER;
2010
2011        // All the standard BL checks.
2012        if (empty($CFG->messaging)) {
2013            throw new moodle_exception('disabled', 'message');
2014        }
2015
2016        $params = [
2017            'userid' => $userid,
2018            'otheruserid' => $otheruserid,
2019            'includecontactrequests' => $includecontactrequests,
2020            'includeprivacyinfo' => $includeprivacyinfo,
2021            'memberlimit' => $memberlimit,
2022            'memberoffset' => $memberoffset,
2023            'messagelimit' => $messagelimit,
2024            'messageoffset' => $messageoffset,
2025            'newestmessagesfirst' => $newestmessagesfirst
2026        ];
2027        self::validate_parameters(self::get_conversation_between_users_parameters(), $params);
2028
2029        $systemcontext = context_system::instance();
2030        self::validate_context($systemcontext);
2031
2032        $conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']]);
2033        $conversation = null;
2034
2035        if ($conversationid) {
2036            $conversation = \core_message\api::get_conversation(
2037                $params['userid'],
2038                $conversationid,
2039                $params['includecontactrequests'],
2040                $params['includeprivacyinfo'],
2041                $params['memberlimit'],
2042                $params['memberoffset'],
2043                $params['messagelimit'],
2044                $params['messageoffset'],
2045                $params['newestmessagesfirst']
2046            );
2047        }
2048
2049        if ($conversation) {
2050            return $conversation;
2051        } else {
2052            // We have to throw an exception here because the external functions annoyingly
2053            // don't accept null to be returned for a single structure.
2054            throw new \moodle_exception('errorconversationdoesnotexist', 'message');
2055        }
2056    }
2057
2058    /**
2059     * Get conversation returns.
2060     *
2061     * @return external_single_structure
2062     */
2063    public static function get_conversation_between_users_returns() {
2064        return self::get_conversation_structure(true);
2065    }
2066
2067    /**
2068     * Get self-conversation parameters.
2069     *
2070     * @return external_function_parameters
2071     */
2072    public static function get_self_conversation_parameters() {
2073        return new external_function_parameters(
2074            array(
2075                'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing self-conversations for'),
2076                'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
2077                'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
2078                'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
2079            )
2080        );
2081    }
2082
2083    /**
2084     * Get a single self-conversation.
2085     *
2086     * @param int $userid The user id to get the self-conversation for
2087     * @param int $messagelimit Limit number of messages to load
2088     * @param int $messageoffset Offset the messages
2089     * @param bool $newestmessagesfirst Order messages by newest first
2090     * @return stdClass
2091     * @throws \moodle_exception if the messaging feature is disabled on the site.
2092     * @since Moodle 3.7
2093     */
2094    public static function get_self_conversation(
2095        int $userid,
2096        int $messagelimit = 0,
2097        int $messageoffset = 0,
2098        bool $newestmessagesfirst = true
2099    ) {
2100        global $CFG;
2101
2102        // All the standard BL checks.
2103        if (empty($CFG->messaging)) {
2104            throw new moodle_exception('disabled', 'message');
2105        }
2106
2107        $params = [
2108            'userid' => $userid,
2109            'messagelimit' => $messagelimit,
2110            'messageoffset' => $messageoffset,
2111            'newestmessagesfirst' => $newestmessagesfirst
2112        ];
2113        self::validate_parameters(self::get_self_conversation_parameters(), $params);
2114
2115        $systemcontext = context_system::instance();
2116        self::validate_context($systemcontext);
2117
2118        $conversation = \core_message\api::get_self_conversation($params['userid']);
2119
2120        if ($conversation) {
2121            $conversation = \core_message\api::get_conversation(
2122                $params['userid'],
2123                $conversation->id,
2124                false,
2125                false,
2126                0,
2127                0,
2128                $params['messagelimit'],
2129                $params['messageoffset'],
2130                $params['newestmessagesfirst']
2131            );
2132        }
2133
2134        if ($conversation) {
2135            return $conversation;
2136        } else {
2137            // We have to throw an exception here because the external functions annoyingly
2138            // don't accept null to be returned for a single structure.
2139            throw new \moodle_exception('errorconversationdoesnotexist', 'message');
2140        }
2141    }
2142
2143    /**
2144     * Get conversation returns.
2145     *
2146     * @return external_single_structure
2147     */
2148    public static function get_self_conversation_returns() {
2149        return self::get_conversation_structure();
2150    }
2151
2152    /**
2153     * The messagearea conversations parameters.
2154     *
2155     * @deprecated since 3.6
2156     * @return external_function_parameters
2157     * @since 3.2
2158     */
2159    public static function data_for_messagearea_conversations_parameters() {
2160        return new external_function_parameters(
2161            array(
2162                'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
2163                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
2164                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
2165            )
2166        );
2167    }
2168
2169    /**
2170     * Get messagearea conversations.
2171     *
2172     * NOTE FOR FINAL DEPRECATION:
2173     * When removing this method, please also consider removal of get_conversations_legacy_formatter()
2174     * from the \core_message\helper class. This helper method was used solely to format the new get_conversations() return data
2175     * into the old format used here, and in message/index.php. If we no longer need either of these, then that method can be
2176     * removed.
2177     *
2178     * @deprecated since 3.6
2179     * @param int $userid The id of the user who we are viewing conversations for
2180     * @param int $limitfrom
2181     * @param int $limitnum
2182     * @return stdClass
2183     * @throws moodle_exception
2184     * @since 3.2
2185     */
2186    public static function data_for_messagearea_conversations($userid, $limitfrom = 0, $limitnum = 0) {
2187        global $CFG, $PAGE, $USER;
2188
2189        // Check if messaging is enabled.
2190        if (empty($CFG->messaging)) {
2191            throw new moodle_exception('disabled', 'message');
2192        }
2193
2194        $systemcontext = context_system::instance();
2195
2196        $params = array(
2197            'userid' => $userid,
2198            'limitfrom' => $limitfrom,
2199            'limitnum' => $limitnum
2200        );
2201        $params = self::validate_parameters(self::data_for_messagearea_conversations_parameters(), $params);
2202        self::validate_context($systemcontext);
2203
2204        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2205            throw new moodle_exception('You do not have permission to perform this action.');
2206        }
2207
2208        $conversations = \core_message\api::get_conversations($params['userid'], $params['limitfrom'], $params['limitnum']);
2209
2210        // Format the conversations in the legacy style, as the get_conversations method has since been changed.
2211        $conversations = \core_message\helper::get_conversations_legacy_formatter($conversations);
2212
2213        $conversations = new \core_message\output\messagearea\contacts(null, $conversations);
2214
2215        $renderer = $PAGE->get_renderer('core_message');
2216        return $conversations->export_for_template($renderer);
2217    }
2218
2219    /**
2220     * The messagearea conversations return structure.
2221     *
2222     * @deprecated since 3.6
2223     * @return external_single_structure
2224     * @since 3.2
2225     */
2226    public static function data_for_messagearea_conversations_returns() {
2227        return new external_single_structure(
2228            array(
2229                'contacts' => new external_multiple_structure(
2230                    self::get_messagearea_contact_structure()
2231                )
2232            )
2233        );
2234    }
2235
2236    /**
2237     * Marking the method as deprecated.
2238     *
2239     * @return bool
2240     */
2241    public static function data_for_messagearea_conversations_is_deprecated() {
2242        return true;
2243    }
2244
2245    /**
2246     * The messagearea contacts return parameters.
2247     *
2248     * @deprecated since 3.6
2249     * @return external_function_parameters
2250     * @since 3.2
2251     */
2252    public static function data_for_messagearea_contacts_parameters() {
2253        return self::data_for_messagearea_conversations_parameters();
2254    }
2255
2256    /**
2257     * Get messagearea contacts parameters.
2258     *
2259     * @deprecated since 3.6
2260     * @param int $userid The id of the user who we are viewing conversations for
2261     * @param int $limitfrom
2262     * @param int $limitnum
2263     * @return stdClass
2264     * @throws moodle_exception
2265     * @since 3.2
2266     */
2267    public static function data_for_messagearea_contacts($userid, $limitfrom = 0, $limitnum = 0) {
2268        global $CFG, $PAGE, $USER;
2269
2270        // Check if messaging is enabled.
2271        if (empty($CFG->messaging)) {
2272            throw new moodle_exception('disabled', 'message');
2273        }
2274
2275        $systemcontext = context_system::instance();
2276
2277        $params = array(
2278            'userid' => $userid,
2279            'limitfrom' => $limitfrom,
2280            'limitnum' => $limitnum
2281        );
2282        $params = self::validate_parameters(self::data_for_messagearea_contacts_parameters(), $params);
2283        self::validate_context($systemcontext);
2284
2285        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2286            throw new moodle_exception('You do not have permission to perform this action.');
2287        }
2288
2289        $contacts = \core_message\api::get_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
2290        $contacts = new \core_message\output\messagearea\contacts(null, $contacts);
2291
2292        $renderer = $PAGE->get_renderer('core_message');
2293        return $contacts->export_for_template($renderer);
2294    }
2295
2296    /**
2297     * The messagearea contacts return structure.
2298     *
2299     * @deprecated since 3.6
2300     * @return external_single_structure
2301     * @since 3.2
2302     */
2303    public static function data_for_messagearea_contacts_returns() {
2304        return self::data_for_messagearea_conversations_returns();
2305    }
2306
2307    /**
2308     * Marking the method as deprecated.
2309     *
2310     * @return bool
2311     */
2312    public static function data_for_messagearea_contacts_is_deprecated() {
2313        return true;
2314    }
2315
2316    /**
2317     * The messagearea messages parameters.
2318     *
2319     * @deprecated since 3.6
2320     * @return external_function_parameters
2321     * @since 3.2
2322     */
2323    public static function data_for_messagearea_messages_parameters() {
2324        return new external_function_parameters(
2325            array(
2326                'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
2327                'otheruserid' => new external_value(PARAM_INT, 'The other user\'s id'),
2328                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
2329                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
2330                'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
2331                'timefrom' => new external_value(PARAM_INT,
2332                    'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
2333            )
2334        );
2335    }
2336
2337    /**
2338     * Get messagearea messages.
2339     *
2340     * @deprecated since 3.6
2341     * @param int $currentuserid The current user's id
2342     * @param int $otheruserid The other user's id
2343     * @param int $limitfrom
2344     * @param int $limitnum
2345     * @param boolean $newest
2346     * @return stdClass
2347     * @throws moodle_exception
2348     * @since 3.2
2349     */
2350    public static function data_for_messagearea_messages($currentuserid, $otheruserid, $limitfrom = 0, $limitnum = 0,
2351                                                         $newest = false, $timefrom = 0) {
2352        global $CFG, $PAGE, $USER;
2353
2354        // Check if messaging is enabled.
2355        if (empty($CFG->messaging)) {
2356            throw new moodle_exception('disabled', 'message');
2357        }
2358
2359        $systemcontext = context_system::instance();
2360
2361        $params = array(
2362            'currentuserid' => $currentuserid,
2363            'otheruserid' => $otheruserid,
2364            'limitfrom' => $limitfrom,
2365            'limitnum' => $limitnum,
2366            'newest' => $newest,
2367            'timefrom' => $timefrom,
2368        );
2369        $params = self::validate_parameters(self::data_for_messagearea_messages_parameters(), $params);
2370        self::validate_context($systemcontext);
2371
2372        if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2373            throw new moodle_exception('You do not have permission to perform this action.');
2374        }
2375
2376        if ($params['newest']) {
2377            $sort = 'timecreated DESC';
2378        } else {
2379            $sort = 'timecreated ASC';
2380        }
2381
2382        // We need to enforce a one second delay on messages to avoid race conditions of current
2383        // messages still being sent.
2384        //
2385        // There is a chance that we could request messages before the current time's
2386        // second has elapsed and while other messages are being sent in that same second. In which
2387        // case those messages will be lost.
2388        //
2389        // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
2390        if (!empty($params['timefrom'])) {
2391            $timeto = time() - 1;
2392        } else {
2393            $timeto = 0;
2394        }
2395
2396        // No requesting messages from the current time, as stated above.
2397        if ($params['timefrom'] == time()) {
2398            $messages = [];
2399        } else {
2400            $messages = \core_message\api::get_messages($params['currentuserid'], $params['otheruserid'], $params['limitfrom'],
2401                                                        $params['limitnum'], $sort, $params['timefrom'], $timeto);
2402        }
2403
2404        $messages = new \core_message\output\messagearea\messages($params['currentuserid'], $params['otheruserid'], $messages);
2405
2406        $renderer = $PAGE->get_renderer('core_message');
2407        return $messages->export_for_template($renderer);
2408    }
2409
2410    /**
2411     * The messagearea messages return structure.
2412     *
2413     * @deprecated since 3.6
2414     * @return external_single_structure
2415     * @since 3.2
2416     */
2417    public static function data_for_messagearea_messages_returns() {
2418        return new external_single_structure(
2419            array(
2420                'iscurrentuser' => new external_value(PARAM_BOOL, 'Is the currently logged in user the user we are viewing
2421                    the messages on behalf of?'),
2422                'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
2423                'otheruserid' => new external_value(PARAM_INT, 'The other user\'s id'),
2424                'otheruserfullname' => new external_value(PARAM_NOTAGS, 'The other user\'s fullname'),
2425                'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
2426                'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
2427                'messages' => new external_multiple_structure(
2428                    self::get_messagearea_message_structure()
2429                ),
2430                'isblocked' => new external_value(PARAM_BOOL, 'Is this user blocked by the current user?', VALUE_DEFAULT, false),
2431            )
2432        );
2433    }
2434
2435    /**
2436     * Marking the method as deprecated.
2437     *
2438     * @return bool
2439     */
2440    public static function data_for_messagearea_messages_is_deprecated() {
2441        return true;
2442    }
2443
2444    /**
2445     * The conversation messages parameters.
2446     *
2447     * @return external_function_parameters
2448     * @since 3.6
2449     */
2450    public static function get_conversation_messages_parameters() {
2451        return new external_function_parameters(
2452            array(
2453                'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
2454                'convid' => new external_value(PARAM_INT, 'The conversation id'),
2455                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
2456                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
2457                'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
2458                'timefrom' => new external_value(PARAM_INT,
2459                    'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
2460            )
2461        );
2462    }
2463
2464    /**
2465     * Get conversation messages.
2466     *
2467     * @param  int $currentuserid The current user's id.
2468     * @param  int $convid The conversation id.
2469     * @param  int $limitfrom Return a subset of records, starting at this point (optional).
2470     * @param  int $limitnum Return a subset comprising this many records in total (optional, required if $limitfrom is set).
2471     * @param  bool $newest True for getting first newest messages, false otherwise.
2472     * @param  int  $timefrom The time from the conversation messages to get.
2473     * @return array The messages and members who have sent some of these messages.
2474     * @throws moodle_exception
2475     * @since 3.6
2476     */
2477    public static function get_conversation_messages(int $currentuserid, int $convid, int $limitfrom = 0, int $limitnum = 0,
2478                                                         bool $newest = false, int $timefrom = 0) {
2479        global $CFG, $USER;
2480
2481        // Check if messaging is enabled.
2482        if (empty($CFG->messaging)) {
2483            throw new moodle_exception('disabled', 'message');
2484        }
2485
2486        $systemcontext = context_system::instance();
2487
2488        $params = array(
2489            'currentuserid' => $currentuserid,
2490            'convid' => $convid,
2491            'limitfrom' => $limitfrom,
2492            'limitnum' => $limitnum,
2493            'newest' => $newest,
2494            'timefrom' => $timefrom,
2495        );
2496        $params = self::validate_parameters(self::get_conversation_messages_parameters(), $params);
2497        self::validate_context($systemcontext);
2498
2499        if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2500            throw new moodle_exception('You do not have permission to perform this action.');
2501        }
2502
2503        // Check that the user belongs to the conversation.
2504        if (!\core_message\api::is_user_in_conversation($params['currentuserid'], $params['convid'])) {
2505            throw new moodle_exception('User is not part of conversation.');
2506        }
2507
2508        $sort = $newest ? 'timecreated DESC' : 'timecreated ASC';
2509
2510        // We need to enforce a one second delay on messages to avoid race conditions of current
2511        // messages still being sent.
2512        //
2513        // There is a chance that we could request messages before the current time's
2514        // second has elapsed and while other messages are being sent in that same second. In which
2515        // case those messages will be lost.
2516        //
2517        // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
2518        $timeto = empty($params['timefrom']) ? 0 : time() - 1;
2519
2520        // No requesting messages from the current time, as stated above.
2521        if ($params['timefrom'] == time()) {
2522            $messages = [];
2523        } else {
2524            $messages = \core_message\api::get_conversation_messages(
2525                $params['currentuserid'],
2526                $params['convid'],
2527                $params['limitfrom'],
2528                $params['limitnum'],
2529                $sort,
2530                $params['timefrom'],
2531                $timeto);
2532        }
2533
2534        return $messages;
2535    }
2536
2537    /**
2538     * The messagearea messages return structure.
2539     *
2540     * @return external_single_structure
2541     * @since 3.6
2542     */
2543    public static function get_conversation_messages_returns() {
2544        return new external_single_structure(
2545            array(
2546                'id' => new external_value(PARAM_INT, 'The conversation id'),
2547                'members' => new external_multiple_structure(
2548                    self::get_conversation_member_structure()
2549                ),
2550                'messages' => new external_multiple_structure(
2551                    self::get_conversation_message_structure()
2552                ),
2553            )
2554        );
2555    }
2556
2557    /**
2558     * The user contacts return parameters.
2559     *
2560     * @return external_function_parameters
2561     */
2562    public static function get_user_contacts_parameters() {
2563        return new external_function_parameters(
2564            array(
2565                'userid' => new external_value(PARAM_INT, 'The id of the user who we retrieving the contacts for'),
2566                'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
2567                'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
2568            )
2569        );
2570    }
2571
2572    /**
2573     * Get user contacts.
2574     *
2575     * @param int $userid The id of the user who we are viewing conversations for
2576     * @param int $limitfrom
2577     * @param int $limitnum
2578     * @return array
2579     * @throws moodle_exception
2580     */
2581    public static function get_user_contacts(int $userid, int $limitfrom = 0, int $limitnum = 0) {
2582        global $CFG, $USER;
2583
2584        // Check if messaging is enabled.
2585        if (empty($CFG->messaging)) {
2586            throw new moodle_exception('disabled', 'message');
2587        }
2588
2589        $systemcontext = context_system::instance();
2590
2591        $params = array(
2592            'userid' => $userid,
2593            'limitfrom' => $limitfrom,
2594            'limitnum' => $limitnum
2595        );
2596        $params = self::validate_parameters(self::get_user_contacts_parameters(), $params);
2597        self::validate_context($systemcontext);
2598
2599        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2600            throw new moodle_exception('You do not have permission to perform this action.');
2601        }
2602
2603        return \core_message\api::get_user_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
2604    }
2605
2606    /**
2607     * The user contacts return structure.
2608     *
2609     * @return external_multiple_structure
2610     */
2611    public static function get_user_contacts_returns() {
2612        return new external_multiple_structure(
2613            self::get_conversation_member_structure()
2614        );
2615    }
2616
2617    /**
2618     * The get most recent message return parameters.
2619     *
2620     * @deprecated since 3.6
2621     * @return external_function_parameters
2622     * @since 3.2
2623     */
2624    public static function data_for_messagearea_get_most_recent_message_parameters() {
2625        return new external_function_parameters(
2626            array(
2627                'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
2628                'otheruserid' => new external_value(PARAM_INT, 'The other user\'s id'),
2629            )
2630        );
2631    }
2632
2633    /**
2634     * Get the most recent message in a conversation.
2635     *
2636     * @deprecated since 3.6
2637     * @param int $currentuserid The current user's id
2638     * @param int $otheruserid The other user's id
2639     * @return stdClass
2640     * @throws moodle_exception
2641     * @since 3.2
2642     */
2643    public static function data_for_messagearea_get_most_recent_message($currentuserid, $otheruserid) {
2644        global $CFG, $PAGE, $USER;
2645
2646        // Check if messaging is enabled.
2647        if (empty($CFG->messaging)) {
2648            throw new moodle_exception('disabled', 'message');
2649        }
2650
2651        $systemcontext = context_system::instance();
2652
2653        $params = array(
2654            'currentuserid' => $currentuserid,
2655            'otheruserid' => $otheruserid
2656        );
2657        $params = self::validate_parameters(self::data_for_messagearea_get_most_recent_message_parameters(), $params);
2658        self::validate_context($systemcontext);
2659
2660        if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2661            throw new moodle_exception('You do not have permission to perform this action.');
2662        }
2663
2664        $message = \core_message\api::get_most_recent_message($params['currentuserid'], $params['otheruserid']);
2665        $message = new \core_message\output\messagearea\message($message);
2666
2667        $renderer = $PAGE->get_renderer('core_message');
2668        return $message->export_for_template($renderer);
2669    }
2670
2671    /**
2672     * The get most recent message return structure.
2673     *
2674     * @deprecated since 3.6
2675     * @return external_single_structure
2676     * @since 3.2
2677     */
2678    public static function data_for_messagearea_get_most_recent_message_returns() {
2679        return self::get_messagearea_message_structure();
2680    }
2681
2682    /**
2683     * Marking the method as deprecated.
2684     *
2685     * @return bool
2686     */
2687    public static function data_for_messagearea_get_most_recent_message_is_deprecated() {
2688        return true;
2689    }
2690
2691    /**
2692     * The get profile parameters.
2693     *
2694     * @deprecated since 3.6
2695     * @return external_function_parameters
2696     * @since 3.2
2697     */
2698    public static function data_for_messagearea_get_profile_parameters() {
2699        return new external_function_parameters(
2700            array(
2701                'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
2702                'otheruserid' => new external_value(PARAM_INT, 'The id of the user whose profile we want to view'),
2703            )
2704        );
2705    }
2706
2707    /**
2708     * Get the profile information for a contact.
2709     *
2710     * @deprecated since 3.6
2711     * @param int $currentuserid The current user's id
2712     * @param int $otheruserid The id of the user whose profile we are viewing
2713     * @return stdClass
2714     * @throws moodle_exception
2715     * @since 3.2
2716     */
2717    public static function data_for_messagearea_get_profile($currentuserid, $otheruserid) {
2718        global $CFG, $PAGE, $USER;
2719
2720        // Check if messaging is enabled.
2721        if (empty($CFG->messaging)) {
2722            throw new moodle_exception('disabled', 'message');
2723        }
2724
2725        $systemcontext = context_system::instance();
2726
2727        $params = array(
2728            'currentuserid' => $currentuserid,
2729            'otheruserid' => $otheruserid
2730        );
2731        $params = self::validate_parameters(self::data_for_messagearea_get_profile_parameters(), $params);
2732        self::validate_context($systemcontext);
2733
2734        if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2735            throw new moodle_exception('You do not have permission to perform this action.');
2736        }
2737
2738        $profile = \core_message\api::get_profile($params['currentuserid'], $params['otheruserid']);
2739        $profile = new \core_message\output\messagearea\profile($profile);
2740
2741        $renderer = $PAGE->get_renderer('core_message');
2742        return $profile->export_for_template($renderer);
2743    }
2744
2745    /**
2746     * The get profile return structure.
2747     *
2748     * @deprecated since 3.6
2749     * @return external_single_structure
2750     * @since 3.2
2751     */
2752    public static function data_for_messagearea_get_profile_returns() {
2753        return new external_single_structure(
2754            array(
2755                'userid' => new external_value(PARAM_INT, 'The id of the user whose profile we are viewing'),
2756                'email' => new external_value(core_user::get_property_type('email'), 'An email address'),
2757                'country' => new external_value(PARAM_TEXT, 'Home country of the user'),
2758                'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user'),
2759                'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
2760                'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
2761                'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
2762                'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
2763                'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
2764                'isblocked' => new external_value(PARAM_BOOL, 'Is the user blocked?'),
2765                'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?')
2766            )
2767        );
2768    }
2769
2770    /**
2771     * Marking the method as deprecated.
2772     *
2773     * @return bool
2774     */
2775    public static function data_for_messagearea_get_profile_is_deprecated() {
2776        return true;
2777    }
2778
2779    /**
2780     * Get contacts parameters description.
2781     *
2782     * @deprecated since 3.6
2783     * @return external_function_parameters
2784     * @since Moodle 2.5
2785     */
2786    public static function get_contacts_parameters() {
2787        return new external_function_parameters(array());
2788    }
2789
2790    /**
2791     * Get contacts.
2792     *
2793     * @deprecated since 3.6
2794     * @return external_description
2795     * @since Moodle 2.5
2796     */
2797    public static function get_contacts() {
2798        global $CFG, $PAGE, $USER;
2799
2800        // Check if messaging is enabled.
2801        if (empty($CFG->messaging)) {
2802            throw new moodle_exception('disabled', 'message');
2803        }
2804
2805        require_once($CFG->dirroot . '/user/lib.php');
2806
2807        $allcontacts = array('online' => [], 'offline' => [], 'strangers' => []);
2808        $contacts = \core_message\api::get_contacts_with_unread_message_count($USER->id);
2809        foreach ($contacts as $contact) {
2810            // Set the mode.
2811            $mode = 'offline';
2812            if (\core_message\helper::is_online($contact->lastaccess)) {
2813                $mode = 'online';
2814            }
2815
2816            $newcontact = array(
2817                'id' => $contact->id,
2818                'fullname' => fullname($contact),
2819                'unread' => $contact->messagecount
2820            );
2821
2822            $userpicture = new user_picture($contact);
2823            $userpicture->size = 1; // Size f1.
2824            $newcontact['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2825            $userpicture->size = 0; // Size f2.
2826            $newcontact['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
2827
2828            $allcontacts[$mode][$contact->id] = $newcontact;
2829        }
2830
2831        $strangers = \core_message\api::get_non_contacts_with_unread_message_count($USER->id);
2832        foreach ($strangers as $contact) {
2833            $newcontact = array(
2834                'id' => $contact->id,
2835                'fullname' => fullname($contact),
2836                'unread' => $contact->messagecount
2837            );
2838
2839            $userpicture = new user_picture($contact);
2840            $userpicture->size = 1; // Size f1.
2841            $newcontact['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2842            $userpicture->size = 0; // Size f2.
2843            $newcontact['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
2844
2845            $allcontacts['strangers'][$contact->id] = $newcontact;
2846        }
2847
2848        // Add noreply user and support user to the list, if they don't exist.
2849        $supportuser = core_user::get_support_user();
2850        if (!isset($strangers[$supportuser->id]) && !$supportuser->deleted) {
2851            $supportuser->messagecount = message_count_unread_messages($USER, $supportuser);
2852            if ($supportuser->messagecount > 0) {
2853                $supportuser->fullname = fullname($supportuser);
2854                $supportuser->unread = $supportuser->messagecount;
2855                $allcontacts['strangers'][$supportuser->id] = $supportuser;
2856            }
2857        }
2858
2859        $noreplyuser = core_user::get_noreply_user();
2860        if (!isset($strangers[$noreplyuser->id]) && !$noreplyuser->deleted) {
2861            $noreplyuser->messagecount = message_count_unread_messages($USER, $noreplyuser);
2862            if ($noreplyuser->messagecount > 0) {
2863                $noreplyuser->fullname = fullname($noreplyuser);
2864                $noreplyuser->unread = $noreplyuser->messagecount;
2865                $allcontacts['strangers'][$noreplyuser->id] = $noreplyuser;
2866            }
2867        }
2868
2869        return $allcontacts;
2870    }
2871
2872    /**
2873     * Get contacts return description.
2874     *
2875     * @deprecated since 3.6
2876     * @return external_description
2877     * @since Moodle 2.5
2878     */
2879    public static function get_contacts_returns() {
2880        return new external_single_structure(
2881            array(
2882                'online' => new external_multiple_structure(
2883                    new external_single_structure(
2884                        array(
2885                            'id' => new external_value(PARAM_INT, 'User ID'),
2886                            'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2887                            'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2888                            'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL),
2889                            'unread' => new external_value(PARAM_INT, 'Unread message count')
2890                        )
2891                    ),
2892                    'List of online contacts'
2893                ),
2894                'offline' => new external_multiple_structure(
2895                    new external_single_structure(
2896                        array(
2897                            'id' => new external_value(PARAM_INT, 'User ID'),
2898                            'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2899                            'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2900                            'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL),
2901                            'unread' => new external_value(PARAM_INT, 'Unread message count')
2902                        )
2903                    ),
2904                    'List of offline contacts'
2905                ),
2906                'strangers' => new external_multiple_structure(
2907                    new external_single_structure(
2908                        array(
2909                            'id' => new external_value(PARAM_INT, 'User ID'),
2910                            'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2911                            'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2912                            'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL),
2913                            'unread' => new external_value(PARAM_INT, 'Unread message count')
2914                        )
2915                    ),
2916                    'List of users that are not in the user\'s contact list but have sent a message'
2917                )
2918            )
2919        );
2920    }
2921
2922    /**
2923     * Marking the method as deprecated.
2924     *
2925     * @return bool
2926     */
2927    public static function get_contacts_is_deprecated() {
2928        return true;
2929    }
2930
2931    /**
2932     * Search contacts parameters description.
2933     *
2934     * @return external_function_parameters
2935     * @since Moodle 2.5
2936     */
2937    public static function search_contacts_parameters() {
2938        return new external_function_parameters(
2939            array(
2940                'searchtext' => new external_value(PARAM_CLEAN, 'String the user\'s fullname has to match to be found'),
2941                'onlymycourses' => new external_value(PARAM_BOOL, 'Limit search to the user\'s courses',
2942                    VALUE_DEFAULT, false)
2943            )
2944        );
2945    }
2946
2947    /**
2948     * Search contacts.
2949     *
2950     * @param string $searchtext query string.
2951     * @param bool $onlymycourses limit the search to the user's courses only.
2952     * @return external_description
2953     * @since Moodle 2.5
2954     */
2955    public static function search_contacts($searchtext, $onlymycourses = false) {
2956        global $CFG, $USER, $PAGE;
2957        require_once($CFG->dirroot . '/user/lib.php');
2958
2959        // Check if messaging is enabled.
2960        if (empty($CFG->messaging)) {
2961            throw new moodle_exception('disabled', 'message');
2962        }
2963
2964        require_once($CFG->libdir . '/enrollib.php');
2965
2966        $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses);
2967        $params = self::validate_parameters(self::search_contacts_parameters(), $params);
2968
2969        // Extra validation, we do not allow empty queries.
2970        if ($params['searchtext'] === '') {
2971            throw new moodle_exception('querystringcannotbeempty');
2972        }
2973
2974        $courseids = array();
2975        if ($params['onlymycourses']) {
2976            $mycourses = enrol_get_my_courses(array('id'));
2977            foreach ($mycourses as $mycourse) {
2978                $courseids[] = $mycourse->id;
2979            }
2980        } else {
2981            $courseids[] = SITEID;
2982        }
2983
2984        // Retrieving the users matching the query.
2985        $users = message_search_users($courseids, $params['searchtext']);
2986        $results = array();
2987        foreach ($users as $user) {
2988            $results[$user->id] = $user;
2989        }
2990
2991        // Reorganising information.
2992        foreach ($results as &$user) {
2993            $newuser = array(
2994                'id' => $user->id,
2995                'fullname' => fullname($user)
2996            );
2997
2998            // Avoid undefined property notice as phone not specified.
2999            $user->phone1 = null;
3000            $user->phone2 = null;
3001
3002            $userpicture = new user_picture($user);
3003            $userpicture->size = 1; // Size f1.
3004            $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
3005            $userpicture->size = 0; // Size f2.
3006            $newuser['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
3007
3008            $user = $newuser;
3009        }
3010
3011        return $results;
3012    }
3013
3014    /**
3015     * Search contacts return description.
3016     *
3017     * @return external_description
3018     * @since Moodle 2.5
3019     */
3020    public static function search_contacts_returns() {
3021        return new external_multiple_structure(
3022            new external_single_structure(
3023                array(
3024                    'id' => new external_value(PARAM_INT, 'User ID'),
3025                    'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
3026                    'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
3027                    'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL)
3028                )
3029            ),
3030            'List of contacts'
3031        );
3032    }
3033
3034    /**
3035     * Get messages parameters description.
3036     *
3037     * @return external_function_parameters
3038     * @since 2.8
3039     */
3040    public static function get_messages_parameters() {
3041        return new external_function_parameters(
3042            array(
3043                'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
3044                'useridfrom' => new external_value(
3045                    PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
3046                    VALUE_DEFAULT, 0),
3047                'type' => new external_value(
3048                    PARAM_ALPHA, 'type of message to return, expected values are: notifications, conversations and both',
3049                    VALUE_DEFAULT, 'both'),
3050                'read' => new external_value(PARAM_BOOL, 'true for getting read messages, false for unread', VALUE_DEFAULT, true),
3051                'newestfirst' => new external_value(
3052                    PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
3053                    VALUE_DEFAULT, true),
3054                'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
3055                'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
3056            )
3057        );
3058    }
3059
3060    /**
3061     * Get messages function implementation.
3062     *
3063     * @since  2.8
3064     * @throws invalid_parameter_exception
3065     * @throws moodle_exception
3066     * @param  int      $useridto       the user id who received the message
3067     * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
3068     * @param  string   $type           type of message to return, expected values: notifications, conversations and both
3069     * @param  bool     $read           true for retreiving read messages, false for unread
3070     * @param  bool     $newestfirst    true for ordering by newest first, false for oldest first
3071     * @param  int      $limitfrom      limit from
3072     * @param  int      $limitnum       limit num
3073     * @return external_description
3074     */
3075    public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true,
3076                                        $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
3077        global $CFG, $USER;
3078
3079        $warnings = array();
3080
3081        $params = array(
3082            'useridto' => $useridto,
3083            'useridfrom' => $useridfrom,
3084            'type' => $type,
3085            'read' => $read,
3086            'newestfirst' => $newestfirst,
3087            'limitfrom' => $limitfrom,
3088            'limitnum' => $limitnum
3089        );
3090
3091        $params = self::validate_parameters(self::get_messages_parameters(), $params);
3092
3093        $context = context_system::instance();
3094        self::validate_context($context);
3095
3096        $useridto = $params['useridto'];
3097        $useridfrom = $params['useridfrom'];
3098        $type = $params['type'];
3099        $read = $params['read'];
3100        $newestfirst = $params['newestfirst'];
3101        $limitfrom = $params['limitfrom'];
3102        $limitnum = $params['limitnum'];
3103
3104        $allowedvalues = array('notifications', 'conversations', 'both');
3105        if (!in_array($type, $allowedvalues)) {
3106            throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
3107                'allowed values are: ' . implode(',', $allowedvalues));
3108        }
3109
3110        // Check if private messaging between users is allowed.
3111        if (empty($CFG->messaging)) {
3112            // If we are retreiving only conversations, and messaging is disabled, throw an exception.
3113            if ($type == "conversations") {
3114                throw new moodle_exception('disabled', 'message');
3115            }
3116            if ($type == "both") {
3117                $warning = array();
3118                $warning['item'] = 'message';
3119                $warning['itemid'] = $USER->id;
3120                $warning['warningcode'] = '1';
3121                $warning['message'] = 'Private messages (conversations) are not enabled in this site.
3122                    Only notifications will be returned';
3123                $warnings[] = $warning;
3124            }
3125        }
3126
3127        if (!empty($useridto)) {
3128            if (core_user::is_real_user($useridto)) {
3129                $userto = core_user::get_user($useridto, '*', MUST_EXIST);
3130            } else {
3131                throw new moodle_exception('invaliduser');
3132            }
3133        }
3134
3135        if (!empty($useridfrom)) {
3136            // We use get_user here because the from user can be the noreply or support user.
3137            $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
3138        }
3139
3140        // Check if the current user is the sender/receiver or just a privileged user.
3141        if ($useridto != $USER->id and $useridfrom != $USER->id and
3142             !has_capability('moodle/site:readallmessages', $context)) {
3143            throw new moodle_exception('accessdenied', 'admin');
3144        }
3145
3146        // Which type of messages to retrieve.
3147        $notifications = -1;
3148        if ($type != 'both') {
3149            $notifications = ($type == 'notifications') ? 1 : 0;
3150        }
3151
3152        $orderdirection = $newestfirst ? 'DESC' : 'ASC';
3153        $sort = "mr.timecreated $orderdirection";
3154
3155        if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
3156            $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
3157
3158            // In some cases, we don't need to get the to/from user objects from the sql query.
3159            $userfromfullname = '';
3160            $usertofullname = '';
3161
3162            // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
3163            if (!empty($useridto)) {
3164                $usertofullname = fullname($userto, $canviewfullname);
3165                // The user from may or may not be filled.
3166                if (!empty($useridfrom)) {
3167                    $userfromfullname = fullname($userfrom, $canviewfullname);
3168                }
3169            } else {
3170                // If the useridto field is empty, the useridfrom must be filled.
3171                $userfromfullname = fullname($userfrom, $canviewfullname);
3172            }
3173            foreach ($messages as $mid => $message) {
3174
3175                // Do not return deleted messages.
3176                if (!$message->notification) {
3177                    if (($useridto == $USER->id and $message->timeusertodeleted) or
3178                        ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
3179                        unset($messages[$mid]);
3180                        continue;
3181                    }
3182                }
3183
3184                // We need to get the user from the query.
3185                if (empty($userfromfullname)) {
3186                    // Check for non-reply and support users.
3187                    if (core_user::is_real_user($message->useridfrom)) {
3188                        $user = new stdClass();
3189                        $user = username_load_fields_from_object($user, $message, 'userfrom');
3190                        $message->userfromfullname = fullname($user, $canviewfullname);
3191                    } else {
3192                        $user = core_user::get_user($message->useridfrom);
3193                        $message->userfromfullname = fullname($user, $canviewfullname);
3194                    }
3195                } else {
3196                    $message->userfromfullname = $userfromfullname;
3197                }
3198
3199                // We need to get the user from the query.
3200                if (empty($usertofullname)) {
3201                    $user = new stdClass();
3202                    $user = username_load_fields_from_object($user, $message, 'userto');
3203                    $message->usertofullname = fullname($user, $canviewfullname);
3204                } else {
3205                    $message->usertofullname = $usertofullname;
3206                }
3207
3208                $message->text = message_format_message_text($message);
3209                $messages[$mid] = (array) $message;
3210            }
3211        }
3212
3213        $results = array(
3214            'messages' => $messages,
3215            'warnings' => $warnings
3216        );
3217
3218        return $results;
3219    }
3220
3221    /**
3222     * Get messages return description.
3223     *
3224     * @return external_single_structure
3225     * @since 2.8
3226     */
3227    public static function get_messages_returns() {
3228        return new external_single_structure(
3229            array(
3230                'messages' => new external_multiple_structure(
3231                    new external_single_structure(
3232                        array(
3233                            'id' => new external_value(PARAM_INT, 'Message id'),
3234                            'useridfrom' => new external_value(PARAM_INT, 'User from id'),
3235                            'useridto' => new external_value(PARAM_INT, 'User to id'),
3236                            'subject' => new external_value(PARAM_TEXT, 'The message subject'),
3237                            'text' => new external_value(PARAM_RAW, 'The message text formated'),
3238                            'fullmessage' => new external_value(PARAM_RAW, 'The message'),
3239                            'fullmessageformat' => new external_format_value('fullmessage'),
3240                            'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
3241                            'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
3242                            'notification' => new external_value(PARAM_INT, 'Is a notification?'),
3243                            'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
3244                            'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
3245                            'timecreated' => new external_value(PARAM_INT, 'Time created'),
3246                            'timeread' => new external_value(PARAM_INT, 'Time read'),
3247                            'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
3248                            'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name'),
3249                            'component' => new external_value(PARAM_TEXT, 'The component that generated the notification',
3250                                VALUE_OPTIONAL),
3251                            'eventtype' => new external_value(PARAM_TEXT, 'The type of notification', VALUE_OPTIONAL),
3252                            'customdata' => new external_value(PARAM_RAW, 'Custom data to be passed to the message processor.
3253                                The data here is serialised using json_encode().', VALUE_OPTIONAL),
3254                        ), 'message'
3255                    )
3256                ),
3257                'warnings' => new external_warnings()
3258            )
3259        );
3260    }
3261
3262    /**
3263     * Mark all notifications as read parameters description.
3264     *
3265     * @return external_function_parameters
3266     * @since 3.2
3267     */
3268    public static function mark_all_notifications_as_read_parameters() {
3269        return new external_function_parameters(
3270            array(
3271                'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
3272                'useridfrom' => new external_value(
3273                    PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
3274                    VALUE_DEFAULT, 0),
3275                'timecreatedto' => new external_value(
3276                    PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
3277                    VALUE_DEFAULT, 0),
3278            )
3279        );
3280    }
3281
3282    /**
3283     * Mark all notifications as read function.
3284     *
3285     * @since  3.2
3286     * @throws invalid_parameter_exception
3287     * @throws moodle_exception
3288     * @param  int      $useridto       the user id who received the message
3289     * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
3290     * @param  int      $timecreatedto  mark message created before this time as read, 0 for all messages
3291     * @return external_description
3292     */
3293    public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
3294        global $USER;
3295
3296        $params = self::validate_parameters(
3297            self::mark_all_notifications_as_read_parameters(),
3298            array(
3299                'useridto' => $useridto,
3300                'useridfrom' => $useridfrom,
3301                'timecreatedto' => $timecreatedto,
3302            )
3303        );
3304
3305        $context = context_system::instance();
3306        self::validate_context($context);
3307
3308        $useridto = $params['useridto'];
3309        $useridfrom = $params['useridfrom'];
3310        $timecreatedto = $params['timecreatedto'];
3311
3312        if (!empty($useridto)) {
3313            if (core_user::is_real_user($useridto)) {
3314                $userto = core_user::get_user($useridto, '*', MUST_EXIST);
3315            } else {
3316                throw new moodle_exception('invaliduser');
3317            }
3318        }
3319
3320        if (!empty($useridfrom)) {
3321            // We use get_user here because the from user can be the noreply or support user.
3322            $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
3323        }
3324
3325        // Check if the current user is the sender/receiver or just a privileged user.
3326        if ($useridto != $USER->id and $useridfrom != $USER->id and
3327            // The deleteanymessage cap seems more reasonable here than readallmessages.
3328             !has_capability('moodle/site:deleteanymessage', $context)) {
3329            throw new moodle_exception('accessdenied', 'admin');
3330        }
3331
3332        \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
3333
3334        return true;
3335    }
3336
3337    /**
3338     * Mark all notifications as read return description.
3339     *
3340     * @return external_single_structure
3341     * @since 3.2
3342     */
3343    public static function mark_all_notifications_as_read_returns() {
3344        return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
3345    }
3346
3347    /**
3348     * Get unread conversations count parameters description.
3349     *
3350     * @return external_function_parameters
3351     * @since 3.2
3352     */
3353    public static function get_unread_conversations_count_parameters() {
3354        return new external_function_parameters(
3355            array(
3356                'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
3357            )
3358        );
3359    }
3360
3361    /**
3362     * Get unread messages count function.
3363     *
3364     * @since  3.2
3365     * @throws invalid_parameter_exception
3366     * @throws moodle_exception
3367     * @param  int      $useridto       the user id who received the message
3368     * @return external_description
3369     */
3370    public static function get_unread_conversations_count($useridto) {
3371        global $USER, $CFG;
3372
3373        // Check if messaging is enabled.
3374        if (empty($CFG->messaging)) {
3375            throw new moodle_exception('disabled', 'message');
3376        }
3377
3378        $params = self::validate_parameters(
3379            self::get_unread_conversations_count_parameters(),
3380            array('useridto' => $useridto)
3381        );
3382
3383        $context = context_system::instance();
3384        self::validate_context($context);
3385
3386        $useridto = $params['useridto'];
3387
3388        if (!empty($useridto)) {
3389            if (core_user::is_real_user($useridto)) {
3390                $userto = core_user::get_user($useridto, '*', MUST_EXIST);
3391            } else {
3392                throw new moodle_exception('invaliduser');
3393            }
3394        } else {
3395            $useridto = $USER->id;
3396        }
3397
3398        // Check if the current user is the receiver or just a privileged user.
3399        if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
3400            throw new moodle_exception('accessdenied', 'admin');
3401        }
3402
3403        return \core_message\api::count_unread_conversations($userto);
3404    }
3405
3406    /**
3407     * Get unread conversations count return description.
3408     *
3409     * @return external_single_structure
3410     * @since 3.2
3411     */
3412    public static function get_unread_conversations_count_returns() {
3413        return new external_value(PARAM_INT, 'The count of unread messages for the user');
3414    }
3415
3416    /**
3417     * Get blocked users parameters description.
3418     *
3419     * @return external_function_parameters
3420     * @since 2.9
3421     */
3422    public static function get_blocked_users_parameters() {
3423        return new external_function_parameters(
3424            array(
3425                'userid' => new external_value(PARAM_INT,
3426                                'the user whose blocked users we want to retrieve',
3427                                VALUE_REQUIRED),
3428            )
3429        );
3430    }
3431
3432    /**
3433     * Retrieve a list of users blocked
3434     *
3435     * @param  int $userid the user whose blocked users we want to retrieve
3436     * @return external_description
3437     * @since 2.9
3438     */
3439    public static function get_blocked_users($userid) {
3440        global $CFG, $USER, $PAGE;
3441
3442        // Warnings array, it can be empty at the end but is mandatory.
3443        $warnings = array();
3444
3445        // Validate params.
3446        $params = array(
3447            'userid' => $userid
3448        );
3449        $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
3450        $userid = $params['userid'];
3451
3452        // Validate context.
3453        $context = context_system::instance();
3454        self::validate_context($context);
3455
3456        // Check if private messaging between users is allowed.
3457        if (empty($CFG->messaging)) {
3458            throw new moodle_exception('disabled', 'message');
3459        }
3460
3461        $user = core_user::get_user($userid, '*', MUST_EXIST);
3462        core_user::require_active_user($user);
3463
3464        // Check if we have permissions for retrieve the information.
3465        $capability = 'moodle/site:manageallmessaging';
3466        if (($USER->id != $userid) && !has_capability($capability, $context)) {
3467            throw new required_capability_exception($context, $capability, 'nopermissions', '');
3468        }
3469
3470        // Now, we can get safely all the blocked users.
3471        $users = \core_message\api::get_blocked_users($user->id);
3472
3473        $blockedusers = array();
3474        foreach ($users as $user) {
3475            $newuser = array(
3476                'id' => $user->id,
3477                'fullname' => fullname($user),
3478            );
3479
3480            $userpicture = new user_picture($user);
3481            $userpicture->size = 1; // Size f1.
3482            $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
3483
3484            $blockedusers[] = $newuser;
3485        }
3486
3487        $results = array(
3488            'users' => $blockedusers,
3489            'warnings' => $warnings
3490        );
3491        return $results;
3492    }
3493
3494    /**
3495     * Get blocked users return description.
3496     *
3497     * @return external_single_structure
3498     * @since 2.9
3499     */
3500    public static function get_blocked_users_returns() {
3501        return new external_single_structure(
3502            array(
3503                'users' => new external_multiple_structure(
3504                    new external_single_structure(
3505                        array(
3506                            'id' => new external_value(PARAM_INT, 'User ID'),
3507                            'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
3508                            'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
3509                        )
3510                    ),
3511                    'List of blocked users'
3512                ),
3513                'warnings' => new external_warnings()
3514            )
3515        );
3516    }
3517
3518    /**
3519     * Returns description of method parameters
3520     *
3521     * @return external_function_parameters
3522     * @since 2.9
3523     */
3524    public static function mark_message_read_parameters() {
3525        return new external_function_parameters(
3526            array(
3527                'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
3528                'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
3529                    VALUE_DEFAULT, 0)
3530            )
3531        );
3532    }
3533
3534    /**
3535     * Mark a single message as read, trigger message_viewed event
3536     *
3537     * @param  int $messageid id of the message (in the message table)
3538     * @param  int $timeread timestamp for when the message should be marked read
3539     * @return external_description
3540     * @throws invalid_parameter_exception
3541     * @throws moodle_exception
3542     * @since 2.9
3543     */
3544    public static function mark_message_read($messageid, $timeread) {
3545        global $CFG, $DB, $USER;
3546
3547        // Check if private messaging between users is allowed.
3548        if (empty($CFG->messaging)) {
3549            throw new moodle_exception('disabled', 'message');
3550        }
3551
3552        // Warnings array, it can be empty at the end but is mandatory.
3553        $warnings = array();
3554
3555        // Validate params.
3556        $params = array(
3557            'messageid' => $messageid,
3558            'timeread' => $timeread
3559        );
3560        $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
3561
3562        if (empty($params['timeread'])) {
3563            $timeread = time();
3564        } else {
3565            $timeread = $params['timeread'];
3566        }
3567
3568        // Validate context.
3569        $context = context_system::instance();
3570        self::validate_context($context);
3571
3572        $sql = "SELECT m.*, mcm.userid as useridto
3573                  FROM {messages} m
3574            INNER JOIN {message_conversations} mc
3575                    ON m.conversationid = mc.id
3576            INNER JOIN {message_conversation_members} mcm
3577                    ON mcm.conversationid = mc.id
3578             LEFT JOIN {message_user_actions} mua
3579                    ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
3580                 WHERE mua.id is NULL
3581                   AND mcm.userid != m.useridfrom
3582                   AND m.id = ?";
3583        $messageparams = [];
3584        $messageparams[] = $USER->id;
3585        $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
3586        $messageparams[] = $params['messageid'];
3587        $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
3588
3589        if ($message->useridto != $USER->id) {
3590            throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
3591        }
3592
3593        \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
3594
3595        $results = array(
3596            'messageid' => $message->id,
3597            'warnings' => $warnings
3598        );
3599        return $results;
3600    }
3601
3602    /**
3603     * Returns description of method result value
3604     *
3605     * @return external_description
3606     * @since 2.9
3607     */
3608    public static function mark_message_read_returns() {
3609        return new external_single_structure(
3610            array(
3611                'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
3612                'warnings' => new external_warnings()
3613            )
3614        );
3615    }
3616
3617    /**
3618     * Returns description of method parameters
3619     *
3620     * @return external_function_parameters
3621     */
3622    public static function mark_notification_read_parameters() {
3623        return new external_function_parameters(
3624            array(
3625                'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
3626                'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
3627                    VALUE_DEFAULT, 0)
3628            )
3629        );
3630    }
3631
3632    /**
3633     * Mark a single notification as read.
3634     *
3635     * This will trigger a 'notification_viewed' event.
3636     *
3637     * @param int $notificationid id of the notification
3638     * @param int $timeread timestamp for when the notification should be marked read
3639     * @return external_description
3640     * @throws invalid_parameter_exception
3641     * @throws moodle_exception
3642     */
3643    public static function mark_notification_read($notificationid, $timeread) {
3644        global $CFG, $DB, $USER;
3645
3646        // Warnings array, it can be empty at the end but is mandatory.
3647        $warnings = array();
3648
3649        // Validate params.
3650        $params = array(
3651            'notificationid' => $notificationid,
3652            'timeread' => $timeread
3653        );
3654        $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
3655
3656        if (empty($params['timeread'])) {
3657            $timeread = time();
3658        } else {
3659            $timeread = $params['timeread'];
3660        }
3661
3662        // Validate context.
3663        $context = context_system::instance();
3664        self::validate_context($context);
3665
3666        $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
3667
3668        if ($notification->useridto != $USER->id) {
3669            throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
3670                'notification as read');
3671        }
3672
3673        \core_message\api::mark_notification_as_read($notification, $timeread);
3674
3675        $results = array(
3676            'notificationid' => $notification->id,
3677            'warnings' => $warnings
3678        );
3679
3680        return $results;
3681    }
3682
3683    /**
3684     * Returns description of method result value
3685     *
3686     * @return external_description
3687     */
3688    public static function mark_notification_read_returns() {
3689        return new external_single_structure(
3690            array(
3691                'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
3692                'warnings' => new external_warnings()
3693            )
3694        );
3695    }
3696
3697    /**
3698     * Mark all messages as read parameters description.
3699     *
3700     * @deprecated since 3.6
3701     * @return external_function_parameters
3702     * @since 3.2
3703     */
3704    public static function mark_all_messages_as_read_parameters() {
3705        return new external_function_parameters(
3706            array(
3707                'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
3708                'useridfrom' => new external_value(
3709                    PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
3710                    VALUE_DEFAULT, 0),
3711            )
3712        );
3713    }
3714
3715    /**
3716     * Mark all messages as read function.
3717     *
3718     * @deprecated since 3.6
3719     * @throws invalid_parameter_exception
3720     * @throws moodle_exception
3721     * @param  int      $useridto       the user id who received the message
3722     * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
3723     * @return external_description
3724     * @since  3.2
3725     */
3726    public static function mark_all_messages_as_read($useridto, $useridfrom) {
3727        global $USER, $CFG;
3728
3729        // Check if messaging is enabled.
3730        if (empty($CFG->messaging)) {
3731            throw new moodle_exception('disabled', 'message');
3732        }
3733
3734        $params = self::validate_parameters(
3735            self::mark_all_messages_as_read_parameters(),
3736            array(
3737                'useridto' => $useridto,
3738                'useridfrom' => $useridfrom,
3739            )
3740        );
3741
3742        $context = context_system::instance();
3743        self::validate_context($context);
3744
3745        $useridto = $params['useridto'];
3746        $useridfrom = $params['useridfrom'];
3747
3748        if (!empty($useridto)) {
3749            if (core_user::is_real_user($useridto)) {
3750                $userto = core_user::get_user($useridto, '*', MUST_EXIST);
3751            } else {
3752                throw new moodle_exception('invaliduser');
3753            }
3754        }
3755
3756        if (!empty($useridfrom)) {
3757            // We use get_user here because the from user can be the noreply or support user.
3758            $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
3759        }
3760
3761        // Check if the current user is the sender/receiver or just a privileged user.
3762        if ($useridto != $USER->id and $useridfrom != $USER->id and
3763            // The deleteanymessage cap seems more reasonable here than readallmessages.
3764             !has_capability('moodle/site:deleteanymessage', $context)) {
3765            throw new moodle_exception('accessdenied', 'admin');
3766        }
3767
3768        if ($useridfrom) {
3769            if ($conversationid = \core_message\api::get_conversation_between_users([$useridto, $useridfrom])) {
3770                \core_message\api::mark_all_messages_as_read($useridto, $conversationid);
3771            }
3772        } else {
3773            \core_message\api::mark_all_messages_as_read($useridto);
3774        }
3775
3776        return true;
3777    }
3778
3779    /**
3780     * Mark all messages as read return description.
3781     *
3782     * @deprecated since 3.6
3783     * @return external_single_structure
3784     * @since 3.2
3785     */
3786    public static function mark_all_messages_as_read_returns() {
3787        return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
3788    }
3789
3790    /**
3791     * Marking the method as deprecated.
3792     *
3793     * @return bool
3794     */
3795    public static function mark_all_messages_as_read_is_deprecated() {
3796        return true;
3797    }
3798
3799    /**
3800     * Mark all conversation messages as read parameters description.
3801     *
3802     * @return external_function_parameters
3803     * @since 3.6
3804     */
3805    public static function mark_all_conversation_messages_as_read_parameters() {
3806        return new external_function_parameters(
3807            array(
3808                'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
3809                'conversationid' =>
3810                    new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
3811            )
3812        );
3813    }
3814
3815    /**
3816     * Mark all conversation messages as read function.
3817     *
3818     * @param int $userid The user id of who we want to delete the conversation for
3819     * @param int $conversationid The id of the conversations
3820     * @since 3.6
3821     */
3822    public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
3823        global $CFG;
3824
3825        // Check if messaging is enabled.
3826        if (empty($CFG->messaging)) {
3827            throw new moodle_exception('disabled', 'message');
3828        }
3829
3830        $params = array(
3831            'userid' => $userid,
3832            'conversationid' => $conversationid,
3833        );
3834        $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
3835
3836        $context = context_system::instance();
3837        self::validate_context($context);
3838
3839        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3840        core_user::require_active_user($user);
3841
3842        if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
3843            \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
3844        } else {
3845            throw new moodle_exception('accessdenied', 'admin');
3846        }
3847    }
3848
3849    /**
3850     * Mark all conversation messages as read return description.
3851     *
3852     * @return external_warnings
3853     * @since 3.6
3854     */
3855    public static function mark_all_conversation_messages_as_read_returns() {
3856        return null;
3857    }
3858
3859    /**
3860     * Returns description of method parameters.
3861     *
3862     * @deprecated since 3.6
3863     * @return external_function_parameters
3864     * @since 3.2
3865     */
3866    public static function delete_conversation_parameters() {
3867        return new external_function_parameters(
3868            array(
3869                'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
3870                'otheruserid' => new external_value(PARAM_INT, 'The user id of the other user in the conversation'),
3871            )
3872        );
3873    }
3874
3875    /**
3876     * Deletes a conversation.
3877     *
3878     * @deprecated since 3.6
3879     * @param int $userid The user id of who we want to delete the conversation for
3880     * @param int $otheruserid The user id of the other user in the conversation
3881     * @return array
3882     * @throws moodle_exception
3883     * @since 3.2
3884     */
3885    public static function delete_conversation($userid, $otheruserid) {
3886        global $CFG;
3887
3888        // Check if private messaging between users is allowed.
3889        if (empty($CFG->messaging)) {
3890            throw new moodle_exception('disabled', 'message');
3891        }
3892
3893        // Warnings array, it can be empty at the end but is mandatory.
3894        $warnings = array();
3895
3896        // Validate params.
3897        $params = array(
3898            'userid' => $userid,
3899            'otheruserid' => $otheruserid,
3900        );
3901        $params = self::validate_parameters(self::delete_conversation_parameters(), $params);
3902
3903        // Validate context.
3904        $context = context_system::instance();
3905        self::validate_context($context);
3906
3907        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3908        core_user::require_active_user($user);
3909
3910        if (!$conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']])) {
3911            return [];
3912        }
3913
3914        if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
3915            \core_message\api::delete_conversation_by_id($user->id, $conversationid);
3916            $status = true;
3917        } else {
3918            throw new moodle_exception('You do not have permission to delete messages');
3919        }
3920
3921        $results = array(
3922            'status' => $status,
3923            'warnings' => $warnings
3924        );
3925
3926        return $results;
3927    }
3928
3929    /**
3930     * Returns description of method result value.
3931     *
3932     * @deprecated since 3.6
3933     * @return external_description
3934     * @since 3.2
3935     */
3936    public static function delete_conversation_returns() {
3937        return new external_single_structure(
3938            array(
3939                'status' => new external_value(PARAM_BOOL, 'True if the conversation was deleted, false otherwise'),
3940                'warnings' => new external_warnings()
3941            )
3942        );
3943    }
3944
3945    /**
3946     * Marking the method as deprecated.
3947     *
3948     * @return bool
3949     */
3950    public static function delete_conversation_is_deprecated() {
3951        return true;
3952    }
3953
3954    /**
3955     * Returns description of method parameters.
3956     *
3957     * @return external_function_parameters
3958     * @since 3.6
3959     */
3960    public static function delete_conversations_by_id_parameters() {
3961        return new external_function_parameters(
3962            array(
3963                'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
3964                'conversationids' => new external_multiple_structure(
3965                    new external_value(PARAM_INT, 'The id of the conversation'),
3966                    'List of conversation IDs'
3967                ),
3968            )
3969        );
3970    }
3971
3972    /**
3973     * Deletes a conversation.
3974     *
3975     * @param int $userid The user id of who we want to delete the conversation for
3976     * @param int[] $conversationids The ids of the conversations
3977     * @return array
3978     * @throws moodle_exception
3979     * @since 3.6
3980     */
3981    public static function delete_conversations_by_id($userid, array $conversationids) {
3982        global $CFG;
3983
3984        // Check if private messaging between users is allowed.
3985        if (empty($CFG->messaging)) {
3986            throw new moodle_exception('disabled', 'message');
3987        }
3988
3989        // Validate params.
3990        $params = [
3991            'userid' => $userid,
3992            'conversationids' => $conversationids,
3993        ];
3994        $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
3995
3996        // Validate context.
3997        $context = context_system::instance();
3998        self::validate_context($context);
3999
4000        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
4001        core_user::require_active_user($user);
4002
4003        foreach ($params['conversationids'] as $conversationid) {
4004            if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
4005                \core_message\api::delete_conversation_by_id($user->id, $conversationid);
4006            } else {
4007                throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
4008            }
4009        }
4010
4011        return [];
4012    }
4013
4014    /**
4015     * Returns description of method result value.
4016     *
4017     * @return external_description
4018     * @since 3.6
4019     */
4020    public static function delete_conversations_by_id_returns() {
4021        return new external_warnings();
4022    }
4023
4024    /**
4025     * Returns description of method parameters
4026     *
4027     * @return external_function_parameters
4028     * @since 3.1
4029     */
4030    public static function delete_message_parameters() {
4031        return new external_function_parameters(
4032            array(
4033                'messageid' => new external_value(PARAM_INT, 'The message id'),
4034                'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
4035                'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
4036            )
4037        );
4038    }
4039
4040    /**
4041     * Deletes a message
4042     *
4043     * @param  int $messageid the message id
4044     * @param  int $userid the user id of who we want to delete the message for
4045     * @param  bool $read if is a message read (default to true)
4046     * @return external_description
4047     * @throws moodle_exception
4048     * @since 3.1
4049     */
4050    public static function delete_message($messageid, $userid, $read = true) {
4051        global $CFG;
4052
4053        // Check if private messaging between users is allowed.
4054        if (empty($CFG->messaging)) {
4055            throw new moodle_exception('disabled', 'message');
4056        }
4057
4058        // Warnings array, it can be empty at the end but is mandatory.
4059        $warnings = array();
4060
4061        // Validate params.
4062        $params = array(
4063            'messageid' => $messageid,
4064            'userid' => $userid,
4065            'read' => $read
4066        );
4067        $params = self::validate_parameters(self::delete_message_parameters(), $params);
4068
4069        // Validate context.
4070        $context = context_system::instance();
4071        self::validate_context($context);
4072
4073        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
4074        core_user::require_active_user($user);
4075
4076        if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
4077            $status = \core_message\api::delete_message($user->id, $params['messageid']);
4078        } else {
4079            throw new moodle_exception('You do not have permission to delete this message');
4080        }
4081
4082        $results = array(
4083            'status' => $status,
4084            'warnings' => $warnings
4085        );
4086        return $results;
4087    }
4088
4089    /**
4090     * Returns description of method result value
4091     *
4092     * @return external_description
4093     * @since 3.1
4094     */
4095    public static function delete_message_returns() {
4096        return new external_single_structure(
4097            array(
4098                'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
4099                'warnings' => new external_warnings()
4100            )
4101        );
4102    }
4103
4104    /**
4105     * Returns description of method parameters
4106     *
4107     * @return external_function_parameters
4108     * @since 3.2
4109     */
4110    public static function message_processor_config_form_parameters() {
4111        return new external_function_parameters(
4112            array(
4113                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
4114                'name' => new external_value(PARAM_TEXT, 'The name of the message processor'),
4115                'formvalues' => new external_multiple_structure(
4116                    new external_single_structure(
4117                        array(
4118                            'name' => new external_value(PARAM_TEXT, 'name of the form element', VALUE_REQUIRED),
4119                            'value' => new external_value(PARAM_RAW, 'value of the form element', VALUE_REQUIRED),
4120                        )
4121                    ),
4122                    'Config form values',
4123                    VALUE_REQUIRED
4124                ),
4125            )
4126        );
4127    }
4128
4129    /**
4130     * Processes a message processor config form.
4131     *
4132     * @param  int $userid the user id
4133     * @param  string $name the name of the processor
4134     * @param  array $formvalues the form values
4135     * @return external_description
4136     * @throws moodle_exception
4137     * @since 3.2
4138     */
4139    public static function message_processor_config_form($userid, $name, $formvalues) {
4140        global $USER, $CFG;
4141
4142        $params = self::validate_parameters(
4143            self::message_processor_config_form_parameters(),
4144            array(
4145                'userid' => $userid,
4146                'name' => $name,
4147                'formvalues' => $formvalues,
4148            )
4149        );
4150
4151        $user = self::validate_preferences_permissions($params['userid']);
4152
4153        $processor = get_message_processor($params['name']);
4154        $preferences = [];
4155        $form = new stdClass();
4156
4157        foreach ($params['formvalues'] as $formvalue) {
4158            // Curly braces to ensure interpretation is consistent between
4159            // php 5 and php 7.
4160            $form->{$formvalue['name']} = $formvalue['value'];
4161        }
4162
4163        $processor->process_form($form, $preferences);
4164
4165        if (!empty($preferences)) {
4166            set_user_preferences($preferences, $params['userid']);
4167        }
4168    }
4169
4170    /**
4171     * Returns description of method result value
4172     *
4173     * @return external_description
4174     * @since 3.2
4175     */
4176    public static function message_processor_config_form_returns() {
4177        return null;
4178    }
4179
4180    /**
4181     * Returns description of method parameters
4182     *
4183     * @return external_function_parameters
4184     * @since 3.2
4185     */
4186    public static function get_message_processor_parameters() {
4187        return new external_function_parameters(
4188            array(
4189                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user'),
4190                'name' => new external_value(PARAM_TEXT, 'The name of the message processor', VALUE_REQUIRED),
4191            )
4192        );
4193    }
4194
4195    /**
4196     * Get a message processor.
4197     *
4198     * @param int $userid
4199     * @param string $name the name of the processor
4200     * @return external_description
4201     * @throws moodle_exception
4202     * @since 3.2
4203     */
4204    public static function get_message_processor($userid = 0, $name) {
4205        global $USER, $PAGE, $CFG;
4206
4207        // Check if messaging is enabled.
4208        if (empty($CFG->messaging)) {
4209            throw new moodle_exception('disabled', 'message');
4210        }
4211
4212        $params = self::validate_parameters(
4213            self::get_message_processor_parameters(),
4214            array(
4215                'userid' => $userid,
4216                'name' => $name,
4217            )
4218        );
4219
4220        if (empty($params['userid'])) {
4221            $params['userid'] = $USER->id;
4222        }
4223
4224        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
4225        core_user::require_active_user($user);
4226        self::validate_context(context_user::instance($params['userid']));
4227
4228        $processor = get_message_processor($params['name']);
4229
4230        $processoroutput = new \core_message\output\processor($processor, $user);
4231        $renderer = $PAGE->get_renderer('core_message');
4232
4233        return $processoroutput->export_for_template($renderer);
4234    }
4235
4236    /**
4237     * Returns description of method result value
4238     *
4239     * @return external_description
4240     * @since 3.2
4241     */
4242    public static function get_message_processor_returns() {
4243        return new external_function_parameters(
4244            array(
4245                'systemconfigured' => new external_value(PARAM_BOOL, 'Site configuration status'),
4246                'userconfigured' => new external_value(PARAM_BOOL, 'The user configuration status'),
4247            )
4248        );
4249    }
4250
4251    /**
4252     * Check that the user has enough permission to retrieve message or notifications preferences.
4253     *
4254     * @param  int $userid the user id requesting the preferences
4255     * @return stdClass full user object
4256     * @throws moodle_exception
4257     * @since  Moodle 3.2
4258     */
4259    protected static function validate_preferences_permissions($userid) {
4260        global $USER;
4261
4262        if (empty($userid)) {
4263            $user = $USER;
4264        } else {
4265            $user = core_user::get_user($userid, '*', MUST_EXIST);
4266            core_user::require_active_user($user);
4267        }
4268
4269        $systemcontext = context_system::instance();
4270        self::validate_context($systemcontext);
4271
4272        // Check access control.
4273        if ($user->id == $USER->id) {
4274            // Editing own message profile.
4275            require_capability('moodle/user:editownmessageprofile', $systemcontext);
4276        } else {
4277            // Teachers, parents, etc.
4278            $personalcontext = context_user::instance($user->id);
4279            require_capability('moodle/user:editmessageprofile', $personalcontext);
4280        }
4281        return $user;
4282    }
4283
4284    /**
4285     * Returns a notification or message preference structure.
4286     *
4287     * @return external_single_structure the structure
4288     * @since  Moodle 3.2
4289     */
4290    protected static function get_preferences_structure() {
4291        return new external_single_structure(
4292            array(
4293                'userid' => new external_value(PARAM_INT, 'User id'),
4294                'disableall' => new external_value(PARAM_INT, 'Whether all the preferences are disabled'),
4295                'processors' => new external_multiple_structure(
4296                    new external_single_structure(
4297                        array(
4298                            'displayname' => new external_value(PARAM_TEXT, 'Display name'),
4299                            'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
4300                            'hassettings' => new external_value(PARAM_BOOL, 'Whether has settings'),
4301                            'contextid' => new external_value(PARAM_INT, 'Context id'),
4302                            'userconfigured' => new external_value(PARAM_INT, 'Whether is configured by the user'),
4303                        )
4304                    ),
4305                    'Config form values'
4306                ),
4307                'components' => new external_multiple_structure(
4308                    new external_single_structure(
4309                        array(
4310                            'displayname' => new external_value(PARAM_TEXT, 'Display name'),
4311                            'notifications' => new external_multiple_structure(
4312                                new external_single_structure(
4313                                    array(
4314                                        'displayname' => new external_value(PARAM_TEXT, 'Display name'),
4315                                        'preferencekey' => new external_value(PARAM_ALPHANUMEXT, 'Preference key'),
4316                                        'processors' => new external_multiple_structure(
4317                                            new external_single_structure(
4318                                                array(
4319                                                    'displayname' => new external_value(PARAM_TEXT, 'Display name'),
4320                                                    'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
4321                                                    'locked' => new external_value(PARAM_BOOL, 'Is locked by admin?'),
4322                                                    'lockedmessage' => new external_value(PARAM_TEXT,
4323                                                        'Text to display if locked', VALUE_OPTIONAL),
4324                                                    'userconfigured' => new external_value(PARAM_INT, 'Is configured?'),
4325                                                    'loggedin' => new external_single_structure(
4326                                                        array(
4327                                                            'name' => new external_value(PARAM_NOTAGS, 'Name'),
4328                                                            'displayname' => new external_value(PARAM_TEXT, 'Display name'),
4329                                                            'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
4330                                                        )
4331                                                    ),
4332                                                    'loggedoff' => new external_single_structure(
4333                                                        array(
4334                                                            'name' => new external_value(PARAM_NOTAGS, 'Name'),
4335                                                            'displayname' => new external_value(PARAM_TEXT, 'Display name'),
4336                                                            'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
4337                                                        )
4338                                                    ),
4339                                                )
4340                                            ),
4341                                            'Processors values for this notification'
4342                                        ),
4343                                    )
4344                                ),
4345                                'List of notificaitons for the component'
4346                            ),
4347                        )
4348                    ),
4349                    'Available components'
4350                ),
4351            )
4352        );
4353    }
4354
4355    /**
4356     * Returns description of method parameters
4357     *
4358     * @return external_function_parameters
4359     * @since 3.2
4360     */
4361    public static function get_user_notification_preferences_parameters() {
4362        return new external_function_parameters(
4363            array(
4364                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
4365            )
4366        );
4367    }
4368
4369    /**
4370     * Get the notification preferences for a given user.
4371     *
4372     * @param int $userid id of the user, 0 for current user
4373     * @return external_description
4374     * @throws moodle_exception
4375     * @since 3.2
4376     */
4377    public static function get_user_notification_preferences($userid = 0) {
4378        global $PAGE;
4379
4380        $params = self::validate_parameters(
4381            self::get_user_notification_preferences_parameters(),
4382            array(
4383                'userid' => $userid,
4384            )
4385        );
4386        $user = self::validate_preferences_permissions($params['userid']);
4387
4388        $processors = get_message_processors();
4389        $providers = message_get_providers_for_user($user->id);
4390        $preferences = \core_message\api::get_all_message_preferences($processors, $providers, $user);
4391        $notificationlist = new \core_message\output\preferences\notification_list($processors, $providers, $preferences, $user);
4392
4393        $renderer = $PAGE->get_renderer('core_message');
4394
4395        $result = array(
4396            'warnings' => array(),
4397            'preferences' => $notificationlist->export_for_template($renderer)
4398        );
4399        return $result;
4400    }
4401
4402    /**
4403     * Returns description of method result value
4404     *
4405     * @return external_description
4406     * @since 3.2
4407     */
4408    public static function get_user_notification_preferences_returns() {
4409        return new external_function_parameters(
4410            array(
4411                'preferences' => self::get_preferences_structure(),
4412                'warnings' => new external_warnings(),
4413            )
4414        );
4415    }
4416
4417    /**
4418     * Returns description of method parameters
4419     *
4420     * @return external_function_parameters
4421     * @since 3.2
4422     */
4423    public static function get_user_message_preferences_parameters() {
4424        return new external_function_parameters(
4425            array(
4426                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
4427            )
4428        );
4429    }
4430
4431    /**
4432     * Get the notification preferences for a given user.
4433     *
4434     * @param int $userid id of the user, 0 for current user
4435     * @return external_description
4436     * @throws moodle_exception
4437     * @since 3.2
4438     */
4439    public static function get_user_message_preferences($userid = 0) {
4440        global $CFG, $PAGE;
4441
4442        $params = self::validate_parameters(
4443            self::get_user_message_preferences_parameters(),
4444            array(
4445                'userid' => $userid,
4446            )
4447        );
4448
4449        $user = self::validate_preferences_permissions($params['userid']);
4450
4451        // Filter out enabled, available system_configured and user_configured processors only.
4452        $readyprocessors = array_filter(get_message_processors(), function($processor) {
4453            return $processor->enabled &&
4454                $processor->configured &&
4455                $processor->object->is_user_configured() &&
4456                // Filter out processors that don't have and message preferences to configure.
4457                $processor->object->has_message_preferences();
4458        });
4459
4460        $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
4461            return $provider->component === 'moodle';
4462        });
4463        $preferences = \core_message\api::get_all_message_preferences($readyprocessors, $providers, $user);
4464        $notificationlistoutput = new \core_message\output\preferences\message_notification_list($readyprocessors,
4465            $providers, $preferences, $user);
4466
4467        $renderer = $PAGE->get_renderer('core_message');
4468
4469        $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
4470
4471        $result = array(
4472            'warnings' => array(),
4473            'preferences' => $notificationlistoutput->export_for_template($renderer),
4474            'blocknoncontacts' => \core_message\api::get_user_privacy_messaging_preference($user->id),
4475            'entertosend' => $entertosend
4476        );
4477        return $result;
4478    }
4479
4480    /**
4481     * Returns description of method result value
4482     *
4483     * @return external_description
4484     * @since 3.2
4485     */
4486    public static function get_user_message_preferences_returns() {
4487        return new external_function_parameters(
4488            array(
4489                'preferences' => self::get_preferences_structure(),
4490                'blocknoncontacts' => new external_value(PARAM_INT, 'Privacy messaging setting to define who can message you'),
4491                'entertosend' => new external_value(PARAM_BOOL, 'User preference for using enter to send messages'),
4492                'warnings' => new external_warnings(),
4493            )
4494        );
4495    }
4496
4497    /**
4498     * Returns description of method parameters for the favourite_conversations() method.
4499     *
4500     * @return external_function_parameters
4501     */
4502    public static function set_favourite_conversations_parameters() {
4503        return new external_function_parameters(
4504            array(
4505                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
4506                'conversations' => new external_multiple_structure(
4507                    new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
4508                )
4509            )
4510        );
4511    }
4512
4513    /**
4514     * Favourite a conversation, or list of conversations for a user.
4515     *
4516     * @param int $userid the id of the user, or 0 for the current user.
4517     * @param array $conversationids the list of conversations ids to favourite.
4518     * @return array
4519     * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
4520     */
4521    public static function set_favourite_conversations(int $userid, array $conversationids) {
4522        global $CFG, $USER;
4523
4524        // All the business logic checks that really shouldn't be in here.
4525        if (empty($CFG->messaging)) {
4526            throw new moodle_exception('disabled', 'message');
4527        }
4528        $params = [
4529            'userid' => $userid,
4530            'conversations' => $conversationids
4531        ];
4532        $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
4533        $systemcontext = context_system::instance();
4534        self::validate_context($systemcontext);
4535
4536        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
4537            throw new moodle_exception('You do not have permission to perform this action.');
4538        }
4539
4540        foreach ($params['conversations'] as $conversationid) {
4541            \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
4542        }
4543
4544        return [];
4545    }
4546
4547    /**
4548     * Return a description of the returns for the create_user_favourite_conversations() method.
4549     *
4550     * @return external_description
4551     */
4552    public static function set_favourite_conversations_returns() {
4553        return new external_warnings();
4554    }
4555
4556    /**
4557     * Returns description of method parameters for unfavourite_conversations() method.
4558     *
4559     * @return external_function_parameters
4560     */
4561    public static function unset_favourite_conversations_parameters() {
4562        return new external_function_parameters(
4563            array(
4564                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
4565                'conversations' => new external_multiple_structure(
4566                    new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
4567                )
4568            )
4569        );
4570    }
4571
4572    /**
4573     * Unfavourite a conversation, or list of conversations for a user.
4574     *
4575     * @param int $userid the id of the user, or 0 for the current user.
4576     * @param array $conversationids the list of conversations ids unset as favourites.
4577     * @return array
4578     * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
4579     */
4580    public static function unset_favourite_conversations(int $userid, array $conversationids) {
4581        global $CFG, $USER;
4582
4583        // All the business logic checks that really shouldn't be in here.
4584        if (empty($CFG->messaging)) {
4585            throw new moodle_exception('disabled', 'message');
4586        }
4587        $params = [
4588            'userid' => $userid,
4589            'conversations' => $conversationids
4590        ];
4591        $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
4592        $systemcontext = context_system::instance();
4593        self::validate_context($systemcontext);
4594
4595        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
4596            throw new moodle_exception('You do not have permission to perform this action.');
4597        }
4598
4599        foreach ($params['conversations'] as $conversationid) {
4600            \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
4601        }
4602
4603        return [];
4604    }
4605
4606    /**
4607     * Unset favourite conversations return description.
4608     *
4609     * @return external_description
4610     */
4611    public static function unset_favourite_conversations_returns() {
4612        return new external_warnings();
4613    }
4614
4615    /**
4616     * Returns description of method parameters for get_member_info() method.
4617     *
4618     * @return external_function_parameters
4619     */
4620    public static function get_member_info_parameters() {
4621        return new external_function_parameters(
4622            array(
4623                'referenceuserid' => new external_value(PARAM_INT, 'id of the user'),
4624                'userids' => new external_multiple_structure(
4625                    new external_value(PARAM_INT, 'id of members to get')
4626                ),
4627                'includecontactrequests' => new external_value(PARAM_BOOL, 'include contact requests in response', VALUE_DEFAULT, false),
4628                'includeprivacyinfo' => new external_value(PARAM_BOOL, 'include privacy info in response', VALUE_DEFAULT, false)
4629            )
4630        );
4631    }
4632
4633    /**
4634     * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
4635     *
4636     * This is the basic structure used when returning members, and includes information about the relationship between each member
4637     * and the referenceuser, such as a whether the referenceuser has marked the member as a contact, or has blocked them.
4638     *
4639     * @param int $referenceuserid the id of the user which check contact and blocked status.
4640     * @param array $userids
4641     * @return array the array of objects containing member info.
4642     * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
4643     */
4644    public static function get_member_info(
4645        int $referenceuserid,
4646        array $userids,
4647        bool $includecontactrequests = false,
4648        bool $includeprivacyinfo = false
4649    ) {
4650        global $CFG, $USER;
4651
4652        // All the business logic checks that really shouldn't be in here.
4653        if (empty($CFG->messaging)) {
4654            throw new moodle_exception('disabled', 'message');
4655        }
4656        $params = [
4657            'referenceuserid' => $referenceuserid,
4658            'userids' => $userids,
4659            'includecontactrequests' => $includecontactrequests,
4660            'includeprivacyinfo' => $includeprivacyinfo
4661        ];
4662        $params = self::validate_parameters(self::get_member_info_parameters(), $params);
4663        $systemcontext = context_system::instance();
4664        self::validate_context($systemcontext);
4665
4666        if (($USER->id != $referenceuserid) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
4667            throw new moodle_exception('You do not have permission to perform this action.');
4668        }
4669
4670        return \core_message\helper::get_member_info(
4671            $params['referenceuserid'],
4672            $params['userids'],
4673            $params['includecontactrequests'],
4674            $params['includeprivacyinfo']
4675        );
4676    }
4677
4678    /**
4679     * Get member info return description.
4680     *
4681     * @return external_description
4682     */
4683    public static function get_member_info_returns() {
4684        return new external_multiple_structure(
4685            self::get_conversation_member_structure()
4686        );
4687    }
4688
4689    /**
4690     * Returns description of method parameters for get_conversation_counts() method.
4691     *
4692     * @return external_function_parameters
4693     */
4694    public static function get_conversation_counts_parameters() {
4695        return new external_function_parameters(
4696            [
4697                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
4698            ]
4699        );
4700    }
4701
4702    /**
4703     * Returns an array of conversation counts for the various types of conversations, including favourites.
4704     *
4705     * Return format:
4706     * [
4707     *     'favourites' => 0,
4708     *     'types' => [
4709     *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
4710     *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
4711     *      ]
4712     * ]
4713     *
4714     * @param int $userid the id of the user whose counts we are fetching.
4715     * @return array the array of conversation counts, indexed by type.
4716     * @throws moodle_exception if the current user cannot perform this action.
4717     */
4718    public static function get_conversation_counts(int $userid) {
4719        global $CFG, $USER;
4720
4721        // All the business logic checks that really shouldn't be in here.
4722        if (empty($CFG->messaging)) {
4723            throw new moodle_exception('disabled', 'message');
4724        }
4725
4726        if (empty($userid)) {
4727            $userid = $USER->id;
4728        }
4729
4730        $params = ['userid' => $userid];
4731        $params = self::validate_parameters(self::get_conversation_counts_parameters(), $params);
4732
4733        $systemcontext = context_system::instance();
4734        self::validate_context($systemcontext);
4735
4736        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
4737            throw new moodle_exception('You do not have permission to perform this action.');
4738        }
4739
4740        return \core_message\api::get_conversation_counts($params['userid']);
4741    }
4742
4743    /**
4744     * Get conversation counts return description.
4745     *
4746     * @return external_description
4747     */
4748    public static function get_conversation_counts_returns() {
4749        return new external_single_structure(
4750            [
4751                'favourites' => new external_value(PARAM_INT, 'Total number of favourite conversations'),
4752                'types' => new external_single_structure(
4753                    [
4754                        \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
4755                            'Total number of individual conversations'),
4756                        \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
4757                            'Total number of group conversations'),
4758                        \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
4759                            'Total number of self conversations'),
4760                    ]
4761                ),
4762            ]
4763        );
4764    }
4765
4766    /**
4767     * Returns description of method parameters for get_unread_conversation_counts() method.
4768     *
4769     * @return external_function_parameters
4770     */
4771    public static function get_unread_conversation_counts_parameters() {
4772        return new external_function_parameters(
4773            [
4774                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
4775            ]
4776        );
4777    }
4778
4779    /**
4780     * Returns an array of unread conversation counts for the various types of conversations, including favourites.
4781     *
4782     * Return format:
4783     * [
4784     *     'favourites' => 0,
4785     *     'types' => [
4786     *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
4787     *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
4788     *      ]
4789     * ]
4790     *
4791     * @param int $userid the id of the user whose counts we are fetching.
4792     * @return array the array of unread conversation counts, indexed by type.
4793     * @throws moodle_exception if the current user cannot perform this action.
4794     */
4795    public static function get_unread_conversation_counts(int $userid) {
4796        global $CFG, $USER;
4797
4798        // All the business logic checks that really shouldn't be in here.
4799        if (empty($CFG->messaging)) {
4800            throw new moodle_exception('disabled', 'message');
4801        }
4802
4803        if (empty($userid)) {
4804            $userid = $USER->id;
4805        }
4806
4807        $params = ['userid' => $userid];
4808        $params = self::validate_parameters(self::get_unread_conversation_counts_parameters(), $params);
4809
4810        $systemcontext = context_system::instance();
4811        self::validate_context($systemcontext);
4812
4813        if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
4814            throw new moodle_exception('You do not have permission to perform this action.');
4815        }
4816
4817        return \core_message\api::get_unread_conversation_counts($params['userid']);
4818    }
4819
4820    /**
4821     * Get unread conversation counts return description.
4822     *
4823     * @return external_description
4824     */
4825    public static function get_unread_conversation_counts_returns() {
4826        return new external_single_structure(
4827            [
4828                'favourites' => new external_value(PARAM_INT, 'Total number of unread favourite conversations'),
4829                'types' => new external_single_structure(
4830                    [
4831                        \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
4832                            'Total number of unread individual conversations'),
4833                        \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
4834                            'Total number of unread group conversations'),
4835                        \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
4836                            'Total number of unread self conversations'),
4837                    ]
4838                ),
4839            ]
4840        );
4841    }
4842
4843    /**
4844     * Returns description of method parameters
4845     *
4846     * @return external_function_parameters
4847     * @since 3.7
4848     */
4849    public static function delete_message_for_all_users_parameters() {
4850        return new external_function_parameters(
4851            array(
4852                'messageid' => new external_value(PARAM_INT, 'The message id'),
4853                'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for all users')
4854            )
4855        );
4856    }
4857    /**
4858     * Deletes a message for all users
4859     *
4860     * @param  int $messageid the message id
4861     * @param  int $userid the user id of who we want to delete the message for all users, is no longer used.
4862     * @return external_description
4863     * @throws moodle_exception
4864     * @since 3.7
4865     */
4866    public static function delete_message_for_all_users(int $messageid, int $userid) {
4867        global $CFG, $USER;
4868
4869        // Check if private messaging between users is allowed.
4870        if (empty($CFG->messaging)) {
4871            throw new moodle_exception('disabled', 'message');
4872        }
4873
4874        // Validate params.
4875        $params = array(
4876            'messageid' => $messageid,
4877            'userid' => $userid
4878        );
4879        $params = self::validate_parameters(self::delete_message_for_all_users_parameters(), $params);
4880
4881        // Validate context.
4882        $context = context_system::instance();
4883        self::validate_context($context);
4884
4885        core_user::require_active_user($USER);
4886
4887        // Checks if a user can delete a message for all users.
4888        if (core_message\api::can_delete_message_for_all_users($USER->id, $params['messageid'])) {
4889            \core_message\api::delete_message_for_all_users($params['messageid']);
4890        } else {
4891            throw new moodle_exception('You do not have permission to delete this message for everyone.');
4892        }
4893
4894        return [];
4895    }
4896    /**
4897     * Returns description of method result value
4898     *
4899     * @return external_description
4900     * @since 3.7
4901     */
4902    public static function delete_message_for_all_users_returns() {
4903        return new external_warnings();
4904    }
4905}
4906