32504c6acf215b332cad92057bd75dbd57b15a8f
[moodle.git] / message / externallib.php
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/>.
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  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once("$CFG->libdir/externallib.php");
30 require_once($CFG->dirroot . "/message/lib.php");
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  */
41 class 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     }
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;
77         // Check if messaging is enabled.
78         if (empty($CFG->messaging)) {
79             throw new moodle_exception('disabled', 'message');
80         }
82         // Ensure the current user is allowed to run this function.
83         $context = context_system::instance();
84         self::validate_context($context);
86         $params = self::validate_parameters(self::send_messages_to_conversation_parameters(), [
87             'conversationid' => $conversationid,
88             'messages' => $messages
89         ]);
91         $messages = [];
92         foreach ($params['messages'] as $message) {
93             $createdmessage = \core_message\api::send_message_to_conversation($USER->id, $params['conversationid'], $message['text'],
94                 $message['textformat']);
95             $createdmessage->text = message_format_message_text((object) [
96                 'smallmessage' => $createdmessage->text,
97                 'fullmessageformat' => external_validate_format($message['textformat']),
98                 'fullmessagetrust' => $createdmessage->fullmessagetrust
99             ]);
100             $messages[] = $createdmessage;
101         }
103         return $messages;
104     }
106     /**
107      * Returns description of method result value.
108      *
109      * @return external_description
110      * @since Moodle 3.6
111      */
112     public static function send_messages_to_conversation_returns() {
113         return new external_multiple_structure(
114             self::get_conversation_message_structure()
115         );
116     }
119     /**
120      * Returns description of method parameters
121      *
122      * @return external_function_parameters
123      * @since Moodle 2.2
124      */
125     public static function send_instant_messages_parameters() {
126         return new external_function_parameters(
127             array(
128                 'messages' => new external_multiple_structure(
129                     new external_single_structure(
130                         array(
131                             'touserid' => new external_value(PARAM_INT, 'id of the user to send the private message'),
132                             'text' => new external_value(PARAM_RAW, 'the text of the message'),
133                             'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
134                             '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),
135                         )
136                     )
137                 )
138             )
139         );
140     }
142     /**
143      * Send private messages from the current USER to other users
144      *
145      * @param array $messages An array of message to send.
146      * @return array
147      * @since Moodle 2.2
148      */
149     public static function send_instant_messages($messages = array()) {
150         global $CFG, $USER, $DB;
152         // Check if messaging is enabled.
153         if (empty($CFG->messaging)) {
154             throw new moodle_exception('disabled', 'message');
155         }
157         // Ensure the current user is allowed to run this function
158         $context = context_system::instance();
159         self::validate_context($context);
160         require_capability('moodle/site:sendmessage', $context);
162         // Ensure the current user is allowed to delete message for everyone.
163         $candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $context);
165         $params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
167         //retrieve all tousers of the messages
168         $receivers = array();
169         foreach($params['messages'] as $message) {
170             $receivers[] = $message['touserid'];
171         }
172         list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
173         $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
175         $resultmessages = array();
176         $messageids = array();
177         foreach ($params['messages'] as $message) {
178             $resultmsg = array(); //the infos about the success of the operation
180             // We are going to do some checking.
181             // Code should match /messages/index.php checks.
182             $success = true;
184             // Check the user exists.
185             if (empty($tousers[$message['touserid']])) {
186                 $success = false;
187                 $errormessage = get_string('touserdoesntexist', 'message', $message['touserid']);
188             }
190             // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead userid
191             // Check if the recipient can be messaged by the sender.
192             if ($success && !\core_message\api::can_send_message($tousers[$message['touserid']]->id, $USER->id)) {
193                 $success = false;
194                 $errormessage = get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message['touserid'])));
195             }
197             // Now we can send the message (at least try).
198             if ($success) {
199                 // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead one touser object.
200                 $success = message_post_message($USER, $tousers[$message['touserid']],
201                         $message['text'], external_validate_format($message['textformat']));
202             }
204             // Build the resultmsg.
205             if (isset($message['clientmsgid'])) {
206                 $resultmsg['clientmsgid'] = $message['clientmsgid'];
207             }
208             if ($success) {
209                 $resultmsg['msgid'] = $success;
210                 $resultmsg['timecreated'] = time();
211                 $resultmsg['candeletemessagesforallusers'] = $candeletemessagesforallusers;
212                 $messageids[] = $success;
213             } else {
214                 // WARNINGS: for backward compatibility we return this errormessage.
215                 //          We should have thrown exceptions as these errors prevent results to be returned.
216                 // See http://docs.moodle.org/dev/Errors_handling_in_web_services#When_to_send_a_warning_on_the_server_side .
217                 $resultmsg['msgid'] = -1;
218                 $resultmsg['errormessage'] = $errormessage;
219             }
221             $resultmessages[] = $resultmsg;
222         }
224         if (!empty($messageids)) {
225             $messagerecords = $DB->get_records_list(
226                 'messages',
227                 'id',
228                 $messageids,
229                 '',
230                 'id, conversationid, smallmessage, fullmessageformat, fullmessagetrust');
231             $resultmessages = array_map(function($resultmessage) use ($messagerecords, $USER) {
232                 $id = $resultmessage['msgid'];
233                 $resultmessage['conversationid'] = isset($messagerecords[$id]) ? $messagerecords[$id]->conversationid : null;
234                 $resultmessage['useridfrom'] = $USER->id;
235                 $resultmessage['text'] = message_format_message_text((object) [
236                     'smallmessage' => $messagerecords[$id]->smallmessage,
237                     'fullmessageformat' => external_validate_format($messagerecords[$id]->fullmessageformat),
238                     'fullmessagetrust' => $messagerecords[$id]->fullmessagetrust
239                 ]);
240                 return $resultmessage;
241             }, $resultmessages);
242         }
244         return $resultmessages;
245     }
247     /**
248      * Returns description of method result value
249      *
250      * @return external_description
251      * @since Moodle 2.2
252      */
253     public static function send_instant_messages_returns() {
254         return new external_multiple_structure(
255             new external_single_structure(
256                 array(
257                     'msgid' => new external_value(PARAM_INT, 'test this to know if it succeeds:  id of the created message if it succeeded, -1 when failed'),
258                     'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
259                     'errormessage' => new external_value(PARAM_TEXT, 'error message - if it failed', VALUE_OPTIONAL),
260                     'text' => new external_value(PARAM_RAW, 'The text of the message', VALUE_OPTIONAL),
261                     'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message', VALUE_OPTIONAL),
262                     'conversationid' => new external_value(PARAM_INT, 'The conversation id for this message', VALUE_OPTIONAL),
263                     'useridfrom' => new external_value(PARAM_INT, 'The user id who sent the message', VALUE_OPTIONAL),
264                     'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
265                         'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
266                 )
267             )
268         );
269     }
271     /**
272      * Delete contacts parameters description.
273      *
274      * @return external_function_parameters
275      * @since Moodle 2.5
276      */
277     public static function delete_contacts_parameters() {
278         return new external_function_parameters(
279             array(
280                 'userids' => new external_multiple_structure(
281                     new external_value(PARAM_INT, 'User ID'),
282                     'List of user IDs'
283                 ),
284                 'userid' => new external_value(PARAM_INT, 'The id of the user we are deleting the contacts for, 0 for the
285                     current user', VALUE_DEFAULT, 0)
286             )
287         );
288     }
290     /**
291      * Delete contacts.
292      *
293      * @param array $userids array of user IDs.
294      * @param int $userid The id of the user we are deleting the contacts for
295      * @return null
296      * @since Moodle 2.5
297      */
298     public static function delete_contacts($userids, $userid = 0) {
299         global $CFG, $USER;
301         // Check if messaging is enabled.
302         if (empty($CFG->messaging)) {
303             throw new moodle_exception('disabled', 'message');
304         }
306         if (empty($userid)) {
307             $userid = $USER->id;
308         }
310         // Validate context.
311         $context = context_system::instance();
312         self::validate_context($context);
314         $params = array('userids' => $userids, 'userid' => $userid);
315         $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
317         $capability = 'moodle/site:manageallmessaging';
318         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
319             throw new required_capability_exception($context, $capability, 'nopermissions', '');
320         }
322         foreach ($params['userids'] as $id) {
323             \core_message\api::remove_contact($params['userid'], $id);
324         }
326         return null;
327     }
329     /**
330      * Delete contacts return description.
331      *
332      * @return external_description
333      * @since Moodle 2.5
334      */
335     public static function delete_contacts_returns() {
336         return null;
337     }
339     /**
340      * Mute conversations parameters description.
341      *
342      * @return external_function_parameters
343      */
344     public static function mute_conversations_parameters() {
345         return new external_function_parameters(
346             [
347                 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
348                 'conversationids' => new external_multiple_structure(
349                     new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
350                 ),
351             ]
352         );
353     }
355     /**
356      * Mutes conversations.
357      *
358      * @param int $userid The id of the user who is blocking
359      * @param array $conversationids The list of conversations being muted
360      * @return external_description
361      */
362     public static function mute_conversations(int $userid, array $conversationids) {
363         global $CFG, $USER;
365         // Check if messaging is enabled.
366         if (empty($CFG->messaging)) {
367             throw new moodle_exception('disabled', 'message');
368         }
370         // Validate context.
371         $context = context_system::instance();
372         self::validate_context($context);
374         $params = ['userid' => $userid, 'conversationids' => $conversationids];
375         $params = self::validate_parameters(self::mute_conversations_parameters(), $params);
377         $capability = 'moodle/site:manageallmessaging';
378         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
379             throw new required_capability_exception($context, $capability, 'nopermissions', '');
380         }
382         foreach ($params['conversationids'] as $conversationid) {
383             if (!\core_message\api::is_conversation_muted($params['userid'], $conversationid)) {
384                 \core_message\api::mute_conversation($params['userid'], $conversationid);
385             }
386         }
388         return [];
389     }
391     /**
392      * Mute conversations return description.
393      *
394      * @return external_description
395      */
396     public static function mute_conversations_returns() {
397         return new external_warnings();
398     }
400     /**
401      * Unmute conversations parameters description.
402      *
403      * @return external_function_parameters
404      */
405     public static function unmute_conversations_parameters() {
406         return new external_function_parameters(
407             [
408                 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
409                 'conversationids' => new external_multiple_structure(
410                     new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
411                 ),
412             ]
413         );
414     }
416     /**
417      * Unmute conversations.
418      *
419      * @param int $userid The id of the user who is unblocking
420      * @param array $conversationids The list of conversations being muted
421      */
422     public static function unmute_conversations(int $userid, array $conversationids) {
423         global $CFG, $USER;
425         // Check if messaging is enabled.
426         if (empty($CFG->messaging)) {
427             throw new moodle_exception('disabled', 'message');
428         }
430         // Validate context.
431         $context = context_system::instance();
432         self::validate_context($context);
434         $params = ['userid' => $userid, 'conversationids' => $conversationids];
435         $params = self::validate_parameters(self::unmute_conversations_parameters(), $params);
437         $capability = 'moodle/site:manageallmessaging';
438         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
439             throw new required_capability_exception($context, $capability, 'nopermissions', '');
440         }
442         foreach ($params['conversationids'] as $conversationid) {
443             \core_message\api::unmute_conversation($params['userid'], $conversationid);
444         }
446         return [];
447     }
449     /**
450      * Unmute conversations return description.
451      *
452      * @return external_description
453      */
454     public static function unmute_conversations_returns() {
455         return new external_warnings();
456     }
458     /**
459      * Block user parameters description.
460      *
461      * @return external_function_parameters
462      */
463     public static function block_user_parameters() {
464         return new external_function_parameters(
465             [
466                 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
467                 'blockeduserid' => new external_value(PARAM_INT, 'The id of the user being blocked'),
468             ]
469         );
470     }
472     /**
473      * Blocks a user.
474      *
475      * @param int $userid The id of the user who is blocking
476      * @param int $blockeduserid The id of the user being blocked
477      * @return external_description
478      */
479     public static function block_user(int $userid, int $blockeduserid) {
480         global $CFG, $USER;
482         // Check if messaging is enabled.
483         if (empty($CFG->messaging)) {
484             throw new moodle_exception('disabled', 'message');
485         }
487         // Validate context.
488         $context = context_system::instance();
489         self::validate_context($context);
491         $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
492         $params = self::validate_parameters(self::block_user_parameters(), $params);
494         $capability = 'moodle/site:manageallmessaging';
495         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
496             throw new required_capability_exception($context, $capability, 'nopermissions', '');
497         }
499         // If the blocking is going to be useless then don't do it.
500         if (\core_message\api::can_send_message($userid, $blockeduserid, true)) {
501             return [];
502         }
504         if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
505             \core_message\api::block_user($params['userid'], $params['blockeduserid']);
506         }
508         return [];
509     }
511     /**
512      * Block user return description.
513      *
514      * @return external_description
515      */
516     public static function block_user_returns() {
517         return new external_warnings();
518     }
520     /**
521      * Unblock user parameters description.
522      *
523      * @return external_function_parameters
524      */
525     public static function unblock_user_parameters() {
526         return new external_function_parameters(
527             [
528                 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
529                 'unblockeduserid' => new external_value(PARAM_INT, 'The id of the user being unblocked'),
530             ]
531         );
532     }
534     /**
535      * Unblock user.
536      *
537      * @param int $userid The id of the user who is unblocking
538      * @param int $unblockeduserid The id of the user being unblocked
539      */
540     public static function unblock_user(int $userid, int $unblockeduserid) {
541         global $CFG, $USER;
543         // Check if messaging is enabled.
544         if (empty($CFG->messaging)) {
545             throw new moodle_exception('disabled', 'message');
546         }
548         // Validate context.
549         $context = context_system::instance();
550         self::validate_context($context);
552         $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
553         $params = self::validate_parameters(self::unblock_user_parameters(), $params);
555         $capability = 'moodle/site:manageallmessaging';
556         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
557             throw new required_capability_exception($context, $capability, 'nopermissions', '');
558         }
560         \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
562         return [];
563     }
565     /**
566      * Unblock user return description.
567      *
568      * @return external_description
569      */
570     public static function unblock_user_returns() {
571         return new external_warnings();
572     }
574     /**
575      * Returns contact requests parameters description.
576      *
577      * @return external_function_parameters
578      */
579     public static function get_contact_requests_parameters() {
580         return new external_function_parameters(
581             [
582                 'userid' => new external_value(PARAM_INT, 'The id of the user we want the requests for'),
583                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
584                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
585             ]
586         );
587     }
589     /**
590      * Handles returning the contact requests for a user.
591      *
592      * This also includes the user data necessary to display information
593      * about the user.
594      *
595      * It will not include blocked users.
596      *
597      * @param int $userid The id of the user we want to get the contact requests for
598      * @param int $limitfrom
599      * @param int $limitnum
600      */
601     public static function get_contact_requests(int $userid, int $limitfrom = 0, int $limitnum = 0) {
602         global $CFG, $USER;
604         // Check if messaging is enabled.
605         if (empty($CFG->messaging)) {
606             throw new moodle_exception('disabled', 'message');
607         }
609         // Validate context.
610         $context = context_system::instance();
611         self::validate_context($context);
613         $params = [
614             'userid' => $userid,
615             'limitfrom' => $limitfrom,
616             'limitnum' => $limitnum
617         ];
618         $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
620         $capability = 'moodle/site:manageallmessaging';
621         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
622             throw new required_capability_exception($context, $capability, 'nopermissions', '');
623         }
625         return \core_message\api::get_contact_requests($params['userid'], $params['limitfrom'], $params['limitnum']);
626     }
628     /**
629      * Returns the contact requests return description.
630      *
631      * @return external_description
632      */
633     public static function get_contact_requests_returns() {
634         return new external_multiple_structure(
635             self::get_conversation_member_structure()
636         );
637     }
639     /**
640      * Returns the number of contact requests the user has received parameters description.
641      *
642      * @return external_function_parameters
643      */
644     public static function get_received_contact_requests_count_parameters() {
645         return new external_function_parameters(
646             array(
647                 'userid' => new external_value(PARAM_INT, 'The id of the user we want to return the number of ' .
648                     'received contact requests for', VALUE_REQUIRED),
649             )
650         );
651     }
653     /**
654      * Returns the number of contact requests the user has received.
655      *
656      * @param int $userid The ID of the user we want to return the number of received contact requests for
657      * @return external_value
658      */
659     public static function get_received_contact_requests_count(int $userid) {
660         global $CFG, $USER;
662         // Check if messaging is enabled.
663         if (empty($CFG->messaging)) {
664             throw new moodle_exception('disabled', 'message');
665         }
667         // Validate context.
668         $context = context_system::instance();
669         self::validate_context($context);
671         $params = [
672             'userid' => $userid,
673         ];
674         $params = self::validate_parameters(self::get_received_contact_requests_count_parameters(), $params);
676         $capability = 'moodle/site:manageallmessaging';
677         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
678             throw new required_capability_exception($context, $capability, 'nopermissions', '');
679         }
681         return \core_message\api::get_received_contact_requests_count($params['userid']);
682     }
684     /**
685      * Returns the number of contact requests the user has received return description.
686      *
687      * @return external_value
688      */
689     public static function get_received_contact_requests_count_returns() {
690         return new external_value(PARAM_INT, 'The number of received contact requests');
691     }
693     /**
694      * Returns get conversation members parameters description.
695      *
696      * @return external_function_parameters
697      */
698     public static function get_conversation_members_parameters() {
699         return new external_function_parameters(
700             [
701                 'userid' => new external_value(PARAM_INT, 'The id of the user we are performing this action on behalf of'),
702                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation'),
703                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Do we want to include contact requests?',
704                     VALUE_DEFAULT, false),
705                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Do we want to include privacy info?',
706                     VALUE_DEFAULT, false),
707                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
708                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
709             ]
710         );
711     }
713     /**
714      * Returns a list of conversation members.
715      *
716      * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info.
717      * @param int $conversationid The id of the conversation
718      * @param bool $includecontactrequests Do we want to include contact requests with this data?
719      * @param bool $includeprivacyinfo Do we want to include privacy info?
720      * @param int $limitfrom
721      * @param int $limitnum
722      * @return array
723      */
724     public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false,
725                                                     bool $includeprivacyinfo = false, int $limitfrom = 0, int $limitnum = 0) {
726         global $CFG, $USER;
728         // Check if messaging is enabled.
729         if (empty($CFG->messaging)) {
730             throw new moodle_exception('disabled', 'message');
731         }
733         // Validate context.
734         $context = context_system::instance();
735         self::validate_context($context);
737         $params = [
738             'userid' => $userid,
739             'conversationid' => $conversationid,
740             'includecontactrequests' => $includecontactrequests,
741             'includeprivacyinfo' => $includeprivacyinfo,
742             'limitfrom' => $limitfrom,
743             'limitnum' => $limitnum
744         ];
745         $params = self::validate_parameters(self::get_conversation_members_parameters(), $params);
747         $capability = 'moodle/site:manageallmessaging';
748         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
749             throw new required_capability_exception($context, $capability, 'nopermissions', '');
750         }
752         // The user needs to be a part of the conversation before querying who the members are.
753         if (!\core_message\api::is_user_in_conversation($params['userid'], $params['conversationid'])) {
754             throw new moodle_exception('You are not a member of this conversation.');
755         }
757         return \core_message\api::get_conversation_members($params['userid'], $params['conversationid'], $params['includecontactrequests'],
758             $params['includeprivacyinfo'], $params['limitfrom'], $params['limitnum']);
759     }
761     /**
762      * Returns the get conversation members return description.
763      *
764      * @return external_description
765      */
766     public static function get_conversation_members_returns() {
767         return new external_multiple_structure(
768             self::get_conversation_member_structure()
769         );
770     }
772     /**
773      * Creates a contact request parameters description.
774      *
775      * @return external_function_parameters
776      */
777     public static function create_contact_request_parameters() {
778         return new external_function_parameters(
779             [
780                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
781                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
782             ]
783         );
784     }
786     /**
787      * Creates a contact request.
788      *
789      * @param int $userid The id of the user who is creating the contact request
790      * @param int $requesteduserid The id of the user being requested
791      */
792     public static function create_contact_request(int $userid, int $requesteduserid) {
793         global $CFG, $USER;
795         // Check if messaging is enabled.
796         if (empty($CFG->messaging)) {
797             throw new moodle_exception('disabled', 'message');
798         }
800         // Validate context.
801         $context = context_system::instance();
802         self::validate_context($context);
804         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
805         $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
807         $capability = 'moodle/site:manageallmessaging';
808         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
809             throw new required_capability_exception($context, $capability, 'nopermissions', '');
810         }
812         $result = [
813             'warnings' => []
814         ];
816         if (!\core_message\api::can_create_contact($params['userid'], $params['requesteduserid'])) {
817             $result['warnings'][] = [
818                 'item' => 'user',
819                 'itemid' => $params['requesteduserid'],
820                 'warningcode' => 'cannotcreatecontactrequest',
821                 'message' => 'You are unable to create a contact request for this user'
822             ];
823         } else {
824             if ($requests = \core_message\api::get_contact_requests_between_users($params['userid'], $params['requesteduserid'])) {
825                 // There should only ever be one but just in case there are multiple then we can return the first.
826                 $result['request'] = array_shift($requests);
827             } else {
828                 $result['request'] = \core_message\api::create_contact_request($params['userid'], $params['requesteduserid']);
829             }
830         }
832         return $result;
833     }
835     /**
836      * Creates a contact request return description.
837      *
838      * @return external_description
839      */
840     public static function create_contact_request_returns() {
841         return new external_single_structure(
842             array(
843                 'request' => new external_single_structure(
844                     array(
845                         'id' => new external_value(PARAM_INT, 'Message id'),
846                         'userid' => new external_value(PARAM_INT, 'User from id'),
847                         'requesteduserid' => new external_value(PARAM_INT, 'User to id'),
848                         'timecreated' => new external_value(PARAM_INT, 'Time created'),
849                     ),
850                     'request record',
851                     VALUE_OPTIONAL
852                 ),
853                 'warnings' => new external_warnings()
854             )
855         );
856     }
858     /**
859      * Confirm a contact request parameters description.
860      *
861      * @return external_function_parameters
862      */
863     public static function confirm_contact_request_parameters() {
864         return new external_function_parameters(
865             [
866                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
867                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
868             ]
869         );
870     }
872     /**
873      * Confirm a contact request.
874      *
875      * @param int $userid The id of the user who is creating the contact request
876      * @param int $requesteduserid The id of the user being requested
877      */
878     public static function confirm_contact_request(int $userid, int $requesteduserid) {
879         global $CFG, $USER;
881         // Check if messaging is enabled.
882         if (empty($CFG->messaging)) {
883             throw new moodle_exception('disabled', 'message');
884         }
886         // Validate context.
887         $context = context_system::instance();
888         self::validate_context($context);
890         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
891         $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
893         $capability = 'moodle/site:manageallmessaging';
894         if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
895             throw new required_capability_exception($context, $capability, 'nopermissions', '');
896         }
898         \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
900         return [];
901     }
903     /**
904      * Confirm a contact request return description.
905      *
906      * @return external_description
907      */
908     public static function confirm_contact_request_returns() {
909         return new external_warnings();
910     }
912     /**
913      * Declines a contact request parameters description.
914      *
915      * @return external_function_parameters
916      */
917     public static function decline_contact_request_parameters() {
918         return new external_function_parameters(
919             [
920                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
921                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
922             ]
923         );
924     }
926     /**
927      * Declines a contact request.
928      *
929      * @param int $userid The id of the user who is creating the contact request
930      * @param int $requesteduserid The id of the user being requested
931      */
932     public static function decline_contact_request(int $userid, int $requesteduserid) {
933         global $CFG, $USER;
935         // Check if messaging is enabled.
936         if (empty($CFG->messaging)) {
937             throw new moodle_exception('disabled', 'message');
938         }
940         // Validate context.
941         $context = context_system::instance();
942         self::validate_context($context);
944         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
945         $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
947         $capability = 'moodle/site:manageallmessaging';
948         if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
949             throw new required_capability_exception($context, $capability, 'nopermissions', '');
950         }
952         \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
954         return [];
955     }
957     /**
958      * Declines a contact request return description.
959      *
960      * @return external_description
961      */
962     public static function decline_contact_request_returns() {
963         return new external_warnings();
964     }
966     /**
967      * Return the structure of a message area contact.
968      *
969      * @return external_single_structure
970      * @since Moodle 3.2
971      */
972     private static function get_messagearea_contact_structure() {
973         return new external_single_structure(
974             array(
975                 'userid' => new external_value(PARAM_INT, 'The user\'s id'),
976                 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
977                 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
978                 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
979                 'ismessaging' => new external_value(PARAM_BOOL, 'If we are messaging the user'),
980                 'sentfromcurrentuser' => new external_value(PARAM_BOOL, 'Was the last message sent from the current user?'),
981                 'lastmessage' => new external_value(PARAM_NOTAGS, 'The user\'s last message'),
982                 'lastmessagedate' => new external_value(PARAM_INT, 'Timestamp for last message', VALUE_DEFAULT, null),
983                 'messageid' => new external_value(PARAM_INT, 'The unique search message id', VALUE_DEFAULT, null),
984                 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
985                 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
986                 'isread' => new external_value(PARAM_BOOL, 'If the user has read the message'),
987                 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
988                 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
989                     VALUE_DEFAULT, null),
990                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation', VALUE_DEFAULT, null),
991             )
992         );
993     }
995     /**
996      * Return the structure of a conversation.
997      *
998      * @return external_single_structure
999      * @since Moodle 3.6
1000      */
1001     private static function get_conversation_structure() {
1002         return new external_single_structure(
1003             array(
1004                 'id' => new external_value(PARAM_INT, 'The conversation id'),
1005                 'name' => new external_value(PARAM_RAW, 'The conversation name, if set', VALUE_DEFAULT, null),
1006                 'subname' => new external_value(PARAM_RAW, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null),
1007                 'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null),
1008                 'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group,3=self)'),
1009                 'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'),
1010                 'ismuted' => new external_value(PARAM_BOOL, 'If the user muted this conversation'),
1011                 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this conversation as a favourite'),
1012                 'isread' => new external_value(PARAM_BOOL, 'If the user has read all messages in the conversation'),
1013                 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1014                     VALUE_DEFAULT, null),
1015                 'members' => new external_multiple_structure(
1016                     self::get_conversation_member_structure()
1017                 ),
1018                 'messages' => new external_multiple_structure(
1019                     self::get_conversation_message_structure()
1020                 ),
1021                 'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
1022                     'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
1023             )
1024         );
1025     }
1027     /**
1028      * Return the structure of a conversation member.
1029      *
1030      * @return external_single_structure
1031      * @since Moodle 3.6
1032      */
1033     private static function get_conversation_member_structure() {
1034         $result = [
1035             'id' => new external_value(PARAM_INT, 'The user id'),
1036             'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1037             'profileurl' => new external_value(PARAM_URL, 'The link to the user\'s profile page'),
1038             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1039             'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1040             'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1041             'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1042             'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1043             'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'),
1044             'isdeleted' => new external_value(PARAM_BOOL, 'Is the user deleted?'),
1045             'canmessageevenifblocked' => new external_value(PARAM_BOOL,
1046                 'If the user can still message even if they get blocked'),
1047             'canmessage' => new external_value(PARAM_BOOL, 'If the user can be messaged'),
1048             'requirescontact' => new external_value(PARAM_BOOL, 'If the user requires to be contacts'),
1049         ];
1051         $result['contactrequests'] = new external_multiple_structure(
1052             new external_single_structure(
1053                 [
1054                     'id' => new external_value(PARAM_INT, 'The id of the contact request'),
1055                     'userid' => new external_value(PARAM_INT, 'The id of the user who created the contact request'),
1056                     'requesteduserid' => new external_value(PARAM_INT, 'The id of the user confirming the request'),
1057                     'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the contact request'),
1058                 ]
1059             ), 'The contact requests', VALUE_OPTIONAL
1060         );
1062         $result['conversations'] = new external_multiple_structure(new external_single_structure(
1063             array(
1064                 'id' => new external_value(PARAM_INT, 'Conversations id'),
1065                 'type' => new external_value(PARAM_INT, 'Conversation type: private or public'),
1066                 'name' => new external_value(PARAM_RAW, 'Multilang compatible conversation name'. VALUE_OPTIONAL),
1067                 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'),
1068             ), 'information about conversation', VALUE_OPTIONAL),
1069             'Conversations between users', VALUE_OPTIONAL
1070         );
1072         return new external_single_structure(
1073             $result
1074         );
1075     }
1077     /**
1078      * Return the structure of a message area message.
1079      *
1080      * @return external_single_structure
1081      * @since Moodle 3.6
1082      */
1083     private static function get_conversation_message_structure() {
1084         return new external_single_structure(
1085             array(
1086                 'id' => new external_value(PARAM_INT, 'The id of the message'),
1087                 'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1088                 'text' => new external_value(PARAM_RAW, 'The text of the message'),
1089                 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1090             )
1091         );
1092     }
1094     /**
1095      * Get messagearea message search users parameters.
1096      *
1097      * @return external_function_parameters
1098      * @since 3.6
1099      */
1100     public static function message_search_users_parameters() {
1101         return new external_function_parameters(
1102             array(
1103                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1104                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1105                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1106                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1107             )
1108         );
1109     }
1111     /**
1112      * Get search users results.
1113      *
1114      * @param int $userid The id of the user who is performing the search
1115      * @param string $search The string being searched
1116      * @param int $limitfrom
1117      * @param int $limitnum
1118      * @return array
1119      * @throws moodle_exception
1120      * @since 3.6
1121      */
1122     public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) {
1123         global $USER;
1125         $systemcontext = context_system::instance();
1127         $params = array(
1128             'userid' => $userid,
1129             'search' => $search,
1130             'limitfrom' => $limitfrom,
1131             'limitnum' => $limitnum
1132         );
1133         $params = self::validate_parameters(self::message_search_users_parameters(), $params);
1134         self::validate_context($systemcontext);
1136         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1137             throw new moodle_exception('You do not have permission to perform this action.');
1138         }
1140         list($contacts, $noncontacts) = \core_message\api::message_search_users(
1141             $params['userid'],
1142             $params['search'],
1143             $params['limitfrom'],
1144             $params['limitnum']);
1146         return array('contacts' => $contacts, 'noncontacts' => $noncontacts);
1147     }
1149     /**
1150      * Get messagearea message search users returns.
1151      *
1152      * @return external_single_structure
1153      * @since 3.2
1154      */
1155     public static function message_search_users_returns() {
1156         return new external_single_structure(
1157             array(
1158                 'contacts' => new external_multiple_structure(
1159                     self::get_conversation_member_structure()
1160                 ),
1161                 'noncontacts' => new external_multiple_structure(
1162                     self::get_conversation_member_structure()
1163                 )
1164             )
1165         );
1166     }
1168     /**
1169      * Get messagearea search messages parameters.
1170      *
1171      * @return external_function_parameters
1172      * @since 3.2
1173      */
1174     public static function data_for_messagearea_search_messages_parameters() {
1175         return new external_function_parameters(
1176             array(
1177                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1178                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1179                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1180                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1181             )
1182         );
1183     }
1185     /**
1186      * Get messagearea search messages results.
1187      *
1188      * @param int $userid The id of the user who is performing the search
1189      * @param string $search The string being searched
1190      * @param int $limitfrom
1191      * @param int $limitnum
1192      * @return stdClass
1193      * @throws moodle_exception
1194      * @since 3.2
1195      */
1196     public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
1197         global $CFG, $USER;
1199         // Check if messaging is enabled.
1200         if (empty($CFG->messaging)) {
1201             throw new moodle_exception('disabled', 'message');
1202         }
1204         $systemcontext = context_system::instance();
1206         $params = array(
1207             'userid' => $userid,
1208             'search' => $search,
1209             'limitfrom' => $limitfrom,
1210             'limitnum' => $limitnum
1212         );
1213         $params = self::validate_parameters(self::data_for_messagearea_search_messages_parameters(), $params);
1214         self::validate_context($systemcontext);
1216         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1217             throw new moodle_exception('You do not have permission to perform this action.');
1218         }
1220         $messages = \core_message\api::search_messages(
1221             $params['userid'],
1222             $params['search'],
1223             $params['limitfrom'],
1224             $params['limitnum']
1225         );
1227         $data = new \stdClass();
1228         $data->contacts = [];
1229         foreach ($messages as $message) {
1230             $contact = new \stdClass();
1231             $contact->userid = $message->userid;
1232             $contact->fullname = $message->fullname;
1233             $contact->profileimageurl = $message->profileimageurl;
1234             $contact->profileimageurlsmall = $message->profileimageurlsmall;
1235             $contact->messageid = $message->messageid;
1236             $contact->ismessaging = $message->ismessaging;
1237             $contact->sentfromcurrentuser = false;
1238             if ($message->lastmessage) {
1239                 if ($message->userid !== $message->useridfrom) {
1240                     $contact->sentfromcurrentuser = true;
1241                 }
1242                 $contact->lastmessage = shorten_text($message->lastmessage, 60);
1243             } else {
1244                 $contact->lastmessage = null;
1245             }
1246             $contact->lastmessagedate = $message->lastmessagedate;
1247             $contact->showonlinestatus = is_null($message->isonline) ? false : true;
1248             $contact->isonline = $message->isonline;
1249             $contact->isblocked = $message->isblocked;
1250             $contact->isread = $message->isread;
1251             $contact->unreadcount = $message->unreadcount;
1252             $contact->conversationid = $message->conversationid;
1254             $data->contacts[] = $contact;
1255         }
1257         return $data;
1258     }
1260     /**
1261      * Get messagearea search messages returns.
1262      *
1263      * @return external_single_structure
1264      * @since 3.2
1265      */
1266     public static function data_for_messagearea_search_messages_returns() {
1267         return new external_single_structure(
1268             array(
1269                 'contacts' => new external_multiple_structure(
1270                     self::get_messagearea_contact_structure()
1271                 )
1272             )
1273         );
1274     }
1276     /**
1277      * Get conversations parameters.
1278      *
1279      * @return external_function_parameters
1280      * @since 3.6
1281      */
1282     public static function get_conversations_parameters() {
1283         return new external_function_parameters(
1284             array(
1285                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1286                 'limitfrom' => new external_value(PARAM_INT, 'The offset to start at', VALUE_DEFAULT, 0),
1287                 'limitnum' => new external_value(PARAM_INT, 'Limit number of conversations to this', VALUE_DEFAULT, 0),
1288                 'type' => new external_value(PARAM_INT, 'Filter by type', VALUE_DEFAULT, null),
1289                 'favourites' => new external_value(PARAM_BOOL, 'Whether to restrict the results to contain NO favourite
1290                 conversations (false), ONLY favourite conversation (true), or ignore any restriction altogether (null)',
1291                     VALUE_DEFAULT, null),
1292                 'mergeself' => new external_value(PARAM_BOOL, 'Whether to include self-conversations (true) or ONLY private
1293                     conversations (false) when private conversations are requested.',
1294                     VALUE_DEFAULT, false),
1295             )
1296         );
1297     }
1299     /**
1300      * Get the list of conversations for the user.
1301      *
1302      * @param int $userid The id of the user who is performing the search
1303      * @param int $limitfrom
1304      * @param int $limitnum
1305      * @param int|null $type
1306      * @param bool|null $favourites
1307      * @param bool $mergeself whether to include self-conversations (true) or ONLY private conversations (false)
1308      *             when private conversations are requested.
1309      * @return stdClass
1310      * @throws \moodle_exception if the messaging feature is disabled on the site.
1311      * @since 3.2
1312      */
1313     public static function get_conversations($userid, $limitfrom = 0, $limitnum = 0, int $type = null, bool $favourites = null,
1314             bool $mergeself = false) {
1315         global $CFG, $USER;
1317         // All the standard BL checks.
1318         if (empty($CFG->messaging)) {
1319             throw new moodle_exception('disabled', 'message');
1320         }
1322         $params = array(
1323             'userid' => $userid,
1324             'limitfrom' => $limitfrom,
1325             'limitnum' => $limitnum,
1326             'type' => $type,
1327             'favourites' => $favourites,
1328             'mergeself' => $mergeself
1329         );
1330         $params = self::validate_parameters(self::get_conversations_parameters(), $params);
1332         $systemcontext = context_system::instance();
1333         self::validate_context($systemcontext);
1335         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1336             throw new moodle_exception('You do not have permission to perform this action.');
1337         }
1339         $conversations = \core_message\api::get_conversations(
1340             $params['userid'],
1341             $params['limitfrom'],
1342             $params['limitnum'],
1343             $params['type'],
1344             $params['favourites'],
1345             $params['mergeself']
1346         );
1348         return (object) ['conversations' => $conversations];
1349     }
1351     /**
1352      * Get conversations returns.
1353      *
1354      * @return external_single_structure
1355      * @since 3.6
1356      */
1357     public static function get_conversations_returns() {
1358         return new external_single_structure(
1359             [
1360                 'conversations' => new external_multiple_structure(
1361                     self::get_conversation_structure(true)
1362                 )
1363             ]
1364         );
1365     }
1367     /**
1368      * Get conversation parameters.
1369      *
1370      * @return external_function_parameters
1371      */
1372     public static function get_conversation_parameters() {
1373         return new external_function_parameters(
1374             array(
1375                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1376                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation to fetch'),
1377                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1378                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1379                 'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1380                 'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1381                 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1382                 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1383                 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1384             )
1385         );
1386     }
1388     /**
1389      * Get a single conversation.
1390      *
1391      * @param int $userid The user id to get the conversation for
1392      * @param int $conversationid The id of the conversation to fetch
1393      * @param bool $includecontactrequests Should contact requests be included between members
1394      * @param bool $includeprivacyinfo Should privacy info be included between members
1395      * @param int $memberlimit Limit number of members to load
1396      * @param int $memberoffset Offset members by this amount
1397      * @param int $messagelimit Limit number of messages to load
1398      * @param int $messageoffset Offset the messages
1399      * @param bool $newestmessagesfirst Order messages by newest first
1400      * @return stdClass
1401      * @throws \moodle_exception if the messaging feature is disabled on the site.
1402      */
1403     public static function get_conversation(
1404         int $userid,
1405         int $conversationid,
1406         bool $includecontactrequests = false,
1407         bool $includeprivacyinfo = false,
1408         int $memberlimit = 0,
1409         int $memberoffset = 0,
1410         int $messagelimit = 0,
1411         int $messageoffset = 0,
1412         bool $newestmessagesfirst = true
1413     ) {
1414         global $CFG, $DB, $USER;
1416         // All the standard BL checks.
1417         if (empty($CFG->messaging)) {
1418             throw new moodle_exception('disabled', 'message');
1419         }
1421         $params = [
1422             'userid' => $userid,
1423             'conversationid' => $conversationid,
1424             'includecontactrequests' => $includecontactrequests,
1425             'includeprivacyinfo' => $includeprivacyinfo,
1426             'memberlimit' => $memberlimit,
1427             'memberoffset' => $memberoffset,
1428             'messagelimit' => $messagelimit,
1429             'messageoffset' => $messageoffset,
1430             'newestmessagesfirst' => $newestmessagesfirst
1431         ];
1432         self::validate_parameters(self::get_conversation_parameters(), $params);
1434         $systemcontext = context_system::instance();
1435         self::validate_context($systemcontext);
1437         $conversation = \core_message\api::get_conversation(
1438             $params['userid'],
1439             $params['conversationid'],
1440             $params['includecontactrequests'],
1441             $params['includeprivacyinfo'],
1442             $params['memberlimit'],
1443             $params['memberoffset'],
1444             $params['messagelimit'],
1445             $params['messageoffset'],
1446             $params['newestmessagesfirst']
1447         );
1449         if ($conversation) {
1450             return $conversation;
1451         } else {
1452             // We have to throw an exception here because the external functions annoyingly
1453             // don't accept null to be returned for a single structure.
1454             throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1455         }
1456     }
1458     /**
1459      * Get conversation returns.
1460      *
1461      * @return external_single_structure
1462      */
1463     public static function get_conversation_returns() {
1464         return self::get_conversation_structure();
1465     }
1467     /**
1468      * Get conversation parameters.
1469      *
1470      * @return external_function_parameters
1471      */
1472     public static function get_conversation_between_users_parameters() {
1473         return new external_function_parameters(
1474             array(
1475                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1476                 'otheruserid' => new external_value(PARAM_INT, 'The other user id'),
1477                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1478                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1479                 'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1480                 'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1481                 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1482                 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1483                 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1484             )
1485         );
1486     }
1488     /**
1489      * Get a single conversation between users.
1490      *
1491      * @param int $userid The user id to get the conversation for
1492      * @param int $otheruserid The other user id
1493      * @param bool $includecontactrequests Should contact requests be included between members
1494      * @param bool $includeprivacyinfo Should privacy info be included between members
1495      * @param int $memberlimit Limit number of members to load
1496      * @param int $memberoffset Offset members by this amount
1497      * @param int $messagelimit Limit number of messages to load
1498      * @param int $messageoffset Offset the messages
1499      * @param bool $newestmessagesfirst Order messages by newest first
1500      * @return stdClass
1501      * @throws \moodle_exception if the messaging feature is disabled on the site.
1502      */
1503     public static function get_conversation_between_users(
1504         int $userid,
1505         int $otheruserid,
1506         bool $includecontactrequests = false,
1507         bool $includeprivacyinfo = false,
1508         int $memberlimit = 0,
1509         int $memberoffset = 0,
1510         int $messagelimit = 0,
1511         int $messageoffset = 0,
1512         bool $newestmessagesfirst = true
1513     ) {
1514         global $CFG, $DB, $USER;
1516         // All the standard BL checks.
1517         if (empty($CFG->messaging)) {
1518             throw new moodle_exception('disabled', 'message');
1519         }
1521         $params = [
1522             'userid' => $userid,
1523             'otheruserid' => $otheruserid,
1524             'includecontactrequests' => $includecontactrequests,
1525             'includeprivacyinfo' => $includeprivacyinfo,
1526             'memberlimit' => $memberlimit,
1527             'memberoffset' => $memberoffset,
1528             'messagelimit' => $messagelimit,
1529             'messageoffset' => $messageoffset,
1530             'newestmessagesfirst' => $newestmessagesfirst
1531         ];
1532         self::validate_parameters(self::get_conversation_between_users_parameters(), $params);
1534         $systemcontext = context_system::instance();
1535         self::validate_context($systemcontext);
1537         $conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']]);
1538         $conversation = null;
1540         if ($conversationid) {
1541             $conversation = \core_message\api::get_conversation(
1542                 $params['userid'],
1543                 $conversationid,
1544                 $params['includecontactrequests'],
1545                 $params['includeprivacyinfo'],
1546                 $params['memberlimit'],
1547                 $params['memberoffset'],
1548                 $params['messagelimit'],
1549                 $params['messageoffset'],
1550                 $params['newestmessagesfirst']
1551             );
1552         }
1554         if ($conversation) {
1555             return $conversation;
1556         } else {
1557             // We have to throw an exception here because the external functions annoyingly
1558             // don't accept null to be returned for a single structure.
1559             throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1560         }
1561     }
1563     /**
1564      * Get conversation returns.
1565      *
1566      * @return external_single_structure
1567      */
1568     public static function get_conversation_between_users_returns() {
1569         return self::get_conversation_structure(true);
1570     }
1572     /**
1573      * Get self-conversation parameters.
1574      *
1575      * @return external_function_parameters
1576      */
1577     public static function get_self_conversation_parameters() {
1578         return new external_function_parameters(
1579             array(
1580                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing self-conversations for'),
1581                 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1582                 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1583                 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1584             )
1585         );
1586     }
1588     /**
1589      * Get a single self-conversation.
1590      *
1591      * @param int $userid The user id to get the self-conversation for
1592      * @param int $messagelimit Limit number of messages to load
1593      * @param int $messageoffset Offset the messages
1594      * @param bool $newestmessagesfirst Order messages by newest first
1595      * @return stdClass
1596      * @throws \moodle_exception if the messaging feature is disabled on the site.
1597      * @since Moodle 3.7
1598      */
1599     public static function get_self_conversation(
1600         int $userid,
1601         int $messagelimit = 0,
1602         int $messageoffset = 0,
1603         bool $newestmessagesfirst = true
1604     ) {
1605         global $CFG;
1607         // All the standard BL checks.
1608         if (empty($CFG->messaging)) {
1609             throw new moodle_exception('disabled', 'message');
1610         }
1612         $params = [
1613             'userid' => $userid,
1614             'messagelimit' => $messagelimit,
1615             'messageoffset' => $messageoffset,
1616             'newestmessagesfirst' => $newestmessagesfirst
1617         ];
1618         self::validate_parameters(self::get_self_conversation_parameters(), $params);
1620         $systemcontext = context_system::instance();
1621         self::validate_context($systemcontext);
1623         $conversation = \core_message\api::get_self_conversation($params['userid']);
1625         if ($conversation) {
1626             $conversation = \core_message\api::get_conversation(
1627                 $params['userid'],
1628                 $conversation->id,
1629                 false,
1630                 false,
1631                 0,
1632                 0,
1633                 $params['messagelimit'],
1634                 $params['messageoffset'],
1635                 $params['newestmessagesfirst']
1636             );
1637         }
1639         if ($conversation) {
1640             return $conversation;
1641         } else {
1642             // We have to throw an exception here because the external functions annoyingly
1643             // don't accept null to be returned for a single structure.
1644             throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1645         }
1646     }
1648     /**
1649      * Get conversation returns.
1650      *
1651      * @return external_single_structure
1652      */
1653     public static function get_self_conversation_returns() {
1654         return self::get_conversation_structure();
1655     }
1657     /**
1658      * The conversation messages parameters.
1659      *
1660      * @return external_function_parameters
1661      * @since 3.6
1662      */
1663     public static function get_conversation_messages_parameters() {
1664         return new external_function_parameters(
1665             array(
1666                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1667                 'convid' => new external_value(PARAM_INT, 'The conversation id'),
1668                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1669                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1670                 'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
1671                 'timefrom' => new external_value(PARAM_INT,
1672                     'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
1673             )
1674         );
1675     }
1677     /**
1678      * Get conversation messages.
1679      *
1680      * @param  int $currentuserid The current user's id.
1681      * @param  int $convid The conversation id.
1682      * @param  int $limitfrom Return a subset of records, starting at this point (optional).
1683      * @param  int $limitnum Return a subset comprising this many records in total (optional, required if $limitfrom is set).
1684      * @param  bool $newest True for getting first newest messages, false otherwise.
1685      * @param  int  $timefrom The time from the conversation messages to get.
1686      * @return array The messages and members who have sent some of these messages.
1687      * @throws moodle_exception
1688      * @since 3.6
1689      */
1690     public static function get_conversation_messages(int $currentuserid, int $convid, int $limitfrom = 0, int $limitnum = 0,
1691                                                          bool $newest = false, int $timefrom = 0) {
1692         global $CFG, $USER;
1694         // Check if messaging is enabled.
1695         if (empty($CFG->messaging)) {
1696             throw new moodle_exception('disabled', 'message');
1697         }
1699         $systemcontext = context_system::instance();
1701         $params = array(
1702             'currentuserid' => $currentuserid,
1703             'convid' => $convid,
1704             'limitfrom' => $limitfrom,
1705             'limitnum' => $limitnum,
1706             'newest' => $newest,
1707             'timefrom' => $timefrom,
1708         );
1709         $params = self::validate_parameters(self::get_conversation_messages_parameters(), $params);
1710         self::validate_context($systemcontext);
1712         if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1713             throw new moodle_exception('You do not have permission to perform this action.');
1714         }
1716         // Check that the user belongs to the conversation.
1717         if (!\core_message\api::is_user_in_conversation($params['currentuserid'], $params['convid'])) {
1718             throw new moodle_exception('User is not part of conversation.');
1719         }
1721         $sort = $newest ? 'timecreated DESC' : 'timecreated ASC';
1723         // We need to enforce a one second delay on messages to avoid race conditions of current
1724         // messages still being sent.
1725         //
1726         // There is a chance that we could request messages before the current time's
1727         // second has elapsed and while other messages are being sent in that same second. In which
1728         // case those messages will be lost.
1729         //
1730         // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
1731         $timeto = empty($params['timefrom']) ? 0 : time() - 1;
1733         // No requesting messages from the current time, as stated above.
1734         if ($params['timefrom'] == time()) {
1735             $messages = [];
1736         } else {
1737             $messages = \core_message\api::get_conversation_messages(
1738                 $params['currentuserid'],
1739                 $params['convid'],
1740                 $params['limitfrom'],
1741                 $params['limitnum'],
1742                 $sort,
1743                 $params['timefrom'],
1744                 $timeto);
1745         }
1747         return $messages;
1748     }
1750     /**
1751      * The messagearea messages return structure.
1752      *
1753      * @return external_single_structure
1754      * @since 3.6
1755      */
1756     public static function get_conversation_messages_returns() {
1757         return new external_single_structure(
1758             array(
1759                 'id' => new external_value(PARAM_INT, 'The conversation id'),
1760                 'members' => new external_multiple_structure(
1761                     self::get_conversation_member_structure()
1762                 ),
1763                 'messages' => new external_multiple_structure(
1764                     self::get_conversation_message_structure()
1765                 ),
1766             )
1767         );
1768     }
1770     /**
1771      * The user contacts return parameters.
1772      *
1773      * @return external_function_parameters
1774      */
1775     public static function get_user_contacts_parameters() {
1776         return new external_function_parameters(
1777             array(
1778                 'userid' => new external_value(PARAM_INT, 'The id of the user who we retrieving the contacts for'),
1779                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1780                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1781             )
1782         );
1783     }
1785     /**
1786      * Get user contacts.
1787      *
1788      * @param int $userid The id of the user who we are viewing conversations for
1789      * @param int $limitfrom
1790      * @param int $limitnum
1791      * @return array
1792      * @throws moodle_exception
1793      */
1794     public static function get_user_contacts(int $userid, int $limitfrom = 0, int $limitnum = 0) {
1795         global $CFG, $USER;
1797         // Check if messaging is enabled.
1798         if (empty($CFG->messaging)) {
1799             throw new moodle_exception('disabled', 'message');
1800         }
1802         $systemcontext = context_system::instance();
1804         $params = array(
1805             'userid' => $userid,
1806             'limitfrom' => $limitfrom,
1807             'limitnum' => $limitnum
1808         );
1809         $params = self::validate_parameters(self::get_user_contacts_parameters(), $params);
1810         self::validate_context($systemcontext);
1812         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1813             throw new moodle_exception('You do not have permission to perform this action.');
1814         }
1816         return \core_message\api::get_user_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
1817     }
1819     /**
1820      * The user contacts return structure.
1821      *
1822      * @return external_multiple_structure
1823      */
1824     public static function get_user_contacts_returns() {
1825         return new external_multiple_structure(
1826             self::get_conversation_member_structure()
1827         );
1828     }
1830     /**
1831      * Search contacts parameters description.
1832      *
1833      * @return external_function_parameters
1834      * @since Moodle 2.5
1835      */
1836     public static function search_contacts_parameters() {
1837         return new external_function_parameters(
1838             array(
1839                 'searchtext' => new external_value(PARAM_CLEAN, 'String the user\'s fullname has to match to be found'),
1840                 'onlymycourses' => new external_value(PARAM_BOOL, 'Limit search to the user\'s courses',
1841                     VALUE_DEFAULT, false)
1842             )
1843         );
1844     }
1846     /**
1847      * Search contacts.
1848      *
1849      * @param string $searchtext query string.
1850      * @param bool $onlymycourses limit the search to the user's courses only.
1851      * @return external_description
1852      * @since Moodle 2.5
1853      */
1854     public static function search_contacts($searchtext, $onlymycourses = false) {
1855         global $CFG, $USER, $PAGE;
1856         require_once($CFG->dirroot . '/user/lib.php');
1858         // Check if messaging is enabled.
1859         if (empty($CFG->messaging)) {
1860             throw new moodle_exception('disabled', 'message');
1861         }
1863         require_once($CFG->libdir . '/enrollib.php');
1865         $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses);
1866         $params = self::validate_parameters(self::search_contacts_parameters(), $params);
1868         // Extra validation, we do not allow empty queries.
1869         if ($params['searchtext'] === '') {
1870             throw new moodle_exception('querystringcannotbeempty');
1871         }
1873         $courseids = array();
1874         if ($params['onlymycourses']) {
1875             $mycourses = enrol_get_my_courses(array('id'));
1876             foreach ($mycourses as $mycourse) {
1877                 $courseids[] = $mycourse->id;
1878             }
1879         } else {
1880             $courseids[] = SITEID;
1881         }
1883         // Retrieving the users matching the query.
1884         $users = message_search_users($courseids, $params['searchtext']);
1885         $results = array();
1886         foreach ($users as $user) {
1887             $results[$user->id] = $user;
1888         }
1890         // Reorganising information.
1891         foreach ($results as &$user) {
1892             $newuser = array(
1893                 'id' => $user->id,
1894                 'fullname' => fullname($user)
1895             );
1897             // Avoid undefined property notice as phone not specified.
1898             $user->phone1 = null;
1899             $user->phone2 = null;
1901             $userpicture = new user_picture($user);
1902             $userpicture->size = 1; // Size f1.
1903             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
1904             $userpicture->size = 0; // Size f2.
1905             $newuser['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
1907             $user = $newuser;
1908         }
1910         return $results;
1911     }
1913     /**
1914      * Search contacts return description.
1915      *
1916      * @return external_description
1917      * @since Moodle 2.5
1918      */
1919     public static function search_contacts_returns() {
1920         return new external_multiple_structure(
1921             new external_single_structure(
1922                 array(
1923                     'id' => new external_value(PARAM_INT, 'User ID'),
1924                     'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
1925                     'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
1926                     'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL)
1927                 )
1928             ),
1929             'List of contacts'
1930         );
1931     }
1933     /**
1934      * Get messages parameters description.
1935      *
1936      * @return external_function_parameters
1937      * @since 2.8
1938      */
1939     public static function get_messages_parameters() {
1940         return new external_function_parameters(
1941             array(
1942                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
1943                 'useridfrom' => new external_value(
1944                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
1945                     VALUE_DEFAULT, 0),
1946                 'type' => new external_value(
1947                     PARAM_ALPHA, 'type of message to return, expected values are: notifications, conversations and both',
1948                     VALUE_DEFAULT, 'both'),
1949                 'read' => new external_value(PARAM_BOOL, 'true for getting read messages, false for unread', VALUE_DEFAULT, true),
1950                 'newestfirst' => new external_value(
1951                     PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
1952                     VALUE_DEFAULT, true),
1953                 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
1954                 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
1955             )
1956         );
1957     }
1959     /**
1960      * Get messages function implementation.
1961      *
1962      * @since  2.8
1963      * @throws invalid_parameter_exception
1964      * @throws moodle_exception
1965      * @param  int      $useridto       the user id who received the message
1966      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
1967      * @param  string   $type           type of message to return, expected values: notifications, conversations and both
1968      * @param  bool     $read           true for retreiving read messages, false for unread
1969      * @param  bool     $newestfirst    true for ordering by newest first, false for oldest first
1970      * @param  int      $limitfrom      limit from
1971      * @param  int      $limitnum       limit num
1972      * @return external_description
1973      */
1974     public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true,
1975                                         $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
1976         global $CFG, $USER;
1978         $warnings = array();
1980         $params = array(
1981             'useridto' => $useridto,
1982             'useridfrom' => $useridfrom,
1983             'type' => $type,
1984             'read' => $read,
1985             'newestfirst' => $newestfirst,
1986             'limitfrom' => $limitfrom,
1987             'limitnum' => $limitnum
1988         );
1990         $params = self::validate_parameters(self::get_messages_parameters(), $params);
1992         $context = context_system::instance();
1993         self::validate_context($context);
1995         $useridto = $params['useridto'];
1996         $useridfrom = $params['useridfrom'];
1997         $type = $params['type'];
1998         $read = $params['read'];
1999         $newestfirst = $params['newestfirst'];
2000         $limitfrom = $params['limitfrom'];
2001         $limitnum = $params['limitnum'];
2003         $allowedvalues = array('notifications', 'conversations', 'both');
2004         if (!in_array($type, $allowedvalues)) {
2005             throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
2006                 'allowed values are: ' . implode(',', $allowedvalues));
2007         }
2009         // Check if private messaging between users is allowed.
2010         if (empty($CFG->messaging)) {
2011             // If we are retreiving only conversations, and messaging is disabled, throw an exception.
2012             if ($type == "conversations") {
2013                 throw new moodle_exception('disabled', 'message');
2014             }
2015             if ($type == "both") {
2016                 $warning = array();
2017                 $warning['item'] = 'message';
2018                 $warning['itemid'] = $USER->id;
2019                 $warning['warningcode'] = '1';
2020                 $warning['message'] = 'Private messages (conversations) are not enabled in this site.
2021                     Only notifications will be returned';
2022                 $warnings[] = $warning;
2023             }
2024         }
2026         if (!empty($useridto)) {
2027             if (core_user::is_real_user($useridto)) {
2028                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2029             } else {
2030                 throw new moodle_exception('invaliduser');
2031             }
2032         }
2034         if (!empty($useridfrom)) {
2035             // We use get_user here because the from user can be the noreply or support user.
2036             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2037         }
2039         // Check if the current user is the sender/receiver or just a privileged user.
2040         if ($useridto != $USER->id and $useridfrom != $USER->id and
2041              !has_capability('moodle/site:readallmessages', $context)) {
2042             throw new moodle_exception('accessdenied', 'admin');
2043         }
2045         // Which type of messages to retrieve.
2046         $notifications = -1;
2047         if ($type != 'both') {
2048             $notifications = ($type == 'notifications') ? 1 : 0;
2049         }
2051         $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2052         $sort = "mr.timecreated $orderdirection";
2054         if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2055             $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2057             // In some cases, we don't need to get the to/from user objects from the sql query.
2058             $userfromfullname = '';
2059             $usertofullname = '';
2061             // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
2062             if (!empty($useridto)) {
2063                 $usertofullname = fullname($userto, $canviewfullname);
2064                 // The user from may or may not be filled.
2065                 if (!empty($useridfrom)) {
2066                     $userfromfullname = fullname($userfrom, $canviewfullname);
2067                 }
2068             } else {
2069                 // If the useridto field is empty, the useridfrom must be filled.
2070                 $userfromfullname = fullname($userfrom, $canviewfullname);
2071             }
2072             foreach ($messages as $mid => $message) {
2074                 // Do not return deleted messages.
2075                 if (!$message->notification) {
2076                     if (($useridto == $USER->id and $message->timeusertodeleted) or
2077                         ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
2078                         unset($messages[$mid]);
2079                         continue;
2080                     }
2081                 }
2083                 // We need to get the user from the query.
2084                 if (empty($userfromfullname)) {
2085                     // Check for non-reply and support users.
2086                     if (core_user::is_real_user($message->useridfrom)) {
2087                         $user = new stdClass();
2088                         $user = username_load_fields_from_object($user, $message, 'userfrom');
2089                         $message->userfromfullname = fullname($user, $canviewfullname);
2090                     } else {
2091                         $user = core_user::get_user($message->useridfrom);
2092                         $message->userfromfullname = fullname($user, $canviewfullname);
2093                     }
2094                 } else {
2095                     $message->userfromfullname = $userfromfullname;
2096                 }
2098                 // We need to get the user from the query.
2099                 if (empty($usertofullname)) {
2100                     $user = new stdClass();
2101                     $user = username_load_fields_from_object($user, $message, 'userto');
2102                     $message->usertofullname = fullname($user, $canviewfullname);
2103                 } else {
2104                     $message->usertofullname = $usertofullname;
2105                 }
2107                 $message->text = message_format_message_text($message);
2108                 $messages[$mid] = (array) $message;
2109             }
2110         }
2112         $results = array(
2113             'messages' => $messages,
2114             'warnings' => $warnings
2115         );
2117         return $results;
2118     }
2120     /**
2121      * Get messages return description.
2122      *
2123      * @return external_single_structure
2124      * @since 2.8
2125      */
2126     public static function get_messages_returns() {
2127         return new external_single_structure(
2128             array(
2129                 'messages' => new external_multiple_structure(
2130                     new external_single_structure(
2131                         array(
2132                             'id' => new external_value(PARAM_INT, 'Message id'),
2133                             'useridfrom' => new external_value(PARAM_INT, 'User from id'),
2134                             'useridto' => new external_value(PARAM_INT, 'User to id'),
2135                             'subject' => new external_value(PARAM_TEXT, 'The message subject'),
2136                             'text' => new external_value(PARAM_RAW, 'The message text formated'),
2137                             'fullmessage' => new external_value(PARAM_RAW, 'The message'),
2138                             'fullmessageformat' => new external_format_value('fullmessage'),
2139                             'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
2140                             'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
2141                             'notification' => new external_value(PARAM_INT, 'Is a notification?'),
2142                             'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
2143                             'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
2144                             'timecreated' => new external_value(PARAM_INT, 'Time created'),
2145                             'timeread' => new external_value(PARAM_INT, 'Time read'),
2146                             'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
2147                             'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name'),
2148                             'component' => new external_value(PARAM_TEXT, 'The component that generated the notification',
2149                                 VALUE_OPTIONAL),
2150                             'eventtype' => new external_value(PARAM_TEXT, 'The type of notification', VALUE_OPTIONAL),
2151                             'customdata' => new external_value(PARAM_RAW, 'Custom data to be passed to the message processor.
2152                                 The data here is serialised using json_encode().', VALUE_OPTIONAL),
2153                         ), 'message'
2154                     )
2155                 ),
2156                 'warnings' => new external_warnings()
2157             )
2158         );
2159     }
2161     /**
2162      * Mark all notifications as read parameters description.
2163      *
2164      * @return external_function_parameters
2165      * @since 3.2
2166      */
2167     public static function mark_all_notifications_as_read_parameters() {
2168         return new external_function_parameters(
2169             array(
2170                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2171                 'useridfrom' => new external_value(
2172                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2173                     VALUE_DEFAULT, 0),
2174                 'timecreatedto' => new external_value(
2175                     PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
2176                     VALUE_DEFAULT, 0),
2177             )
2178         );
2179     }
2181     /**
2182      * Mark all notifications as read function.
2183      *
2184      * @since  3.2
2185      * @throws invalid_parameter_exception
2186      * @throws moodle_exception
2187      * @param  int      $useridto       the user id who received the message
2188      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
2189      * @param  int      $timecreatedto  mark message created before this time as read, 0 for all messages
2190      * @return external_description
2191      */
2192     public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
2193         global $USER;
2195         $params = self::validate_parameters(
2196             self::mark_all_notifications_as_read_parameters(),
2197             array(
2198                 'useridto' => $useridto,
2199                 'useridfrom' => $useridfrom,
2200                 'timecreatedto' => $timecreatedto,
2201             )
2202         );
2204         $context = context_system::instance();
2205         self::validate_context($context);
2207         $useridto = $params['useridto'];
2208         $useridfrom = $params['useridfrom'];
2209         $timecreatedto = $params['timecreatedto'];
2211         if (!empty($useridto)) {
2212             if (core_user::is_real_user($useridto)) {
2213                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2214             } else {
2215                 throw new moodle_exception('invaliduser');
2216             }
2217         }
2219         if (!empty($useridfrom)) {
2220             // We use get_user here because the from user can be the noreply or support user.
2221             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2222         }
2224         // Check if the current user is the sender/receiver or just a privileged user.
2225         if ($useridto != $USER->id and $useridfrom != $USER->id and
2226             // The deleteanymessage cap seems more reasonable here than readallmessages.
2227              !has_capability('moodle/site:deleteanymessage', $context)) {
2228             throw new moodle_exception('accessdenied', 'admin');
2229         }
2231         \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
2233         return true;
2234     }
2236     /**
2237      * Mark all notifications as read return description.
2238      *
2239      * @return external_single_structure
2240      * @since 3.2
2241      */
2242     public static function mark_all_notifications_as_read_returns() {
2243         return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
2244     }
2246     /**
2247      * Get unread conversations count parameters description.
2248      *
2249      * @return external_function_parameters
2250      * @since 3.2
2251      */
2252     public static function get_unread_conversations_count_parameters() {
2253         return new external_function_parameters(
2254             array(
2255                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2256             )
2257         );
2258     }
2260     /**
2261      * Get unread messages count function.
2262      *
2263      * @since  3.2
2264      * @throws invalid_parameter_exception
2265      * @throws moodle_exception
2266      * @param  int      $useridto       the user id who received the message
2267      * @return external_description
2268      */
2269     public static function get_unread_conversations_count($useridto) {
2270         global $USER, $CFG;
2272         // Check if messaging is enabled.
2273         if (empty($CFG->messaging)) {
2274             throw new moodle_exception('disabled', 'message');
2275         }
2277         $params = self::validate_parameters(
2278             self::get_unread_conversations_count_parameters(),
2279             array('useridto' => $useridto)
2280         );
2282         $context = context_system::instance();
2283         self::validate_context($context);
2285         $useridto = $params['useridto'];
2287         if (!empty($useridto)) {
2288             if (core_user::is_real_user($useridto)) {
2289                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2290             } else {
2291                 throw new moodle_exception('invaliduser');
2292             }
2293         } else {
2294             $useridto = $USER->id;
2295         }
2297         // Check if the current user is the receiver or just a privileged user.
2298         if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
2299             throw new moodle_exception('accessdenied', 'admin');
2300         }
2302         return \core_message\api::count_unread_conversations($userto);
2303     }
2305     /**
2306      * Get unread conversations count return description.
2307      *
2308      * @return external_single_structure
2309      * @since 3.2
2310      */
2311     public static function get_unread_conversations_count_returns() {
2312         return new external_value(PARAM_INT, 'The count of unread messages for the user');
2313     }
2315     /**
2316      * Get blocked users parameters description.
2317      *
2318      * @return external_function_parameters
2319      * @since 2.9
2320      */
2321     public static function get_blocked_users_parameters() {
2322         return new external_function_parameters(
2323             array(
2324                 'userid' => new external_value(PARAM_INT,
2325                                 'the user whose blocked users we want to retrieve',
2326                                 VALUE_REQUIRED),
2327             )
2328         );
2329     }
2331     /**
2332      * Retrieve a list of users blocked
2333      *
2334      * @param  int $userid the user whose blocked users we want to retrieve
2335      * @return external_description
2336      * @since 2.9
2337      */
2338     public static function get_blocked_users($userid) {
2339         global $CFG, $USER, $PAGE;
2341         // Warnings array, it can be empty at the end but is mandatory.
2342         $warnings = array();
2344         // Validate params.
2345         $params = array(
2346             'userid' => $userid
2347         );
2348         $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2349         $userid = $params['userid'];
2351         // Validate context.
2352         $context = context_system::instance();
2353         self::validate_context($context);
2355         // Check if private messaging between users is allowed.
2356         if (empty($CFG->messaging)) {
2357             throw new moodle_exception('disabled', 'message');
2358         }
2360         $user = core_user::get_user($userid, '*', MUST_EXIST);
2361         core_user::require_active_user($user);
2363         // Check if we have permissions for retrieve the information.
2364         $capability = 'moodle/site:manageallmessaging';
2365         if (($USER->id != $userid) && !has_capability($capability, $context)) {
2366             throw new required_capability_exception($context, $capability, 'nopermissions', '');
2367         }
2369         // Now, we can get safely all the blocked users.
2370         $users = \core_message\api::get_blocked_users($user->id);
2372         $blockedusers = array();
2373         foreach ($users as $user) {
2374             $newuser = array(
2375                 'id' => $user->id,
2376                 'fullname' => fullname($user),
2377             );
2379             $userpicture = new user_picture($user);
2380             $userpicture->size = 1; // Size f1.
2381             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2383             $blockedusers[] = $newuser;
2384         }
2386         $results = array(
2387             'users' => $blockedusers,
2388             'warnings' => $warnings
2389         );
2390         return $results;
2391     }
2393     /**
2394      * Get blocked users return description.
2395      *
2396      * @return external_single_structure
2397      * @since 2.9
2398      */
2399     public static function get_blocked_users_returns() {
2400         return new external_single_structure(
2401             array(
2402                 'users' => new external_multiple_structure(
2403                     new external_single_structure(
2404                         array(
2405                             'id' => new external_value(PARAM_INT, 'User ID'),
2406                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2407                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
2408                         )
2409                     ),
2410                     'List of blocked users'
2411                 ),
2412                 'warnings' => new external_warnings()
2413             )
2414         );
2415     }
2417     /**
2418      * Returns description of method parameters
2419      *
2420      * @return external_function_parameters
2421      * @since 2.9
2422      */
2423     public static function mark_message_read_parameters() {
2424         return new external_function_parameters(
2425             array(
2426                 'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
2427                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
2428                     VALUE_DEFAULT, 0)
2429             )
2430         );
2431     }
2433     /**
2434      * Mark a single message as read, trigger message_viewed event
2435      *
2436      * @param  int $messageid id of the message (in the message table)
2437      * @param  int $timeread timestamp for when the message should be marked read
2438      * @return external_description
2439      * @throws invalid_parameter_exception
2440      * @throws moodle_exception
2441      * @since 2.9
2442      */
2443     public static function mark_message_read($messageid, $timeread) {
2444         global $CFG, $DB, $USER;
2446         // Check if private messaging between users is allowed.
2447         if (empty($CFG->messaging)) {
2448             throw new moodle_exception('disabled', 'message');
2449         }
2451         // Warnings array, it can be empty at the end but is mandatory.
2452         $warnings = array();
2454         // Validate params.
2455         $params = array(
2456             'messageid' => $messageid,
2457             'timeread' => $timeread
2458         );
2459         $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2461         if (empty($params['timeread'])) {
2462             $timeread = time();
2463         } else {
2464             $timeread = $params['timeread'];
2465         }
2467         // Validate context.
2468         $context = context_system::instance();
2469         self::validate_context($context);
2471         $sql = "SELECT m.*, mcm.userid as useridto
2472                   FROM {messages} m
2473             INNER JOIN {message_conversations} mc
2474                     ON m.conversationid = mc.id
2475             INNER JOIN {message_conversation_members} mcm
2476                     ON mcm.conversationid = mc.id
2477              LEFT JOIN {message_user_actions} mua
2478                     ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
2479                  WHERE mua.id is NULL
2480                    AND mcm.userid != m.useridfrom
2481                    AND m.id = ?";
2482         $messageparams = [];
2483         $messageparams[] = $USER->id;
2484         $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
2485         $messageparams[] = $params['messageid'];
2486         $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
2488         if ($message->useridto != $USER->id) {
2489             throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
2490         }
2492         \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2494         $results = array(
2495             'messageid' => $message->id,
2496             'warnings' => $warnings
2497         );
2498         return $results;
2499     }
2501     /**
2502      * Returns description of method result value
2503      *
2504      * @return external_description
2505      * @since 2.9
2506      */
2507     public static function mark_message_read_returns() {
2508         return new external_single_structure(
2509             array(
2510                 'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
2511                 'warnings' => new external_warnings()
2512             )
2513         );
2514     }
2516     /**
2517      * Returns description of method parameters
2518      *
2519      * @return external_function_parameters
2520      */
2521     public static function mark_notification_read_parameters() {
2522         return new external_function_parameters(
2523             array(
2524                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2525                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
2526                     VALUE_DEFAULT, 0)
2527             )
2528         );
2529     }
2531     /**
2532      * Mark a single notification as read.
2533      *
2534      * This will trigger a 'notification_viewed' event.
2535      *
2536      * @param int $notificationid id of the notification
2537      * @param int $timeread timestamp for when the notification should be marked read
2538      * @return external_description
2539      * @throws invalid_parameter_exception
2540      * @throws moodle_exception
2541      */
2542     public static function mark_notification_read($notificationid, $timeread) {
2543         global $CFG, $DB, $USER;
2545         // Warnings array, it can be empty at the end but is mandatory.
2546         $warnings = array();
2548         // Validate params.
2549         $params = array(
2550             'notificationid' => $notificationid,
2551             'timeread' => $timeread
2552         );
2553         $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
2555         if (empty($params['timeread'])) {
2556             $timeread = time();
2557         } else {
2558             $timeread = $params['timeread'];
2559         }
2561         // Validate context.
2562         $context = context_system::instance();
2563         self::validate_context($context);
2565         $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
2567         if ($notification->useridto != $USER->id) {
2568             throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
2569                 'notification as read');
2570         }
2572         \core_message\api::mark_notification_as_read($notification, $timeread);
2574         $results = array(
2575             'notificationid' => $notification->id,
2576             'warnings' => $warnings
2577         );
2579         return $results;
2580     }
2582     /**
2583      * Returns description of method result value
2584      *
2585      * @return external_description
2586      */
2587     public static function mark_notification_read_returns() {
2588         return new external_single_structure(
2589             array(
2590                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2591                 'warnings' => new external_warnings()
2592             )
2593         );
2594     }
2596     /**
2597      * Mark all conversation messages as read parameters description.
2598      *
2599      * @return external_function_parameters
2600      * @since 3.6
2601      */
2602     public static function mark_all_conversation_messages_as_read_parameters() {
2603         return new external_function_parameters(
2604             array(
2605                 'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
2606                 'conversationid' =>
2607                     new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
2608             )
2609         );
2610     }
2612     /**
2613      * Mark all conversation messages as read function.
2614      *
2615      * @param int $userid The user id of who we want to delete the conversation for
2616      * @param int $conversationid The id of the conversations
2617      * @since 3.6
2618      */
2619     public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
2620         global $CFG;
2622         // Check if messaging is enabled.
2623         if (empty($CFG->messaging)) {
2624             throw new moodle_exception('disabled', 'message');
2625         }
2627         $params = array(
2628             'userid' => $userid,
2629             'conversationid' => $conversationid,
2630         );
2631         $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
2633         $context = context_system::instance();
2634         self::validate_context($context);
2636         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2637         core_user::require_active_user($user);
2639         if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
2640             \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
2641         } else {
2642             throw new moodle_exception('accessdenied', 'admin');
2643         }
2644     }
2646     /**
2647      * Mark all conversation messages as read return description.
2648      *
2649      * @return external_warnings
2650      * @since 3.6
2651      */
2652     public static function mark_all_conversation_messages_as_read_returns() {
2653         return null;
2654     }
2656     /**
2657      * Returns description of method parameters.
2658      *
2659      * @return external_function_parameters
2660      * @since 3.6
2661      */
2662     public static function delete_conversations_by_id_parameters() {
2663         return new external_function_parameters(
2664             array(
2665                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
2666                 'conversationids' => new external_multiple_structure(
2667                     new external_value(PARAM_INT, 'The id of the conversation'),
2668                     'List of conversation IDs'
2669                 ),
2670             )
2671         );
2672     }
2674     /**
2675      * Deletes a conversation.
2676      *
2677      * @param int $userid The user id of who we want to delete the conversation for
2678      * @param int[] $conversationids The ids of the conversations
2679      * @return array
2680      * @throws moodle_exception
2681      * @since 3.6
2682      */
2683     public static function delete_conversations_by_id($userid, array $conversationids) {
2684         global $CFG;
2686         // Check if private messaging between users is allowed.
2687         if (empty($CFG->messaging)) {
2688             throw new moodle_exception('disabled', 'message');
2689         }
2691         // Validate params.
2692         $params = [
2693             'userid' => $userid,
2694             'conversationids' => $conversationids,
2695         ];
2696         $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
2698         // Validate context.
2699         $context = context_system::instance();
2700         self::validate_context($context);
2702         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2703         core_user::require_active_user($user);
2705         foreach ($params['conversationids'] as $conversationid) {
2706             if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
2707                 \core_message\api::delete_conversation_by_id($user->id, $conversationid);
2708             } else {
2709                 throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
2710             }
2711         }
2713         return [];
2714     }
2716     /**
2717      * Returns description of method result value.
2718      *
2719      * @return external_description
2720      * @since 3.6
2721      */
2722     public static function delete_conversations_by_id_returns() {
2723         return new external_warnings();
2724     }
2726     /**
2727      * Returns description of method parameters
2728      *
2729      * @return external_function_parameters
2730      * @since 3.1
2731      */
2732     public static function delete_message_parameters() {
2733         return new external_function_parameters(
2734             array(
2735                 'messageid' => new external_value(PARAM_INT, 'The message id'),
2736                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
2737                 'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
2738             )
2739         );
2740     }
2742     /**
2743      * Deletes a message
2744      *
2745      * @param  int $messageid the message id
2746      * @param  int $userid the user id of who we want to delete the message for
2747      * @param  bool $read if is a message read (default to true)
2748      * @return external_description
2749      * @throws moodle_exception
2750      * @since 3.1
2751      */
2752     public static function delete_message($messageid, $userid, $read = true) {
2753         global $CFG;
2755         // Check if private messaging between users is allowed.
2756         if (empty($CFG->messaging)) {
2757             throw new moodle_exception('disabled', 'message');
2758         }
2760         // Warnings array, it can be empty at the end but is mandatory.
2761         $warnings = array();
2763         // Validate params.
2764         $params = array(
2765             'messageid' => $messageid,
2766             'userid' => $userid,
2767             'read' => $read
2768         );
2769         $params = self::validate_parameters(self::delete_message_parameters(), $params);
2771         // Validate context.
2772         $context = context_system::instance();
2773         self::validate_context($context);
2775         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2776         core_user::require_active_user($user);
2778         if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
2779             $status = \core_message\api::delete_message($user->id, $params['messageid']);
2780         } else {
2781             throw new moodle_exception('You do not have permission to delete this message');
2782         }
2784         $results = array(
2785             'status' => $status,
2786             'warnings' => $warnings
2787         );
2788         return $results;
2789     }
2791     /**
2792      * Returns description of method result value
2793      *
2794      * @return external_description
2795      * @since 3.1
2796      */
2797     public static function delete_message_returns() {
2798         return new external_single_structure(
2799             array(
2800                 'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
2801                 'warnings' => new external_warnings()
2802             )
2803         );
2804     }
2806     /**
2807      * Returns description of method parameters
2808      *
2809      * @return external_function_parameters
2810      * @since 3.2
2811      */
2812     public static function message_processor_config_form_parameters() {
2813         return new external_function_parameters(
2814             array(
2815                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
2816                 'name' => new external_value(PARAM_TEXT, 'The name of the message processor'),
2817                 'formvalues' => new external_multiple_structure(
2818                     new external_single_structure(
2819                         array(
2820                             'name' => new external_value(PARAM_TEXT, 'name of the form element', VALUE_REQUIRED),
2821                             'value' => new external_value(PARAM_RAW, 'value of the form element', VALUE_REQUIRED),
2822                         )
2823                     ),
2824                     'Config form values',
2825                     VALUE_REQUIRED
2826                 ),
2827             )
2828         );
2829     }
2831     /**
2832      * Processes a message processor config form.
2833      *
2834      * @param  int $userid the user id
2835      * @param  string $name the name of the processor
2836      * @param  array $formvalues the form values
2837      * @return external_description
2838      * @throws moodle_exception
2839      * @since 3.2
2840      */
2841     public static function message_processor_config_form($userid, $name, $formvalues) {
2842         global $USER, $CFG;
2844         // Check if messaging is enabled.
2845         if (empty($CFG->messaging)) {
2846             throw new moodle_exception('disabled', 'message');
2847         }
2849         $params = self::validate_parameters(
2850             self::message_processor_config_form_parameters(),
2851             array(
2852                 'userid' => $userid,
2853                 'name' => $name,
2854                 'formvalues' => $formvalues,
2855             )
2856         );
2858         $user = self::validate_preferences_permissions($params['userid']);
2860         $processor = get_message_processor($params['name']);
2861         $preferences = [];
2862         $form = new stdClass();
2864         foreach ($params['formvalues'] as $formvalue) {
2865             // Curly braces to ensure interpretation is consistent between
2866             // php 5 and php 7.
2867             $form->{$formvalue['name']} = $formvalue['value'];
2868         }
2870         $processor->process_form($form, $preferences);
2872         if (!empty($preferences)) {
2873             set_user_preferences($preferences, $params['userid']);
2874         }
2875     }
2877     /**
2878      * Returns description of method result value
2879      *
2880      * @return external_description
2881      * @since 3.2
2882      */
2883     public static function message_processor_config_form_returns() {
2884         return null;
2885     }
2887     /**
2888      * Returns description of method parameters
2889      *
2890      * @return external_function_parameters
2891      * @since 3.2
2892      */
2893     public static function get_message_processor_parameters() {
2894         return new external_function_parameters(
2895             array(
2896                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user'),
2897                 'name' => new external_value(PARAM_TEXT, 'The name of the message processor', VALUE_REQUIRED),
2898             )
2899         );
2900     }
2902     /**
2903      * Get a message processor.
2904      *
2905      * @param int $userid
2906      * @param string $name the name of the processor
2907      * @return external_description
2908      * @throws moodle_exception
2909      * @since 3.2
2910      */
2911     public static function get_message_processor($userid = 0, $name) {
2912         global $USER, $PAGE, $CFG;
2914         // Check if messaging is enabled.
2915         if (empty($CFG->messaging)) {
2916             throw new moodle_exception('disabled', 'message');
2917         }
2919         $params = self::validate_parameters(
2920             self::get_message_processor_parameters(),
2921             array(
2922                 'userid' => $userid,
2923                 'name' => $name,
2924             )
2925         );
2927         if (empty($params['userid'])) {
2928             $params['userid'] = $USER->id;
2929         }
2931         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2932         core_user::require_active_user($user);
2933         self::validate_context(context_user::instance($params['userid']));
2935         $processor = get_message_processor($params['name']);
2937         $processoroutput = new \core_message\output\processor($processor, $user);
2938         $renderer = $PAGE->get_renderer('core_message');
2940         return $processoroutput->export_for_template($renderer);
2941     }
2943     /**
2944      * Returns description of method result value
2945      *
2946      * @return external_description
2947      * @since 3.2
2948      */
2949     public static function get_message_processor_returns() {
2950         return new external_function_parameters(
2951             array(
2952                 'systemconfigured' => new external_value(PARAM_BOOL, 'Site configuration status'),
2953                 'userconfigured' => new external_value(PARAM_BOOL, 'The user configuration status'),
2954             )
2955         );
2956     }
2958     /**
2959      * Check that the user has enough permission to retrieve message or notifications preferences.
2960      *
2961      * @param  int $userid the user id requesting the preferences
2962      * @return stdClass full user object
2963      * @throws moodle_exception
2964      * @since  Moodle 3.2
2965      */
2966     protected static function validate_preferences_permissions($userid) {
2967         global $USER;
2969         if (empty($userid)) {
2970             $user = $USER;
2971         } else {
2972             $user = core_user::get_user($userid, '*', MUST_EXIST);
2973             core_user::require_active_user($user);
2974         }
2976         $systemcontext = context_system::instance();
2977         self::validate_context($systemcontext);
2979         // Check access control.
2980         if ($user->id == $USER->id) {
2981             // Editing own message profile.
2982             require_capability('moodle/user:editownmessageprofile', $systemcontext);
2983         } else {
2984             // Teachers, parents, etc.
2985             $personalcontext = context_user::instance($user->id);
2986             require_capability('moodle/user:editmessageprofile', $personalcontext);
2987         }
2988         return $user;
2989     }
2991     /**
2992      * Returns a notification or message preference structure.
2993      *
2994      * @return external_single_structure the structure
2995      * @since  Moodle 3.2
2996      */
2997     protected static function get_preferences_structure() {
2998         return new external_single_structure(
2999             array(
3000                 'userid' => new external_value(PARAM_INT, 'User id'),
3001                 'disableall' => new external_value(PARAM_INT, 'Whether all the preferences are disabled'),
3002                 'processors' => new external_multiple_structure(
3003                     new external_single_structure(
3004                         array(
3005                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3006                             'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3007                             'hassettings' => new external_value(PARAM_BOOL, 'Whether has settings'),
3008                             'contextid' => new external_value(PARAM_INT, 'Context id'),
3009                             'userconfigured' => new external_value(PARAM_INT, 'Whether is configured by the user'),
3010                         )
3011                     ),
3012                     'Config form values'
3013                 ),
3014                 'components' => new external_multiple_structure(
3015                     new external_single_structure(
3016                         array(
3017                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3018                             'notifications' => new external_multiple_structure(
3019                                 new external_single_structure(
3020                                     array(
3021                                         'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3022                                         'preferencekey' => new external_value(PARAM_ALPHANUMEXT, 'Preference key'),
3023                                         'processors' => new external_multiple_structure(
3024                                             new external_single_structure(
3025                                                 array(
3026                                                     'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3027                                                     'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3028                                                     'locked' => new external_value(PARAM_BOOL, 'Is locked by admin?'),
3029                                                     'lockedmessage' => new external_value(PARAM_TEXT,
3030                                                         'Text to display if locked', VALUE_OPTIONAL),
3031                                                     'userconfigured' => new external_value(PARAM_INT, 'Is configured?'),
3032                                                     'loggedin' => new external_single_structure(
3033                                                         array(
3034                                                             'name' => new external_value(PARAM_NOTAGS, 'Name'),
3035                                                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3036                                                             'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3037                                                         )
3038                                                     ),
3039                                                     'loggedoff' => new external_single_structure(
3040                                                         array(
3041                                                             'name' => new external_value(PARAM_NOTAGS, 'Name'),
3042                                                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3043                                                             'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3044                                                         )
3045                                                     ),
3046                                                 )
3047                                             ),
3048                                             'Processors values for this notification'
3049                                         ),
3050                                     )
3051                                 ),
3052                                 'List of notificaitons for the component'
3053                             ),
3054                         )
3055                     ),
3056                     'Available components'
3057                 ),
3058             )
3059         );
3060     }
3062     /**
3063      * Returns description of method parameters
3064      *
3065      * @return external_function_parameters
3066      * @since 3.2
3067      */
3068     public static function get_user_notification_preferences_parameters() {
3069         return new external_function_parameters(
3070             array(
3071                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3072             )
3073         );
3074     }
3076     /**
3077      * Get the notification preferences for a given user.
3078      *
3079      * @param int $userid id of the user, 0 for current user
3080      * @return external_description
3081      * @throws moodle_exception
3082      * @since 3.2
3083      */
3084     public static function get_user_notification_preferences($userid = 0) {
3085         global $PAGE;
3087         $params = self::validate_parameters(
3088             self::get_user_notification_preferences_parameters(),
3089             array(
3090                 'userid' => $userid,
3091             )
3092         );
3093         $user = self::validate_preferences_permissions($params['userid']);
3095         $processors = get_message_processors();
3096         $providers = message_get_providers_for_user($user->id);
3097         $preferences = \core_message\api::get_all_message_preferences($processors, $providers, $user);
3098         $notificationlist = new \core_message\output\preferences\notification_list($processors, $providers, $preferences, $user);
3100         $renderer = $PAGE->get_renderer('core_message');
3102         $result = array(
3103             'warnings' => array(),
3104             'preferences' => $notificationlist->export_for_template($renderer)
3105         );
3106         return $result;
3107     }
3109     /**
3110      * Returns description of method result value
3111      *
3112      * @return external_description
3113      * @since 3.2
3114      */
3115     public static function get_user_notification_preferences_returns() {
3116         return new external_function_parameters(
3117             array(
3118                 'preferences' => self::get_preferences_structure(),
3119                 'warnings' => new external_warnings(),
3120             )
3121         );
3122     }
3124     /**
3125      * Returns description of method parameters
3126      *
3127      * @return external_function_parameters
3128      * @since 3.2
3129      */
3130     public static function get_user_message_preferences_parameters() {
3131         return new external_function_parameters(
3132             array(
3133                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3134             )
3135         );
3136     }
3138     /**
3139      * Get the notification preferences for a given user.
3140      *
3141      * @param int $userid id of the user, 0 for current user
3142      * @return external_description
3143      * @throws moodle_exception
3144      * @since 3.2
3145      */
3146     public static function get_user_message_preferences($userid = 0) {
3147         global $CFG, $PAGE;
3149         $params = self::validate_parameters(
3150             self::get_user_message_preferences_parameters(),
3151             array(
3152                 'userid' => $userid,
3153             )
3154         );
3156         $user = self::validate_preferences_permissions($params['userid']);
3158         // Filter out enabled, available system_configured and user_configured processors only.
3159         $readyprocessors = array_filter(get_message_processors(), function($processor) {
3160             return $processor->enabled &&
3161                 $processor->configured &&
3162                 $processor->object->is_user_configured() &&
3163                 // Filter out processors that don't have and message preferences to configure.
3164                 $processor->object->has_message_preferences();
3165         });
3167         $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
3168             return $provider->component === 'moodle';
3169         });
3170         $preferences = \core_message\api::get_all_message_preferences($readyprocessors, $providers, $user);
3171         $notificationlistoutput = new \core_message\output\preferences\message_notification_list($readyprocessors,
3172             $providers, $preferences, $user);
3174         $renderer = $PAGE->get_renderer('core_message');
3176         $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
3178         $result = array(
3179             'warnings' => array(),
3180             'preferences' => $notificationlistoutput->export_for_template($renderer),
3181             'blocknoncontacts' => \core_message\api::get_user_privacy_messaging_preference($user->id),
3182             'entertosend' => $entertosend
3183         );
3184         return $result;
3185     }
3187     /**
3188      * Returns description of method result value
3189      *
3190      * @return external_description
3191      * @since 3.2
3192      */
3193     public static function get_user_message_preferences_returns() {
3194         return new external_function_parameters(
3195             array(
3196                 'preferences' => self::get_preferences_structure(),
3197                 'blocknoncontacts' => new external_value(PARAM_INT, 'Privacy messaging setting to define who can message you'),
3198                 'entertosend' => new external_value(PARAM_BOOL, 'User preference for using enter to send messages'),
3199                 'warnings' => new external_warnings(),
3200             )
3201         );
3202     }
3204     /**
3205      * Returns description of method parameters for the favourite_conversations() method.
3206      *
3207      * @return external_function_parameters
3208      */
3209     public static function set_favourite_conversations_parameters() {
3210         return new external_function_parameters(
3211             array(
3212                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3213                 'conversations' => new external_multiple_structure(
3214                     new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3215                 )
3216             )
3217         );
3218     }
3220     /**
3221      * Favourite a conversation, or list of conversations for a user.
3222      *
3223      * @param int $userid the id of the user, or 0 for the current user.
3224      * @param array $conversationids the list of conversations ids to favourite.
3225      * @return array
3226      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3227      */
3228     public static function set_favourite_conversations(int $userid, array $conversationids) {
3229         global $CFG, $USER;
3231         // All the business logic checks that really shouldn't be in here.
3232         if (empty($CFG->messaging)) {
3233             throw new moodle_exception('disabled', 'message');
3234         }
3235         $params = [
3236             'userid' => $userid,
3237             'conversations' => $conversationids
3238         ];
3239         $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
3240         $systemcontext = context_system::instance();
3241         self::validate_context($systemcontext);
3243         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3244             throw new moodle_exception('You do not have permission to perform this action.');
3245         }
3247         foreach ($params['conversations'] as $conversationid) {
3248             \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
3249         }
3251         return [];
3252     }
3254     /**
3255      * Return a description of the returns for the create_user_favourite_conversations() method.
3256      *
3257      * @return external_description
3258      */
3259     public static function set_favourite_conversations_returns() {
3260         return new external_warnings();
3261     }
3263     /**
3264      * Returns description of method parameters for unfavourite_conversations() method.
3265      *
3266      * @return external_function_parameters
3267      */
3268     public static function unset_favourite_conversations_parameters() {
3269         return new external_function_parameters(
3270             array(
3271                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3272                 'conversations' => new external_multiple_structure(
3273                     new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3274                 )
3275             )
3276         );
3277     }
3279     /**
3280      * Unfavourite a conversation, or list of conversations for a user.
3281      *
3282      * @param int $userid the id of the user, or 0 for the current user.
3283      * @param array $conversationids the list of conversations ids unset as favourites.
3284      * @return array
3285      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3286      */
3287     public static function unset_favourite_conversations(int $userid, array $conversationids) {
3288         global $CFG, $USER;
3290         // All the business logic checks that really shouldn't be in here.
3291         if (empty($CFG->messaging)) {
3292             throw new moodle_exception('disabled', 'message');
3293         }
3294         $params = [
3295             'userid' => $userid,
3296             'conversations' => $conversationids
3297         ];
3298         $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
3299         $systemcontext = context_system::instance();
3300         self::validate_context($systemcontext);
3302         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3303             throw new moodle_exception('You do not have permission to perform this action.');
3304         }
3306         foreach ($params['conversations'] as $conversationid) {
3307             \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
3308         }
3310         return [];
3311     }
3313     /**
3314      * Unset favourite conversations return description.
3315      *
3316      * @return external_description
3317      */
3318     public static function unset_favourite_conversations_returns() {
3319         return new external_warnings();
3320     }
3322     /**
3323      * Returns description of method parameters for get_member_info() method.
3324      *
3325      * @return external_function_parameters
3326      */
3327     public static function get_member_info_parameters() {
3328         return new external_function_parameters(
3329             array(
3330                 'referenceuserid' => new external_value(PARAM_INT, 'id of the user'),
3331                 'userids' => new external_multiple_structure(
3332                     new external_value(PARAM_INT, 'id of members to get')
3333                 ),
3334                 'includecontactrequests' => new external_value(PARAM_BOOL, 'include contact requests in response', VALUE_DEFAULT, false),
3335                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'include privacy info in response', VALUE_DEFAULT, false)
3336             )
3337         );
3338     }
3340     /**
3341      * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
3342      *
3343      * This is the basic structure used when returning members, and includes information about the relationship between each member
3344      * and the referenceuser, such as a whether the referenceuser has marked the member as a contact, or has blocked them.
3345      *
3346      * @param int $referenceuserid the id of the user which check contact and blocked status.
3347      * @param array $userids
3348      * @return array the array of objects containing member info.
3349      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3350      */
3351     public static function get_member_info(
3352         int $referenceuserid,
3353         array $userids,
3354         bool $includecontactrequests = false,
3355         bool $includeprivacyinfo = false
3356     ) {
3357         global $CFG, $USER;
3359         // All the business logic checks that really shouldn't be in here.
3360         if (empty($CFG->messaging)) {
3361             throw new moodle_exception('disabled', 'message');
3362         }
3363         $params = [
3364             'referenceuserid' => $referenceuserid,
3365             'userids' => $userids,
3366             'includecontactrequests' => $includecontactrequests,
3367             'includeprivacyinfo' => $includeprivacyinfo
3368         ];
3369         $params = self::validate_parameters(self::get_member_info_parameters(), $params);
3370         $systemcontext = context_system::instance();
3371         self::validate_context($systemcontext);
3373         if (($USER->id != $referenceuserid) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3374             throw new moodle_exception('You do not have permission to perform this action.');
3375         }
3377         return \core_message\helper::get_member_info(
3378             $params['referenceuserid'],
3379             $params['userids'],
3380             $params['includecontactrequests'],
3381             $params['includeprivacyinfo']
3382         );
3383     }
3385     /**
3386      * Get member info return description.
3387      *
3388      * @return external_description
3389      */
3390     public static function get_member_info_returns() {
3391         return new external_multiple_structure(
3392             self::get_conversation_member_structure()
3393         );
3394     }
3396     /**
3397      * Returns description of method parameters for get_conversation_counts() method.
3398      *
3399      * @return external_function_parameters
3400      */
3401     public static function get_conversation_counts_parameters() {
3402         return new external_function_parameters(
3403             [
3404                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3405             ]
3406         );
3407     }
3409     /**
3410      * Returns an array of conversation counts for the various types of conversations, including favourites.
3411      *
3412      * Return format:
3413      * [
3414      *     'favourites' => 0,
3415      *     'types' => [
3416      *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3417      *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3418      *      ]
3419      * ]
3420      *
3421      * @param int $userid the id of the user whose counts we are fetching.
3422      * @return array the array of conversation counts, indexed by type.
3423      * @throws moodle_exception if the current user cannot perform this action.
3424      */
3425     public static function get_conversation_counts(int $userid) {
3426         global $CFG, $USER;
3428         // All the business logic checks that really shouldn't be in here.
3429         if (empty($CFG->messaging)) {
3430             throw new moodle_exception('disabled', 'message');
3431         }
3433         if (empty($userid)) {
3434             $userid = $USER->id;
3435         }
3437         $params = ['userid' => $userid];
3438         $params = self::validate_parameters(self::get_conversation_counts_parameters(), $params);
3440         $systemcontext = context_system::instance();
3441         self::validate_context($systemcontext);
3443         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3444             throw new moodle_exception('You do not have permission to perform this action.');
3445         }
3447         return \core_message\api::get_conversation_counts($params['userid']);
3448     }
3450     /**
3451      * Get conversation counts return description.
3452      *
3453      * @return external_description
3454      */
3455     public static function get_conversation_counts_returns() {
3456         return new external_single_structure(
3457             [
3458                 'favourites' => new external_value(PARAM_INT, 'Total number of favourite conversations'),
3459                 'types' => new external_single_structure(
3460                     [
3461                         \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
3462                             'Total number of individual conversations'),
3463                         \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
3464                             'Total number of group conversations'),
3465                         \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
3466                             'Total number of self conversations'),
3467                     ]
3468                 ),
3469             ]
3470         );
3471     }
3473     /**
3474      * Returns description of method parameters for get_unread_conversation_counts() method.
3475      *
3476      * @return external_function_parameters
3477      */
3478     public static function get_unread_conversation_counts_parameters() {
3479         return new external_function_parameters(
3480             [
3481                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3482             ]
3483         );
3484     }
3486     /**
3487      * Returns an array of unread conversation counts for the various types of conversations, including favourites.
3488      *
3489      * Return format:
3490      * [
3491      *     'favourites' => 0,
3492      *     'types' => [
3493      *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3494      *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3495      *      ]
3496      * ]
3497      *
3498      * @param int $userid the id of the user whose counts we are fetching.
3499      * @return array the array of unread conversation counts, indexed by type.
3500      * @throws moodle_exception if the current user cannot perform this action.
3501      */
3502     public static function get_unread_conversation_counts(int $userid) {
3503         global $CFG, $USER;
3505         // All the business logic checks that really shouldn't be in here.
3506         if (empty($CFG->messaging)) {
3507             throw new moodle_exception('disabled', 'message');
3508         }
3510         if (empty($userid)) {
3511             $userid = $USER->id;
3512         }
3514         $params = ['userid' => $userid];
3515         $params = self::validate_parameters(self::get_unread_conversation_counts_parameters(), $params);
3517         $systemcontext = context_system::instance();
3518         self::validate_context($systemcontext);
3520         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3521             throw new moodle_exception('You do not have permission to perform this action.');
3522         }
3524         return \core_message\api::get_unread_conversation_counts($params['userid']);
3525     }
3527     /**
3528      * Get unread conversation counts return description.
3529      *
3530      * @return external_description
3531      */
3532     public static function get_unread_conversation_counts_returns() {
3533         return new external_single_structure(
3534             [
3535                 'favourites' => new external_value(PARAM_INT, 'Total number of unread favourite conversations'),
3536                 'types' => new external_single_structure(
3537                     [
3538                         \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
3539                             'Total number of unread individual conversations'),
3540                         \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
3541                             'Total number of unread group conversations'),
3542                         \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
3543                             'Total number of unread self conversations'),
3544                     ]
3545                 ),
3546             ]
3547         );
3548     }
3550     /**
3551      * Returns description of method parameters
3552      *
3553      * @return external_function_parameters
3554      * @since 3.7
3555      */
3556     public static function delete_message_for_all_users_parameters() {
3557         return new external_function_parameters(
3558             array(
3559                 'messageid' => new external_value(PARAM_INT, 'The message id'),
3560                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for all users')
3561             )
3562         );
3563     }
3564     /**
3565      * Deletes a message for all users
3566      *
3567      * @param  int $messageid the message id
3568      * @param  int $userid the user id of who we want to delete the message for all users
3569      * @return external_description
3570      * @throws moodle_exception
3571      * @since 3.7
3572      */
3573     public static function delete_message_for_all_users(int $messageid, int $userid) {
3574         global $CFG;
3576         // Check if private messaging between users is allowed.
3577         if (empty($CFG->messaging)) {
3578             throw new moodle_exception('disabled', 'message');
3579         }
3581         // Validate params.
3582         $params = array(
3583             'messageid' => $messageid,
3584             'userid' => $userid
3585         );
3586         $params = self::validate_parameters(self::delete_message_for_all_users_parameters(), $params);
3588         // Validate context.
3589         $context = context_system::instance();
3590         self::validate_context($context);
3592         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3593         core_user::require_active_user($user);
3595         // Checks if a user can delete a message for all users.
3596         if (core_message\api::can_delete_message_for_all_users($user->id, $params['messageid'])) {
3597             \core_message\api::delete_message_for_all_users($params['messageid']);
3598         } else {
3599             throw new moodle_exception('You do not have permission to delete this message for everyone.');
3600         }
3602         return [];
3603     }
3604     /**
3605      * Returns description of method result value
3606      *
3607      * @return external_description
3608      * @since 3.7
3609      */
3610     public static function delete_message_for_all_users_returns() {
3611         return new external_warnings();
3612     }