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