Merge branch 'MDL-72585-master' of git://github.com/dpalou/moodle
[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_INT, '1 for getting read messages, 0 for unread, 2 for both',
1967                     VALUE_DEFAULT, 1),
1968                 'newestfirst' => new external_value(
1969                     PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
1970                     VALUE_DEFAULT, true),
1971                 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
1972                 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
1973             )
1974         );
1975     }
1977     /**
1978      * Get messages function implementation.
1979      *
1980      * @since  2.8
1981      * @throws invalid_parameter_exception
1982      * @throws moodle_exception
1983      * @param  int      $useridto       the user id who received the message
1984      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
1985      * @param  string   $type           type of message to return, expected values: notifications, conversations and both
1986      * @param  int      $read           1 for getting read messages, 0 for unread, 2 for both
1987      * @param  bool     $newestfirst    true for ordering by newest first, false for oldest first
1988      * @param  int      $limitfrom      limit from
1989      * @param  int      $limitnum       limit num
1990      * @return external_description
1991      */
1992     public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = MESSAGE_GET_READ,
1993                                         $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
1994         global $CFG, $USER, $PAGE;
1996         $warnings = array();
1998         $params = array(
1999             'useridto' => $useridto,
2000             'useridfrom' => $useridfrom,
2001             'type' => $type,
2002             'read' => $read,
2003             'newestfirst' => $newestfirst,
2004             'limitfrom' => $limitfrom,
2005             'limitnum' => $limitnum
2006         );
2008         $params = self::validate_parameters(self::get_messages_parameters(), $params);
2010         $context = context_system::instance();
2011         self::validate_context($context);
2013         $useridto = $params['useridto'];
2014         $useridfrom = $params['useridfrom'];
2015         $type = $params['type'];
2016         $read = $params['read'];
2017         $newestfirst = $params['newestfirst'];
2018         $limitfrom = $params['limitfrom'];
2019         $limitnum = $params['limitnum'];
2021         $allowedvalues = array('notifications', 'conversations', 'both');
2022         if (!in_array($type, $allowedvalues)) {
2023             throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
2024                 'allowed values are: ' . implode(',', $allowedvalues));
2025         }
2027         // Check if private messaging between users is allowed.
2028         if (empty($CFG->messaging)) {
2029             // If we are retreiving only conversations, and messaging is disabled, throw an exception.
2030             if ($type == "conversations") {
2031                 throw new moodle_exception('disabled', 'message');
2032             }
2033             if ($type == "both") {
2034                 $warning = array();
2035                 $warning['item'] = 'message';
2036                 $warning['itemid'] = $USER->id;
2037                 $warning['warningcode'] = '1';
2038                 $warning['message'] = 'Private messages (conversations) are not enabled in this site.
2039                     Only notifications will be returned';
2040                 $warnings[] = $warning;
2041             }
2042         }
2044         if (!empty($useridto)) {
2045             if (core_user::is_real_user($useridto)) {
2046                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2047             } else {
2048                 throw new moodle_exception('invaliduser');
2049             }
2050         }
2052         if (!empty($useridfrom)) {
2053             // We use get_user here because the from user can be the noreply or support user.
2054             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2055         }
2057         // Check if the current user is the sender/receiver or just a privileged user.
2058         if ($useridto != $USER->id and $useridfrom != $USER->id and
2059              !has_capability('moodle/site:readallmessages', $context)) {
2060             throw new moodle_exception('accessdenied', 'admin');
2061         }
2063         // Which type of messages to retrieve.
2064         $notifications = -1;
2065         if ($type != 'both') {
2066             $notifications = ($type == 'notifications') ? 1 : 0;
2067         }
2069         $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2070         $sort = "mr.timecreated $orderdirection";
2072         if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2073             $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2075             // In some cases, we don't need to get the to/from user objects from the sql query.
2076             $userfromfullname = '';
2077             $usertofullname = '';
2079             // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
2080             if (!empty($useridto)) {
2081                 $usertofullname = fullname($userto, $canviewfullname);
2082                 // The user from may or may not be filled.
2083                 if (!empty($useridfrom)) {
2084                     $userfromfullname = fullname($userfrom, $canviewfullname);
2085                 }
2086             } else {
2087                 // If the useridto field is empty, the useridfrom must be filled.
2088                 $userfromfullname = fullname($userfrom, $canviewfullname);
2089             }
2090             foreach ($messages as $mid => $message) {
2092                 if (!$message->notification) {
2093                     // Do not return deleted messages.
2094                     if (($useridto == $USER->id and $message->timeusertodeleted) or
2095                         ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
2096                         unset($messages[$mid]);
2097                         continue;
2098                     }
2099                 } else {
2100                     // Return iconurl for notifications.
2101                     if (!isset($output)) {
2102                         $output = $PAGE->get_renderer('core');
2103                     }
2105                     if (!empty($message->component) && substr($message->component, 0, 4) == 'mod_') {
2106                         $iconurl = $output->image_url('icon', $message->component);
2107                     } else {
2108                         $iconurl = $output->image_url('i/marker', 'core');
2109                     }
2111                     $message->iconurl = clean_param($iconurl->out(), PARAM_URL);
2112                 }
2114                 // We need to get the user from the query.
2115                 if (empty($userfromfullname)) {
2116                     // Check for non-reply and support users.
2117                     if (core_user::is_real_user($message->useridfrom)) {
2118                         $user = new stdClass();
2119                         $user = username_load_fields_from_object($user, $message, 'userfrom');
2120                         $message->userfromfullname = fullname($user, $canviewfullname);
2121                     } else {
2122                         $user = core_user::get_user($message->useridfrom);
2123                         $message->userfromfullname = fullname($user, $canviewfullname);
2124                     }
2125                 } else {
2126                     $message->userfromfullname = $userfromfullname;
2127                 }
2129                 // We need to get the user from the query.
2130                 if (empty($usertofullname)) {
2131                     $user = new stdClass();
2132                     $user = username_load_fields_from_object($user, $message, 'userto');
2133                     $message->usertofullname = fullname($user, $canviewfullname);
2134                 } else {
2135                     $message->usertofullname = $usertofullname;
2136                 }
2138                 $message->text = message_format_message_text($message);
2139                 $messages[$mid] = (array) $message;
2140             }
2141         }
2143         $results = array(
2144             'messages' => $messages,
2145             'warnings' => $warnings
2146         );
2148         return $results;
2149     }
2151     /**
2152      * Get messages return description.
2153      *
2154      * @return external_single_structure
2155      * @since 2.8
2156      */
2157     public static function get_messages_returns() {
2158         return new external_single_structure(
2159             array(
2160                 'messages' => new external_multiple_structure(
2161                     new external_single_structure(
2162                         array(
2163                             'id' => new external_value(PARAM_INT, 'Message id'),
2164                             'useridfrom' => new external_value(PARAM_INT, 'User from id'),
2165                             'useridto' => new external_value(PARAM_INT, 'User to id'),
2166                             'subject' => new external_value(PARAM_TEXT, 'The message subject'),
2167                             'text' => new external_value(PARAM_RAW, 'The message text formated'),
2168                             'fullmessage' => new external_value(PARAM_RAW, 'The message'),
2169                             'fullmessageformat' => new external_format_value('fullmessage'),
2170                             'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
2171                             'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
2172                             'notification' => new external_value(PARAM_INT, 'Is a notification?'),
2173                             'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
2174                             'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
2175                             'timecreated' => new external_value(PARAM_INT, 'Time created'),
2176                             'timeread' => new external_value(PARAM_INT, 'Time read'),
2177                             'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
2178                             'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name'),
2179                             'component' => new external_value(PARAM_TEXT, 'The component that generated the notification',
2180                                 VALUE_OPTIONAL),
2181                             'eventtype' => new external_value(PARAM_TEXT, 'The type of notification', VALUE_OPTIONAL),
2182                             'customdata' => new external_value(PARAM_RAW, 'Custom data to be passed to the message processor.
2183                                 The data here is serialised using json_encode().', VALUE_OPTIONAL),
2184                             'iconurl' => new external_value(PARAM_URL, 'URL for icon, only for notifications.', VALUE_OPTIONAL),
2185                         ), 'message'
2186                     )
2187                 ),
2188                 'warnings' => new external_warnings()
2189             )
2190         );
2191     }
2193     /**
2194      * Mark all notifications as read parameters description.
2195      *
2196      * @return external_function_parameters
2197      * @since 3.2
2198      */
2199     public static function mark_all_notifications_as_read_parameters() {
2200         return new external_function_parameters(
2201             array(
2202                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2203                 'useridfrom' => new external_value(
2204                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2205                     VALUE_DEFAULT, 0),
2206                 'timecreatedto' => new external_value(
2207                     PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
2208                     VALUE_DEFAULT, 0),
2209             )
2210         );
2211     }
2213     /**
2214      * Mark all notifications as read function.
2215      *
2216      * @since  3.2
2217      * @throws invalid_parameter_exception
2218      * @throws moodle_exception
2219      * @param  int      $useridto       the user id who received the message
2220      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
2221      * @param  int      $timecreatedto  mark message created before this time as read, 0 for all messages
2222      * @return external_description
2223      */
2224     public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
2225         global $USER;
2227         $params = self::validate_parameters(
2228             self::mark_all_notifications_as_read_parameters(),
2229             array(
2230                 'useridto' => $useridto,
2231                 'useridfrom' => $useridfrom,
2232                 'timecreatedto' => $timecreatedto,
2233             )
2234         );
2236         $context = context_system::instance();
2237         self::validate_context($context);
2239         $useridto = $params['useridto'];
2240         $useridfrom = $params['useridfrom'];
2241         $timecreatedto = $params['timecreatedto'];
2243         if (!empty($useridto)) {
2244             if (core_user::is_real_user($useridto)) {
2245                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2246             } else {
2247                 throw new moodle_exception('invaliduser');
2248             }
2249         }
2251         if (!empty($useridfrom)) {
2252             // We use get_user here because the from user can be the noreply or support user.
2253             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2254         }
2256         // Check if the current user is the sender/receiver or just a privileged user.
2257         if ($useridto != $USER->id and $useridfrom != $USER->id and
2258             // The deleteanymessage cap seems more reasonable here than readallmessages.
2259              !has_capability('moodle/site:deleteanymessage', $context)) {
2260             throw new moodle_exception('accessdenied', 'admin');
2261         }
2263         \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
2265         return true;
2266     }
2268     /**
2269      * Mark all notifications as read return description.
2270      *
2271      * @return external_single_structure
2272      * @since 3.2
2273      */
2274     public static function mark_all_notifications_as_read_returns() {
2275         return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
2276     }
2278     /**
2279      * Get unread conversations count parameters description.
2280      *
2281      * @return external_function_parameters
2282      * @since 3.2
2283      */
2284     public static function get_unread_conversations_count_parameters() {
2285         return new external_function_parameters(
2286             array(
2287                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2288             )
2289         );
2290     }
2292     /**
2293      * Get unread messages count function.
2294      *
2295      * @since  3.2
2296      * @throws invalid_parameter_exception
2297      * @throws moodle_exception
2298      * @param  int      $useridto       the user id who received the message
2299      * @return external_description
2300      */
2301     public static function get_unread_conversations_count($useridto) {
2302         global $USER, $CFG;
2304         // Check if messaging is enabled.
2305         if (empty($CFG->messaging)) {
2306             throw new moodle_exception('disabled', 'message');
2307         }
2309         $params = self::validate_parameters(
2310             self::get_unread_conversations_count_parameters(),
2311             array('useridto' => $useridto)
2312         );
2314         $context = context_system::instance();
2315         self::validate_context($context);
2317         $useridto = $params['useridto'];
2319         if (!empty($useridto)) {
2320             if (core_user::is_real_user($useridto)) {
2321                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2322             } else {
2323                 throw new moodle_exception('invaliduser');
2324             }
2325         } else {
2326             $useridto = $USER->id;
2327         }
2329         // Check if the current user is the receiver or just a privileged user.
2330         if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
2331             throw new moodle_exception('accessdenied', 'admin');
2332         }
2334         return \core_message\api::count_unread_conversations($userto);
2335     }
2337     /**
2338      * Get unread conversations count return description.
2339      *
2340      * @return external_single_structure
2341      * @since 3.2
2342      */
2343     public static function get_unread_conversations_count_returns() {
2344         return new external_value(PARAM_INT, 'The count of unread messages for the user');
2345     }
2347     /**
2348      * Get blocked users parameters description.
2349      *
2350      * @return external_function_parameters
2351      * @since 2.9
2352      */
2353     public static function get_blocked_users_parameters() {
2354         return new external_function_parameters(
2355             array(
2356                 'userid' => new external_value(PARAM_INT,
2357                                 'the user whose blocked users we want to retrieve',
2358                                 VALUE_REQUIRED),
2359             )
2360         );
2361     }
2363     /**
2364      * Retrieve a list of users blocked
2365      *
2366      * @param  int $userid the user whose blocked users we want to retrieve
2367      * @return external_description
2368      * @since 2.9
2369      */
2370     public static function get_blocked_users($userid) {
2371         global $CFG, $USER, $PAGE;
2373         // Warnings array, it can be empty at the end but is mandatory.
2374         $warnings = array();
2376         // Validate params.
2377         $params = array(
2378             'userid' => $userid
2379         );
2380         $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2381         $userid = $params['userid'];
2383         // Validate context.
2384         $context = context_system::instance();
2385         self::validate_context($context);
2387         // Check if private messaging between users is allowed.
2388         if (empty($CFG->messaging)) {
2389             throw new moodle_exception('disabled', 'message');
2390         }
2392         $user = core_user::get_user($userid, '*', MUST_EXIST);
2393         core_user::require_active_user($user);
2395         // Check if we have permissions for retrieve the information.
2396         $capability = 'moodle/site:manageallmessaging';
2397         if (($USER->id != $userid) && !has_capability($capability, $context)) {
2398             throw new required_capability_exception($context, $capability, 'nopermissions', '');
2399         }
2401         // Now, we can get safely all the blocked users.
2402         $users = \core_message\api::get_blocked_users($user->id);
2404         $blockedusers = array();
2405         foreach ($users as $user) {
2406             $newuser = array(
2407                 'id' => $user->id,
2408                 'fullname' => fullname($user),
2409             );
2411             $userpicture = new user_picture($user);
2412             $userpicture->size = 1; // Size f1.
2413             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2415             $blockedusers[] = $newuser;
2416         }
2418         $results = array(
2419             'users' => $blockedusers,
2420             'warnings' => $warnings
2421         );
2422         return $results;
2423     }
2425     /**
2426      * Get blocked users return description.
2427      *
2428      * @return external_single_structure
2429      * @since 2.9
2430      */
2431     public static function get_blocked_users_returns() {
2432         return new external_single_structure(
2433             array(
2434                 'users' => new external_multiple_structure(
2435                     new external_single_structure(
2436                         array(
2437                             'id' => new external_value(PARAM_INT, 'User ID'),
2438                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2439                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
2440                         )
2441                     ),
2442                     'List of blocked users'
2443                 ),
2444                 'warnings' => new external_warnings()
2445             )
2446         );
2447     }
2449     /**
2450      * Returns description of method parameters
2451      *
2452      * @return external_function_parameters
2453      * @since 2.9
2454      */
2455     public static function mark_message_read_parameters() {
2456         return new external_function_parameters(
2457             array(
2458                 'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
2459                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
2460                     VALUE_DEFAULT, 0)
2461             )
2462         );
2463     }
2465     /**
2466      * Mark a single message as read, trigger message_viewed event
2467      *
2468      * @param  int $messageid id of the message (in the message table)
2469      * @param  int $timeread timestamp for when the message should be marked read
2470      * @return external_description
2471      * @throws invalid_parameter_exception
2472      * @throws moodle_exception
2473      * @since 2.9
2474      */
2475     public static function mark_message_read($messageid, $timeread) {
2476         global $CFG, $DB, $USER;
2478         // Check if private messaging between users is allowed.
2479         if (empty($CFG->messaging)) {
2480             throw new moodle_exception('disabled', 'message');
2481         }
2483         // Warnings array, it can be empty at the end but is mandatory.
2484         $warnings = array();
2486         // Validate params.
2487         $params = array(
2488             'messageid' => $messageid,
2489             'timeread' => $timeread
2490         );
2491         $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2493         if (empty($params['timeread'])) {
2494             $timeread = time();
2495         } else {
2496             $timeread = $params['timeread'];
2497         }
2499         // Validate context.
2500         $context = context_system::instance();
2501         self::validate_context($context);
2503         $sql = "SELECT m.*, mcm.userid as useridto
2504                   FROM {messages} m
2505             INNER JOIN {message_conversations} mc
2506                     ON m.conversationid = mc.id
2507             INNER JOIN {message_conversation_members} mcm
2508                     ON mcm.conversationid = mc.id
2509              LEFT JOIN {message_user_actions} mua
2510                     ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
2511                  WHERE mua.id is NULL
2512                    AND mcm.userid != m.useridfrom
2513                    AND m.id = ?";
2514         $messageparams = [];
2515         $messageparams[] = $USER->id;
2516         $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
2517         $messageparams[] = $params['messageid'];
2518         $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
2520         if ($message->useridto != $USER->id) {
2521             throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
2522         }
2524         \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2526         $results = array(
2527             'messageid' => $message->id,
2528             'warnings' => $warnings
2529         );
2530         return $results;
2531     }
2533     /**
2534      * Returns description of method result value
2535      *
2536      * @return external_description
2537      * @since 2.9
2538      */
2539     public static function mark_message_read_returns() {
2540         return new external_single_structure(
2541             array(
2542                 'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
2543                 'warnings' => new external_warnings()
2544             )
2545         );
2546     }
2548     /**
2549      * Returns description of method parameters
2550      *
2551      * @return external_function_parameters
2552      */
2553     public static function mark_notification_read_parameters() {
2554         return new external_function_parameters(
2555             array(
2556                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2557                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
2558                     VALUE_DEFAULT, 0)
2559             )
2560         );
2561     }
2563     /**
2564      * Mark a single notification as read.
2565      *
2566      * This will trigger a 'notification_viewed' event.
2567      *
2568      * @param int $notificationid id of the notification
2569      * @param int $timeread timestamp for when the notification should be marked read
2570      * @return external_description
2571      * @throws invalid_parameter_exception
2572      * @throws moodle_exception
2573      */
2574     public static function mark_notification_read($notificationid, $timeread) {
2575         global $CFG, $DB, $USER;
2577         // Warnings array, it can be empty at the end but is mandatory.
2578         $warnings = array();
2580         // Validate params.
2581         $params = array(
2582             'notificationid' => $notificationid,
2583             'timeread' => $timeread
2584         );
2585         $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
2587         if (empty($params['timeread'])) {
2588             $timeread = time();
2589         } else {
2590             $timeread = $params['timeread'];
2591         }
2593         // Validate context.
2594         $context = context_system::instance();
2595         self::validate_context($context);
2597         $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
2599         if ($notification->useridto != $USER->id) {
2600             throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
2601                 'notification as read');
2602         }
2604         \core_message\api::mark_notification_as_read($notification, $timeread);
2606         $results = array(
2607             'notificationid' => $notification->id,
2608             'warnings' => $warnings
2609         );
2611         return $results;
2612     }
2614     /**
2615      * Returns description of method result value
2616      *
2617      * @return external_description
2618      */
2619     public static function mark_notification_read_returns() {
2620         return new external_single_structure(
2621             array(
2622                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2623                 'warnings' => new external_warnings()
2624             )
2625         );
2626     }
2628     /**
2629      * Mark all conversation messages as read parameters description.
2630      *
2631      * @return external_function_parameters
2632      * @since 3.6
2633      */
2634     public static function mark_all_conversation_messages_as_read_parameters() {
2635         return new external_function_parameters(
2636             array(
2637                 'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
2638                 'conversationid' =>
2639                     new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
2640             )
2641         );
2642     }
2644     /**
2645      * Mark all conversation messages as read function.
2646      *
2647      * @param int $userid The user id of who we want to delete the conversation for
2648      * @param int $conversationid The id of the conversations
2649      * @since 3.6
2650      */
2651     public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
2652         global $CFG;
2654         // Check if messaging is enabled.
2655         if (empty($CFG->messaging)) {
2656             throw new moodle_exception('disabled', 'message');
2657         }
2659         $params = array(
2660             'userid' => $userid,
2661             'conversationid' => $conversationid,
2662         );
2663         $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
2665         $context = context_system::instance();
2666         self::validate_context($context);
2668         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2669         core_user::require_active_user($user);
2671         if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
2672             \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
2673         } else {
2674             throw new moodle_exception('accessdenied', 'admin');
2675         }
2676     }
2678     /**
2679      * Mark all conversation messages as read return description.
2680      *
2681      * @return external_warnings
2682      * @since 3.6
2683      */
2684     public static function mark_all_conversation_messages_as_read_returns() {
2685         return null;
2686     }
2688     /**
2689      * Returns description of method parameters.
2690      *
2691      * @return external_function_parameters
2692      * @since 3.6
2693      */
2694     public static function delete_conversations_by_id_parameters() {
2695         return new external_function_parameters(
2696             array(
2697                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
2698                 'conversationids' => new external_multiple_structure(
2699                     new external_value(PARAM_INT, 'The id of the conversation'),
2700                     'List of conversation IDs'
2701                 ),
2702             )
2703         );
2704     }
2706     /**
2707      * Deletes a conversation.
2708      *
2709      * @param int $userid The user id of who we want to delete the conversation for
2710      * @param int[] $conversationids The ids of the conversations
2711      * @return array
2712      * @throws moodle_exception
2713      * @since 3.6
2714      */
2715     public static function delete_conversations_by_id($userid, array $conversationids) {
2716         global $CFG;
2718         // Check if private messaging between users is allowed.
2719         if (empty($CFG->messaging)) {
2720             throw new moodle_exception('disabled', 'message');
2721         }
2723         // Validate params.
2724         $params = [
2725             'userid' => $userid,
2726             'conversationids' => $conversationids,
2727         ];
2728         $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
2730         // Validate context.
2731         $context = context_system::instance();
2732         self::validate_context($context);
2734         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2735         core_user::require_active_user($user);
2737         foreach ($params['conversationids'] as $conversationid) {
2738             if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
2739                 \core_message\api::delete_conversation_by_id($user->id, $conversationid);
2740             } else {
2741                 throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
2742             }
2743         }
2745         return [];
2746     }
2748     /**
2749      * Returns description of method result value.
2750      *
2751      * @return external_description
2752      * @since 3.6
2753      */
2754     public static function delete_conversations_by_id_returns() {
2755         return new external_warnings();
2756     }
2758     /**
2759      * Returns description of method parameters
2760      *
2761      * @return external_function_parameters
2762      * @since 3.1
2763      */
2764     public static function delete_message_parameters() {
2765         return new external_function_parameters(
2766             array(
2767                 'messageid' => new external_value(PARAM_INT, 'The message id'),
2768                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
2769                 'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
2770             )
2771         );
2772     }
2774     /**
2775      * Deletes a message
2776      *
2777      * @param  int $messageid the message id
2778      * @param  int $userid the user id of who we want to delete the message for
2779      * @param  bool $read if is a message read (default to true)
2780      * @return external_description
2781      * @throws moodle_exception
2782      * @since 3.1
2783      */
2784     public static function delete_message($messageid, $userid, $read = true) {
2785         global $CFG;
2787         // Check if private messaging between users is allowed.
2788         if (empty($CFG->messaging)) {
2789             throw new moodle_exception('disabled', 'message');
2790         }
2792         // Warnings array, it can be empty at the end but is mandatory.
2793         $warnings = array();
2795         // Validate params.
2796         $params = array(
2797             'messageid' => $messageid,
2798             'userid' => $userid,
2799             'read' => $read
2800         );
2801         $params = self::validate_parameters(self::delete_message_parameters(), $params);
2803         // Validate context.
2804         $context = context_system::instance();
2805         self::validate_context($context);
2807         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2808         core_user::require_active_user($user);
2810         if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
2811             $status = \core_message\api::delete_message($user->id, $params['messageid']);
2812         } else {
2813             throw new moodle_exception('You do not have permission to delete this message');
2814         }
2816         $results = array(
2817             'status' => $status,
2818             'warnings' => $warnings
2819         );
2820         return $results;
2821     }
2823     /**
2824      * Returns description of method result value
2825      *
2826      * @return external_description
2827      * @since 3.1
2828      */
2829     public static function delete_message_returns() {
2830         return new external_single_structure(
2831             array(
2832                 'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
2833                 'warnings' => new external_warnings()
2834             )
2835         );
2836     }
2838     /**
2839      * Returns description of method parameters
2840      *
2841      * @return external_function_parameters
2842      * @since 3.2
2843      */
2844     public static function message_processor_config_form_parameters() {
2845         return new external_function_parameters(
2846             array(
2847                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
2848                 'name' => new external_value(PARAM_TEXT, 'The name of the message processor'),
2849                 'formvalues' => new external_multiple_structure(
2850                     new external_single_structure(
2851                         array(
2852                             'name' => new external_value(PARAM_TEXT, 'name of the form element', VALUE_REQUIRED),
2853                             'value' => new external_value(PARAM_RAW, 'value of the form element', VALUE_REQUIRED),
2854                         )
2855                     ),
2856                     'Config form values',
2857                     VALUE_REQUIRED
2858                 ),
2859             )
2860         );
2861     }
2863     /**
2864      * Processes a message processor config form.
2865      *
2866      * @param  int $userid the user id
2867      * @param  string $name the name of the processor
2868      * @param  array $formvalues the form values
2869      * @return external_description
2870      * @throws moodle_exception
2871      * @since 3.2
2872      */
2873     public static function message_processor_config_form($userid, $name, $formvalues) {
2874         global $USER, $CFG;
2876         $params = self::validate_parameters(
2877             self::message_processor_config_form_parameters(),
2878             array(
2879                 'userid' => $userid,
2880                 'name' => $name,
2881                 'formvalues' => $formvalues,
2882             )
2883         );
2885         $user = self::validate_preferences_permissions($params['userid']);
2887         $processor = get_message_processor($params['name']);
2888         $preferences = [];
2889         $form = new stdClass();
2891         foreach ($params['formvalues'] as $formvalue) {
2892             // Curly braces to ensure interpretation is consistent between
2893             // php 5 and php 7.
2894             $form->{$formvalue['name']} = $formvalue['value'];
2895         }
2897         $processor->process_form($form, $preferences);
2899         if (!empty($preferences)) {
2900             set_user_preferences($preferences, $params['userid']);
2901         }
2902     }
2904     /**
2905      * Returns description of method result value
2906      *
2907      * @return external_description
2908      * @since 3.2
2909      */
2910     public static function message_processor_config_form_returns() {
2911         return null;
2912     }
2914     /**
2915      * Returns description of method parameters
2916      *
2917      * @return external_function_parameters
2918      * @since 3.2
2919      */
2920     public static function get_message_processor_parameters() {
2921         return new external_function_parameters(
2922             array(
2923                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user'),
2924                 'name' => new external_value(PARAM_TEXT, 'The name of the message processor', VALUE_REQUIRED),
2925             )
2926         );
2927     }
2929     /**
2930      * Get a message processor.
2931      *
2932      * @param int $userid
2933      * @param string $name the name of the processor
2934      * @return external_description
2935      * @throws moodle_exception
2936      * @since 3.2
2937      */
2938     public static function get_message_processor($userid, $name) {
2939         global $USER, $PAGE, $CFG;
2941         // Check if messaging is enabled.
2942         if (empty($CFG->messaging)) {
2943             throw new moodle_exception('disabled', 'message');
2944         }
2946         $params = self::validate_parameters(
2947             self::get_message_processor_parameters(),
2948             array(
2949                 'userid' => $userid,
2950                 'name' => $name,
2951             )
2952         );
2954         if (empty($params['userid'])) {
2955             $params['userid'] = $USER->id;
2956         }
2958         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2959         core_user::require_active_user($user);
2960         self::validate_context(context_user::instance($params['userid']));
2962         $processor = get_message_processor($params['name']);
2964         $processoroutput = new \core_message\output\processor($processor, $user);
2965         $renderer = $PAGE->get_renderer('core_message');
2967         return $processoroutput->export_for_template($renderer);
2968     }
2970     /**
2971      * Returns description of method result value
2972      *
2973      * @return external_description
2974      * @since 3.2
2975      */
2976     public static function get_message_processor_returns() {
2977         return new external_function_parameters(
2978             array(
2979                 'systemconfigured' => new external_value(PARAM_BOOL, 'Site configuration status'),
2980                 'userconfigured' => new external_value(PARAM_BOOL, 'The user configuration status'),
2981             )
2982         );
2983     }
2985     /**
2986      * Check that the user has enough permission to retrieve message or notifications preferences.
2987      *
2988      * @param  int $userid the user id requesting the preferences
2989      * @return stdClass full user object
2990      * @throws moodle_exception
2991      * @since  Moodle 3.2
2992      */
2993     protected static function validate_preferences_permissions($userid) {
2994         global $USER;
2996         if (empty($userid)) {
2997             $user = $USER;
2998         } else {
2999             $user = core_user::get_user($userid, '*', MUST_EXIST);
3000             core_user::require_active_user($user);
3001         }
3003         $systemcontext = context_system::instance();
3004         self::validate_context($systemcontext);
3006         // Check access control.
3007         if ($user->id == $USER->id) {
3008             // Editing own message profile.
3009             require_capability('moodle/user:editownmessageprofile', $systemcontext);
3010         } else {
3011             // Teachers, parents, etc.
3012             $personalcontext = context_user::instance($user->id);
3013             require_capability('moodle/user:editmessageprofile', $personalcontext);
3014         }
3015         return $user;
3016     }
3018     /**
3019      * Returns a notification or message preference structure.
3020      *
3021      * @return external_single_structure the structure
3022      * @since  Moodle 3.2
3023      */
3024     protected static function get_preferences_structure() {
3025         return new external_single_structure(
3026             array(
3027                 'userid' => new external_value(PARAM_INT, 'User id'),
3028                 'disableall' => new external_value(PARAM_INT, 'Whether all the preferences are disabled'),
3029                 'processors' => new external_multiple_structure(
3030                     new external_single_structure(
3031                         array(
3032                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3033                             'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3034                             'hassettings' => new external_value(PARAM_BOOL, 'Whether has settings'),
3035                             'contextid' => new external_value(PARAM_INT, 'Context id'),
3036                             'userconfigured' => new external_value(PARAM_INT, 'Whether is configured by the user'),
3037                         )
3038                     ),
3039                     'Config form values'
3040                 ),
3041                 'components' => new external_multiple_structure(
3042                     new external_single_structure(
3043                         array(
3044                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3045                             'notifications' => new external_multiple_structure(
3046                                 new external_single_structure(
3047                                     array(
3048                                         'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3049                                         'preferencekey' => new external_value(PARAM_ALPHANUMEXT, 'Preference key'),
3050                                         'processors' => new external_multiple_structure(
3051                                             new external_single_structure(
3052                                                 array(
3053                                                     'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3054                                                     'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3055                                                     'locked' => new external_value(PARAM_BOOL, 'Is locked by admin?'),
3056                                                     'lockedmessage' => new external_value(PARAM_TEXT,
3057                                                         'Text to display if locked', VALUE_OPTIONAL),
3058                                                     'userconfigured' => new external_value(PARAM_INT, 'Is configured?'),
3059                                                     'loggedin' => new external_single_structure(
3060                                                         array(
3061                                                             'name' => new external_value(PARAM_NOTAGS, 'Name'),
3062                                                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3063                                                             'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3064                                                         )
3065                                                     ),
3066                                                     'loggedoff' => new external_single_structure(
3067                                                         array(
3068                                                             'name' => new external_value(PARAM_NOTAGS, 'Name'),
3069                                                             'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3070                                                             'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3071                                                         )
3072                                                     ),
3073                                                 )
3074                                             ),
3075                                             'Processors values for this notification'
3076                                         ),
3077                                     )
3078                                 ),
3079                                 'List of notificaitons for the component'
3080                             ),
3081                         )
3082                     ),
3083                     'Available components'
3084                 ),
3085             )
3086         );
3087     }
3089     /**
3090      * Returns description of method parameters
3091      *
3092      * @return external_function_parameters
3093      * @since 3.2
3094      */
3095     public static function get_user_notification_preferences_parameters() {
3096         return new external_function_parameters(
3097             array(
3098                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3099             )
3100         );
3101     }
3103     /**
3104      * Get the notification preferences for a given user.
3105      *
3106      * @param int $userid id of the user, 0 for current user
3107      * @return external_description
3108      * @throws moodle_exception
3109      * @since 3.2
3110      */
3111     public static function get_user_notification_preferences($userid = 0) {
3112         global $PAGE;
3114         $params = self::validate_parameters(
3115             self::get_user_notification_preferences_parameters(),
3116             array(
3117                 'userid' => $userid,
3118             )
3119         );
3120         $user = self::validate_preferences_permissions($params['userid']);
3122         $processors = get_message_processors();
3123         $providers = message_get_providers_for_user($user->id);
3124         $preferences = \core_message\api::get_all_message_preferences($processors, $providers, $user);
3125         $notificationlist = new \core_message\output\preferences\notification_list($processors, $providers, $preferences, $user);
3127         $renderer = $PAGE->get_renderer('core_message');
3129         $result = array(
3130             'warnings' => array(),
3131             'preferences' => $notificationlist->export_for_template($renderer)
3132         );
3133         return $result;
3134     }
3136     /**
3137      * Returns description of method result value
3138      *
3139      * @return external_description
3140      * @since 3.2
3141      */
3142     public static function get_user_notification_preferences_returns() {
3143         return new external_function_parameters(
3144             array(
3145                 'preferences' => self::get_preferences_structure(),
3146                 'warnings' => new external_warnings(),
3147             )
3148         );
3149     }
3151     /**
3152      * Returns description of method parameters
3153      *
3154      * @return external_function_parameters
3155      * @since 3.2
3156      */
3157     public static function get_user_message_preferences_parameters() {
3158         return new external_function_parameters(
3159             array(
3160                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3161             )
3162         );
3163     }
3165     /**
3166      * Get the notification preferences for a given user.
3167      *
3168      * @param int $userid id of the user, 0 for current user
3169      * @return external_description
3170      * @throws moodle_exception
3171      * @since 3.2
3172      */
3173     public static function get_user_message_preferences($userid = 0) {
3174         global $CFG, $PAGE;
3176         $params = self::validate_parameters(
3177             self::get_user_message_preferences_parameters(),
3178             array(
3179                 'userid' => $userid,
3180             )
3181         );
3183         $user = self::validate_preferences_permissions($params['userid']);
3185         // Filter out enabled, available system_configured and user_configured processors only.
3186         $readyprocessors = array_filter(get_message_processors(), function($processor) {
3187             return $processor->enabled &&
3188                 $processor->configured &&
3189                 $processor->object->is_user_configured() &&
3190                 // Filter out processors that don't have and message preferences to configure.
3191                 $processor->object->has_message_preferences();
3192         });
3194         $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
3195             return $provider->component === 'moodle';
3196         });
3197         $preferences = \core_message\api::get_all_message_preferences($readyprocessors, $providers, $user);
3198         $notificationlistoutput = new \core_message\output\preferences\message_notification_list($readyprocessors,
3199             $providers, $preferences, $user);
3201         $renderer = $PAGE->get_renderer('core_message');
3203         $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
3205         $result = array(
3206             'warnings' => array(),
3207             'preferences' => $notificationlistoutput->export_for_template($renderer),
3208             'blocknoncontacts' => \core_message\api::get_user_privacy_messaging_preference($user->id),
3209             'entertosend' => $entertosend
3210         );
3211         return $result;
3212     }
3214     /**
3215      * Returns description of method result value
3216      *
3217      * @return external_description
3218      * @since 3.2
3219      */
3220     public static function get_user_message_preferences_returns() {
3221         return new external_function_parameters(
3222             array(
3223                 'preferences' => self::get_preferences_structure(),
3224                 'blocknoncontacts' => new external_value(PARAM_INT, 'Privacy messaging setting to define who can message you'),
3225                 'entertosend' => new external_value(PARAM_BOOL, 'User preference for using enter to send messages'),
3226                 'warnings' => new external_warnings(),
3227             )
3228         );
3229     }
3231     /**
3232      * Returns description of method parameters for the favourite_conversations() method.
3233      *
3234      * @return external_function_parameters
3235      */
3236     public static function set_favourite_conversations_parameters() {
3237         return new external_function_parameters(
3238             array(
3239                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3240                 'conversations' => new external_multiple_structure(
3241                     new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3242                 )
3243             )
3244         );
3245     }
3247     /**
3248      * Favourite a conversation, or list of conversations for a user.
3249      *
3250      * @param int $userid the id of the user, or 0 for the current user.
3251      * @param array $conversationids the list of conversations ids to favourite.
3252      * @return array
3253      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3254      */
3255     public static function set_favourite_conversations(int $userid, array $conversationids) {
3256         global $CFG, $USER;
3258         // All the business logic checks that really shouldn't be in here.
3259         if (empty($CFG->messaging)) {
3260             throw new moodle_exception('disabled', 'message');
3261         }
3262         $params = [
3263             'userid' => $userid,
3264             'conversations' => $conversationids
3265         ];
3266         $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
3267         $systemcontext = context_system::instance();
3268         self::validate_context($systemcontext);
3270         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3271             throw new moodle_exception('You do not have permission to perform this action.');
3272         }
3274         foreach ($params['conversations'] as $conversationid) {
3275             \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
3276         }
3278         return [];
3279     }
3281     /**
3282      * Return a description of the returns for the create_user_favourite_conversations() method.
3283      *
3284      * @return external_description
3285      */
3286     public static function set_favourite_conversations_returns() {
3287         return new external_warnings();
3288     }
3290     /**
3291      * Returns description of method parameters for unfavourite_conversations() method.
3292      *
3293      * @return external_function_parameters
3294      */
3295     public static function unset_favourite_conversations_parameters() {
3296         return new external_function_parameters(
3297             array(
3298                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3299                 'conversations' => new external_multiple_structure(
3300                     new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3301                 )
3302             )
3303         );
3304     }
3306     /**
3307      * Unfavourite a conversation, or list of conversations for a user.
3308      *
3309      * @param int $userid the id of the user, or 0 for the current user.
3310      * @param array $conversationids the list of conversations ids unset as favourites.
3311      * @return array
3312      * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3313      */
3314     public static function unset_favourite_conversations(int $userid, array $conversationids) {
3315         global $CFG, $USER;
3317         // All the business logic checks that really shouldn't be in here.
3318         if (empty($CFG->messaging)) {
3319             throw new moodle_exception('disabled', 'message');
3320         }
3321         $params = [
3322             'userid' => $userid,
3323             'conversations' => $conversationids
3324         ];
3325         $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
3326         $systemcontext = context_system::instance();
3327         self::validate_context($systemcontext);
3329         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3330             throw new moodle_exception('You do not have permission to perform this action.');
3331         }
3333         foreach ($params['conversations'] as $conversationid) {
3334             \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
3335         }
3337         return [];
3338     }
3340     /**
3341      * Unset favourite conversations return description.
3342      *
3343      * @return external_description
3344      */
3345     public static function unset_favourite_conversations_returns() {
3346         return new external_warnings();
3347     }
3349     /**
3350      * Returns description of method parameters for get_member_info() method.
3351      *
3352      * @return external_function_parameters
3353      */
3354     public static function get_member_info_parameters() {
3355         return new external_function_parameters(
3356             array(
3357                 'referenceuserid' => new external_value(PARAM_INT, 'id of the user'),
3358                 'userids' => new external_multiple_structure(
3359                     new external_value(PARAM_INT, 'id of members to get')
3360                 ),
3361                 'includecontactrequests' => new external_value(PARAM_BOOL, 'include contact requests in response', VALUE_DEFAULT, false),
3362                 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'include privacy info in response', VALUE_DEFAULT, false)
3363             )
3364         );
3365     }
3367     /**
3368      * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
3369      *
3370      * This is the basic structure used when returning members, and includes information about the relationship between each member
3371      * and the referenceuser, such as a whether the referenceuser has marked