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