MDL-72585 message: Return notifications iconurl in get_messages WS
[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         // Validate messages content before posting them.
92         foreach ($params['messages'] as $message) {
93             // Check message length.
94             if (strlen($message['text']) > \core_message\api::MESSAGE_MAX_LENGTH) {
95                 throw new moodle_exception('errormessagetoolong', 'message');
96             }
97         }
99         $messages = [];
100         foreach ($params['messages'] as $message) {
101             $createdmessage = \core_message\api::send_message_to_conversation($USER->id, $params['conversationid'], $message['text'],
102                 $message['textformat']);
103             $createdmessage->text = message_format_message_text((object) [
104                 'smallmessage' => $createdmessage->text,
105                 'fullmessageformat' => external_validate_format($message['textformat']),
106                 'fullmessagetrust' => $createdmessage->fullmessagetrust
107             ]);
108             $messages[] = $createdmessage;
109         }
111         return $messages;
112     }
114     /**
115      * Returns description of method result value.
116      *
117      * @return external_description
118      * @since Moodle 3.6
119      */
120     public static function send_messages_to_conversation_returns() {
121         return new external_multiple_structure(
122             self::get_conversation_message_structure()
123         );
124     }
127     /**
128      * Returns description of method parameters
129      *
130      * @return external_function_parameters
131      * @since Moodle 2.2
132      */
133     public static function send_instant_messages_parameters() {
134         return new external_function_parameters(
135             array(
136                 'messages' => new external_multiple_structure(
137                     new external_single_structure(
138                         array(
139                             'touserid' => new external_value(PARAM_INT, 'id of the user to send the private message'),
140                             'text' => new external_value(PARAM_RAW, 'the text of the message'),
141                             'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
142                             'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own client id for the message. If this id is provided, the fail message id will be returned to you', VALUE_OPTIONAL),
143                         )
144                     )
145                 )
146             )
147         );
148     }
150     /**
151      * Send private messages from the current USER to other users
152      *
153      * @param array $messages An array of message to send.
154      * @return array
155      * @since Moodle 2.2
156      */
157     public static function send_instant_messages($messages = array()) {
158         global $CFG, $USER, $DB;
160         // Check if messaging is enabled.
161         if (empty($CFG->messaging)) {
162             throw new moodle_exception('disabled', 'message');
163         }
165         // Ensure the current user is allowed to run this function
166         $context = context_system::instance();
167         self::validate_context($context);
168         require_capability('moodle/site:sendmessage', $context);
170         // Ensure the current user is allowed to delete message for everyone.
171         $candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $context);
173         $params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
175         //retrieve all tousers of the messages
176         $receivers = array();
177         foreach($params['messages'] as $message) {
178             $receivers[] = $message['touserid'];
179         }
180         list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
181         $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
183         $resultmessages = array();
184         $messageids = array();
185         foreach ($params['messages'] as $message) {
186             $resultmsg = array(); //the infos about the success of the operation
188             // We are going to do some checking.
189             // Code should match /messages/index.php checks.
190             $success = true;
192             // Check the user exists.
193             if (empty($tousers[$message['touserid']])) {
194                 $success = false;
195                 $errormessage = get_string('touserdoesntexist', 'message', $message['touserid']);
196             }
198             // Check message length.
199             if ($success && strlen($message['text']) > \core_message\api::MESSAGE_MAX_LENGTH) {
200                 $success = false;
201                 $errormessage = get_string('errormessagetoolong', 'message');
202             }
204             // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead userid
205             // Check if the recipient can be messaged by the sender.
206             if ($success && !\core_message\api::can_send_message($tousers[$message['touserid']]->id, $USER->id)) {
207                 $success = false;
208                 $errormessage = get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message['touserid'])));
209             }
211             // Now we can send the message (at least try).
212             if ($success) {
213                 // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead one touser object.
214                 $success = message_post_message($USER, $tousers[$message['touserid']],
215                         $message['text'], external_validate_format($message['textformat']));
216             }
218             // Build the resultmsg.
219             if (isset($message['clientmsgid'])) {
220                 $resultmsg['clientmsgid'] = $message['clientmsgid'];
221             }
222             if ($success) {
223                 $resultmsg['msgid'] = $success;
224                 $resultmsg['timecreated'] = time();
225                 $resultmsg['candeletemessagesforallusers'] = $candeletemessagesforallusers;
226                 $messageids[] = $success;
227             } else {
228                 // WARNINGS: for backward compatibility we return this errormessage.
229                 //          We should have thrown exceptions as these errors prevent results to be returned.
230                 // See http://docs.moodle.org/dev/Errors_handling_in_web_services#When_to_send_a_warning_on_the_server_side .
231                 $resultmsg['msgid'] = -1;
232                 if (!isset($errormessage)) { // Nobody has set a message error or thrown an exception, let's set it.
233                     $errormessage = get_string('messageundeliveredbynotificationsettings', 'error');
234                 }
235                 $resultmsg['errormessage'] = $errormessage;
236             }
238             $resultmessages[] = $resultmsg;
239         }
241         if (!empty($messageids)) {
242             $messagerecords = $DB->get_records_list(
243                 'messages',
244                 'id',
245                 $messageids,
246                 '',
247                 'id, conversationid, smallmessage, fullmessageformat, fullmessagetrust');
248             $resultmessages = array_map(function($resultmessage) use ($messagerecords, $USER) {
249                 $id = $resultmessage['msgid'];
250                 $resultmessage['conversationid'] = isset($messagerecords[$id]) ? $messagerecords[$id]->conversationid : null;
251                 $resultmessage['useridfrom'] = $USER->id;
252                 $resultmessage['text'] = message_format_message_text((object) [
253                     'smallmessage' => $messagerecords[$id]->smallmessage,
254                     'fullmessageformat' => external_validate_format($messagerecords[$id]->fullmessageformat),
255                     'fullmessagetrust' => $messagerecords[$id]->fullmessagetrust
256                 ]);
257                 return $resultmessage;
258             }, $resultmessages);
259         }
261         return $resultmessages;
262     }
264     /**
265      * Returns description of method result value
266      *
267      * @return external_description
268      * @since Moodle 2.2
269      */
270     public static function send_instant_messages_returns() {
271         return new external_multiple_structure(
272             new external_single_structure(
273                 array(
274                     'msgid' => new external_value(PARAM_INT, 'test this to know if it succeeds:  id of the created message if it succeeded, -1 when failed'),
275                     'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
276                     'errormessage' => new external_value(PARAM_TEXT, 'error message - if it failed', VALUE_OPTIONAL),
277                     'text' => new external_value(PARAM_RAW, 'The text of the message', VALUE_OPTIONAL),
278                     'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message', VALUE_OPTIONAL),
279                     'conversationid' => new external_value(PARAM_INT, 'The conversation id for this message', VALUE_OPTIONAL),
280                     'useridfrom' => new external_value(PARAM_INT, 'The user id who sent the message', VALUE_OPTIONAL),
281                     'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
282                         'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
283                 )
284             )
285         );
286     }
288     /**
289      * Delete contacts parameters description.
290      *
291      * @return external_function_parameters
292      * @since Moodle 2.5
293      */
294     public static function delete_contacts_parameters() {
295         return new external_function_parameters(
296             array(
297                 'userids' => new external_multiple_structure(
298                     new external_value(PARAM_INT, 'User ID'),
299                     'List of user IDs'
300                 ),
301                 'userid' => new external_value(PARAM_INT, 'The id of the user we are deleting the contacts for, 0 for the
302                     current user', VALUE_DEFAULT, 0)
303             )
304         );
305     }
307     /**
308      * Delete contacts.
309      *
310      * @param array $userids array of user IDs.
311      * @param int $userid The id of the user we are deleting the contacts for
312      * @return null
313      * @since Moodle 2.5
314      */
315     public static function delete_contacts($userids, $userid = 0) {
316         global $CFG, $USER;
318         // Check if messaging is enabled.
319         if (empty($CFG->messaging)) {
320             throw new moodle_exception('disabled', 'message');
321         }
323         if (empty($userid)) {
324             $userid = $USER->id;
325         }
327         // Validate context.
328         $context = context_system::instance();
329         self::validate_context($context);
331         $params = array('userids' => $userids, 'userid' => $userid);
332         $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
334         $capability = 'moodle/site:manageallmessaging';
335         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
336             throw new required_capability_exception($context, $capability, 'nopermissions', '');
337         }
339         foreach ($params['userids'] as $id) {
340             \core_message\api::remove_contact($params['userid'], $id);
341         }
343         return null;
344     }
346     /**
347      * Delete contacts return description.
348      *
349      * @return external_description
350      * @since Moodle 2.5
351      */
352     public static function delete_contacts_returns() {
353         return null;
354     }
356     /**
357      * Mute conversations parameters description.
358      *
359      * @return external_function_parameters
360      */
361     public static function mute_conversations_parameters() {
362         return new external_function_parameters(
363             [
364                 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
365                 'conversationids' => new external_multiple_structure(
366                     new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
367                 ),
368             ]
369         );
370     }
372     /**
373      * Mutes conversations.
374      *
375      * @param int $userid The id of the user who is blocking
376      * @param array $conversationids The list of conversations being muted
377      * @return external_description
378      */
379     public static function mute_conversations(int $userid, array $conversationids) {
380         global $CFG, $USER;
382         // Check if messaging is enabled.
383         if (empty($CFG->messaging)) {
384             throw new moodle_exception('disabled', 'message');
385         }
387         // Validate context.
388         $context = context_system::instance();
389         self::validate_context($context);
391         $params = ['userid' => $userid, 'conversationids' => $conversationids];
392         $params = self::validate_parameters(self::mute_conversations_parameters(), $params);
394         $capability = 'moodle/site:manageallmessaging';
395         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
396             throw new required_capability_exception($context, $capability, 'nopermissions', '');
397         }
399         foreach ($params['conversationids'] as $conversationid) {
400             if (!\core_message\api::is_conversation_muted($params['userid'], $conversationid)) {
401                 \core_message\api::mute_conversation($params['userid'], $conversationid);
402             }
403         }
405         return [];
406     }
408     /**
409      * Mute conversations return description.
410      *
411      * @return external_description
412      */
413     public static function mute_conversations_returns() {
414         return new external_warnings();
415     }
417     /**
418      * Unmute conversations parameters description.
419      *
420      * @return external_function_parameters
421      */
422     public static function unmute_conversations_parameters() {
423         return new external_function_parameters(
424             [
425                 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
426                 'conversationids' => new external_multiple_structure(
427                     new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
428                 ),
429             ]
430         );
431     }
433     /**
434      * Unmute conversations.
435      *
436      * @param int $userid The id of the user who is unblocking
437      * @param array $conversationids The list of conversations being muted
438      */
439     public static function unmute_conversations(int $userid, array $conversationids) {
440         global $CFG, $USER;
442         // Check if messaging is enabled.
443         if (empty($CFG->messaging)) {
444             throw new moodle_exception('disabled', 'message');
445         }
447         // Validate context.
448         $context = context_system::instance();
449         self::validate_context($context);
451         $params = ['userid' => $userid, 'conversationids' => $conversationids];
452         $params = self::validate_parameters(self::unmute_conversations_parameters(), $params);
454         $capability = 'moodle/site:manageallmessaging';
455         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
456             throw new required_capability_exception($context, $capability, 'nopermissions', '');
457         }
459         foreach ($params['conversationids'] as $conversationid) {
460             \core_message\api::unmute_conversation($params['userid'], $conversationid);
461         }
463         return [];
464     }
466     /**
467      * Unmute conversations return description.
468      *
469      * @return external_description
470      */
471     public static function unmute_conversations_returns() {
472         return new external_warnings();
473     }
475     /**
476      * Block user parameters description.
477      *
478      * @return external_function_parameters
479      */
480     public static function block_user_parameters() {
481         return new external_function_parameters(
482             [
483                 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
484                 'blockeduserid' => new external_value(PARAM_INT, 'The id of the user being blocked'),
485             ]
486         );
487     }
489     /**
490      * Blocks a user.
491      *
492      * @param int $userid The id of the user who is blocking
493      * @param int $blockeduserid The id of the user being blocked
494      * @return external_description
495      */
496     public static function block_user(int $userid, int $blockeduserid) {
497         global $CFG, $USER;
499         // Check if messaging is enabled.
500         if (empty($CFG->messaging)) {
501             throw new moodle_exception('disabled', 'message');
502         }
504         // Validate context.
505         $context = context_system::instance();
506         self::validate_context($context);
508         $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
509         $params = self::validate_parameters(self::block_user_parameters(), $params);
511         $capability = 'moodle/site:manageallmessaging';
512         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
513             throw new required_capability_exception($context, $capability, 'nopermissions', '');
514         }
516         // If the blocking is going to be useless then don't do it.
517         if (\core_message\api::can_send_message($userid, $blockeduserid, true)) {
518             return [];
519         }
521         if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
522             \core_message\api::block_user($params['userid'], $params['blockeduserid']);
523         }
525         return [];
526     }
528     /**
529      * Block user return description.
530      *
531      * @return external_description
532      */
533     public static function block_user_returns() {
534         return new external_warnings();
535     }
537     /**
538      * Unblock user parameters description.
539      *
540      * @return external_function_parameters
541      */
542     public static function unblock_user_parameters() {
543         return new external_function_parameters(
544             [
545                 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
546                 'unblockeduserid' => new external_value(PARAM_INT, 'The id of the user being unblocked'),
547             ]
548         );
549     }
551     /**
552      * Unblock user.
553      *
554      * @param int $userid The id of the user who is unblocking
555      * @param int $unblockeduserid The id of the user being unblocked
556      */
557     public static function unblock_user(int $userid, int $unblockeduserid) {
558         global $CFG, $USER;
560         // Check if messaging is enabled.
561         if (empty($CFG->messaging)) {
562             throw new moodle_exception('disabled', 'message');
563         }
565         // Validate context.
566         $context = context_system::instance();
567         self::validate_context($context);
569         $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
570         $params = self::validate_parameters(self::unblock_user_parameters(), $params);
572         $capability = 'moodle/site:manageallmessaging';
573         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
574             throw new required_capability_exception($context, $capability, 'nopermissions', '');
575         }
577         \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
579         return [];
580     }
582     /**
583      * Unblock user return description.
584      *
585      * @return external_description
586      */
587     public static function unblock_user_returns() {
588         return new external_warnings();
589     }
591     /**
592      * Returns contact requests parameters description.
593      *
594      * @return external_function_parameters
595      */
596     public static function get_contact_requests_parameters() {
597         return new external_function_parameters(
598             [
599                 'userid' => new external_value(PARAM_INT, 'The id of the user we want the requests for'),
600                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
601                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
602             ]
603         );
604     }
606     /**
607      * Handles returning the contact requests for a user.
608      *
609      * This also includes the user data necessary to display information
610      * about the user.
611      *
612      * It will not include blocked users.
613      *
614      * @param int $userid The id of the user we want to get the contact requests for
615      * @param int $limitfrom
616      * @param int $limitnum
617      */
618     public static function get_contact_requests(int $userid, int $limitfrom = 0, int $limitnum = 0) {
619         global $CFG, $USER;
621         // Check if messaging is enabled.
622         if (empty($CFG->messaging)) {
623             throw new moodle_exception('disabled', 'message');
624         }
626         // Validate context.
627         $context = context_system::instance();
628         self::validate_context($context);
630         $params = [
631             'userid' => $userid,
632             'limitfrom' => $limitfrom,
633             'limitnum' => $limitnum
634         ];
635         $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
637         $capability = 'moodle/site:manageallmessaging';
638         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
639             throw new required_capability_exception($context, $capability, 'nopermissions', '');
640         }
642         return \core_message\api::get_contact_requests($params['userid'], $params['limitfrom'], $params['limitnum']);
643     }
645     /**
646      * Returns the contact requests return description.
647      *
648      * @return external_description
649      */
650     public static function get_contact_requests_returns() {
651         return new external_multiple_structure(
652             self::get_conversation_member_structure()
653         );
654     }
656     /**
657      * Returns the number of contact requests the user has received parameters description.
658      *
659      * @return external_function_parameters
660      */
661     public static function get_received_contact_requests_count_parameters() {
662         return new external_function_parameters(
663             array(
664                 'userid' => new external_value(PARAM_INT, 'The id of the user we want to return the number of ' .
665                     'received contact requests for', VALUE_REQUIRED),
666             )
667         );
668     }
670     /**
671      * Returns the number of contact requests the user has received.
672      *
673      * @param int $userid The ID of the user we want to return the number of received contact requests for
674      * @return external_value
675      */
676     public static function get_received_contact_requests_count(int $userid) {
677         global $CFG, $USER;
679         // Check if messaging is enabled.
680         if (empty($CFG->messaging)) {
681             throw new moodle_exception('disabled', 'message');
682         }
684         // Validate context.
685         $context = context_system::instance();
686         self::validate_context($context);
688         $params = [
689             'userid' => $userid,
690         ];
691         $params = self::validate_parameters(self::get_received_contact_requests_count_parameters(), $params);
693         $capability = 'moodle/site:manageallmessaging';
694         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
695             throw new required_capability_exception($context, $capability, 'nopermissions', '');
696         }
698         return \core_message\api::get_received_contact_requests_count($params['userid']);
699     }
701     /**
702      * Returns the number of contact requests the user has received return description.
703      *
704      * @return external_value
705      */
706     public static function get_received_contact_requests_count_returns() {
707         return new external_value(PARAM_INT, 'The number of received contact requests');
708     }
710     /**
711      * Returns get conversation members parameters description.
712      *
713      * @return external_function_parameters
714      */
715     public static function get_conversation_members_parameters() {
716         return new external_function_parameters(
717             [
718                 'userid' => new external_value(PARAM_INT, 'The id of the user we are performing this action on behalf of'),
719                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation'),
720                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Do we want to include contact requests?',
721                     VALUE_DEFAULT, false),
722                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Do we want to include privacy info?',
723                     VALUE_DEFAULT, false),
724                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
725                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
726             ]
727         );
728     }
730     /**
731      * Returns a list of conversation members.
732      *
733      * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info.
734      * @param int $conversationid The id of the conversation
735      * @param bool $includecontactrequests Do we want to include contact requests with this data?
736      * @param bool $includeprivacyinfo Do we want to include privacy info?
737      * @param int $limitfrom
738      * @param int $limitnum
739      * @return array
740      */
741     public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false,
742                                                     bool $includeprivacyinfo = false, int $limitfrom = 0, int $limitnum = 0) {
743         global $CFG, $USER;
745         // Check if messaging is enabled.
746         if (empty($CFG->messaging)) {
747             throw new moodle_exception('disabled', 'message');
748         }
750         // Validate context.
751         $context = context_system::instance();
752         self::validate_context($context);
754         $params = [
755             'userid' => $userid,
756             'conversationid' => $conversationid,
757             'includecontactrequests' => $includecontactrequests,
758             'includeprivacyinfo' => $includeprivacyinfo,
759             'limitfrom' => $limitfrom,
760             'limitnum' => $limitnum
761         ];
762         $params = self::validate_parameters(self::get_conversation_members_parameters(), $params);
764         $capability = 'moodle/site:manageallmessaging';
765         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
766             throw new required_capability_exception($context, $capability, 'nopermissions', '');
767         }
769         // The user needs to be a part of the conversation before querying who the members are.
770         if (!\core_message\api::is_user_in_conversation($params['userid'], $params['conversationid'])) {
771             throw new moodle_exception('You are not a member of this conversation.');
772         }
774         return \core_message\api::get_conversation_members($params['userid'], $params['conversationid'], $params['includecontactrequests'],
775             $params['includeprivacyinfo'], $params['limitfrom'], $params['limitnum']);
776     }
778     /**
779      * Returns the get conversation members return description.
780      *
781      * @return external_description
782      */
783     public static function get_conversation_members_returns() {
784         return new external_multiple_structure(
785             self::get_conversation_member_structure()
786         );
787     }
789     /**
790      * Creates a contact request parameters description.
791      *
792      * @return external_function_parameters
793      */
794     public static function create_contact_request_parameters() {
795         return new external_function_parameters(
796             [
797                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
798                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
799             ]
800         );
801     }
803     /**
804      * Creates a contact request.
805      *
806      * @param int $userid The id of the user who is creating the contact request
807      * @param int $requesteduserid The id of the user being requested
808      */
809     public static function create_contact_request(int $userid, int $requesteduserid) {
810         global $CFG, $USER;
812         // Check if messaging is enabled.
813         if (empty($CFG->messaging)) {
814             throw new moodle_exception('disabled', 'message');
815         }
817         // Validate context.
818         $context = context_system::instance();
819         self::validate_context($context);
821         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
822         $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
824         $capability = 'moodle/site:manageallmessaging';
825         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
826             throw new required_capability_exception($context, $capability, 'nopermissions', '');
827         }
829         $result = [
830             'warnings' => []
831         ];
833         if (!\core_message\api::can_create_contact($params['userid'], $params['requesteduserid'])) {
834             $result['warnings'][] = [
835                 'item' => 'user',
836                 'itemid' => $params['requesteduserid'],
837                 'warningcode' => 'cannotcreatecontactrequest',
838                 'message' => 'You are unable to create a contact request for this user'
839             ];
840         } else {
841             if ($requests = \core_message\api::get_contact_requests_between_users($params['userid'], $params['requesteduserid'])) {
842                 // There should only ever be one but just in case there are multiple then we can return the first.
843                 $result['request'] = array_shift($requests);
844             } else {
845                 $result['request'] = \core_message\api::create_contact_request($params['userid'], $params['requesteduserid']);
846             }
847         }
849         return $result;
850     }
852     /**
853      * Creates a contact request return description.
854      *
855      * @return external_description
856      */
857     public static function create_contact_request_returns() {
858         return new external_single_structure(
859             array(
860                 'request' => new external_single_structure(
861                     array(
862                         'id' => new external_value(PARAM_INT, 'Message id'),
863                         'userid' => new external_value(PARAM_INT, 'User from id'),
864                         'requesteduserid' => new external_value(PARAM_INT, 'User to id'),
865                         'timecreated' => new external_value(PARAM_INT, 'Time created'),
866                     ),
867                     'request record',
868                     VALUE_OPTIONAL
869                 ),
870                 'warnings' => new external_warnings()
871             )
872         );
873     }
875     /**
876      * Confirm a contact request parameters description.
877      *
878      * @return external_function_parameters
879      */
880     public static function confirm_contact_request_parameters() {
881         return new external_function_parameters(
882             [
883                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
884                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
885             ]
886         );
887     }
889     /**
890      * Confirm a contact request.
891      *
892      * @param int $userid The id of the user who is creating the contact request
893      * @param int $requesteduserid The id of the user being requested
894      */
895     public static function confirm_contact_request(int $userid, int $requesteduserid) {
896         global $CFG, $USER;
898         // Check if messaging is enabled.
899         if (empty($CFG->messaging)) {
900             throw new moodle_exception('disabled', 'message');
901         }
903         // Validate context.
904         $context = context_system::instance();
905         self::validate_context($context);
907         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
908         $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
910         $capability = 'moodle/site:manageallmessaging';
911         if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
912             throw new required_capability_exception($context, $capability, 'nopermissions', '');
913         }
915         \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
917         return [];
918     }
920     /**
921      * Confirm a contact request return description.
922      *
923      * @return external_description
924      */
925     public static function confirm_contact_request_returns() {
926         return new external_warnings();
927     }
929     /**
930      * Declines a contact request parameters description.
931      *
932      * @return external_function_parameters
933      */
934     public static function decline_contact_request_parameters() {
935         return new external_function_parameters(
936             [
937                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
938                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
939             ]
940         );
941     }
943     /**
944      * Declines a contact request.
945      *
946      * @param int $userid The id of the user who is creating the contact request
947      * @param int $requesteduserid The id of the user being requested
948      */
949     public static function decline_contact_request(int $userid, int $requesteduserid) {
950         global $CFG, $USER;
952         // Check if messaging is enabled.
953         if (empty($CFG->messaging)) {
954             throw new moodle_exception('disabled', 'message');
955         }
957         // Validate context.
958         $context = context_system::instance();
959         self::validate_context($context);
961         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
962         $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
964         $capability = 'moodle/site:manageallmessaging';
965         if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
966             throw new required_capability_exception($context, $capability, 'nopermissions', '');
967         }
969         \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
971         return [];
972     }
974     /**
975      * Declines a contact request return description.
976      *
977      * @return external_description
978      */
979     public static function decline_contact_request_returns() {
980         return new external_warnings();
981     }
983     /**
984      * Return the structure of a message area contact.
985      *
986      * @return external_single_structure
987      * @since Moodle 3.2
988      */
989     private static function get_messagearea_contact_structure() {
990         return new external_single_structure(
991             array(
992                 'userid' => new external_value(PARAM_INT, 'The user\'s id'),
993                 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
994                 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
995                 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
996                 'ismessaging' => new external_value(PARAM_BOOL, 'If we are messaging the user'),
997                 'sentfromcurrentuser' => new external_value(PARAM_BOOL, 'Was the last message sent from the current user?'),
998                 'lastmessage' => new external_value(PARAM_NOTAGS, 'The user\'s last message'),
999                 'lastmessagedate' => new external_value(PARAM_INT, 'Timestamp for last message', VALUE_DEFAULT, null),
1000                 'messageid' => new external_value(PARAM_INT, 'The unique search message id', VALUE_DEFAULT, null),
1001                 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1002                 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1003                 'isread' => new external_value(PARAM_BOOL, 'If the user has read the message'),
1004                 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1005                 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1006                     VALUE_DEFAULT, null),
1007                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation', VALUE_DEFAULT, null),
1008             )
1009         );
1010     }
1012     /**
1013      * Return the structure of a conversation.
1014      *
1015      * @return external_single_structure
1016      * @since Moodle 3.6
1017      */
1018     private static function get_conversation_structure() {
1019         return new external_single_structure(
1020             array(
1021                 'id' => new external_value(PARAM_INT, 'The conversation id'),
1022                 'name' => new external_value(PARAM_RAW, 'The conversation name, if set', VALUE_DEFAULT, null),
1023                 'subname' => new external_value(PARAM_RAW, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null),
1024                 'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null),
1025                 'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group,3=self)'),
1026                 'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'),
1027                 'ismuted' => new external_value(PARAM_BOOL, 'If the user muted this conversation'),
1028                 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this conversation as a favourite'),
1029                 'isread' => new external_value(PARAM_BOOL, 'If the user has read all messages in the conversation'),
1030                 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1031                     VALUE_DEFAULT, null),
1032                 'members' => new external_multiple_structure(
1033                     self::get_conversation_member_structure()
1034                 ),
1035                 'messages' => new external_multiple_structure(
1036                     self::get_conversation_message_structure()
1037                 ),
1038                 'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
1039                     'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
1040             )
1041         );
1042     }
1044     /**
1045      * Return the structure of a conversation member.
1046      *
1047      * @return external_single_structure
1048      * @since Moodle 3.6
1049      */
1050     private static function get_conversation_member_structure() {
1051         $result = [
1052             'id' => new external_value(PARAM_INT, 'The user id'),
1053             'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1054             'profileurl' => new external_value(PARAM_URL, 'The link to the user\'s profile page'),
1055             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1056             'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1057             'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1058             'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1059             'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1060             'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'),
1061             'isdeleted' => new external_value(PARAM_BOOL, 'Is the user deleted?'),
1062             'canmessageevenifblocked' => new external_value(PARAM_BOOL,
1063                 'If the user can still message even if they get blocked'),
1064             'canmessage' => new external_value(PARAM_BOOL, 'If the user can be messaged'),
1065             'requirescontact' => new external_value(PARAM_BOOL, 'If the user requires to be contacts'),
1066         ];
1068         $result['contactrequests'] = new external_multiple_structure(
1069             new external_single_structure(
1070                 [
1071                     'id' => new external_value(PARAM_INT, 'The id of the contact request'),
1072                     'userid' => new external_value(PARAM_INT, 'The id of the user who created the contact request'),
1073                     'requesteduserid' => new external_value(PARAM_INT, 'The id of the user confirming the request'),
1074                     'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the contact request'),
1075                 ]
1076             ), 'The contact requests', VALUE_OPTIONAL
1077         );
1079         $result['conversations'] = new external_multiple_structure(new external_single_structure(
1080             array(
1081                 'id' => new external_value(PARAM_INT, 'Conversations id'),
1082                 'type' => new external_value(PARAM_INT, 'Conversation type: private or public'),
1083                 'name' => new external_value(PARAM_RAW, 'Multilang compatible conversation name'. VALUE_OPTIONAL),
1084                 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'),
1085             ), 'information about conversation', VALUE_OPTIONAL),
1086             'Conversations between users', VALUE_OPTIONAL
1087         );
1089         return new external_single_structure(
1090             $result
1091         );
1092     }
1094     /**
1095      * Return the structure of a message area message.
1096      *
1097      * @return external_single_structure
1098      * @since Moodle 3.6
1099      */
1100     private static function get_conversation_message_structure() {
1101         return new external_single_structure(
1102             array(
1103                 'id' => new external_value(PARAM_INT, 'The id of the message'),
1104                 'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1105                 'text' => new external_value(PARAM_RAW, 'The text of the message'),
1106                 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1107             )
1108         );
1109     }
1111     /**
1112      * Get messagearea message search users parameters.
1113      *
1114      * @return external_function_parameters
1115      * @since 3.6
1116      */
1117     public static function message_search_users_parameters() {
1118         return new external_function_parameters(
1119             array(
1120                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1121                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1122                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1123                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1124             )
1125         );
1126     }
1128     /**
1129      * Get search users results.
1130      *
1131      * @param int $userid The id of the user who is performing the search
1132      * @param string $search The string being searched
1133      * @param int $limitfrom
1134      * @param int $limitnum
1135      * @return array
1136      * @throws moodle_exception
1137      * @since 3.6
1138      */
1139     public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) {
1140         global $USER;
1142         $systemcontext = context_system::instance();
1144         $params = array(
1145             'userid' => $userid,
1146             'search' => $search,
1147             'limitfrom' => $limitfrom,
1148             'limitnum' => $limitnum
1149         );
1150         $params = self::validate_parameters(self::message_search_users_parameters(), $params);
1151         self::validate_context($systemcontext);
1153         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1154             throw new moodle_exception('You do not have permission to perform this action.');
1155         }
1157         list($contacts, $noncontacts) = \core_message\api::message_search_users(
1158             $params['userid'],
1159             $params['search'],
1160             $params['limitfrom'],
1161             $params['limitnum']);
1163         return array('contacts' => $contacts, 'noncontacts' => $noncontacts);
1164     }
1166     /**
1167      * Get messagearea message search users returns.
1168      *
1169      * @return external_single_structure
1170      * @since 3.2
1171      */
1172     public static function message_search_users_returns() {
1173         return new external_single_structure(
1174             array(
1175                 'contacts' => new external_multiple_structure(
1176                     self::get_conversation_member_structure()
1177                 ),
1178                 'noncontacts' => new external_multiple_structure(
1179                     self::get_conversation_member_structure()
1180                 )
1181             )
1182         );
1183     }
1185     /**
1186      * Get messagearea search messages parameters.
1187      *
1188      * @return external_function_parameters
1189      * @since 3.2
1190      */
1191     public static function data_for_messagearea_search_messages_parameters() {
1192         return new external_function_parameters(
1193             array(
1194                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1195                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1196                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1197                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1198             )
1199         );
1200     }
1202     /**
1203      * Get messagearea search messages results.
1204      *
1205      * @param int $userid The id of the user who is performing the search
1206      * @param string $search The string being searched
1207      * @param int $limitfrom
1208      * @param int $limitnum
1209      * @return stdClass
1210      * @throws moodle_exception
1211      * @since 3.2
1212      */
1213     public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
1214         global $CFG, $USER;
1216         // Check if messaging is enabled.
1217         if (empty($CFG->messaging)) {
1218             throw new moodle_exception('disabled', 'message');
1219         }
1221         $systemcontext = context_system::instance();
1223         $params = array(
1224             'userid' => $userid,
1225             'search' => $search,
1226             'limitfrom' => $limitfrom,
1227             'limitnum' => $limitnum
1229         );
1230         $params = self::validate_parameters(self::data_for_messagearea_search_messages_parameters(), $params);
1231         self::validate_context($systemcontext);
1233         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1234             throw new moodle_exception('You do not have permission to perform this action.');
1235         }
1237         $messages = \core_message\api::search_messages(
1238             $params['userid'],
1239             $params['search'],
1240             $params['limitfrom'],
1241             $params['limitnum']
1242         );
1244         $data = new \stdClass();
1245         $data->contacts = [];
1246         foreach ($messages as $message) {
1247             $contact = new \stdClass();
1248             $contact->userid = $message->userid;
1249             $contact->fullname = $message->fullname;
1250             $contact->profileimageurl = $message->profileimageurl;
1251             $contact->profileimageurlsmall = $message->profileimageurlsmall;
1252             $contact->messageid = $message->messageid;
1253             $contact->ismessaging = $message->ismessaging;
1254             $contact->sentfromcurrentuser = false;
1255             if ($message->lastmessage) {
1256                 if ($message->userid !== $message->useridfrom) {
1257                     $contact->sentfromcurrentuser = true;
1258                 }
1259                 $contact->lastmessage = shorten_text($message->lastmessage, 60);
1260             } else {
1261                 $contact->lastmessage = null;
1262             }
1263             $contact->lastmessagedate = $message->lastmessagedate;
1264             $contact->showonlinestatus = is_null($message->isonline) ? false : true;
1265             $contact->isonline = $message->isonline;
1266             $contact->isblocked = $message->isblocked;
1267             $contact->isread = $message->isread;
1268             $contact->unreadcount = $message->unreadcount;
1269             $contact->conversationid = $message->conversationid;
1271             $data->contacts[] = $contact;
1272         }
1274         return $data;
1275     }
1277     /**
1278      * Get messagearea search messages returns.
1279      *
1280      * @return external_single_structure
1281      * @since 3.2
1282      */
1283     public static function data_for_messagearea_search_messages_returns() {
1284         return new external_single_structure(
1285             array(
1286                 'contacts' => new external_multiple_structure(
1287                     self::get_messagearea_contact_structure()
1288                 )
1289             )
1290         );
1291     }
1293     /**
1294      * Get conversations parameters.
1295      *
1296      * @return external_function_parameters
1297      * @since 3.6
1298      */
1299     public static function get_conversations_parameters() {
1300         return new external_function_parameters(
1301             array(
1302                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1303                 'limitfrom' => new external_value(PARAM_INT, 'The offset to start at', VALUE_DEFAULT, 0),
1304                 'limitnum' => new external_value(PARAM_INT, 'Limit number of conversations to this', VALUE_DEFAULT, 0),
1305                 'type' => new external_value(PARAM_INT, 'Filter by type', VALUE_DEFAULT, null),
1306                 'favourites' => new external_value(PARAM_BOOL, 'Whether to restrict the results to contain NO favourite
1307                 conversations (false), ONLY favourite conversation (true), or ignore any restriction altogether (null)',
1308                     VALUE_DEFAULT, null),
1309                 'mergeself' => new external_value(PARAM_BOOL, 'Whether to include self-conversations (true) or ONLY private
1310                     conversations (false) when private conversations are requested.',
1311                     VALUE_DEFAULT, false),
1312             )
1313         );
1314     }
1316     /**
1317      * Get the list of conversations for the user.
1318      *
1319      * @param int $userid The id of the user who is performing the search
1320      * @param int $limitfrom
1321      * @param int $limitnum
1322      * @param int|null $type
1323      * @param bool|null $favourites
1324      * @param bool $mergeself whether to include self-conversations (true) or ONLY private conversations (false)
1325      *             when private conversations are requested.
1326      * @return stdClass
1327      * @throws \moodle_exception if the messaging feature is disabled on the site.
1328      * @since 3.2
1329      */
1330     public static function get_conversations($userid, $limitfrom = 0, $limitnum = 0, int $type = null, bool $favourites = null,
1331             bool $mergeself = false) {
1332         global $CFG, $USER;
1334         // All the standard BL checks.
1335         if (empty($CFG->messaging)) {
1336             throw new moodle_exception('disabled', 'message');
1337         }
1339         $params = array(
1340             'userid' => $userid,
1341             'limitfrom' => $limitfrom,
1342             'limitnum' => $limitnum,
1343             'type' => $type,
1344             'favourites' => $favourites,
1345             'mergeself' => $mergeself
1346         );
1347         $params = self::validate_parameters(self::get_conversations_parameters(), $params);
1349         $systemcontext = context_system::instance();
1350         self::validate_context($systemcontext);
1352         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1353             throw new moodle_exception('You do not have permission to perform this action.');
1354         }
1356         $conversations = \core_message\api::get_conversations(
1357             $params['userid'],
1358             $params['limitfrom'],
1359             $params['limitnum'],
1360             $params['type'],
1361             $params['favourites'],
1362             $params['mergeself']
1363         );
1365         return (object) ['conversations' => $conversations];
1366     }
1368     /**
1369      * Get conversations returns.
1370      *
1371      * @return external_single_structure
1372      * @since 3.6
1373      */
1374     public static function get_conversations_returns() {
1375         return new external_single_structure(
1376             [
1377                 'conversations' => new external_multiple_structure(
1378                     self::get_conversation_structure(true)
1379                 )
1380             ]
1381         );
1382     }
1384     /**
1385      * Get conversation parameters.
1386      *
1387      * @return external_function_parameters
1388      */
1389     public static function get_conversation_parameters() {
1390         return new external_function_parameters(
1391             array(
1392                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1393                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation to fetch'),
1394                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1395                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1396                 'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1397                 'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1398                 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1399                 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1400                 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1401             )
1402         );
1403     }
1405     /**
1406      * Get a single conversation.
1407      *
1408      * @param int $userid The user id to get the conversation for
1409      * @param int $conversationid The id of the conversation to fetch
1410      * @param bool $includecontactrequests Should contact requests be included between members
1411      * @param bool $includeprivacyinfo Should privacy info be included between members
1412      * @param int $memberlimit Limit number of members to load
1413      * @param int $memberoffset Offset members by this amount
1414      * @param int $messagelimit Limit number of messages to load
1415      * @param int $messageoffset Offset the messages
1416      * @param bool $newestmessagesfirst Order messages by newest first
1417      * @return stdClass
1418      * @throws \moodle_exception if the messaging feature is disabled on the site.
1419      */
1420     public static function get_conversation(
1421         int $userid,
1422         int $conversationid,
1423         bool $includecontactrequests = false,
1424         bool $includeprivacyinfo = false,
1425         int $memberlimit = 0,
1426         int $memberoffset = 0,
1427         int $messagelimit = 0,
1428         int $messageoffset = 0,
1429         bool $newestmessagesfirst = true
1430     ) {
1431         global $CFG, $DB, $USER;
1433         // All the standard BL checks.
1434         if (empty($CFG->messaging)) {
1435             throw new moodle_exception('disabled', 'message');
1436         }
1438         $params = [
1439             'userid' => $userid,
1440             'conversationid' => $conversationid,
1441             'includecontactrequests' => $includecontactrequests,
1442             'includeprivacyinfo' => $includeprivacyinfo,
1443             'memberlimit' => $memberlimit,
1444             'memberoffset' => $memberoffset,
1445             'messagelimit' => $messagelimit,
1446             'messageoffset' => $messageoffset,
1447             'newestmessagesfirst' => $newestmessagesfirst
1448         ];
1449         self::validate_parameters(self::get_conversation_parameters(), $params);
1451         $systemcontext = context_system::instance();
1452         self::validate_context($systemcontext);
1454         $conversation = \core_message\api::get_conversation(
1455             $params['userid'],
1456             $params['conversationid'],
1457             $params['includecontactrequests'],
1458             $params['includeprivacyinfo'],
1459             $params['memberlimit'],
1460             $params['memberoffset'],
1461             $params['messagelimit'],
1462             $params['messageoffset'],
1463             $params['newestmessagesfirst']
1464         );
1466         if ($conversation) {
1467             return $conversation;
1468         } else {
1469             // We have to throw an exception here because the external functions annoyingly
1470             // don't accept null to be returned for a single structure.
1471             throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1472         }
1473     }
1475     /**
1476      * Get conversation returns.
1477      *
1478      * @return external_single_structure
1479      */
1480     public static function get_conversation_returns() {
1481         return self::get_conversation_structure();
1482     }
1484     /**
1485      * Get conversation parameters.
1486      *
1487      * @return external_function_parameters
1488      */
1489     public static function get_conversation_between_users_parameters() {
1490         return new external_function_parameters(
1491             array(
1492                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1493                 'otheruserid' => new external_value(PARAM_INT, 'The other user id'),
1494                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1495                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1496                 'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1497                 'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1498                 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1499                 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1500                 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1501             )
1502         );
1503     }
1505     /**
1506      * Get a single conversation between users.
1507      *
1508      * @param int $userid The user id to get the conversation for
1509      * @param int $otheruserid The other user id
1510      * @param bool $includecontactrequests Should contact requests be included between members
1511      * @param bool $includeprivacyinfo Should privacy info be included between members
1512      * @param int $memberlimit Limit number of members to load
1513      * @param int $memberoffset Offset members by this amount
1514      * @param int $messagelimit Limit number of messages to load
1515      * @param int $messageoffset Offset the messages
1516      * @param bool $newestmessagesfirst Order messages by newest first
1517      * @return stdClass
1518      * @throws \moodle_exception if the messaging feature is disabled on the site.
1519      */
1520     public static function get_conversation_between_users(
1521         int $userid,
1522         int $otheruserid,
1523         bool $includecontactrequests = false,
1524         bool $includeprivacyinfo = false,
1525         int $memberlimit = 0,
1526         int $memberoffset = 0,
1527         int $messagelimit = 0,
1528         int $messageoffset = 0,
1529         bool $newestmessagesfirst = true
1530     ) {
1531         global $CFG, $DB, $USER;
1533         // All the standard BL checks.
1534         if (empty($CFG->messaging)) {
1535             throw new moodle_exception('disabled', 'message');
1536         }
1538         $params = [
1539             'userid' => $userid,
1540             'otheruserid' => $otheruserid,
1541             'includecontactrequests' => $includecontactrequests,
1542             'includeprivacyinfo' => $includeprivacyinfo,
1543             'memberlimit' => $memberlimit,
1544             'memberoffset' => $memberoffset,
1545             'messagelimit' => $messagelimit,
1546             'messageoffset' => $messageoffset,
1547             'newestmessagesfirst' => $newestmessagesfirst
1548         ];
1549         self::validate_parameters(self::get_conversation_between_users_parameters(), $params);
1551         $systemcontext = context_system::instance();
1552         self::validate_context($systemcontext);
1554         $conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']]);
1555         $conversation = null;
1557         if ($conversationid) {
1558             $conversation = \core_message\api::get_conversation(
1559                 $params['userid'],
1560                 $conversationid,
1561                 $params['includecontactrequests'],
1562                 $params['includeprivacyinfo'],
1563                 $params['memberlimit'],
1564                 $params['memberoffset'],
1565                 $params['messagelimit'],
1566                 $params['messageoffset'],
1567                 $params['newestmessagesfirst']
1568             );
1569         }
1571         if ($conversation) {
1572             return $conversation;
1573         } else {
1574             // We have to throw an exception here because the external functions annoyingly
1575             // don't accept null to be returned for a single structure.
1576             throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1577         }
1578     }
1580     /**
1581      * Get conversation returns.
1582      *
1583      * @return external_single_structure
1584      */
1585     public static function get_conversation_between_users_returns() {
1586         return self::get_conversation_structure(true);
1587     }
1589     /**
1590      * Get self-conversation parameters.
1591      *
1592      * @return external_function_parameters
1593      */
1594     public static function get_self_conversation_parameters() {
1595         return new external_function_parameters(
1596             array(
1597                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing self-conversations for'),
1598                 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1599                 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1600                 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1601             )
1602         );
1603     }
1605     /**
1606      * Get a single self-conversation.
1607      *
1608      * @param int $userid The user id to get the self-conversation for
1609      * @param int $messagelimit Limit number of messages to load
1610      * @param int $messageoffset Offset the messages
1611      * @param bool $newestmessagesfirst Order messages by newest first
1612      * @return stdClass
1613      * @throws \moodle_exception if the messaging feature is disabled on the site.
1614      * @since Moodle 3.7
1615      */
1616     public static function get_self_conversation(
1617         int $userid,
1618         int $messagelimit = 0,
1619         int $messageoffset = 0,
1620         bool $newestmessagesfirst = true
1621     ) {
1622         global $CFG;
1624         // All the standard BL checks.
1625         if (empty($CFG->messaging)) {
1626             throw new moodle_exception('disabled', 'message');
1627         }
1629         $params = [
1630             'userid' => $userid,
1631             'messagelimit' => $messagelimit,
1632             'messageoffset' => $messageoffset,
1633             'newestmessagesfirst' => $newestmessagesfirst
1634         ];
1635         self::validate_parameters(self::get_self_conversation_parameters(), $params);
1637         $systemcontext = context_system::instance();
1638         self::validate_context($systemcontext);
1640         $conversation = \core_message\api::get_self_conversation($params['userid']);
1642         if ($conversation) {
1643             $conversation = \core_message\api::get_conversation(
1644                 $params['userid'],
1645                 $conversation->id,
1646                 false,
1647                 false,
1648                 0,
1649                 0,
1650                 $params['messagelimit'],
1651                 $params['messageoffset'],
1652                 $params['newestmessagesfirst']
1653             );
1654         }
1656         if ($conversation) {
1657             return $conversation;
1658         } else {
1659             // We have to throw an exception here because the external functions annoyingly
1660             // don't accept null to be returned for a single structure.
1661             throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1662         }
1663     }
1665     /**
1666      * Get conversation returns.
1667      *
1668      * @return external_single_structure
1669      */
1670     public static function get_self_conversation_returns() {
1671         return self::get_conversation_structure();
1672     }
1674     /**
1675      * The conversation messages parameters.
1676      *
1677      * @return external_function_parameters
1678      * @since 3.6
1679      */
1680     public static function get_conversation_messages_parameters() {
1681         return new external_function_parameters(
1682             array(
1683                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1684                 'convid' => new external_value(PARAM_INT, 'The conversation id'),
1685                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1686                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1687                 'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
1688                 'timefrom' => new external_value(PARAM_INT,
1689                     'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
1690             )
1691         );
1692     }
1694     /**
1695      * Get conversation messages.
1696      *
1697      * @param  int $currentuserid The current user's id.
1698      * @param  int $convid The conversation id.
1699      * @param  int $limitfrom Return a subset of records, starting at this point (optional).
1700      * @param  int $limitnum Return a subset comprising this many records in total (optional, required if $limitfrom is set).
1701      * @param  bool $newest True for getting first newest messages, false otherwise.
1702      * @param  int  $timefrom The time from the conversation messages to get.
1703      * @return array The messages and members who have sent some of these messages.
1704      * @throws moodle_exception
1705      * @since 3.6
1706      */
1707     public static function get_conversation_messages(int $currentuserid, int $convid, int $limitfrom = 0, int $limitnum = 0,
1708                                                          bool $newest = false, int $timefrom = 0) {
1709         global $CFG, $USER;
1711         // Check if messaging is enabled.
1712         if (empty($CFG->messaging)) {
1713             throw new moodle_exception('disabled', 'message');
1714         }
1716         $systemcontext = context_system::instance();
1718         $params = array(
1719             'currentuserid' => $currentuserid,
1720             'convid' => $convid,
1721             'limitfrom' => $limitfrom,
1722             'limitnum' => $limitnum,
1723             'newest' => $newest,
1724             'timefrom' => $timefrom,
1725         );
1726         $params = self::validate_parameters(self::get_conversation_messages_parameters(), $params);
1727         self::validate_context($systemcontext);
1729         if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1730             throw new moodle_exception('You do not have permission to perform this action.');
1731         }
1733         // Check that the user belongs to the conversation.
1734         if (!\core_message\api::is_user_in_conversation($params['currentuserid'], $params['convid'])) {
1735             throw new moodle_exception('User is not part of conversation.');
1736         }
1738         $sort = $newest ? 'timecreated DESC' : 'timecreated ASC';
1740         // We need to enforce a one second delay on messages to avoid race conditions of current
1741         // messages still being sent.
1742         //
1743         // There is a chance that we could request messages before the current time's
1744         // second has elapsed and while other messages are being sent in that same second. In which
1745         // case those messages will be lost.
1746         //
1747         // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
1748         $timeto = empty($params['timefrom']) ? 0 : time() - 1;
1750         // No requesting messages from the current time, as stated above.
1751         if ($params['timefrom'] == time()) {
1752             $messages = [];
1753         } else {
1754             $messages = \core_message\api::get_conversation_messages(
1755                 $params['currentuserid'],
1756                 $params['convid'],
1757                 $params['limitfrom'],
1758                 $params['limitnum'],
1759                 $sort,
1760                 $params['timefrom'],
1761                 $timeto);
1762         }
1764         return $messages;
1765     }
1767     /**
1768      * The messagearea messages return structure.
1769      *
1770      * @return external_single_structure
1771      * @since 3.6
1772      */
1773     public static function get_conversation_messages_returns() {
1774         return new external_single_structure(
1775             array(
1776                 'id' => new external_value(PARAM_INT, 'The conversation id'),
1777                 'members' => new external_multiple_structure(
1778                     self::get_conversation_member_structure()
1779                 ),
1780                 'messages' => new external_multiple_structure(
1781                     self::get_conversation_message_structure()
1782                 ),
1783             )
1784         );
1785     }
1787     /**
1788      * The user contacts return parameters.
1789      *
1790      * @return external_function_parameters
1791      */
1792     public static function get_user_contacts_parameters() {
1793         return new external_function_parameters(
1794             array(
1795                 'userid' => new external_value(PARAM_INT, 'The id of the user who we retrieving the contacts for'),
1796                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1797                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1798             )
1799         );
1800     }
1802     /**
1803      * Get user contacts.
1804      *
1805      * @param int $userid The id of the user who we are viewing conversations for
1806      * @param int $limitfrom
1807      * @param int $limitnum
1808      * @return array
1809      * @throws moodle_exception
1810      */
1811     public static function get_user_contacts(int $userid, int $limitfrom = 0, int $limitnum = 0) {
1812         global $CFG, $USER;
1814         // Check if messaging is enabled.
1815         if (empty($CFG->messaging)) {
1816             throw new moodle_exception('disabled', 'message');
1817         }
1819         $systemcontext = context_system::instance();
1821         $params = array(
1822             'userid' => $userid,
1823             'limitfrom' => $limitfrom,
1824             'limitnum' => $limitnum
1825         );
1826         $params = self::validate_parameters(self::get_user_contacts_parameters(), $params);
1827         self::validate_context($systemcontext);
1829         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1830             throw new moodle_exception('You do not have permission to perform this action.');
1831         }
1833         return \core_message\api::get_user_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
1834     }
1836     /**
1837      * The user contacts return structure.
1838      *
1839      * @return external_multiple_structure
1840      */
1841     public static function get_user_contacts_returns() {
1842         return new external_multiple_structure(
1843             self::get_conversation_member_structure()
1844         );
1845     }
1847     /**
1848      * Search contacts parameters description.
1849      *
1850      * @return external_function_parameters
1851      * @since Moodle 2.5
1852      */
1853     public static function search_contacts_parameters() {
1854         return new external_function_parameters(
1855             array(
1856                 'searchtext' => new external_value(PARAM_CLEAN, 'String the user\'s fullname has to match to be found'),
1857                 'onlymycourses' => new external_value(PARAM_BOOL, 'Limit search to the user\'s courses',
1858                     VALUE_DEFAULT, false)
1859             )
1860         );
1861     }
1863     /**
1864      * Search contacts.
1865      *
1866      * @param string $searchtext query string.
1867      * @param bool $onlymycourses limit the search to the user's courses only.
1868      * @return external_description
1869      * @since Moodle 2.5
1870      */
1871     public static function search_contacts($searchtext, $onlymycourses = false) {
1872         global $CFG, $USER, $PAGE;
1873         require_once($CFG->dirroot . '/user/lib.php');
1875         // Check if messaging is enabled.
1876         if (empty($CFG->messaging)) {
1877             throw new moodle_exception('disabled', 'message');
1878         }
1880         require_once($CFG->libdir . '/enrollib.php');
1882         $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses);
1883         $params = self::validate_parameters(self::search_contacts_parameters(), $params);
1885         // Extra validation, we do not allow empty queries.
1886         if ($params['searchtext'] === '') {
1887             throw new moodle_exception('querystringcannotbeempty');
1888         }
1890         $courseids = array();
1891         if ($params['onlymycourses']) {
1892             $mycourses = enrol_get_my_courses(array('id'));
1893             foreach ($mycourses as $mycourse) {
1894                 $courseids[] = $mycourse->id;
1895             }
1896         } else {
1897             $courseids[] = SITEID;
1898         }
1900         // Retrieving the users matching the query.
1901         $users = message_search_users($courseids, $params['searchtext']);
1902         $results = array();
1903         foreach ($users as $user) {
1904             $results[$user->id] = $user;
1905         }
1907         // Reorganising information.
1908         foreach ($results as &$user) {
1909             $newuser = array(
1910                 'id' => $user->id,
1911                 'fullname' => fullname($user)
1912             );
1914             // Avoid undefined property notice as phone not specified.
1915             $user->phone1 = null;
1916             $user->phone2 = null;
1918             $userpicture = new user_picture($user);
1919             $userpicture->size = 1; // Size f1.
1920             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
1921             $userpicture->size = 0; // Size f2.
1922             $newuser['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
1924             $user = $newuser;
1925         }
1927         return $results;
1928     }
1930     /**
1931      * Search contacts return description.
1932      *
1933      * @return external_description
1934      * @since Moodle 2.5
1935      */
1936     public static function search_contacts_returns() {
1937         return new external_multiple_structure(
1938             new external_single_structure(
1939                 array(
1940                     'id' => new external_value(PARAM_INT, 'User ID'),
1941                     'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
1942                     'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
1943                     'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL)
1944                 )
1945             ),
1946             'List of contacts'
1947         );
1948     }
1950     /**
1951      * Get messages parameters description.
1952      *
1953      * @return external_function_parameters
1954      * @since 2.8
1955      */
1956     public static function get_messages_parameters() {
1957         return new external_function_parameters(
1958             array(
1959                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
1960                 'useridfrom' => new external_value(
1961                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
1962                     VALUE_DEFAULT, 0),
1963                 'type' => new external_value(
1964                     PARAM_ALPHA, 'type of message to return, expected values are: notifications, conversations and both',
1965                     VALUE_DEFAULT, 'both'),
1966                 'read' => new external_value(PARAM_BOOL, 'true for getting read messages, false for unread', VALUE_DEFAULT, true),
1967                 'newestfirst' => new external_value(
1968                     PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
1969                     VALUE_DEFAULT, true),
1970                 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
1971                 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
1972             )
1973         );
1974     }
1976     /**
1977      * Get messages function implementation.
1978      *
1979      * @since  2.8
1980      * @throws invalid_parameter_exception
1981      * @throws moodle_exception
1982      * @param  int      $useridto       the user id who received the message
1983      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
1984      * @param  string   $type           type of message to return, expected values: notifications, conversations and both
1985      * @param  bool     $read           true for retreiving read messages, false for unread
1986      * @param  bool     $newestfirst    true for ordering by newest first, false for oldest first
1987      * @param  int      $limitfrom      limit from
1988      * @param  int      $limitnum       limit num
1989      * @return external_description
1990      */
1991     public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true,
1992                                         $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
1993         global $CFG, $USER, $PAGE;
1995         $warnings = array();
1997         $params = array(
1998             'useridto' => $useridto,
1999             'useridfrom' => $useridfrom,
2000             'type' => $type,
2001             'read' => $read,
2002             'newestfirst' => $newestfirst,
2003             'limitfrom' => $limitfrom,
2004             'limitnum' => $limitnum
2005         );
2007         $params = self::validate_parameters(self::get_messages_parameters(), $params);
2009         $context = context_system::instance();
2010         self::validate_context($context);
2012         $useridto = $params['useridto'];
2013         $useridfrom = $params['useridfrom'];
2014         $type = $params['type'];
2015         $read = $params['read'];
2016         $newestfirst = $params['newestfirst'];
2017         $limitfrom = $params['limitfrom'];
2018         $limitnum = $params['limitnum'];
2020         $allowedvalues = array('notifications', 'conversations', 'both');
2021         if (!in_array($type, $allowedvalues)) {
2022             throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
2023                 'allowed values are: ' . implode(',', $allowedvalues));
2024         }
2026         // Check if private messaging between users is allowed.
2027         if (empty($CFG->messaging)) {
2028             // If we are retreiving only conversations, and messaging is disabled, throw an exception.
2029             if ($type == "conversations") {
2030                 throw new moodle_exception('disabled', 'message');
2031             }
2032             if ($type == "both") {
2033                 $warning = array();
2034                 $warning['item'] = 'message';
2035                 $warning['itemid'] = $USER->id;
2036                 $warning['warningcode'] = '1';
2037                 $warning['message'] = 'Private messages (conversations) are not enabled in this site.
2038                     Only notifications will be returned';
2039                 $warnings[] = $warning;
2040             }
2041         }
2043         if (!empty($useridto)) {
2044             if (core_user::is_real_user($useridto)) {
2045                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2046             } else {
2047                 throw new moodle_exception('invaliduser');
2048             }
2049         }
2051         if (!empty($useridfrom)) {
2052             // We use get_user here because the from user can be the noreply or support user.
2053             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2054         }
2056         // Check if the current user is the sender/receiver or just a privileged user.
2057         if ($useridto != $USER->id and $useridfrom != $USER->id and
2058              !has_capability('moodle/site:readallmessages', $context)) {
2059             throw new moodle_exception('accessdenied', 'admin');
2060         }
2062         // Which type of messages to retrieve.
2063         $notifications = -1;
2064         if ($type != 'both') {
2065             $notifications = ($type == 'notifications') ? 1 : 0;
2066         }
2068         $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2069         $sort = "mr.timecreated $orderdirection";
2071         if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2072             $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2074             // In some cases, we don't need to get the to/from user objects from the sql query.
2075             $userfromfullname = '';
2076             $usertofullname = '';
2078             // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
2079             if (!empty($useridto)) {
2080                 $usertofullname = fullname($userto, $canviewfullname);
2081                 // The user from may or may not be filled.
2082                 if (!empty($useridfrom)) {
2083                     $userfromfullname = fullname($userfrom, $canviewfullname);
2084                 }
2085             } else {
2086                 // If the useridto field is empty, the useridfrom must be filled.
2087                 $userfromfullname = fullname($userfrom, $canviewfullname);
2088             }
2089             foreach ($messages as $mid => $message) {
2091                 if (!$message->notification) {
2092                     // Do not return deleted messages.
2093                     if (($useridto == $USER->id and $message->timeusertodeleted) or
2094                         ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
2095                         unset($messages[$mid]);
2096                         continue;
2097                     }
2098                 } else {
2099                     // Return iconurl for notifications.
2100                     if (!isset($output)) {
2101                         $output = $PAGE->get_renderer('core');
2102                     }
2104                     if (!empty($message->component) && substr($message->component, 0, 4) == 'mod_') {
2105                         $iconurl = $output->image_url('icon', $message->component);
2106                     } else {
2107                         $iconurl = $output->image_url('i/marker', 'core');
2108                     }
2110                     $message->iconurl = clean_param($iconurl->out(), PARAM_URL);
2111                 }
2113                 // We need to get the user from the query.
2114                 if (empty($userfromfullname)) {
2115                     // Check for non-reply and support users.
2116                     if (core_user::is_real_user($message->useridfrom)) {
2117                         $user = new stdClass();
2118                         $user = username_load_fields_from_object($user, $message, 'userfrom');
2119                         $message->userfromfullname = fullname($user, $canviewfullname);
2120                     } else {
2121                         $user = core_user::get_user($message->useridfrom);
2122                         $message->userfromfullname = fullname($user, $canviewfullname);
2123                     }
2124                 } else {
2125                     $message->userfromfullname = $userfromfullname;
2126                 }
2128                 // We need to get the user from the query.
2129                 if (empty($usertofullname)) {
2130                     $user = new stdClass();
2131                     $user = username_load_fields_from_object($user, $message, 'userto');
2132                     $message->usertofullname = fullname($user, $canviewfullname);
2133                 } else {
2134                     $message->usertofullname = $usertofullname;
2135                 }
2137                 $message->text = message_format_message_text($message);
2138                 $messages[$mid] = (array) $message;
2139             }
2140         }
2142         $results = array(
2143             'messages' => $messages,
2144             'warnings' => $warnings
2145         );
2147         return $results;
2148     }
2150     /**
2151      * Get messages return description.
2152      *
2153      * @return external_single_structure
2154      * @since 2.8
2155      */
2156     public static function get_messages_returns() {
2157         return new external_single_structure(
2158             array(
2159                 'messages' => new external_multiple_structure(
2160                     new external_single_structure(
2161                         array(
2162                             'id' => new external_value(PARAM_INT, 'Message id'),
2163                             'useridfrom' => new external_value(PARAM_INT, 'User from id'),
2164                             'useridto' => new external_value(PARAM_INT, 'User to id'),
2165                             'subject' => new external_value(PARAM_TEXT, 'The message subject'),
2166                             'text' => new external_value(PARAM_RAW, 'The message text formated'),
2167                             'fullmessage' => new external_value(PARAM_RAW, 'The message'),
2168                             'fullmessageformat' => new external_format_value('fullmessage'),
2169                             'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
2170                             'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
2171                             'notification' => new external_value(PARAM_INT, 'Is a notification?'),
2172                             'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
2173                             'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
2174                             'timecreated' => new external_value(PARAM_INT, 'Time created'),
2175                             'timeread' => new external_value(PARAM_INT, 'Time read'),
2176                             'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
2177                             'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name'),
2178                             'component' => new external_value(PARAM_TEXT, 'The component that generated the notification',
2179                                 VALUE_OPTIONAL),
2180                             'eventtype' => new external_value(PARAM_TEXT, 'The type of notification', VALUE_OPTIONAL),
2181                             'customdata' => new external_value(PARAM_RAW, 'Custom data to be passed to the message processor.
2182                                 The data here is serialised using json_encode().', VALUE_OPTIONAL),
2183                             'iconurl' => new external_value(PARAM_URL, 'URL for icon, only for notifications.', VALUE_OPTIONAL),
2184                         ), 'message'
2185                     )
2186                 ),
2187                 'warnings' => new external_warnings()
2188             )
2189         );
2190     }
2192     /**
2193      * Mark all notifications as read parameters description.
2194      *
2195      * @return external_function_parameters
2196      * @since 3.2
2197      */
2198     public static function mark_all_notifications_as_read_parameters() {
2199         return new external_function_parameters(
2200             array(
2201                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2202                 'useridfrom' => new external_value(
2203                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2204                     VALUE_DEFAULT, 0),
2205                 'timecreatedto' => new external_value(
2206                     PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
2207                     VALUE_DEFAULT, 0),
2208             )
2209         );
2210     }
2212     /**
2213      * Mark all notifications as read function.
2214      *
2215      * @since  3.2
2216      * @throws invalid_parameter_exception
2217      * @throws moodle_exception
2218      * @param  int      $useridto       the user id who received the message
2219      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
2220      * @param  int      $timecreatedto  mark message created before this time as read, 0 for all messages
2221      * @return external_description
2222      */
2223     public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
2224         global $USER;
2226         $params = self::validate_parameters(
2227             self::mark_all_notifications_as_read_parameters(),
2228             array(
2229                 'useridto' => $useridto,
2230                 'useridfrom' => $useridfrom,
2231                 'timecreatedto' => $timecreatedto,
2232             )
2233         );
2235         $context = context_system::instance();
2236         self::validate_context($context);
2238         $useridto = $params['useridto'];
2239         $useridfrom = $params['useridfrom'];
2240         $timecreatedto = $params['timecreatedto'];
2242         if (!empty($useridto)) {
2243             if (core_user::is_real_user($useridto)) {
2244                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2245             } else {
2246                 throw new moodle_exception('invaliduser');
2247             }
2248         }
2250         if (!empty($useridfrom)) {
2251             // We use get_user here because the from user can be the noreply or support user.
2252             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2253         }
2255         // Check if the current user is the sender/receiver or just a privileged user.
2256         if ($useridto != $USER->id and $useridfrom != $USER->id and
2257             // The deleteanymessage cap seems more reasonable here than readallmessages.
2258              !has_capability('moodle/site:deleteanymessage', $context)) {
2259             throw new moodle_exception('accessdenied', 'admin');
2260         }
2262         \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
2264         return true;
2265     }
2267     /**
2268      * Mark all notifications as read return description.
2269      *
2270      * @return external_single_structure
2271      * @since 3.2
2272      */
2273     public static function mark_all_notifications_as_read_returns() {
2274         return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
2275     }
2277     /**
2278      * Get unread conversations count parameters description.
2279      *
2280      * @return external_function_parameters
2281      * @since 3.2
2282      */
2283     public static function get_unread_conversations_count_parameters() {
2284         return new external_function_parameters(
2285             array(
2286                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2287             )
2288         );
2289     }
2291     /**
2292      * Get unread messages count function.
2293      *
2294      * @since  3.2
2295      * @throws invalid_parameter_exception
2296      * @throws moodle_exception
2297      * @param  int      $useridto       the user id who received the message
2298      * @return external_description
2299      */
2300     public static function get_unread_conversations_count($useridto) {
2301         global $USER, $CFG;
2303         // Check if messaging is enabled.
2304         if (empty($CFG->messaging)) {
2305             throw new moodle_exception('disabled', 'message');
2306         }
2308         $params = self::validate_parameters(
2309             self::get_unread_conversations_count_parameters(),
2310             array('useridto' => $useridto)
2311         );
2313         $context = context_system::instance();
2314         self::validate_context($context);
2316         $useridto = $params['useridto'];
2318         if (!empty($useridto)) {
2319             if (core_user::is_real_user($useridto)) {
2320                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2321             } else {
2322                 throw new moodle_exception('invaliduser');
2323             }
2324         } else {
2325             $useridto = $USER->id;
2326         }
2328         // Check if the current user is the receiver or just a privileged user.
2329         if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
2330             throw new moodle_exception('accessdenied', 'admin');
2331         }
2333         return \core_message\api::count_unread_conversations($userto);
2334     }
2336     /**
2337      * Get unread conversations count return description.
2338      *
2339      * @return external_single_structure
2340      * @since 3.2
2341      */
2342     public static function get_unread_conversations_count_returns() {
2343         return new external_value(PARAM_INT, 'The count of unread messages for the user');
2344     }
2346     /**
2347      * Get blocked users parameters description.
2348      *
2349      * @return external_function_parameters
2350      * @since 2.9
2351      */
2352     public static function get_blocked_users_parameters() {
2353         return new external_function_parameters(
2354             array(
2355                 'userid' => new external_value(PARAM_INT,
2356                                 'the user whose blocked users we want to retrieve',
2357                                 VALUE_REQUIRED),
2358             )
2359         );
2360     }
2362     /**
2363      * Retrieve a list of users blocked
2364      *
2365      * @param  int $userid the user whose blocked users we want to retrieve
2366      * @return external_description
2367      * @since 2.9
2368      */
2369     public static function get_blocked_users($userid) {
2370         global $CFG, $USER, $PAGE;
2372         // Warnings array, it can be empty at the end but is mandatory.
2373         $warnings = array();
2375         // Validate params.
2376         $params = array(
2377             'userid' => $userid
2378         );
2379         $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2380         $userid = $params['userid'];
2382         // Validate context.
2383         $context = context_system::instance();
2384         self::validate_context($context);
2386         // Check if private messaging between users is allowed.
2387         if (empty($CFG->messaging)) {
2388             throw new moodle_exception('disabled', 'message');
2389         }
2391         $user = core_user::get_user($userid, '*', MUST_EXIST);
2392         core_user::require_active_user($user);
2394         // Check if we have permissions for retrieve the information.
2395         $capability = 'moodle/site:manageallmessaging';
2396         if (($USER->id != $userid) && !has_capability($capability, $context)) {
2397             throw new required_capability_exception($context, $capability, 'nopermissions', '');
2398         }
2400         // Now, we can get safely all the blocked users.
2401         $users = \core_message\api::get_blocked_users($user->id);
2403         $blockedusers = array();
2404         foreach ($users as $user) {
2405             $newuser = array(
2406                 'id' => $user->id,
2407                 'fullname' => fullname($user),
2408             );
2410             $userpicture = new user_picture($user);
2411             $userpicture->size = 1; // Size f1.
2412             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2414             $blockedusers[] = $newuser;
2415         }
2417         $results = array(
2418             'users' => $blockedusers,
2419             'warnings' => $warnings
2420         );
2421         return $results;
2422     }
2424     /**
2425      * Get blocked users return description.
2426      *
2427      * @return external_single_structure
2428      * @since 2.9
2429      */
2430     public static function get_blocked_users_returns() {
2431         return new external_single_structure(
2432             array(
2433                 'users' => new external_multiple_structure(
2434                     new external_single_structure(
2435                         array(
2436                             'id' => new external_value(PARAM_INT, 'User ID'),
2437                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2438                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
2439                         )
2440                     ),
2441                     'List of blocked users'
2442                 ),
2443                 'warnings' => new external_warnings()
2444             )
2445         );
2446     }
2448     /**
2449      * Returns description of method parameters
2450      *
2451      * @return external_function_parameters
2452      * @since 2.9
2453      */
2454     public static function mark_message_read_parameters() {
2455         return new external_function_parameters(
2456             array(
2457                 'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
2458                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
2459                     VALUE_DEFAULT, 0)
2460             )
2461         );
2462     }
2464     /**
2465      * Mark a single message as read, trigger message_viewed event
2466      *
2467      * @param  int $messageid id of the message (in the message table)
2468      * @param  int $timeread timestamp for when the message should be marked read
2469      * @return external_description
2470      * @throws invalid_parameter_exception
2471      * @throws moodle_exception
2472      * @since 2.9
2473      */
2474     public static function mark_message_read($messageid, $timeread) {
2475         global $CFG, $DB, $USER;
2477         // Check if private messaging between users is allowed.
2478         if (empty($CFG->messaging)) {
2479             throw new moodle_exception('disabled', 'message');
2480         }
2482         // Warnings array, it can be empty at the end but is mandatory.
2483         $warnings = array();
2485         // Validate params.
2486         $params = array(
2487             'messageid' => $messageid,
2488             'timeread' => $timeread
2489         );
2490         $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2492         if (empty($params['timeread'])) {
2493             $timeread = time();
2494         } else {
2495             $timeread = $params['timeread'];
2496         }
2498         // Validate context.
2499         $context = context_system::instance();
2500         self::validate_context($context);
2502         $sql = "SELECT m.*, mcm.userid as useridto
2503                   FROM {messages} m
2504             INNER JOIN {message_conversations} mc
2505                     ON m.conversationid = mc.id
2506             INNER JOIN {message_conversation_members} mcm
2507                     ON mcm.conversationid = mc.id
2508              LEFT JOIN {message_user_actions} mua
2509                     ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
2510                  WHERE mua.id is NULL
2511                    AND mcm.userid != m.useridfrom
2512                    AND m.id = ?";
2513         $messageparams = [];
2514         $messageparams[] = $USER->id;
2515         $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
2516         $messageparams[] = $params['messageid'];
2517         $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
2519         if ($message->useridto != $USER->id) {
2520             throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
2521         }
2523         \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2525         $results = array(
2526             'messageid' => $message->id,
2527             'warnings' => $warnings
2528         );
2529         return $results;
2530     }
2532     /**
2533      * Returns description of method result value
2534      *
2535      * @return external_description
2536      * @since 2.9
2537      */
2538     public static function mark_message_read_returns() {
2539         return new external_single_structure(
2540             array(
2541                 'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
2542                 'warnings' => new external_warnings()
2543             )
2544         );
2545     }
2547     /**
2548      * Returns description of method parameters
2549      *
2550      * @return external_function_parameters
2551      */
2552     public static function mark_notification_read_parameters() {
2553         return new external_function_parameters(
2554             array(
2555                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2556                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
2557                     VALUE_DEFAULT, 0)
2558             )
2559         );
2560     }
2562     /**
2563      * Mark a single notification as read.
2564      *
2565      * This will trigger a 'notification_viewed' event.
2566      *
2567      * @param int $notificationid id of the notification
2568      * @param int $timeread timestamp for when the notification should be marked read
2569      * @return external_description
2570      * @throws invalid_parameter_exception
2571      * @throws moodle_exception
2572      */
2573     public static function mark_notification_read($notificationid, $timeread) {
2574         global $CFG, $DB, $USER;
2576         // Warnings array, it can be empty at the end but is mandatory.
2577         $warnings = array();
2579         // Validate params.
2580         $params = array(
2581             'notificationid' => $notificationid,
2582             'timeread' => $timeread
2583         );
2584         $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
2586         if (empty($params['timeread'])) {
2587             $timeread = time();
2588         } else {
2589             $timeread = $params['timeread'];
2590         }
2592         // Validate context.
2593         $context = context_system::instance();
2594         self::validate_context($context);
2596         $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
2598         if ($notification->useridto != $USER->id) {
2599             throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
2600                 'notification as read');
2601         }
2603         \core_message\api::mark_notification_as_read($notification, $timeread);
2605         $results = array(
2606             'notificationid' => $notification->id,
2607             'warnings' => $warnings
2608         );
2610         return $results;
2611     }
2613     /**
2614      * Returns description of method result value
2615      *
2616      * @return external_description
2617      */
2618     public static function mark_notification_read_returns() {
2619         return new external_single_structure(
2620             array(
2621                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2622                 'warnings' => new external_warnings()
2623             )
2624         );
2625     }
2627     /**
2628      * Mark all conversation messages as read parameters description.
2629      *
2630      * @return external_function_parameters
2631      * @since 3.6
2632      */
2633     public static function mark_all_conversation_messages_as_read_parameters() {
2634         return new external_function_parameters(
2635             array(
2636                 'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
2637                 'conversationid' =>
2638                     new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
2639             )
2640         );
2641     }
2643     /**
2644      * Mark all conversation messages as read function.
2645      *
2646      * @param int $userid The user id of who we want to delete the conversation for
2647      * @param int $conversationid The id of the conversations
2648      * @since 3.6
2649      */
2650     public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
2651         global $CFG;
2653         // Check if messaging is enabled.
2654         if (empty($CFG->messaging)) {
2655             throw new moodle_exception('disabled', 'message');
2656         }
2658         $params = array(
2659             'userid' => $userid,
2660             'conversationid' => $conversationid,
2661         );
2662         $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
2664         $context = context_system::instance();
2665         self::validate_context($context);
2667         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2668         core_user::require_active_user($user);
2670         if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
2671             \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
2672         } else {
2673             throw new moodle_exception('accessdenied', 'admin');
2674         }
2675     }
2677     /**
2678      * Mark all conversation messages as read return description.
2679      *
2680      * @return external_warnings
2681      * @since 3.6
2682      */
2683     public static function mark_all_conversation_messages_as_read_returns() {
2684         return null;
2685     }
2687     /**
2688      * Returns description of method parameters.
2689      *
2690      * @return external_function_parameters
2691      * @since 3.6
2692      */
2693     public static function delete_conversations_by_id_parameters() {
2694         return new external_function_parameters(
2695             array(
2696                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
2697                 'conversationids' => new external_multiple_structure(
2698                     new external_value(PARAM_INT, 'The id of the conversation'),
2699                     'List of conversation IDs'
2700                 ),
2701             )
2702         );
2703     }
2705     /**
2706      * Deletes a conversation.
2707      *
2708      * @param int $userid The user id of who we want to delete the conversation for
2709      * @param int[] $conversationids The ids of the conversations
2710      * @return array
2711      * @throws moodle_exception
2712      * @since 3.6
2713      */
2714     public static function delete_conversations_by_id($userid, array $conversationids) {
2715         global $CFG;
2717         // Check if private messaging between users is allowed.
2718         if (empty($CFG->messaging)) {
2719             throw new moodle_exception('disabled', 'message');
2720         }
2722         // Validate params.
2723         $params = [
2724             'userid' => $userid,
2725             'conversationids' => $conversationids,
2726         ];
2727         $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
2729         // Validate context.
2730         $context = context_system::instance();
2731         self::validate_context($context);
2733         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2734         core_user::require_active_user($user);
2736         foreach ($params['conversationids'] as $conversationid) {
2737             if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
2738                 \core_message\api::delete_conversation_by_id($user->id, $conversationid);
2739             } else {
2740                 throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
2741             }
2742         }
2744         return [];
2745     }
2747     /**
2748      * Returns description of method result value.
2749      *
2750      * @return external_description
2751      * @since 3.6
2752      */
2753     public static function delete_conversations_by_id_returns() {
2754         return new external_warnings();
2755     }
2757     /**
2758      * Returns description of method parameters
2759      *
2760      * @return external_function_parameters
2761      * @since 3.1
2762      */
2763     public static function delete_message_parameters() {
2764         return new external_function_parameters(
2765             array(
2766                 'messageid' => new external_value(PARAM_INT, 'The message id'),
2767                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
2768                 'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
2769             )
2770         );
2771     }
2773     /**
2774      * Deletes a message
2775      *
2776      * @param  int $messageid the message id
2777      * @param  int $userid the user id of who we want to delete the message for
2778      * @param  bool $read if is a message read (default to true)
2779      * @return external_description
2780      * @throws moodle_exception
2781      * @since 3.1
2782      */
2783     public static function delete_message($messageid, $userid, $read = true) {
2784         global $CFG;
2786         // Check if private messaging between users is allowed.
2787         if (empty($CFG->messaging)) {
2788             throw new moodle_exception('disabled', 'message');
2789         }
2791         // Warnings array, it can be empty at the end but is mandatory.
2792         $warnings = array();
2794         // Validate params.
2795         $params = array(
2796             'messageid' => $messageid,
2797             'userid' => $userid,
2798             'read' => $read
2799         );
2800         $params = self::validate_parameters(self::delete_message_parameters(), $params);
2802         // Validate context.
2803         $context = context_system::instance();
2804         self::validate_context($context);
2806         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2807         core_user::require_active_user($user);
2809         if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
2810             $status = \core_message\api::delete_message($user->id, $params['messageid']);
2811         } else {
2812             throw new moodle_exception('You do not have permission to delete this message');
2813         }
2815         $results = array(
2816             'status' => $status,
2817             'warnings' => $warnings
2818         );
2819         return $results;
2820     }
2822     /**
2823      * Returns description of method result value
2824      *
2825      * @return external_description
2826      * @since 3.1
2827      */
2828     public static function delete_message_returns() {
2829         return new external_single_structure(
2830             array(
2831                 'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
2832                 'warnings' => new external_warnings()
2833             )
2834         );
2835     }
2837     /**
2838      * Returns description of method parameters
2839      *
2840      * @return external_function_parameters
2841      * @since 3.2
2842      */
2843     public static function message_processor_config_form_parameters() {
2844         return new external_function_parameters(
2845             array(
2846                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
2847                 'name' => new external_value(PARAM_TEXT, 'The name of the message processor'),
2848                 'formvalues' => new external_multiple_structure(
2849                     new external_single_structure(
2850                         array(
2851                             'name' => new external_value(PARAM_TEXT, 'name of the form element', VALUE_REQUIRED),
2852                             'value' => new external_value(PARAM_RAW, 'value of the form element', VALUE_REQUIRED),
2853                         )
2854                     ),
2855                     'Config form values',
2856                     VALUE_REQUIRED
2857                 ),
2858             )
2859         );
2860     }
2862     /**
2863      * Processes a message processor config form.
2864      *
2865      * @param  int $userid the user id
2866      * @param  string $name the name of the processor
2867      * @param  array $formvalues the form values
2868      * @return external_description
2869      * @throws moodle_exception
2870      * @since 3.2
2871      */
2872     public static function message_processor_config_form($userid, $name, $formvalues) {
2873         global $USER, $CFG;
2875         $params = self::validate_parameters(
2876             self::message_processor_config_form_parameters(),
2877             array(
2878                 'userid' => $userid,
2879                 'name' => $name,
2880                 'formvalues' => $formvalues,
2881             )
2882         );
2884         $user = self::validate_preferences_permissions($params['userid']);
2886         $processor = get_message_processor($params['name']);
2887         $preferences = [];
2888         $form = new stdClass();
2890         foreach ($params['formvalues'] as $formvalue) {
2891             // Curly braces to ensure interpretation is consistent between
2892             // php 5 and php 7.
2893             $form->{$formvalue['name']} = $formvalue['value'];
2894         }
2896         $processor->process_form($form, $preferences);
2898         if (!empty($preferences)) {
2899             set_user_preferences($preferences, $params['userid']);
2900         }
2901     }
2903     /**
2904      * Returns description of method result value
2905      *
2906      * @return external_description
2907      * @since 3.2
2908      */
2909     public static function message_processor_config_form_returns() {
2910         return null;
2911     }
2913     /**
2914      * Returns description of method parameters
2915      *
2916      * @return external_function_parameters
2917      * @since 3.2
2918      */
2919     public static function get_message_processor_parameters() {
2920         return new external_function_parameters(
2921             array(
2922                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user'),
2923                 'name' => new external_value(PARAM_TEXT, 'The name of the message processor', VALUE_REQUIRED),
2924             )
2925         );
2926     }
2928     /**
2929      * Get a message processor.
2930      *
2931      * @param int $userid
2932      * @param string $name the name of the processor
2933      * @return external_description
2934      * @throws moodle_exception
2935      * @since 3.2
2936      */
2937     public static function get_message_processor($userid, $name) {
2938         global $USER, $PAGE, $CFG;
2940         // Check if messaging is enabled.
2941         if (empty($CFG->messaging)) {
2942             throw new moodle_exception('disabled', 'message');
2943         }
2945         $params = self::validate_parameters(
2946             self::get_message_processor_parameters(),
2947             array(
2948                 'userid' => $userid,
2949                 'name' => $name,
2950             )
2951         );
2953         if (empty($params['userid'])) {
2954             $params['userid'] = $USER->id;
2955         }
2957         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2958         core_user::require_active_user($user);
2959         self::validate_context(context_user::instance($params['userid']));
2961         $processor = get_message_processor($params['name']);
2963         $processoroutput = new \core_message\output\processor($processor, $user);
2964         $renderer = $PAGE->get_renderer('core_message');
2966         return $processoroutput->export_for_template($renderer);
2967     }
2969     /**
2970      * Returns description of method result value
2971      *
2972      * @return external_description
2973      * @since 3.2
2974      */
2975     public static function get_message_processor_returns() {
2976         return new external_function_parameters(
2977             array(
2978                 'systemconfigured' => new external_value(PARAM_BOOL, 'Site configuration status'),
2979                 'userconfigured' => new external_value(PARAM_BOOL, 'The user configuration status'),
2980             )
2981         );
2982     }
2984     /**
2985      * Check that the user has enough permission to retrieve message or notifications preferences.
2986      *
2987      * @param  int $userid the user id requesting the preferences
2988      * @return stdClass full user object
2989      * @throws moodle_exception
2990      * @since  Moodle 3.2
2991      */
2992     protected static function validate_preferences_permissions($userid) {
2993         global $USER;
2995         if (empty($userid)) {
2996             $user = $USER;
2997         } else {
2998             $user = core_user::get_user($userid, '*', MUST_EXIST);
2999             core_user::require_active_user($user);
3000         }
3002         $systemcontext = context_system::instance();
3003         self::validate_context($systemcontext);
3005         // Check access control.
3006         if ($user->id == $USER->id) {
3007             // Editing own message profile.
3008             require_capability('moodle/user:editownmessageprofile', $systemcontext);
3009         } else {
3010             // Teachers, parents, etc.
3011             $personalcontext = context_user::instance($user->id);
3012             require_capability('moodle/user:editmessageprofile', $personalcontext);
3013         }
3014         return $user;
3015     }
3017     /**
3018      * Returns a notification or message preference structure.
3019      *
3020      * @return external_single_structure the structure
3021      * @since  Moodle 3.2
3022      */
3023     protected static function get_preferences_structure() {
3024         return new external_single_structure(
3025             array(
3026                 'userid' => new external_value(PARAM_INT, 'User id'),
3027                 'disableall' => new external_value(PARAM_INT, 'Whether all the preferences are disabled'),
3028                 'processors' => new external_multiple_structure(
3029                     new external_single_structure(
3030                         array(
3031                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3032                             'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3033                             'hassettings' => new external_value(PARAM_BOOL, 'Whether has settings'),
3034                             'contextid' => new external_value(PARAM_INT, 'Context id'),
3035                             'userconfigured' => new external_value(PARAM_INT, 'Whether is configured by the user'),
3036                         )
3037                     ),
3038                     'Config form values'
3039                 ),
3040                 'components' => new external_multiple_structure(
3041                     new external_single_structure(
3042                         array(
3043                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3044                             'notifications' => new external_multiple_structure(
3045                                 new external_single_structure(
3046                                     array(
3047                                         'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3048                                         'preferencekey' => new external_value(PARAM_ALPHANUMEXT, 'Preference key'),
3049                                         'processors' => new external_multiple_structure(
3050                                             new external_single_structure(
3051                                                 array(
3052                                                     'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3053                                                     'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3054                                                     'locked' => new external_value(PARAM_BOOL, 'Is locked by admin?'),
3055                                                     'lockedmessage' => new external_value(PARAM_TEXT,
3056                                                         'Text to display if locked', VALUE_OPTIONAL),
3057                                                     'userconfigured' => new external_value(PARAM_INT, 'Is configured?'),
3058                                                     'loggedin' => new external_single_structure(
3059                                                         array(
3060                                                             'name' => new external_value(PARAM_NOTAGS, 'Name'),
3061                                                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3062                                                             'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3063                                                         )
3064                                                     ),
3065                                                     'loggedoff' => new external_single_structure(
3066                                                         array(
3067                                                             'name' => new external_value(PARAM_NOTAGS, 'Name'),
3068                                                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3069                                                             'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3070                                                         )
3071                                                     ),
3072                                                 )
3073                                             ),
3074                                             'Processors values for this notification'
3075                                         ),
3076                                     )
3077                                 ),
3078                                 'List of notificaitons for the component'
3079                             ),
3080                         )
3081                     ),
3082                     'Available components'
3083                 ),
3084             )
3085         );
3086     }
3088     /**
3089      * Returns description of method parameters
3090      *
3091      * @return external_function_parameters
3092      * @since 3.2
3093      */
3094     public static function get_user_notification_preferences_parameters() {
3095         return new external_function_parameters(
3096             array(
3097                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3098             )
3099         );
3100     }
3102     /**
3103      * Get the notification preferences for a given user.
3104      *
3105      * @param int $userid id of the user, 0 for current user
3106      * @return external_description
3107      * @throws moodle_exception
3108      * @since 3.2
3109      */
3110     public static function get_user_notification_preferences($userid = 0) {
3111         global $PAGE;
3113         $params = self::validate_parameters(
3114             self::get_user_notification_preferences_parameters(),
3115             array(
3116                 'userid' => $userid,
3117             )
3118         );
3119         $user = self::validate_preferences_permissions($params['userid']);
3121         $processors = get_message_processors();
3122         $providers = message_get_providers_for_user($user->id);
3123         $preferences = \core_message\api::get_all_message_preferences($processors, $providers, $user);
3124         $notificationlist = new \core_message\output\preferences\notification_list($processors, $providers, $preferences, $user);
3126         $renderer = $PAGE->get_renderer('core_message');
3128         $result = array(
3129             'warnings' => array(),
3130             'preferences' => $notificationlist->export_for_template($renderer)
3131         );
3132         return $result;
3133     }
3135     /**
3136      * Returns description of method result value
3137      *
3138      * @return external_description
3139      * @since 3.2
3140      */
3141     public static function get_user_notification_preferences_returns() {
3142         return new external_function_parameters(
3143             array(
3144                 'preferences' => self::get_preferences_structure(),
3145                 'warnings' => new external_warnings(),
3146             )
3147         );
3148     }
3150     /**
3151      * Returns description of method parameters
3152      *
3153      * @return external_function_parameters
3154      * @since 3.2
3155      */
3156     public static function get_user_message_preferences_parameters() {
3157         return new external_function_parameters(
3158             array(
3159                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3160             )
3161         );
3162     }
3164     /**
3165      * Get the notification preferences for a given user.
3166      *
3167      * @param int $userid id of the user, 0 for current user
3168      * @return external_description
3169      * @throws moodle_exception
3170      * @since 3.2
3171      */
3172     public static function get_user_message_preferences($userid = 0) {
3173         global $CFG, $PAGE;
3175         $params = self::validate_parameters(
3176             self::get_user_message_preferences_parameters(),
3177             array(
3178                 'userid' => $userid,
3179             )
3180         );
3182         $user = self::validate_preferences_permissions($params['userid']);
3184         // Filter out enabled, available system_configured and user_configured processors only.
3185         $readyprocessors = array_filter(get_message_processors(), function($processor) {
3186             return $processor->enabled &&
3187                 $processor->configured &&
3188                 $processor->object->is_user_configured() &&
3189                 // Filter out processors that don't have and message preferences to configure.
3190                 $processor->object->has_message_preferences();
3191         });
3193         $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
3194             return $provider->component === 'moodle';
3195         });
3196         $preferences = \core_message\api::get_all_message_preferences($readyprocessors, $providers, $user);
3197         $notificationlistoutput = new \core_message\output\preferences\message_notification_list($readyprocessors,
3198             $providers, $preferences, $user);
3200         $renderer = $PAGE->get_renderer('core_message');
3202         $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
3204         $result = array(
3205             'warnings' => array(),
3206             'preferences' => $notificationlistoutput->export_for_template($renderer),
3207             'blocknoncontacts' => \core_message\api::get_user_privacy_messaging_preference($user->id),
3208             'entertosend' => $entertosend
3209         );
3210         return $result;
3211     }
3213     /**
3214      * Returns description of method result value
3215      *
3216      * @return external_description
3217      * @since 3.2
3218      */
3219     public static function get_user_message_preferences_returns() {
3220         return new external_function_parameters(
3221             array(
3222                 'preferences' => self::get_preferences_structure(),
3223                 'blocknoncontacts' => new external_value(PARAM_INT, 'Privacy messaging setting to define who can message you'),
3224                 'entertosend' => new external_value(PARAM_BOOL, 'User preference for using enter to send messages'),
3225                 'warnings' => new external_warnings(),
3226             )
3227         );
3228     }
3230     /**
3231      * Returns description of method parameters for the favourite_conversations() method.
3232      *
3233      * @return external_function_parameters
3234      */
3235     public static function set_favourite_conversations_parameters() {
3236         return new external_function_parameters(
3237             array(
3238                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3239                 'conversations' => new external_multiple_structure(
3240                     new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3241                 )
3242             )
3243         );
3244     }
3246     /**
3247      * Favourite a conversation, or list of conversations for a user.
3248      *
3249      * @param int $userid the id of the user, or 0 for the current user.
3250      * @param array $conversationids the list of conversations ids to favourite.
3251      * @return array
3252      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3253      */
3254     public static function set_favourite_conversations(int $userid, array $conversationids) {
3255         global $CFG, $USER;
3257         // All the business logic checks that really shouldn't be in here.
3258         if (empty($CFG->messaging)) {
3259             throw new moodle_exception('disabled', 'message');
3260         }
3261         $params = [
3262             'userid' => $userid,
3263             'conversations' => $conversationids
3264         ];
3265         $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
3266         $systemcontext = context_system::instance();
3267         self::validate_context($systemcontext);
3269         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3270             throw new moodle_exception('You do not have permission to perform this action.');
3271         }
3273         foreach ($params['conversations'] as $conversationid) {
3274             \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
3275         }
3277         return [];
3278     }
3280     /**
3281      * Return a description of the returns for the create_user_favourite_conversations() method.
3282      *
3283      * @return external_description
3284      */
3285     public static function set_favourite_conversations_returns() {
3286         return new external_warnings();
3287     }
3289     /**
3290      * Returns description of method parameters for unfavourite_conversations() method.
3291      *
3292      * @return external_function_parameters
3293      */
3294     public static function unset_favourite_conversations_parameters() {
3295         return new external_function_parameters(
3296             array(
3297                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3298                 'conversations' => new external_multiple_structure(
3299                     new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3300                 )
3301             )
3302         );
3303     }
3305     /**
3306      * Unfavourite a conversation, or list of conversations for a user.
3307      *
3308      * @param int $userid the id of the user, or 0 for the current user.
3309      * @param array $conversationids the list of conversations ids unset as favourites.
3310      * @return array
3311      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3312      */
3313     public static function unset_favourite_conversations(int $userid, array $conversationids) {
3314         global $CFG, $USER;
3316         // All the business logic checks that really shouldn't be in here.
3317         if (empty($CFG->messaging)) {
3318             throw new moodle_exception('disabled', 'message');
3319         }
3320         $params = [
3321             'userid' => $userid,
3322             'conversations' => $conversationids
3323         ];
3324         $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
3325         $systemcontext = context_system::instance();
3326         self::validate_context($systemcontext);
3328         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3329             throw new moodle_exception('You do not have permission to perform this action.');
3330         }
3332         foreach ($params['conversations'] as $conversationid) {
3333             \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
3334         }
3336         return [];
3337     }
3339     /**
3340      * Unset favourite conversations return description.
3341      *
3342      * @return external_description
3343      */
3344     public static function unset_favourite_conversations_returns() {
3345         return new external_warnings();
3346     }
3348     /**
3349      * Returns description of method parameters for get_member_info() method.
3350      *
3351      * @return external_function_parameters
3352      */
3353     public static function get_member_info_parameters() {
3354         return new external_function_parameters(
3355             array(
3356                 'referenceuserid' => new external_value(PARAM_INT, 'id of the user'),
3357                 'userids' => new external_multiple_structure(
3358                     new external_value(PARAM_INT, 'id of members to get')
3359                 ),
3360                 'includecontactrequests' => new external_value(PARAM_BOOL, 'include contact requests in response', VALUE_DEFAULT, false),
3361                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'include privacy info in response', VALUE_DEFAULT, false)
3362             )
3363         );
3364     }
3366     /**
3367      * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
3368      *
3369      * This is the basic structure used when returning members, and includes information about the relationship between each member
3370      * and the referenceuser, such as a whether the referenceuser has marked the member as a contact, or has blocked them.
3371      *