Merge branch 'MDL-63923-master' of git://github.com/bmbrands/moodle
[moodle.git] / message / externallib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * External message API
20  *
21  * @package    core_message
22  * @category   external
23  * @copyright  2011 Jerome Mouneyrac
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once("$CFG->libdir/externallib.php");
30 require_once($CFG->dirroot . "/message/lib.php");
32 /**
33  * Message external functions
34  *
35  * @package    core_message
36  * @category   external
37  * @copyright  2011 Jerome Mouneyrac
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  * @since Moodle 2.2
40  */
41 class core_message_external extends external_api {
42     /**
43      * Returns description of method parameters
44      *
45      * @return external_function_parameters
46      * @since Moodle 3.6
47      */
48     public static function send_messages_to_conversation_parameters() {
49         return new external_function_parameters(
50             array(
51                 'conversationid' => new external_value(PARAM_INT, 'id of the conversation'),
52                 'messages' => new external_multiple_structure(
53                     new external_single_structure(
54                         array(
55                             'text' => new external_value(PARAM_RAW, 'the text of the message'),
56                             'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
57                         )
58                     )
59                 )
60             )
61         );
62     }
64     /**
65      * Send messages from the current USER to a conversation.
66      *
67      * This conversation may be any type of conversation, individual or group.
68      *
69      * @param int $conversationid the id of the conversation to which the messages will be sent.
70      * @param array $messages An array of message to send.
71      * @return array the array of messages which were sent (created).
72      * @since Moodle 3.6
73      */
74     public static function send_messages_to_conversation(int $conversationid, array $messages = []) {
75         global $CFG, $USER;
77         // Check if messaging is enabled.
78         if (empty($CFG->messaging)) {
79             throw new moodle_exception('disabled', 'message');
80         }
82         // Ensure the current user is allowed to run this function.
83         $context = context_system::instance();
84         self::validate_context($context);
86         $params = self::validate_parameters(self::send_messages_to_conversation_parameters(), [
87             'conversationid' => $conversationid,
88             'messages' => $messages
89         ]);
91         $messages = [];
92         foreach ($params['messages'] as $message) {
93             $messages[] = \core_message\api::send_message_to_conversation($USER->id, $params['conversationid'], $message['text'],
94                 $message['textformat']);
95         }
97         return $messages;
98     }
100     /**
101      * Returns description of method result value.
102      *
103      * @return external_description
104      * @since Moodle 3.6
105      */
106     public static function send_messages_to_conversation_returns() {
107         return new external_multiple_structure(
108             self::get_conversation_message_structure()
109         );
110     }
113     /**
114      * Returns description of method parameters
115      *
116      * @return external_function_parameters
117      * @since Moodle 2.2
118      */
119     public static function send_instant_messages_parameters() {
120         return new external_function_parameters(
121             array(
122                 'messages' => new external_multiple_structure(
123                     new external_single_structure(
124                         array(
125                             'touserid' => new external_value(PARAM_INT, 'id of the user to send the private message'),
126                             'text' => new external_value(PARAM_RAW, 'the text of the message'),
127                             'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
128                             '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),
129                         )
130                     )
131                 )
132             )
133         );
134     }
136     /**
137      * Send private messages from the current USER to other users
138      *
139      * @param array $messages An array of message to send.
140      * @return array
141      * @since Moodle 2.2
142      */
143     public static function send_instant_messages($messages = array()) {
144         global $CFG, $USER, $DB;
146         // Check if messaging is enabled.
147         if (empty($CFG->messaging)) {
148             throw new moodle_exception('disabled', 'message');
149         }
151         // Ensure the current user is allowed to run this function
152         $context = context_system::instance();
153         self::validate_context($context);
154         require_capability('moodle/site:sendmessage', $context);
156         $params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
158         //retrieve all tousers of the messages
159         $receivers = array();
160         foreach($params['messages'] as $message) {
161             $receivers[] = $message['touserid'];
162         }
163         list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
164         $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
166         $resultmessages = array();
167         foreach ($params['messages'] as $message) {
168             $resultmsg = array(); //the infos about the success of the operation
170             // We are going to do some checking.
171             // Code should match /messages/index.php checks.
172             $success = true;
174             // Check the user exists.
175             if (empty($tousers[$message['touserid']])) {
176                 $success = false;
177                 $errormessage = get_string('touserdoesntexist', 'message', $message['touserid']);
178             }
180             // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead userid
181             // Check if the recipient can be messaged by the sender.
182             if ($success && !\core_message\api::can_post_message($tousers[$message['touserid']], $USER)) {
183                 $success = false;
184                 $errormessage = get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message['touserid'])));
185             }
187             // Now we can send the message (at least try).
188             if ($success) {
189                 // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead one touser object.
190                 $success = message_post_message($USER, $tousers[$message['touserid']],
191                         $message['text'], external_validate_format($message['textformat']));
192             }
194             // Build the resultmsg.
195             if (isset($message['clientmsgid'])) {
196                 $resultmsg['clientmsgid'] = $message['clientmsgid'];
197             }
198             if ($success) {
199                 $resultmsg['msgid'] = $success;
200             } else {
201                 // WARNINGS: for backward compatibility we return this errormessage.
202                 //          We should have thrown exceptions as these errors prevent results to be returned.
203                 // See http://docs.moodle.org/dev/Errors_handling_in_web_services#When_to_send_a_warning_on_the_server_side .
204                 $resultmsg['msgid'] = -1;
205                 $resultmsg['errormessage'] = $errormessage;
206             }
208             $resultmessages[] = $resultmsg;
209         }
211         return $resultmessages;
212     }
214     /**
215      * Returns description of method result value
216      *
217      * @return external_description
218      * @since Moodle 2.2
219      */
220     public static function send_instant_messages_returns() {
221         return new external_multiple_structure(
222             new external_single_structure(
223                 array(
224                     'msgid' => new external_value(PARAM_INT, 'test this to know if it succeeds:  id of the created message if it succeeded, -1 when failed'),
225                     'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
226                     'errormessage' => new external_value(PARAM_TEXT, 'error message - if it failed', VALUE_OPTIONAL)
227                 )
228             )
229         );
230     }
232     /**
233      * Create contacts parameters description.
234      *
235      * @deprecated since Moodle 3.6
236      * @return external_function_parameters
237      * @since Moodle 2.5
238      */
239     public static function create_contacts_parameters() {
240         return new external_function_parameters(
241             array(
242                 'userids' => new external_multiple_structure(
243                     new external_value(PARAM_INT, 'User ID'),
244                     'List of user IDs'
245                 ),
246                 'userid' => new external_value(PARAM_INT, 'The id of the user we are creating the contacts for, 0 for the
247                     current user', VALUE_DEFAULT, 0)
248             )
249         );
250     }
252     /**
253      * Create contacts.
254      *
255      * @deprecated since Moodle 3.6
256      * @param array $userids array of user IDs.
257      * @param int $userid The id of the user we are creating the contacts for
258      * @return external_description
259      * @since Moodle 2.5
260      */
261     public static function create_contacts($userids, $userid = 0) {
262         global $CFG, $USER;
264         // Check if messaging is enabled.
265         if (empty($CFG->messaging)) {
266             throw new moodle_exception('disabled', 'message');
267         }
269         if (empty($userid)) {
270             $userid = $USER->id;
271         }
273         // Validate context.
274         $context = context_system::instance();
275         self::validate_context($context);
277         $params = array('userids' => $userids, 'userid' => $userid);
278         $params = self::validate_parameters(self::create_contacts_parameters(), $params);
280         $capability = 'moodle/site:manageallmessaging';
281         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
282             throw new required_capability_exception($context, $capability, 'nopermissions', '');
283         }
285         $warnings = array();
286         foreach ($params['userids'] as $id) {
287             if (!message_add_contact($id, 0, $params['userid'])) {
288                 $warnings[] = array(
289                     'item' => 'user',
290                     'itemid' => $id,
291                     'warningcode' => 'contactnotcreated',
292                     'message' => 'The contact could not be created'
293                 );
294             }
295         }
296         return $warnings;
297     }
299     /**
300      * Create contacts return description.
301      *
302      * @deprecated since Moodle 3.6
303      * @return external_description
304      * @since Moodle 2.5
305      */
306     public static function create_contacts_returns() {
307         return new external_warnings();
308     }
310     /**
311      * Marking the method as deprecated.
312      *
313      * @return bool
314      */
315     public static function create_contacts_is_deprecated() {
316         return true;
317     }
319     /**
320      * Delete contacts parameters description.
321      *
322      * @return external_function_parameters
323      * @since Moodle 2.5
324      */
325     public static function delete_contacts_parameters() {
326         return new external_function_parameters(
327             array(
328                 'userids' => new external_multiple_structure(
329                     new external_value(PARAM_INT, 'User ID'),
330                     'List of user IDs'
331                 ),
332                 'userid' => new external_value(PARAM_INT, 'The id of the user we are deleting the contacts for, 0 for the
333                     current user', VALUE_DEFAULT, 0)
334             )
335         );
336     }
338     /**
339      * Delete contacts.
340      *
341      * @param array $userids array of user IDs.
342      * @param int $userid The id of the user we are deleting the contacts for
343      * @return null
344      * @since Moodle 2.5
345      */
346     public static function delete_contacts($userids, $userid = 0) {
347         global $CFG, $USER;
349         // Check if messaging is enabled.
350         if (empty($CFG->messaging)) {
351             throw new moodle_exception('disabled', 'message');
352         }
354         if (empty($userid)) {
355             $userid = $USER->id;
356         }
358         // Validate context.
359         $context = context_system::instance();
360         self::validate_context($context);
362         $params = array('userids' => $userids, 'userid' => $userid);
363         $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
365         $capability = 'moodle/site:manageallmessaging';
366         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
367             throw new required_capability_exception($context, $capability, 'nopermissions', '');
368         }
370         foreach ($params['userids'] as $id) {
371             \core_message\api::remove_contact($params['userid'], $id);
372         }
374         return null;
375     }
377     /**
378      * Delete contacts return description.
379      *
380      * @return external_description
381      * @since Moodle 2.5
382      */
383     public static function delete_contacts_returns() {
384         return null;
385     }
387     /**
388      * Block user parameters description.
389      *
390      * @return external_function_parameters
391      */
392     public static function block_user_parameters() {
393         return new external_function_parameters(
394             [
395                 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
396                 'blockeduserid' => new external_value(PARAM_INT, 'The id of the user being blocked'),
397             ]
398         );
399     }
401     /**
402      * Blocks a user.
403      *
404      * @param int $userid The id of the user who is blocking
405      * @param int $blockeduserid The id of the user being blocked
406      * @return external_description
407      */
408     public static function block_user(int $userid, int $blockeduserid) {
409         global $CFG, $USER;
411         // Check if messaging is enabled.
412         if (empty($CFG->messaging)) {
413             throw new moodle_exception('disabled', 'message');
414         }
416         // Validate context.
417         $context = context_system::instance();
418         self::validate_context($context);
420         $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
421         $params = self::validate_parameters(self::block_user_parameters(), $params);
423         $capability = 'moodle/site:manageallmessaging';
424         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
425             throw new required_capability_exception($context, $capability, 'nopermissions', '');
426         }
428         if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
429             \core_message\api::block_user($params['userid'], $params['blockeduserid']);
430         }
432         return [];
433     }
435     /**
436      * Block user return description.
437      *
438      * @return external_description
439      */
440     public static function block_user_returns() {
441         return new external_warnings();
442     }
444     /**
445      * Unblock user parameters description.
446      *
447      * @return external_function_parameters
448      */
449     public static function unblock_user_parameters() {
450         return new external_function_parameters(
451             [
452                 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
453                 'unblockeduserid' => new external_value(PARAM_INT, 'The id of the user being unblocked'),
454             ]
455         );
456     }
458     /**
459      * Unblock user.
460      *
461      * @param int $userid The id of the user who is unblocking
462      * @param int $unblockeduserid The id of the user being unblocked
463      */
464     public static function unblock_user(int $userid, int $unblockeduserid) {
465         global $CFG, $USER;
467         // Check if messaging is enabled.
468         if (empty($CFG->messaging)) {
469             throw new moodle_exception('disabled', 'message');
470         }
472         // Validate context.
473         $context = context_system::instance();
474         self::validate_context($context);
476         $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
477         $params = self::validate_parameters(self::unblock_user_parameters(), $params);
479         $capability = 'moodle/site:manageallmessaging';
480         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
481             throw new required_capability_exception($context, $capability, 'nopermissions', '');
482         }
484         \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
486         return [];
487     }
489     /**
490      * Unblock user return description.
491      *
492      * @return external_description
493      */
494     public static function unblock_user_returns() {
495         return new external_warnings();
496     }
498     /**
499      * Block contacts parameters description.
500      *
501      * @deprecated since Moodle 3.6
502      * @return external_function_parameters
503      * @since Moodle 2.5
504      */
505     public static function block_contacts_parameters() {
506         return new external_function_parameters(
507             array(
508                 'userids' => new external_multiple_structure(
509                     new external_value(PARAM_INT, 'User ID'),
510                     'List of user IDs'
511                 ),
512                 'userid' => new external_value(PARAM_INT, 'The id of the user we are blocking the contacts for, 0 for the
513                     current user', VALUE_DEFAULT, 0)
514             )
515         );
516     }
518     /**
519      * Block contacts.
520      *
521      * @deprecated since Moodle 3.6
522      * @param array $userids array of user IDs.
523      * @param int $userid The id of the user we are blocking the contacts for
524      * @return external_description
525      * @since Moodle 2.5
526      */
527     public static function block_contacts($userids, $userid = 0) {
528         global $CFG, $USER;
530         // Check if messaging is enabled.
531         if (empty($CFG->messaging)) {
532             throw new moodle_exception('disabled', 'message');
533         }
535         if (empty($userid)) {
536             $userid = $USER->id;
537         }
539         // Validate context.
540         $context = context_system::instance();
541         self::validate_context($context);
543         $params = array('userids' => $userids, 'userid' => $userid);
544         $params = self::validate_parameters(self::block_contacts_parameters(), $params);
546         $capability = 'moodle/site:manageallmessaging';
547         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
548             throw new required_capability_exception($context, $capability, 'nopermissions', '');
549         }
551         $warnings = array();
552         foreach ($params['userids'] as $id) {
553             if (!message_block_contact($id, $params['userid'])) {
554                 $warnings[] = array(
555                     'item' => 'user',
556                     'itemid' => $id,
557                     'warningcode' => 'contactnotblocked',
558                     'message' => 'The contact could not be blocked'
559                 );
560             }
561         }
562         return $warnings;
563     }
565     /**
566      * Block contacts return description.
567      *
568      * @deprecated since Moodle 3.6
569      * @return external_description
570      * @since Moodle 2.5
571      */
572     public static function block_contacts_returns() {
573         return new external_warnings();
574     }
576     /**
577      * Marking the method as deprecated.
578      *
579      * @return bool
580      */
581     public static function block_contacts_is_deprecated() {
582         return true;
583     }
585     /**
586      * Unblock contacts parameters description.
587      *
588      * @deprecated since Moodle 3.6
589      * @return external_function_parameters
590      * @since Moodle 2.5
591      */
592     public static function unblock_contacts_parameters() {
593         return new external_function_parameters(
594             array(
595                 'userids' => new external_multiple_structure(
596                     new external_value(PARAM_INT, 'User ID'),
597                     'List of user IDs'
598                 ),
599                 'userid' => new external_value(PARAM_INT, 'The id of the user we are unblocking the contacts for, 0 for the
600                     current user', VALUE_DEFAULT, 0)
601             )
602         );
603     }
605     /**
606      * Unblock contacts.
607      *
608      * @param array $userids array of user IDs.
609      * @param int $userid The id of the user we are unblocking the contacts for
610      * @return null
611      * @since Moodle 2.5
612      */
613     public static function unblock_contacts($userids, $userid = 0) {
614         global $CFG, $USER;
616         // Check if messaging is enabled.
617         if (empty($CFG->messaging)) {
618             throw new moodle_exception('disabled', 'message');
619         }
621         if (empty($userid)) {
622             $userid = $USER->id;
623         }
625         // Validate context.
626         $context = context_system::instance();
627         self::validate_context($context);
629         $params = array('userids' => $userids, 'userid' => $userid);
630         $params = self::validate_parameters(self::unblock_contacts_parameters(), $params);
632         $capability = 'moodle/site:manageallmessaging';
633         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
634             throw new required_capability_exception($context, $capability, 'nopermissions', '');
635         }
637         foreach ($params['userids'] as $id) {
638             message_unblock_contact($id, $params['userid']);
639         }
641         return null;
642     }
644     /**
645      * Unblock contacts return description.
646      *
647      * @deprecated since Moodle 3.6
648      * @return external_description
649      * @since Moodle 2.5
650      */
651     public static function unblock_contacts_returns() {
652         return null;
653     }
655     /**
656      * Marking the method as deprecated.
657      *
658      * @return bool
659      */
660     public static function unblock_contacts_is_deprecated() {
661         return true;
662     }
664     /**
665      * Returns contact requests parameters description.
666      *
667      * @return external_function_parameters
668      */
669     public static function get_contact_requests_parameters() {
670         return new external_function_parameters(
671             [
672                 'userid' => new external_value(PARAM_INT, 'The id of the user we want the requests for'),
673                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
674                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
675             ]
676         );
677     }
679     /**
680      * Handles returning the contact requests for a user.
681      *
682      * This also includes the user data necessary to display information
683      * about the user.
684      *
685      * It will not include blocked users.
686      *
687      * @param int $userid The id of the user we want to get the contact requests for
688      * @param int $limitfrom
689      * @param int $limitnum
690      */
691     public static function get_contact_requests(int $userid, int $limitfrom = 0, int $limitnum = 0) {
692         global $CFG, $USER;
694         // Check if messaging is enabled.
695         if (empty($CFG->messaging)) {
696             throw new moodle_exception('disabled', 'message');
697         }
699         // Validate context.
700         $context = context_system::instance();
701         self::validate_context($context);
703         $params = [
704             'userid' => $userid,
705             'limitfrom' => $limitfrom,
706             'limitnum' => $limitnum
707         ];
708         $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
710         $capability = 'moodle/site:manageallmessaging';
711         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
712             throw new required_capability_exception($context, $capability, 'nopermissions', '');
713         }
715         return \core_message\api::get_contact_requests($params['userid'], $params['limitfrom'], $params['limitnum']);
716     }
718     /**
719      * Returns the contact requests return description.
720      *
721      * @return external_description
722      */
723     public static function get_contact_requests_returns() {
724         return new external_multiple_structure(
725             self::get_conversation_member_structure()
726         );
727     }
729     /**
730      * Returns get conversation members parameters description.
731      *
732      * @return external_function_parameters
733      */
734     public static function get_conversation_members_parameters() {
735         return new external_function_parameters(
736             [
737                 'userid' => new external_value(PARAM_INT, 'The id of the user we are performing this action on behalf of'),
738                 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation'),
739                 'includecontactrequests' => new external_value(PARAM_BOOL, 'Do we want to include contact requests?',
740                     VALUE_DEFAULT, false),
741                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
742                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
743             ]
744         );
745     }
747     /**
748      * Returns a list of conversation members.
749      *
750      * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info.
751      * @param int $conversationid The id of the conversation
752      * @param bool $includecontactrequests Do we want to include contact requests with this data?
753      * @param int $limitfrom
754      * @param int $limitnum
755      * @return array
756      */
757     public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false,
758                                                     int $limitfrom = 0, int $limitnum = 0) {
759         global $CFG, $USER;
761         // Check if messaging is enabled.
762         if (empty($CFG->messaging)) {
763             throw new moodle_exception('disabled', 'message');
764         }
766         // Validate context.
767         $context = context_system::instance();
768         self::validate_context($context);
770         $params = [
771             'userid' => $userid,
772             'conversationid' => $conversationid,
773             'includecontactrequests' => $includecontactrequests,
774             'limitfrom' => $limitfrom,
775             'limitnum' => $limitnum
776         ];
777         $params = self::validate_parameters(self::get_conversation_members_parameters(), $params);
779         $capability = 'moodle/site:manageallmessaging';
780         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
781             throw new required_capability_exception($context, $capability, 'nopermissions', '');
782         }
784         // The user needs to be a part of the conversation before querying who the members are.
785         if (!\core_message\api::is_user_in_conversation($params['userid'], $params['conversationid'])) {
786             throw new moodle_exception('You are not a member of this conversation.');
787         }
790         return \core_message\api::get_conversation_members($params['userid'], $params['conversationid'], $params['includecontactrequests'],
791             $params['limitfrom'], $params['limitnum']);
792     }
794     /**
795      * Returns the get conversation members return description.
796      *
797      * @return external_description
798      */
799     public static function get_conversation_members_returns() {
800         return new external_multiple_structure(
801             self::get_conversation_member_structure(true)
802         );
803     }
805     /**
806      * Creates a contact request parameters description.
807      *
808      * @return external_function_parameters
809      */
810     public static function create_contact_request_parameters() {
811         return new external_function_parameters(
812             [
813                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
814                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
815             ]
816         );
817     }
819     /**
820      * Creates a contact request.
821      *
822      * @param int $userid The id of the user who is creating the contact request
823      * @param int $requesteduserid The id of the user being requested
824      */
825     public static function create_contact_request(int $userid, int $requesteduserid) {
826         global $CFG, $USER;
828         // Check if messaging is enabled.
829         if (empty($CFG->messaging)) {
830             throw new moodle_exception('disabled', 'message');
831         }
833         // Validate context.
834         $context = context_system::instance();
835         self::validate_context($context);
837         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
838         $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
840         $capability = 'moodle/site:manageallmessaging';
841         if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
842             throw new required_capability_exception($context, $capability, 'nopermissions', '');
843         }
845         if (!\core_message\api::can_create_contact($params['userid'], $params['requesteduserid'])) {
846             $warning[] = [
847                 'item' => 'user',
848                 'itemid' => $params['requesteduserid'],
849                 'warningcode' => 'cannotcreatecontactrequest',
850                 'message' => 'You are unable to create a contact request for this user'
851             ];
852             return $warning;
853         }
855         if (!\core_message\api::does_contact_request_exist($params['userid'], $params['requesteduserid'])) {
856             \core_message\api::create_contact_request($params['userid'], $params['requesteduserid']);
857         }
859         return [];
860     }
862     /**
863      * Creates a contact request return description.
864      *
865      * @return external_description
866      */
867     public static function create_contact_request_returns() {
868         return new external_warnings();
869     }
871     /**
872      * Confirm a contact request parameters description.
873      *
874      * @return external_function_parameters
875      */
876     public static function confirm_contact_request_parameters() {
877         return new external_function_parameters(
878             [
879                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
880                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
881             ]
882         );
883     }
885     /**
886      * Confirm a contact request.
887      *
888      * @param int $userid The id of the user who is creating the contact request
889      * @param int $requesteduserid The id of the user being requested
890      */
891     public static function confirm_contact_request(int $userid, int $requesteduserid) {
892         global $CFG, $USER;
894         // Check if messaging is enabled.
895         if (empty($CFG->messaging)) {
896             throw new moodle_exception('disabled', 'message');
897         }
899         // Validate context.
900         $context = context_system::instance();
901         self::validate_context($context);
903         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
904         $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
906         $capability = 'moodle/site:manageallmessaging';
907         if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
908             throw new required_capability_exception($context, $capability, 'nopermissions', '');
909         }
911         \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
913         return [];
914     }
916     /**
917      * Confirm a contact request return description.
918      *
919      * @return external_description
920      */
921     public static function confirm_contact_request_returns() {
922         return new external_warnings();
923     }
925     /**
926      * Declines a contact request parameters description.
927      *
928      * @return external_function_parameters
929      */
930     public static function decline_contact_request_parameters() {
931         return new external_function_parameters(
932             [
933                 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
934                 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
935             ]
936         );
937     }
939     /**
940      * Declines a contact request.
941      *
942      * @param int $userid The id of the user who is creating the contact request
943      * @param int $requesteduserid The id of the user being requested
944      */
945     public static function decline_contact_request(int $userid, int $requesteduserid) {
946         global $CFG, $USER;
948         // Check if messaging is enabled.
949         if (empty($CFG->messaging)) {
950             throw new moodle_exception('disabled', 'message');
951         }
953         // Validate context.
954         $context = context_system::instance();
955         self::validate_context($context);
957         $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
958         $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
960         $capability = 'moodle/site:manageallmessaging';
961         if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
962             throw new required_capability_exception($context, $capability, 'nopermissions', '');
963         }
965         \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
967         return [];
968     }
970     /**
971      * Declines a contact request return description.
972      *
973      * @return external_description
974      */
975     public static function decline_contact_request_returns() {
976         return new external_warnings();
977     }
979     /**
980      * Return the structure of a message area contact.
981      *
982      * @return external_single_structure
983      * @since Moodle 3.2
984      */
985     private static function get_messagearea_contact_structure() {
986         return new external_single_structure(
987             array(
988                 'userid' => new external_value(PARAM_INT, 'The user\'s id'),
989                 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
990                 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
991                 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
992                 'ismessaging' => new external_value(PARAM_BOOL, 'If we are messaging the user'),
993                 'sentfromcurrentuser' => new external_value(PARAM_BOOL, 'Was the last message sent from the current user?'),
994                 'lastmessage' => new external_value(PARAM_NOTAGS, 'The user\'s last message'),
995                 'messageid' => new external_value(PARAM_INT, 'The unique search message id', VALUE_DEFAULT, null),
996                 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
997                 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
998                 'isread' => new external_value(PARAM_BOOL, 'If the user has read the message'),
999                 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1000                 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1001                     VALUE_DEFAULT, null),
1002             )
1003         );
1004     }
1006     /**
1007      * Return the structure of a conversation.
1008      *
1009      * @return external_single_structure
1010      * @since Moodle 3.6
1011      */
1013     private static function get_conversation_structure() {
1014         return new external_single_structure(
1015             array(
1016                 'id' => new external_value(PARAM_INT, 'The conversation id'),
1017                 'name' => new external_value(PARAM_NOTAGS, 'The conversation name, if set', VALUE_DEFAULT, null),
1018                 'subname' => new external_value(PARAM_NOTAGS, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null),
1019                 'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null),
1020                 'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group)'),
1021                 'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'),
1022                 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked conversation this conversation as a favourite'),
1023                 'isread' => new external_value(PARAM_BOOL, 'If the user has read all messages in the conversation'),
1024                 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1025                     VALUE_DEFAULT, null),
1026                 'members' => new external_multiple_structure(
1027                     self::get_conversation_member_structure(true)
1028                 ),
1029                 'messages' => new external_multiple_structure(
1030                     self::get_conversation_message_structure()
1031                 ),
1032             )
1033         );
1034     }
1036     /**
1037      * Return the structure of a conversation member.
1038      *
1039      * @param bool $includecontactrequests Are we including contact requests?
1040      * @param bool $includeconversations Are we including conversations?
1041      * @return external_single_structure
1042      * @since Moodle 3.6
1043      */
1044     private static function get_conversation_member_structure(bool $includecontactrequests = false,
1045                                                               bool $includeconversations = false) {
1046         $result = [
1047             'id' => new external_value(PARAM_INT, 'The user id'),
1048             'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1049             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1050             'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1051             'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1052             'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1053             'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1054             'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'),
1055             'isdeleted' => new external_value(PARAM_BOOL, 'Is the user deleted?'),
1056             'canmessage' => new external_value(PARAM_BOOL, 'If the user can be messaged'),
1057             'requirescontact' => new external_value(PARAM_BOOL, 'If the user requires to be contacts'),
1058         ];
1060         if ($includecontactrequests) {
1061             $result['contactrequests'] = new external_multiple_structure(
1062                 new external_single_structure(
1063                     [
1064                         'id' => new external_value(PARAM_INT, 'The id of the message'),
1065                         'userid' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1066                         'requesteduserid' => new external_value(PARAM_RAW, 'The text of the message'),
1067                         'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1068                     ]
1069                 ), 'The contact requests', VALUE_OPTIONAL
1070             );
1071         }
1073         if ($includeconversations) {
1074             $result['conversations'] = new external_multiple_structure(new external_single_structure(
1075                 array(
1076                     'id' => new external_value(PARAM_INT, 'Conversations id'),
1077                     'type' => new external_value(PARAM_INT, 'Conversation type: private or public'),
1078                     'name' => new external_value(PARAM_TEXT, 'Multilang compatible conversation name'. VALUE_OPTIONAL),
1079                     'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'),
1080                 ), 'information about conversation', VALUE_OPTIONAL),
1081                 'Conversations between users', VALUE_OPTIONAL
1082             );
1083         }
1085         return new external_single_structure(
1086             $result
1087         );
1088     }
1090     /**
1091      * Return the structure of a message area message.
1092      *
1093      * @return external_single_structure
1094      * @since Moodle 3.6
1095      */
1096     private static function get_conversation_message_structure() {
1097         return new external_single_structure(
1098             array(
1099                 'id' => new external_value(PARAM_INT, 'The id of the message'),
1100                 'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1101                 'text' => new external_value(PARAM_RAW, 'The text of the message'),
1102                 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1103             )
1104         );
1105     }
1107     /**
1108      * Return the structure of a message area message.
1109      *
1110      * @return external_single_structure
1111      * @since Moodle 3.2
1112      */
1113     private static function get_messagearea_message_structure() {
1114         return new external_single_structure(
1115             array(
1116                 'id' => new external_value(PARAM_INT, 'The id of the message'),
1117                 'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1118                 'useridto' => new external_value(PARAM_INT, 'The id of the user who received the message'),
1119                 'text' => new external_value(PARAM_RAW, 'The text of the message'),
1120                 'displayblocktime' => new external_value(PARAM_BOOL, 'Should we display the block time?'),
1121                 'blocktime' => new external_value(PARAM_NOTAGS, 'The time to display above the message'),
1122                 'position' => new external_value(PARAM_ALPHA, 'The position of the text'),
1123                 'timesent' => new external_value(PARAM_NOTAGS, 'The time the message was sent'),
1124                 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1125                 'isread' => new external_value(PARAM_INT, 'Determines if the message was read or not'),
1126             )
1127         );
1128     }
1130     /**
1131      * Get messagearea search users in course parameters.
1132      *
1133      * @deprecated since 3.6
1134      *
1135      * @return external_function_parameters
1136      * @since 3.2
1137      */
1138     public static function data_for_messagearea_search_users_in_course_parameters() {
1139         return new external_function_parameters(
1140             array(
1141                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1142                 'courseid' => new external_value(PARAM_INT, 'The id of the course'),
1143                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1144                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1145                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1146             )
1147         );
1148     }
1150     /**
1151      * Get messagearea search users in course results.
1152      *
1153      * @deprecated since 3.6
1154      *
1155      * NOTE: We are deprecating this function but not search_users_in_course API function for backwards compatibility
1156      * with messaging UI. But should be removed once new group messaging UI is in place and old messaging UI is removed.
1157      * Followup: MDL-63915
1158      *
1159      * @param int $userid The id of the user who is performing the search
1160      * @param int $courseid The id of the course
1161      * @param string $search The string being searched
1162      * @param int $limitfrom
1163      * @param int $limitnum
1164      * @return stdClass
1165      * @throws moodle_exception
1166      * @since 3.2
1167      */
1168     public static function data_for_messagearea_search_users_in_course($userid, $courseid, $search, $limitfrom = 0,
1169                                                                        $limitnum = 0) {
1170         global $CFG, $PAGE, $USER;
1172         // Check if messaging is enabled.
1173         if (empty($CFG->messaging)) {
1174             throw new moodle_exception('disabled', 'message');
1175         }
1177         $systemcontext = context_system::instance();
1179         $params = array(
1180             'userid' => $userid,
1181             'courseid' => $courseid,
1182             'search' => $search,
1183             'limitfrom' => $limitfrom,
1184             'limitnum' => $limitnum
1185         );
1186         $params = self::validate_parameters(self::data_for_messagearea_search_users_in_course_parameters(), $params);
1187         self::validate_context($systemcontext);
1189         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1190             throw new moodle_exception('You do not have permission to perform this action.');
1191         }
1193         $users = \core_message\api::search_users_in_course(
1194             $params['userid'],
1195             $params['courseid'],
1196             $params['search'],
1197             $params['limitfrom'],
1198             $params['limitnum']
1199         );
1200         $results = new \core_message\output\messagearea\user_search_results($users);
1202         $renderer = $PAGE->get_renderer('core_message');
1203         return $results->export_for_template($renderer);
1204     }
1206     /**
1207      * Get messagearea search users in course returns.
1208      *
1209      * @deprecated since 3.6
1210      *
1211      * @return external_single_structure
1212      * @since 3.2
1213      */
1214     public static function data_for_messagearea_search_users_in_course_returns() {
1215         return new external_single_structure(
1216             array(
1217                 'contacts' => new external_multiple_structure(
1218                     self::get_messagearea_contact_structure()
1219                 ),
1220             )
1221         );
1222     }
1224     /**
1225      * Marking the method as deprecated.
1226      *
1227      * @return bool
1228      */
1229     public static function data_for_messagearea_search_users_in_course_is_deprecated() {
1230         return true;
1231     }
1233     /**
1234      * Get messagearea search users parameters.
1235      *
1236      * @deprecated since 3.6
1237      *
1238      * @return external_function_parameters
1239      * @since 3.2
1240      */
1241     public static function data_for_messagearea_search_users_parameters() {
1242         return new external_function_parameters(
1243             array(
1244                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1245                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1246                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1247             )
1248         );
1249     }
1251     /**
1252      * Get messagearea search users results.
1253      *
1254      * @deprecated since 3.6
1255      *
1256      * NOTE: We are deprecating this function but not search_users API function for backwards compatibility
1257      * with messaging UI. But should be removed once new group messaging UI is in place and old messaging UI is removed.
1258      * Followup: MDL-63915
1259      *
1260      * @param int $userid The id of the user who is performing the search
1261      * @param string $search The string being searched
1262      * @param int $limitnum
1263      * @return stdClass
1264      * @throws moodle_exception
1265      * @since 3.2
1266      */
1267     public static function data_for_messagearea_search_users($userid, $search, $limitnum = 0) {
1268         global $CFG, $PAGE, $USER;
1270         // Check if messaging is enabled.
1271         if (empty($CFG->messaging)) {
1272             throw new moodle_exception('disabled', 'message');
1273         }
1275         $systemcontext = context_system::instance();
1277         $params = array(
1278             'userid' => $userid,
1279             'search' => $search,
1280             'limitnum' => $limitnum
1281         );
1282         $params = self::validate_parameters(self::data_for_messagearea_search_users_parameters(), $params);
1283         self::validate_context($systemcontext);
1285         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1286             throw new moodle_exception('You do not have permission to perform this action.');
1287         }
1289         list($contacts, $courses, $noncontacts) = \core_message\api::search_users(
1290             $params['userid'],
1291             $params['search'],
1292             $params['limitnum']
1293         );
1295         $search = new \core_message\output\messagearea\user_search_results($contacts, $courses, $noncontacts);
1297         $renderer = $PAGE->get_renderer('core_message');
1298         return $search->export_for_template($renderer);
1299     }
1301     /**
1302      * Get messagearea search users returns.
1303      *
1304      * @deprecated since 3.6
1305      *
1306      * @return external_single_structure
1307      * @since 3.2
1308      */
1309     public static function data_for_messagearea_search_users_returns() {
1310         return new external_single_structure(
1311             array(
1312                 'contacts' => new external_multiple_structure(
1313                     self::get_messagearea_contact_structure()
1314                 ),
1315                 'courses' => new external_multiple_structure(
1316                     new external_single_structure(
1317                         array(
1318                             'id' => new external_value(PARAM_INT, 'The course id'),
1319                             'shortname' => new external_value(PARAM_TEXT, 'The course shortname'),
1320                             'fullname' => new external_value(PARAM_TEXT, 'The course fullname'),
1321                         )
1322                     )
1323                 ),
1324                 'noncontacts' => new external_multiple_structure(
1325                     self::get_messagearea_contact_structure()
1326                 )
1327             )
1328         );
1329     }
1331     /**
1332      * Marking the method as deprecated.
1333      *
1334      * @return bool
1335      */
1336     public static function data_for_messagearea_search_users_is_deprecated() {
1337         return true;
1338     }
1340     /**
1341      * Get messagearea message search users parameters.
1342      *
1343      * @return external_function_parameters
1344      * @since 3.6
1345      */
1346     public static function message_search_users_parameters() {
1347         return new external_function_parameters(
1348             array(
1349                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1350                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1351                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1352                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1353             )
1354         );
1355     }
1357     /**
1358      * Get search users results.
1359      *
1360      * @param int $userid The id of the user who is performing the search
1361      * @param string $search The string being searched
1362      * @param int $limitfrom
1363      * @param int $limitnum
1364      * @return array
1365      * @throws moodle_exception
1366      * @since 3.6
1367      */
1368     public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) {
1369         global $CFG, $USER;
1371         // Check if messaging is enabled.
1372         if (empty($CFG->messaging)) {
1373             throw new moodle_exception('disabled', 'message');
1374         }
1376         $systemcontext = context_system::instance();
1378         $params = array(
1379             'userid' => $userid,
1380             'search' => $search,
1381             'limitfrom' => $limitfrom,
1382             'limitnum' => $limitnum
1383         );
1384         $params = self::validate_parameters(self::message_search_users_parameters(), $params);
1385         self::validate_context($systemcontext);
1387         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1388             throw new moodle_exception('You do not have permission to perform this action.');
1389         }
1391         list($contacts, $noncontacts) = \core_message\api::message_search_users(
1392             $params['userid'],
1393             $params['search'],
1394             $params['limitfrom'],
1395             $params['limitnum']);
1397         return array('contacts' => $contacts, 'noncontacts' => $noncontacts);
1398     }
1400     /**
1401      * Get messagearea message search users returns.
1402      *
1403      * @return external_single_structure
1404      * @since 3.2
1405      */
1406     public static function message_search_users_returns() {
1407         return new external_single_structure(
1408             array(
1409                 'contacts' => new external_multiple_structure(
1410                     self::get_conversation_member_structure(false, true)
1411                 ),
1412                 'noncontacts' => new external_multiple_structure(
1413                     self::get_conversation_member_structure(false, true)
1414                 )
1415             )
1416         );
1417     }
1419     /**
1420      * Get messagearea search messages parameters.
1421      *
1422      * @return external_function_parameters
1423      * @since 3.2
1424      */
1425     public static function data_for_messagearea_search_messages_parameters() {
1426         return new external_function_parameters(
1427             array(
1428                 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1429                 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1430                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1431                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1432             )
1433         );
1434     }
1436     /**
1437      * Get messagearea search messages results.
1438      *
1439      * @param int $userid The id of the user who is performing the search
1440      * @param string $search The string being searched
1441      * @param int $limitfrom
1442      * @param int $limitnum
1443      * @return stdClass
1444      * @throws moodle_exception
1445      * @since 3.2
1446      */
1447     public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
1448         global $CFG, $PAGE, $USER;
1450         // Check if messaging is enabled.
1451         if (empty($CFG->messaging)) {
1452             throw new moodle_exception('disabled', 'message');
1453         }
1455         $systemcontext = context_system::instance();
1457         $params = array(
1458             'userid' => $userid,
1459             'search' => $search,
1460             'limitfrom' => $limitfrom,
1461             'limitnum' => $limitnum
1463         );
1464         $params = self::validate_parameters(self::data_for_messagearea_search_messages_parameters(), $params);
1465         self::validate_context($systemcontext);
1467         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1468             throw new moodle_exception('You do not have permission to perform this action.');
1469         }
1471         $messages = \core_message\api::search_messages(
1472             $params['userid'],
1473             $params['search'],
1474             $params['limitfrom'],
1475             $params['limitnum']
1476         );
1477         $results = new \core_message\output\messagearea\message_search_results($messages);
1479         $renderer = $PAGE->get_renderer('core_message');
1480         return $results->export_for_template($renderer);
1481     }
1483     /**
1484      * Get messagearea search messages returns.
1485      *
1486      * @return external_single_structure
1487      * @since 3.2
1488      */
1489     public static function data_for_messagearea_search_messages_returns() {
1490         return new external_single_structure(
1491             array(
1492                 'contacts' => new external_multiple_structure(
1493                     self::get_messagearea_contact_structure()
1494                 )
1495             )
1496         );
1497     }
1499     /**
1500      * Get conversations parameters.
1501      *
1502      * @return external_function_parameters
1503      * @since 3.6
1504      */
1505     public static function get_conversations_parameters() {
1506         return new external_function_parameters(
1507             array(
1508                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1509                 'limitfrom' => new external_value(PARAM_INT, 'The offset to start at', VALUE_DEFAULT, 0),
1510                 'limitnum' => new external_value(PARAM_INT, 'Limit number of conversations to this', VALUE_DEFAULT, 0),
1511                 'type' => new external_value(PARAM_INT, 'Filter by type', VALUE_DEFAULT, null),
1512                 'favourites' => new external_value(PARAM_BOOL, 'Whether to restrict the results to contain NO favourite
1513                 conversations (false), ONLY favourite conversation (true), or ignore any restriction altogether (null)',
1514                     VALUE_DEFAULT, null),
1516             )
1517         );
1518     }
1520     /**
1521      * Get the list of conversations for the user.
1522      *
1523      * @param int $userid The id of the user who is performing the search
1524      * @param int $limitfrom
1525      * @param int $limitnum
1526      * @param int|null $type
1527      * @param bool|null $favourites
1528      * @return stdClass
1529      * @throws \moodle_exception if the messaging feature is disabled on the site.
1530      * @since 3.2
1531      */
1532     public static function get_conversations($userid, $limitfrom = 0, $limitnum = 0, int $type = null, bool $favourites = null) {
1533         global $CFG, $USER;
1535         // All the standard BL checks.
1536         if (empty($CFG->messaging)) {
1537             throw new moodle_exception('disabled', 'message');
1538         }
1540         $params = array(
1541             'userid' => $userid,
1542             'limitfrom' => $limitfrom,
1543             'limitnum' => $limitnum,
1544             'type' => $type,
1545             'favourites' => $favourites
1546         );
1547         $params = self::validate_parameters(self::get_conversations_parameters(), $params);
1549         $systemcontext = context_system::instance();
1550         self::validate_context($systemcontext);
1552         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1553             throw new moodle_exception('You do not have permission to perform this action.');
1554         }
1556         $conversations = \core_message\api::get_conversations(
1557             $params['userid'],
1558             $params['limitfrom'],
1559             $params['limitnum'],
1560             $params['type'],
1561             $params['favourites']
1562         );
1564         return (object) ['conversations' => $conversations];
1565     }
1567     /**
1568      * Get conversations returns.
1569      *
1570      * @return external_single_structure
1571      * @since 3.6
1572      */
1573     public static function get_conversations_returns() {
1574         return new external_single_structure(
1575             [
1576                 'conversations' => new external_multiple_structure(
1577                     self::get_conversation_structure()
1578                 )
1579             ]
1580         );
1581     }
1583     /**
1584      * The messagearea conversations parameters.
1585      *
1586      * @deprecated since 3.6
1587      * @return external_function_parameters
1588      * @since 3.2
1589      */
1590     public static function data_for_messagearea_conversations_parameters() {
1591         return new external_function_parameters(
1592             array(
1593                 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1594                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1595                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1596             )
1597         );
1598     }
1600     /**
1601      * Get messagearea conversations.
1602      *
1603      * NOTE FOR FINAL DEPRECATION:
1604      * When removing this method, please also consider removal of get_conversations_legacy_formatter()
1605      * from the \core_message\helper class. This helper method was used solely to format the new get_conversations() return data
1606      * into the old format used here, and in message/index.php. If we no longer need either of these, then that method can be
1607      * removed.
1608      *
1609      * @deprecated since 3.6
1610      * @param int $userid The id of the user who we are viewing conversations for
1611      * @param int $limitfrom
1612      * @param int $limitnum
1613      * @return stdClass
1614      * @throws moodle_exception
1615      * @since 3.2
1616      */
1617     public static function data_for_messagearea_conversations($userid, $limitfrom = 0, $limitnum = 0) {
1618         global $CFG, $PAGE, $USER;
1620         // Check if messaging is enabled.
1621         if (empty($CFG->messaging)) {
1622             throw new moodle_exception('disabled', 'message');
1623         }
1625         $systemcontext = context_system::instance();
1627         $params = array(
1628             'userid' => $userid,
1629             'limitfrom' => $limitfrom,
1630             'limitnum' => $limitnum
1631         );
1632         $params = self::validate_parameters(self::data_for_messagearea_conversations_parameters(), $params);
1633         self::validate_context($systemcontext);
1635         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1636             throw new moodle_exception('You do not have permission to perform this action.');
1637         }
1639         $conversations = \core_message\api::get_conversations($params['userid'], $params['limitfrom'], $params['limitnum']);
1641         // Format the conversations in the legacy style, as the get_conversations method has since been changed.
1642         $conversations = \core_message\helper::get_conversations_legacy_formatter($conversations);
1644         $conversations = new \core_message\output\messagearea\contacts(null, $conversations);
1646         $renderer = $PAGE->get_renderer('core_message');
1647         return $conversations->export_for_template($renderer);
1648     }
1650     /**
1651      * The messagearea conversations return structure.
1652      *
1653      * @deprecated since 3.6
1654      * @return external_single_structure
1655      * @since 3.2
1656      */
1657     public static function data_for_messagearea_conversations_returns() {
1658         return new external_single_structure(
1659             array(
1660                 'contacts' => new external_multiple_structure(
1661                     self::get_messagearea_contact_structure()
1662                 )
1663             )
1664         );
1665     }
1667     /**
1668      * Marking the method as deprecated.
1669      *
1670      * @return bool
1671      */
1672     public static function data_for_messagearea_conversations_is_deprecated() {
1673         return true;
1674     }
1676     /**
1677      * The messagearea contacts return parameters.
1678      *
1679      * @deprecated since 3.6
1680      * @return external_function_parameters
1681      * @since 3.2
1682      */
1683     public static function data_for_messagearea_contacts_parameters() {
1684         return self::data_for_messagearea_conversations_parameters();
1685     }
1687     /**
1688      * Get messagearea contacts parameters.
1689      *
1690      * @deprecated since 3.6
1691      * @param int $userid The id of the user who we are viewing conversations for
1692      * @param int $limitfrom
1693      * @param int $limitnum
1694      * @return stdClass
1695      * @throws moodle_exception
1696      * @since 3.2
1697      */
1698     public static function data_for_messagearea_contacts($userid, $limitfrom = 0, $limitnum = 0) {
1699         global $CFG, $PAGE, $USER;
1701         // Check if messaging is enabled.
1702         if (empty($CFG->messaging)) {
1703             throw new moodle_exception('disabled', 'message');
1704         }
1706         $systemcontext = context_system::instance();
1708         $params = array(
1709             'userid' => $userid,
1710             'limitfrom' => $limitfrom,
1711             'limitnum' => $limitnum
1712         );
1713         $params = self::validate_parameters(self::data_for_messagearea_contacts_parameters(), $params);
1714         self::validate_context($systemcontext);
1716         if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1717             throw new moodle_exception('You do not have permission to perform this action.');
1718         }
1720         $contacts = \core_message\api::get_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
1721         $contacts = new \core_message\output\messagearea\contacts(null, $contacts);
1723         $renderer = $PAGE->get_renderer('core_message');
1724         return $contacts->export_for_template($renderer);
1725     }
1727     /**
1728      * The messagearea contacts return structure.
1729      *
1730      * @deprecated since 3.6
1731      * @return external_single_structure
1732      * @since 3.2
1733      */
1734     public static function data_for_messagearea_contacts_returns() {
1735         return self::data_for_messagearea_conversations_returns();
1736     }
1738     /**
1739      * Marking the method as deprecated.
1740      *
1741      * @return bool
1742      */
1743     public static function data_for_messagearea_contacts_is_deprecated() {
1744         return true;
1745     }
1747     /**
1748      * The messagearea messages parameters.
1749      *
1750      * @deprecated since 3.6
1751      * @return external_function_parameters
1752      * @since 3.2
1753      */
1754     public static function data_for_messagearea_messages_parameters() {
1755         return new external_function_parameters(
1756             array(
1757                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1758                 'otheruserid' => new external_value(PARAM_INT, 'The other user\'s id'),
1759                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1760                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1761                 'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
1762                 'timefrom' => new external_value(PARAM_INT,
1763                     'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
1764             )
1765         );
1766     }
1768     /**
1769      * Get messagearea messages.
1770      *
1771      * @deprecated since 3.6
1772      * @param int $currentuserid The current user's id
1773      * @param int $otheruserid The other user's id
1774      * @param int $limitfrom
1775      * @param int $limitnum
1776      * @param boolean $newest
1777      * @return stdClass
1778      * @throws moodle_exception
1779      * @since 3.2
1780      */
1781     public static function data_for_messagearea_messages($currentuserid, $otheruserid, $limitfrom = 0, $limitnum = 0,
1782                                                          $newest = false, $timefrom = 0) {
1783         global $CFG, $PAGE, $USER;
1785         // Check if messaging is enabled.
1786         if (empty($CFG->messaging)) {
1787             throw new moodle_exception('disabled', 'message');
1788         }
1790         $systemcontext = context_system::instance();
1792         $params = array(
1793             'currentuserid' => $currentuserid,
1794             'otheruserid' => $otheruserid,
1795             'limitfrom' => $limitfrom,
1796             'limitnum' => $limitnum,
1797             'newest' => $newest,
1798             'timefrom' => $timefrom,
1799         );
1800         $params = self::validate_parameters(self::data_for_messagearea_messages_parameters(), $params);
1801         self::validate_context($systemcontext);
1803         if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1804             throw new moodle_exception('You do not have permission to perform this action.');
1805         }
1807         if ($params['newest']) {
1808             $sort = 'timecreated DESC';
1809         } else {
1810             $sort = 'timecreated ASC';
1811         }
1813         // We need to enforce a one second delay on messages to avoid race conditions of current
1814         // messages still being sent.
1815         //
1816         // There is a chance that we could request messages before the current time's
1817         // second has elapsed and while other messages are being sent in that same second. In which
1818         // case those messages will be lost.
1819         //
1820         // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
1821         if (!empty($params['timefrom'])) {
1822             $timeto = time() - 1;
1823         } else {
1824             $timeto = 0;
1825         }
1827         // No requesting messages from the current time, as stated above.
1828         if ($params['timefrom'] == time()) {
1829             $messages = [];
1830         } else {
1831             $messages = \core_message\api::get_messages($params['currentuserid'], $params['otheruserid'], $params['limitfrom'],
1832                                                         $params['limitnum'], $sort, $params['timefrom'], $timeto);
1833         }
1835         $messages = new \core_message\output\messagearea\messages($params['currentuserid'], $params['otheruserid'], $messages);
1837         $renderer = $PAGE->get_renderer('core_message');
1838         return $messages->export_for_template($renderer);
1839     }
1841     /**
1842      * The messagearea messages return structure.
1843      *
1844      * @deprecated since 3.6
1845      * @return external_single_structure
1846      * @since 3.2
1847      */
1848     public static function data_for_messagearea_messages_returns() {
1849         return new external_single_structure(
1850             array(
1851                 'iscurrentuser' => new external_value(PARAM_BOOL, 'Is the currently logged in user the user we are viewing
1852                     the messages on behalf of?'),
1853                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1854                 'otheruserid' => new external_value(PARAM_INT, 'The other user\'s id'),
1855                 'otheruserfullname' => new external_value(PARAM_NOTAGS, 'The other user\'s fullname'),
1856                 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1857                 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1858                 'messages' => new external_multiple_structure(
1859                     self::get_messagearea_message_structure()
1860                 ),
1861                 'isblocked' => new external_value(PARAM_BOOL, 'Is this user blocked by the current user?', VALUE_DEFAULT, false),
1862             )
1863         );
1864     }
1866     /**
1867      * Marking the method as deprecated.
1868      *
1869      * @return bool
1870      */
1871     public static function data_for_messagearea_messages_is_deprecated() {
1872         return true;
1873     }
1875     /**
1876      * The conversation messages parameters.
1877      *
1878      * @return external_function_parameters
1879      * @since 3.6
1880      */
1881     public static function get_conversation_messages_parameters() {
1882         return new external_function_parameters(
1883             array(
1884                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1885                 'convid' => new external_value(PARAM_INT, 'The conversation id'),
1886                 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1887                 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1888                 'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
1889                 'timefrom' => new external_value(PARAM_INT,
1890                     'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
1891             )
1892         );
1893     }
1895     /**
1896      * Get conversation messages.
1897      *
1898      * @param  int $currentuserid The current user's id.
1899      * @param  int $convid The conversation id.
1900      * @param  int $limitfrom Return a subset of records, starting at this point (optional).
1901      * @param  int $limitnum Return a subset comprising this many records in total (optional, required if $limitfrom is set).
1902      * @param  bool $newest True for getting first newest messages, false otherwise.
1903      * @param  int  $timefrom The time from the conversation messages to get.
1904      * @return stdClass The messages and members who have sent some of these messages.
1905      * @throws moodle_exception
1906      * @since 3.6
1907      */
1908     public static function get_conversation_messages(int $currentuserid, int $convid, int $limitfrom = 0, int $limitnum = 0,
1909                                                          bool $newest = false, int $timefrom = 0) {
1910         global $CFG, $PAGE, $USER;
1912         // Check if messaging is enabled.
1913         if (empty($CFG->messaging)) {
1914             throw new moodle_exception('disabled', 'message');
1915         }
1917         $systemcontext = context_system::instance();
1919         $params = array(
1920             'currentuserid' => $currentuserid,
1921             'convid' => $convid,
1922             'limitfrom' => $limitfrom,
1923             'limitnum' => $limitnum,
1924             'newest' => $newest,
1925             'timefrom' => $timefrom,
1926         );
1927         $params = self::validate_parameters(self::get_conversation_messages_parameters(), $params);
1928         self::validate_context($systemcontext);
1930         if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1931             throw new moodle_exception('You do not have permission to perform this action.');
1932         }
1934         $sort = $newest ? 'timecreated DESC' : 'timecreated ASC';
1936         // We need to enforce a one second delay on messages to avoid race conditions of current
1937         // messages still being sent.
1938         //
1939         // There is a chance that we could request messages before the current time's
1940         // second has elapsed and while other messages are being sent in that same second. In which
1941         // case those messages will be lost.
1942         //
1943         // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
1944         $timeto = empty($params['timefrom']) ? 0 : time() - 1;
1946         // No requesting messages from the current time, as stated above.
1947         if ($params['timefrom'] == time()) {
1948             $messages = [];
1949         } else {
1950             $messages = \core_message\api::get_conversation_messages(
1951                 $params['currentuserid'],
1952                 $params['convid'],
1953                 $params['limitfrom'],
1954                 $params['limitnum'],
1955                 $sort,
1956                 $params['timefrom'],
1957                 $timeto);
1958         }
1960         return $messages;
1961     }
1963     /**
1964      * The messagearea messages return structure.
1965      *
1966      * @return external_single_structure
1967      * @since 3.6
1968      */
1969     public static function get_conversation_messages_returns() {
1970         return new external_single_structure(
1971             array(
1972                 'id' => new external_value(PARAM_INT, 'The conversation id'),
1973                 'members' => new external_multiple_structure(
1974                     self::get_conversation_member_structure()
1975                 ),
1976                 'messages' => new external_multiple_structure(
1977                     self::get_conversation_message_structure()
1978                 ),
1979             )
1980         );
1981     }
1983     /**
1984      * The get most recent message return parameters.
1985      *
1986      * @deprecated since 3.6
1987      * @return external_function_parameters
1988      * @since 3.2
1989      */
1990     public static function data_for_messagearea_get_most_recent_message_parameters() {
1991         return new external_function_parameters(
1992             array(
1993                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1994                 'otheruserid' => new external_value(PARAM_INT, 'The other user\'s id'),
1995             )
1996         );
1997     }
1999     /**
2000      * Get the most recent message in a conversation.
2001      *
2002      * @deprecated since 3.6
2003      * @param int $currentuserid The current user's id
2004      * @param int $otheruserid The other user's id
2005      * @return stdClass
2006      * @throws moodle_exception
2007      * @since 3.2
2008      */
2009     public static function data_for_messagearea_get_most_recent_message($currentuserid, $otheruserid) {
2010         global $CFG, $PAGE, $USER;
2012         // Check if messaging is enabled.
2013         if (empty($CFG->messaging)) {
2014             throw new moodle_exception('disabled', 'message');
2015         }
2017         $systemcontext = context_system::instance();
2019         $params = array(
2020             'currentuserid' => $currentuserid,
2021             'otheruserid' => $otheruserid
2022         );
2023         $params = self::validate_parameters(self::data_for_messagearea_get_most_recent_message_parameters(), $params);
2024         self::validate_context($systemcontext);
2026         if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2027             throw new moodle_exception('You do not have permission to perform this action.');
2028         }
2030         $message = \core_message\api::get_most_recent_message($params['currentuserid'], $params['otheruserid']);
2031         $message = new \core_message\output\messagearea\message($message);
2033         $renderer = $PAGE->get_renderer('core_message');
2034         return $message->export_for_template($renderer);
2035     }
2037     /**
2038      * The get most recent message return structure.
2039      *
2040      * @deprecated since 3.6
2041      * @return external_single_structure
2042      * @since 3.2
2043      */
2044     public static function data_for_messagearea_get_most_recent_message_returns() {
2045         return self::get_messagearea_message_structure();
2046     }
2048     /**
2049      * Marking the method as deprecated.
2050      *
2051      * @return bool
2052      */
2053     public static function data_for_messagearea_get_most_recent_message_is_deprecated() {
2054         return true;
2055     }
2057     /**
2058      * The get profile parameters.
2059      *
2060      * @deprecated since 3.6
2061      * @return external_function_parameters
2062      * @since 3.2
2063      */
2064     public static function data_for_messagearea_get_profile_parameters() {
2065         return new external_function_parameters(
2066             array(
2067                 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
2068                 'otheruserid' => new external_value(PARAM_INT, 'The id of the user whose profile we want to view'),
2069             )
2070         );
2071     }
2073     /**
2074      * Get the profile information for a contact.
2075      *
2076      * @deprecated since 3.6
2077      * @param int $currentuserid The current user's id
2078      * @param int $otheruserid The id of the user whose profile we are viewing
2079      * @return stdClass
2080      * @throws moodle_exception
2081      * @since 3.2
2082      */
2083     public static function data_for_messagearea_get_profile($currentuserid, $otheruserid) {
2084         global $CFG, $PAGE, $USER;
2086         // Check if messaging is enabled.
2087         if (empty($CFG->messaging)) {
2088             throw new moodle_exception('disabled', 'message');
2089         }
2091         $systemcontext = context_system::instance();
2093         $params = array(
2094             'currentuserid' => $currentuserid,
2095             'otheruserid' => $otheruserid
2096         );
2097         $params = self::validate_parameters(self::data_for_messagearea_get_profile_parameters(), $params);
2098         self::validate_context($systemcontext);
2100         if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
2101             throw new moodle_exception('You do not have permission to perform this action.');
2102         }
2104         $profile = \core_message\api::get_profile($params['currentuserid'], $params['otheruserid']);
2105         $profile = new \core_message\output\messagearea\profile($profile);
2107         $renderer = $PAGE->get_renderer('core_message');
2108         return $profile->export_for_template($renderer);
2109     }
2111     /**
2112      * The get profile return structure.
2113      *
2114      * @deprecated since 3.6
2115      * @return external_single_structure
2116      * @since 3.2
2117      */
2118     public static function data_for_messagearea_get_profile_returns() {
2119         return new external_single_structure(
2120             array(
2121                 'userid' => new external_value(PARAM_INT, 'The id of the user whose profile we are viewing'),
2122                 'email' => new external_value(core_user::get_property_type('email'), 'An email address'),
2123                 'country' => new external_value(PARAM_TEXT, 'Home country of the user'),
2124                 'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user'),
2125                 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
2126                 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
2127                 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
2128                 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
2129                 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
2130                 'isblocked' => new external_value(PARAM_BOOL, 'Is the user blocked?'),
2131                 'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?')
2132             )
2133         );
2134     }
2136     /**
2137      * Marking the method as deprecated.
2138      *
2139      * @return bool
2140      */
2141     public static function data_for_messagearea_get_profile_is_deprecated() {
2142         return true;
2143     }
2145     /**
2146      * Get contacts parameters description.
2147      *
2148      * @return external_function_parameters
2149      * @since Moodle 2.5
2150      */
2151     public static function get_contacts_parameters() {
2152         return new external_function_parameters(array());
2153     }
2155     /**
2156      * Get contacts.
2157      *
2158      * @return external_description
2159      * @since Moodle 2.5
2160      */
2161     public static function get_contacts() {
2162         global $CFG, $PAGE, $USER;
2164         // Check if messaging is enabled.
2165         if (empty($CFG->messaging)) {
2166             throw new moodle_exception('disabled', 'message');
2167         }
2169         require_once($CFG->dirroot . '/user/lib.php');
2171         $allcontacts = array('online' => [], 'offline' => [], 'strangers' => []);
2172         $contacts = \core_message\api::get_contacts_with_unread_message_count($USER->id);
2173         foreach ($contacts as $contact) {
2174             // Set the mode.
2175             $mode = 'offline';
2176             if (\core_message\helper::is_online($contact->lastaccess)) {
2177                 $mode = 'online';
2178             }
2180             $newcontact = array(
2181                 'id' => $contact->id,
2182                 'fullname' => fullname($contact),
2183                 'unread' => $contact->messagecount
2184             );
2186             $userpicture = new user_picture($contact);
2187             $userpicture->size = 1; // Size f1.
2188             $newcontact['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2189             $userpicture->size = 0; // Size f2.
2190             $newcontact['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
2192             $allcontacts[$mode][$contact->id] = $newcontact;
2193         }
2195         $strangers = \core_message\api::get_non_contacts_with_unread_message_count($USER->id);
2196         foreach ($strangers as $contact) {
2197             $newcontact = array(
2198                 'id' => $contact->id,
2199                 'fullname' => fullname($contact),
2200                 'unread' => $contact->messagecount
2201             );
2203             $userpicture = new user_picture($contact);
2204             $userpicture->size = 1; // Size f1.
2205             $newcontact['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2206             $userpicture->size = 0; // Size f2.
2207             $newcontact['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
2209             $allcontacts['strangers'][$contact->id] = $newcontact;
2210         }
2212         // Add noreply user and support user to the list, if they don't exist.
2213         $supportuser = core_user::get_support_user();
2214         if (!isset($strangers[$supportuser->id]) && !$supportuser->deleted) {
2215             $supportuser->messagecount = message_count_unread_messages($USER, $supportuser);
2216             if ($supportuser->messagecount > 0) {
2217                 $supportuser->fullname = fullname($supportuser);
2218                 $supportuser->unread = $supportuser->messagecount;
2219                 $allcontacts['strangers'][$supportuser->id] = $supportuser;
2220             }
2221         }
2223         $noreplyuser = core_user::get_noreply_user();
2224         if (!isset($strangers[$noreplyuser->id]) && !$noreplyuser->deleted) {
2225             $noreplyuser->messagecount = message_count_unread_messages($USER, $noreplyuser);
2226             if ($noreplyuser->messagecount > 0) {
2227                 $noreplyuser->fullname = fullname($noreplyuser);
2228                 $noreplyuser->unread = $noreplyuser->messagecount;
2229                 $allcontacts['strangers'][$noreplyuser->id] = $noreplyuser;
2230             }
2231         }
2233         return $allcontacts;
2234     }
2236     /**
2237      * Get contacts return description.
2238      *
2239      * @return external_description
2240      * @since Moodle 2.5
2241      */
2242     public static function get_contacts_returns() {
2243         return new external_single_structure(
2244             array(
2245                 'online' => new external_multiple_structure(
2246                     new external_single_structure(
2247                         array(
2248                             'id' => new external_value(PARAM_INT, 'User ID'),
2249                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2250                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2251                             'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL),
2252                             'unread' => new external_value(PARAM_INT, 'Unread message count')
2253                         )
2254                     ),
2255                     'List of online contacts'
2256                 ),
2257                 'offline' => new external_multiple_structure(
2258                     new external_single_structure(
2259                         array(
2260                             'id' => new external_value(PARAM_INT, 'User ID'),
2261                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2262                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2263                             'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL),
2264                             'unread' => new external_value(PARAM_INT, 'Unread message count')
2265                         )
2266                     ),
2267                     'List of offline contacts'
2268                 ),
2269                 'strangers' => new external_multiple_structure(
2270                     new external_single_structure(
2271                         array(
2272                             'id' => new external_value(PARAM_INT, 'User ID'),
2273                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2274                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2275                             'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL),
2276                             'unread' => new external_value(PARAM_INT, 'Unread message count')
2277                         )
2278                     ),
2279                     'List of users that are not in the user\'s contact list but have sent a message'
2280                 )
2281             )
2282         );
2283     }
2285     /**
2286      * Search contacts parameters description.
2287      *
2288      * @return external_function_parameters
2289      * @since Moodle 2.5
2290      */
2291     public static function search_contacts_parameters() {
2292         return new external_function_parameters(
2293             array(
2294                 'searchtext' => new external_value(PARAM_CLEAN, 'String the user\'s fullname has to match to be found'),
2295                 'onlymycourses' => new external_value(PARAM_BOOL, 'Limit search to the user\'s courses',
2296                     VALUE_DEFAULT, false)
2297             )
2298         );
2299     }
2301     /**
2302      * Search contacts.
2303      *
2304      * @param string $searchtext query string.
2305      * @param bool $onlymycourses limit the search to the user's courses only.
2306      * @return external_description
2307      * @since Moodle 2.5
2308      */
2309     public static function search_contacts($searchtext, $onlymycourses = false) {
2310         global $CFG, $USER, $PAGE;
2311         require_once($CFG->dirroot . '/user/lib.php');
2313         // Check if messaging is enabled.
2314         if (empty($CFG->messaging)) {
2315             throw new moodle_exception('disabled', 'message');
2316         }
2318         require_once($CFG->libdir . '/enrollib.php');
2320         $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses);
2321         $params = self::validate_parameters(self::search_contacts_parameters(), $params);
2323         // Extra validation, we do not allow empty queries.
2324         if ($params['searchtext'] === '') {
2325             throw new moodle_exception('querystringcannotbeempty');
2326         }
2328         $courseids = array();
2329         if ($params['onlymycourses']) {
2330             $mycourses = enrol_get_my_courses(array('id'));
2331             foreach ($mycourses as $mycourse) {
2332                 $courseids[] = $mycourse->id;
2333             }
2334         } else {
2335             $courseids[] = SITEID;
2336         }
2338         // Retrieving the users matching the query.
2339         $users = message_search_users($courseids, $params['searchtext']);
2340         $results = array();
2341         foreach ($users as $user) {
2342             $results[$user->id] = $user;
2343         }
2345         // Reorganising information.
2346         foreach ($results as &$user) {
2347             $newuser = array(
2348                 'id' => $user->id,
2349                 'fullname' => fullname($user)
2350             );
2352             // Avoid undefined property notice as phone not specified.
2353             $user->phone1 = null;
2354             $user->phone2 = null;
2356             $userpicture = new user_picture($user);
2357             $userpicture->size = 1; // Size f1.
2358             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2359             $userpicture->size = 0; // Size f2.
2360             $newuser['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
2362             $user = $newuser;
2363         }
2365         return $results;
2366     }
2368     /**
2369      * Search contacts return description.
2370      *
2371      * @return external_description
2372      * @since Moodle 2.5
2373      */
2374     public static function search_contacts_returns() {
2375         return new external_multiple_structure(
2376             new external_single_structure(
2377                 array(
2378                     'id' => new external_value(PARAM_INT, 'User ID'),
2379                     'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2380                     'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
2381                     'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL)
2382                 )
2383             ),
2384             'List of contacts'
2385         );
2386     }
2388     /**
2389      * Get messages parameters description.
2390      *
2391      * @return external_function_parameters
2392      * @since 2.8
2393      */
2394     public static function get_messages_parameters() {
2395         return new external_function_parameters(
2396             array(
2397                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2398                 'useridfrom' => new external_value(
2399                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2400                     VALUE_DEFAULT, 0),
2401                 'type' => new external_value(
2402                     PARAM_ALPHA, 'type of message to return, expected values are: notifications, conversations and both',
2403                     VALUE_DEFAULT, 'both'),
2404                 'read' => new external_value(PARAM_BOOL, 'true for getting read messages, false for unread', VALUE_DEFAULT, true),
2405                 'newestfirst' => new external_value(
2406                     PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
2407                     VALUE_DEFAULT, true),
2408                 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
2409                 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
2410             )
2411         );
2412     }
2414     /**
2415      * Get messages function implementation.
2416      *
2417      * @since  2.8
2418      * @throws invalid_parameter_exception
2419      * @throws moodle_exception
2420      * @param  int      $useridto       the user id who received the message
2421      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
2422      * @param  string   $type           type of message to return, expected values: notifications, conversations and both
2423      * @param  bool     $read           true for retreiving read messages, false for unread
2424      * @param  bool     $newestfirst    true for ordering by newest first, false for oldest first
2425      * @param  int      $limitfrom      limit from
2426      * @param  int      $limitnum       limit num
2427      * @return external_description
2428      */
2429     public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true,
2430                                         $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
2431         global $CFG, $USER;
2433         $warnings = array();
2435         $params = array(
2436             'useridto' => $useridto,
2437             'useridfrom' => $useridfrom,
2438             'type' => $type,
2439             'read' => $read,
2440             'newestfirst' => $newestfirst,
2441             'limitfrom' => $limitfrom,
2442             'limitnum' => $limitnum
2443         );
2445         $params = self::validate_parameters(self::get_messages_parameters(), $params);
2447         $context = context_system::instance();
2448         self::validate_context($context);
2450         $useridto = $params['useridto'];
2451         $useridfrom = $params['useridfrom'];
2452         $type = $params['type'];
2453         $read = $params['read'];
2454         $newestfirst = $params['newestfirst'];
2455         $limitfrom = $params['limitfrom'];
2456         $limitnum = $params['limitnum'];
2458         $allowedvalues = array('notifications', 'conversations', 'both');
2459         if (!in_array($type, $allowedvalues)) {
2460             throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
2461                 'allowed values are: ' . implode(',', $allowedvalues));
2462         }
2464         // Check if private messaging between users is allowed.
2465         if (empty($CFG->messaging)) {
2466             // If we are retreiving only conversations, and messaging is disabled, throw an exception.
2467             if ($type == "conversations") {
2468                 throw new moodle_exception('disabled', 'message');
2469             }
2470             if ($type == "both") {
2471                 $warning = array();
2472                 $warning['item'] = 'message';
2473                 $warning['itemid'] = $USER->id;
2474                 $warning['warningcode'] = '1';
2475                 $warning['message'] = 'Private messages (conversations) are not enabled in this site.
2476                     Only notifications will be returned';
2477                 $warnings[] = $warning;
2478             }
2479         }
2481         if (!empty($useridto)) {
2482             if (core_user::is_real_user($useridto)) {
2483                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2484             } else {
2485                 throw new moodle_exception('invaliduser');
2486             }
2487         }
2489         if (!empty($useridfrom)) {
2490             // We use get_user here because the from user can be the noreply or support user.
2491             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2492         }
2494         // Check if the current user is the sender/receiver or just a privileged user.
2495         if ($useridto != $USER->id and $useridfrom != $USER->id and
2496              !has_capability('moodle/site:readallmessages', $context)) {
2497             throw new moodle_exception('accessdenied', 'admin');
2498         }
2500         // Which type of messages to retrieve.
2501         $notifications = -1;
2502         if ($type != 'both') {
2503             $notifications = ($type == 'notifications') ? 1 : 0;
2504         }
2506         $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2507         $sort = "mr.timecreated $orderdirection";
2509         if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2510             $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2512             // In some cases, we don't need to get the to/from user objects from the sql query.
2513             $userfromfullname = '';
2514             $usertofullname = '';
2516             // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
2517             if (!empty($useridto)) {
2518                 $usertofullname = fullname($userto, $canviewfullname);
2519                 // The user from may or may not be filled.
2520                 if (!empty($useridfrom)) {
2521                     $userfromfullname = fullname($userfrom, $canviewfullname);
2522                 }
2523             } else {
2524                 // If the useridto field is empty, the useridfrom must be filled.
2525                 $userfromfullname = fullname($userfrom, $canviewfullname);
2526             }
2527             foreach ($messages as $mid => $message) {
2529                 // Do not return deleted messages.
2530                 if (!$message->notification) {
2531                     if (($useridto == $USER->id and $message->timeusertodeleted) or
2532                         ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
2533                         unset($messages[$mid]);
2534                         continue;
2535                     }
2536                 }
2538                 // We need to get the user from the query.
2539                 if (empty($userfromfullname)) {
2540                     // Check for non-reply and support users.
2541                     if (core_user::is_real_user($message->useridfrom)) {
2542                         $user = new stdClass();
2543                         $user = username_load_fields_from_object($user, $message, 'userfrom');
2544                         $message->userfromfullname = fullname($user, $canviewfullname);
2545                     } else {
2546                         $user = core_user::get_user($message->useridfrom);
2547                         $message->userfromfullname = fullname($user, $canviewfullname);
2548                     }
2549                 } else {
2550                     $message->userfromfullname = $userfromfullname;
2551                 }
2553                 // We need to get the user from the query.
2554                 if (empty($usertofullname)) {
2555                     $user = new stdClass();
2556                     $user = username_load_fields_from_object($user, $message, 'userto');
2557                     $message->usertofullname = fullname($user, $canviewfullname);
2558                 } else {
2559                     $message->usertofullname = $usertofullname;
2560                 }
2562                 $message->text = message_format_message_text($message);
2563                 $messages[$mid] = (array) $message;
2564             }
2565         }
2567         $results = array(
2568             'messages' => $messages,
2569             'warnings' => $warnings
2570         );
2572         return $results;
2573     }
2575     /**
2576      * Get messages return description.
2577      *
2578      * @return external_single_structure
2579      * @since 2.8
2580      */
2581     public static function get_messages_returns() {
2582         return new external_single_structure(
2583             array(
2584                 'messages' => new external_multiple_structure(
2585                     new external_single_structure(
2586                         array(
2587                             'id' => new external_value(PARAM_INT, 'Message id'),
2588                             'useridfrom' => new external_value(PARAM_INT, 'User from id'),
2589                             'useridto' => new external_value(PARAM_INT, 'User to id'),
2590                             'subject' => new external_value(PARAM_TEXT, 'The message subject'),
2591                             'text' => new external_value(PARAM_RAW, 'The message text formated'),
2592                             'fullmessage' => new external_value(PARAM_RAW, 'The message'),
2593                             'fullmessageformat' => new external_format_value('fullmessage'),
2594                             'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
2595                             'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
2596                             'notification' => new external_value(PARAM_INT, 'Is a notification?'),
2597                             'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
2598                             'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
2599                             'timecreated' => new external_value(PARAM_INT, 'Time created'),
2600                             'timeread' => new external_value(PARAM_INT, 'Time read'),
2601                             'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
2602                             'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name')
2603                         ), 'message'
2604                     )
2605                 ),
2606                 'warnings' => new external_warnings()
2607             )
2608         );
2609     }
2611     /**
2612      * Mark all notifications as read parameters description.
2613      *
2614      * @return external_function_parameters
2615      * @since 3.2
2616      */
2617     public static function mark_all_notifications_as_read_parameters() {
2618         return new external_function_parameters(
2619             array(
2620                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2621                 'useridfrom' => new external_value(
2622                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2623                     VALUE_DEFAULT, 0),
2624             )
2625         );
2626     }
2628     /**
2629      * Mark all notifications as read function.
2630      *
2631      * @since  3.2
2632      * @throws invalid_parameter_exception
2633      * @throws moodle_exception
2634      * @param  int      $useridto       the user id who received the message
2635      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
2636      * @return external_description
2637      */
2638     public static function mark_all_notifications_as_read($useridto, $useridfrom) {
2639         global $USER;
2641         $params = self::validate_parameters(
2642             self::mark_all_notifications_as_read_parameters(),
2643             array(
2644                 'useridto' => $useridto,
2645                 'useridfrom' => $useridfrom,
2646             )
2647         );
2649         $context = context_system::instance();
2650         self::validate_context($context);
2652         $useridto = $params['useridto'];
2653         $useridfrom = $params['useridfrom'];
2655         if (!empty($useridto)) {
2656             if (core_user::is_real_user($useridto)) {
2657                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2658             } else {
2659                 throw new moodle_exception('invaliduser');
2660             }
2661         }
2663         if (!empty($useridfrom)) {
2664             // We use get_user here because the from user can be the noreply or support user.
2665             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2666         }
2668         // Check if the current user is the sender/receiver or just a privileged user.
2669         if ($useridto != $USER->id and $useridfrom != $USER->id and
2670             // The deleteanymessage cap seems more reasonable here than readallmessages.
2671              !has_capability('moodle/site:deleteanymessage', $context)) {
2672             throw new moodle_exception('accessdenied', 'admin');
2673         }
2675         \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom);
2677         return true;
2678     }
2680     /**
2681      * Mark all notifications as read return description.
2682      *
2683      * @return external_single_structure
2684      * @since 3.2
2685      */
2686     public static function mark_all_notifications_as_read_returns() {
2687         return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
2688     }
2690     /**
2691      * Get unread conversations count parameters description.
2692      *
2693      * @return external_function_parameters
2694      * @since 3.2
2695      */
2696     public static function get_unread_conversations_count_parameters() {
2697         return new external_function_parameters(
2698             array(
2699                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2700             )
2701         );
2702     }
2704     /**
2705      * Get unread messages count function.
2706      *
2707      * @since  3.2
2708      * @throws invalid_parameter_exception
2709      * @throws moodle_exception
2710      * @param  int      $useridto       the user id who received the message
2711      * @return external_description
2712      */
2713     public static function get_unread_conversations_count($useridto) {
2714         global $USER, $CFG;
2716         // Check if messaging is enabled.
2717         if (empty($CFG->messaging)) {
2718             throw new moodle_exception('disabled', 'message');
2719         }
2721         $params = self::validate_parameters(
2722             self::get_unread_conversations_count_parameters(),
2723             array('useridto' => $useridto)
2724         );
2726         $context = context_system::instance();
2727         self::validate_context($context);
2729         $useridto = $params['useridto'];
2731         if (!empty($useridto)) {
2732             if (core_user::is_real_user($useridto)) {
2733                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2734             } else {
2735                 throw new moodle_exception('invaliduser');
2736             }
2737         } else {
2738             $useridto = $USER->id;
2739         }
2741         // Check if the current user is the receiver or just a privileged user.
2742         if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
2743             throw new moodle_exception('accessdenied', 'admin');
2744         }
2746         return \core_message\api::count_unread_conversations($userto);
2747     }
2749     /**
2750      * Get unread conversations count return description.
2751      *
2752      * @return external_single_structure
2753      * @since 3.2
2754      */
2755     public static function get_unread_conversations_count_returns() {
2756         return new external_value(PARAM_INT, 'The count of unread messages for the user');
2757     }
2759     /**
2760      * Get blocked users parameters description.
2761      *
2762      * @return external_function_parameters
2763      * @since 2.9
2764      */
2765     public static function get_blocked_users_parameters() {
2766         return new external_function_parameters(
2767             array(
2768                 'userid' => new external_value(PARAM_INT,
2769                                 'the user whose blocked users we want to retrieve',
2770                                 VALUE_REQUIRED),
2771             )
2772         );
2773     }
2775     /**
2776      * Retrieve a list of users blocked
2777      *
2778      * @param  int $userid the user whose blocked users we want to retrieve
2779      * @return external_description
2780      * @since 2.9
2781      */
2782     public static function get_blocked_users($userid) {
2783         global $CFG, $USER, $PAGE;
2785         // Warnings array, it can be empty at the end but is mandatory.
2786         $warnings = array();
2788         // Validate params.
2789         $params = array(
2790             'userid' => $userid
2791         );
2792         $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2793         $userid = $params['userid'];
2795         // Validate context.
2796         $context = context_system::instance();
2797         self::validate_context($context);
2799         // Check if private messaging between users is allowed.
2800         if (empty($CFG->messaging)) {
2801             throw new moodle_exception('disabled', 'message');
2802         }
2804         $user = core_user::get_user($userid, '*', MUST_EXIST);
2805         core_user::require_active_user($user);
2807         // Check if we have permissions for retrieve the information.
2808         $capability = 'moodle/site:manageallmessaging';
2809         if (($USER->id != $userid) && !has_capability($capability, $context)) {
2810             throw new required_capability_exception($context, $capability, 'nopermissions', '');
2811         }
2813         // Now, we can get safely all the blocked users.
2814         $users = \core_message\api::get_blocked_users($user->id);
2816         $blockedusers = array();
2817         foreach ($users as $user) {
2818             $newuser = array(
2819                 'id' => $user->id,
2820                 'fullname' => fullname($user),
2821             );
2823             $userpicture = new user_picture($user);
2824             $userpicture->size = 1; // Size f1.
2825             $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2827             $blockedusers[] = $newuser;
2828         }
2830         $results = array(
2831             'users' => $blockedusers,
2832             'warnings' => $warnings
2833         );
2834         return $results;
2835     }
2837     /**
2838      * Get blocked users return description.
2839      *
2840      * @return external_single_structure
2841      * @since 2.9
2842      */
2843     public static function get_blocked_users_returns() {
2844         return new external_single_structure(
2845             array(
2846                 'users' => new external_multiple_structure(
2847                     new external_single_structure(
2848                         array(
2849                             'id' => new external_value(PARAM_INT, 'User ID'),
2850                             'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2851                             'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
2852                         )
2853                     ),
2854                     'List of blocked users'
2855                 ),
2856                 'warnings' => new external_warnings()
2857             )
2858         );
2859     }
2861     /**
2862      * Returns description of method parameters
2863      *
2864      * @return external_function_parameters
2865      * @since 2.9
2866      */
2867     public static function mark_message_read_parameters() {
2868         return new external_function_parameters(
2869             array(
2870                 'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
2871                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
2872                     VALUE_DEFAULT, 0)
2873             )
2874         );
2875     }
2877     /**
2878      * Mark a single message as read, trigger message_viewed event
2879      *
2880      * @param  int $messageid id of the message (in the message table)
2881      * @param  int $timeread timestamp for when the message should be marked read
2882      * @return external_description
2883      * @throws invalid_parameter_exception
2884      * @throws moodle_exception
2885      * @since 2.9
2886      */
2887     public static function mark_message_read($messageid, $timeread) {
2888         global $CFG, $DB, $USER;
2890         // Check if private messaging between users is allowed.
2891         if (empty($CFG->messaging)) {
2892             throw new moodle_exception('disabled', 'message');
2893         }
2895         // Warnings array, it can be empty at the end but is mandatory.
2896         $warnings = array();
2898         // Validate params.
2899         $params = array(
2900             'messageid' => $messageid,
2901             'timeread' => $timeread
2902         );
2903         $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2905         if (empty($params['timeread'])) {
2906             $timeread = time();
2907         } else {
2908             $timeread = $params['timeread'];
2909         }
2911         // Validate context.
2912         $context = context_system::instance();
2913         self::validate_context($context);
2915         $sql = "SELECT m.*, mcm.userid as useridto
2916                   FROM {messages} m
2917             INNER JOIN {message_conversations} mc
2918                     ON m.conversationid = mc.id
2919             INNER JOIN {message_conversation_members} mcm
2920                     ON mcm.conversationid = mc.id
2921              LEFT JOIN {message_user_actions} mua
2922                     ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
2923                  WHERE mua.id is NULL
2924                    AND mcm.userid != m.useridfrom
2925                    AND m.id = ?";
2926         $messageparams = [];
2927         $messageparams[] = $USER->id;
2928         $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
2929         $messageparams[] = $params['messageid'];
2930         $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
2932         if ($message->useridto != $USER->id) {
2933             throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
2934         }
2936         \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2938         $results = array(
2939             'messageid' => $message->id,
2940             'warnings' => $warnings
2941         );
2942         return $results;
2943     }
2945     /**
2946      * Returns description of method result value
2947      *
2948      * @return external_description
2949      * @since 2.9
2950      */
2951     public static function mark_message_read_returns() {
2952         return new external_single_structure(
2953             array(
2954                 'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
2955                 'warnings' => new external_warnings()
2956             )
2957         );
2958     }
2960     /**
2961      * Returns description of method parameters
2962      *
2963      * @return external_function_parameters
2964      */
2965     public static function mark_notification_read_parameters() {
2966         return new external_function_parameters(
2967             array(
2968                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2969                 'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
2970                     VALUE_DEFAULT, 0)
2971             )
2972         );
2973     }
2975     /**
2976      * Mark a single notification as read.
2977      *
2978      * This will trigger a 'notification_viewed' event.
2979      *
2980      * @param int $notificationid id of the notification
2981      * @param int $timeread timestamp for when the notification should be marked read
2982      * @return external_description
2983      * @throws invalid_parameter_exception
2984      * @throws moodle_exception
2985      */
2986     public static function mark_notification_read($notificationid, $timeread) {
2987         global $CFG, $DB, $USER;
2989         // Check if private messaging between users is allowed.
2990         if (empty($CFG->messaging)) {
2991             throw new moodle_exception('disabled', 'message');
2992         }
2994         // Warnings array, it can be empty at the end but is mandatory.
2995         $warnings = array();
2997         // Validate params.
2998         $params = array(
2999             'notificationid' => $notificationid,
3000             'timeread' => $timeread
3001         );
3002         $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
3004         if (empty($params['timeread'])) {
3005             $timeread = time();
3006         } else {
3007             $timeread = $params['timeread'];
3008         }
3010         // Validate context.
3011         $context = context_system::instance();
3012         self::validate_context($context);
3014         $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
3016         if ($notification->useridto != $USER->id) {
3017             throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
3018                 'notification as read');
3019         }
3021         \core_message\api::mark_notification_as_read($notification, $timeread);
3023         $results = array(
3024             'notificationid' => $notification->id,
3025             'warnings' => $warnings
3026         );
3028         return $results;
3029     }
3031     /**
3032      * Returns description of method result value
3033      *
3034      * @return external_description
3035      */
3036     public static function mark_notification_read_returns() {
3037         return new external_single_structure(
3038             array(
3039                 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
3040                 'warnings' => new external_warnings()
3041             )
3042         );
3043     }
3045     /**
3046      * Mark all messages as read parameters description.
3047      *
3048      * @deprecated since 3.6
3049      * @return external_function_parameters
3050      * @since 3.2
3051      */
3052     public static function mark_all_messages_as_read_parameters() {
3053         return new external_function_parameters(
3054             array(
3055                 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
3056                 'useridfrom' => new external_value(
3057                     PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
3058                     VALUE_DEFAULT, 0),
3059             )
3060         );
3061     }
3063     /**
3064      * Mark all messages as read function.
3065      *
3066      * @deprecated since 3.6
3067      * @throws invalid_parameter_exception
3068      * @throws moodle_exception
3069      * @param  int      $useridto       the user id who received the message
3070      * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
3071      * @return external_description
3072      * @since  3.2
3073      */
3074     public static function mark_all_messages_as_read($useridto, $useridfrom) {
3075         global $USER, $CFG;
3077         // Check if messaging is enabled.
3078         if (empty($CFG->messaging)) {
3079             throw new moodle_exception('disabled', 'message');
3080         }
3082         $params = self::validate_parameters(
3083             self::mark_all_messages_as_read_parameters(),
3084             array(
3085                 'useridto' => $useridto,
3086                 'useridfrom' => $useridfrom,
3087             )
3088         );
3090         $context = context_system::instance();
3091         self::validate_context($context);
3093         $useridto = $params['useridto'];
3094         $useridfrom = $params['useridfrom'];
3096         if (!empty($useridto)) {
3097             if (core_user::is_real_user($useridto)) {
3098                 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
3099             } else {
3100                 throw new moodle_exception('invaliduser');
3101             }
3102         }
3104         if (!empty($useridfrom)) {
3105             // We use get_user here because the from user can be the noreply or support user.
3106             $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
3107         }
3109         // Check if the current user is the sender/receiver or just a privileged user.
3110         if ($useridto != $USER->id and $useridfrom != $USER->id and
3111             // The deleteanymessage cap seems more reasonable here than readallmessages.
3112              !has_capability('moodle/site:deleteanymessage', $context)) {
3113             throw new moodle_exception('accessdenied', 'admin');
3114         }
3116         if ($useridfrom) {
3117             if ($conversationid = \core_message\api::get_conversation_between_users([$useridto, $useridfrom])) {
3118                 \core_message\api::mark_all_messages_as_read($useridto, $conversationid);
3119             }
3120         } else {
3121             \core_message\api::mark_all_messages_as_read($useridto);
3122         }
3124         return true;
3125     }
3127     /**
3128      * Mark all messages as read return description.
3129      *
3130      * @deprecated since 3.6
3131      * @return external_single_structure
3132      * @since 3.2
3133      */
3134     public static function mark_all_messages_as_read_returns() {
3135         return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
3136     }
3138     /**
3139      * Marking the method as deprecated.
3140      *
3141      * @return bool
3142      */
3143     public static function mark_all_messages_as_read_is_deprecated() {
3144         return true;
3145     }
3147     /**
3148      * Mark all conversation messages as read parameters description.
3149      *
3150      * @return external_function_parameters
3151      * @since 3.6
3152      */
3153     public static function mark_all_conversation_messages_as_read_parameters() {
3154         return new external_function_parameters(
3155             array(
3156                 'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
3157                 'conversationid' =>
3158                     new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
3159             )
3160         );
3161     }
3163     /**
3164      * Mark all conversation messages as read function.
3165      *
3166      * @param int $userid The user id of who we want to delete the conversation for
3167      * @param int $conversationid The id of the conversations
3168      * @since 3.6
3169      */
3170     public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
3171         global $CFG;
3173         // Check if messaging is enabled.
3174         if (empty($CFG->messaging)) {
3175             throw new moodle_exception('disabled', 'message');
3176         }
3178         $params = array(
3179             'userid' => $userid,
3180             'conversationid' => $conversationid,
3181         );
3182         $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
3184         $context = context_system::instance();
3185         self::validate_context($context);
3187         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3188         core_user::require_active_user($user);
3190         if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
3191             \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
3192         } else {
3193             throw new moodle_exception('accessdenied', 'admin');
3194         }
3195     }
3197     /**
3198      * Mark all conversation messages as read return description.
3199      *
3200      * @return external_warnings
3201      * @since 3.6
3202      */
3203     public static function mark_all_conversation_messages_as_read_returns() {
3204         return new external_warnings();
3205     }
3207     /**
3208      * Returns description of method parameters.
3209      *
3210      * @deprecated since 3.6
3211      * @return external_function_parameters
3212      * @since 3.2
3213      */
3214     public static function delete_conversation_parameters() {
3215         return new external_function_parameters(
3216             array(
3217                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
3218                 'otheruserid' => new external_value(PARAM_INT, 'The user id of the other user in the conversation'),
3219             )
3220         );
3221     }
3223     /**
3224      * Deletes a conversation.
3225      *
3226      * @deprecated since 3.6
3227      * @param int $userid The user id of who we want to delete the conversation for
3228      * @param int $otheruserid The user id of the other user in the conversation
3229      * @return array
3230      * @throws moodle_exception
3231      * @since 3.2
3232      */
3233     public static function delete_conversation($userid, $otheruserid) {
3234         global $CFG;
3236         // Check if private messaging between users is allowed.
3237         if (empty($CFG->messaging)) {
3238             throw new moodle_exception('disabled', 'message');
3239         }
3241         // Warnings array, it can be empty at the end but is mandatory.
3242         $warnings = array();
3244         // Validate params.
3245         $params = array(
3246             'userid' => $userid,
3247             'otheruserid' => $otheruserid,
3248         );
3249         $params = self::validate_parameters(self::delete_conversation_parameters(), $params);
3251         // Validate context.
3252         $context = context_system::instance();
3253         self::validate_context($context);
3255         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3256         core_user::require_active_user($user);
3258         if (!$conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']])) {
3259             return [];
3260         }
3262         if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
3263             \core_message\api::delete_conversation_by_id($user->id, $conversationid);
3264             $status = true;
3265         } else {
3266             throw new moodle_exception('You do not have permission to delete messages');
3267         }
3269         $results = array(
3270             'status' => $status,
3271             'warnings' => $warnings
3272         );
3274         return $results;
3275     }
3277     /**
3278      * Returns description of method result value.
3279      *
3280      * @deprecated since 3.6
3281      * @return external_description
3282      * @since 3.2
3283      */
3284     public static function delete_conversation_returns() {
3285         return new external_single_structure(
3286             array(
3287                 'status' => new external_value(PARAM_BOOL, 'True if the conversation was deleted, false otherwise'),
3288                 'warnings' => new external_warnings()
3289             )
3290         );
3291     }
3293     /**
3294      * Marking the method as deprecated.
3295      *
3296      * @return bool
3297      */
3298     public static function delete_conversation_is_deprecated() {
3299         return true;
3300     }
3302     /**
3303      * Returns description of method parameters.
3304      *
3305      * @return external_function_parameters
3306      * @since 3.6
3307      */
3308     public static function delete_conversations_by_id_parameters() {
3309         return new external_function_parameters(
3310             array(
3311                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
3312                 'conversationids' => new external_multiple_structure(
3313                     new external_value(PARAM_INT, 'The id of the conversation'),
3314                     'List of conversation IDs'
3315                 ),
3316             )
3317         );
3318     }
3320     /**
3321      * Deletes a conversation.
3322      *
3323      * @param int $userid The user id of who we want to delete the conversation for
3324      * @param int[] $conversationids The ids of the conversations
3325      * @return array
3326      * @throws moodle_exception
3327      * @since 3.6
3328      */
3329     public static function delete_conversations_by_id($userid, array $conversationids) {
3330         global $CFG;
3332         // Check if private messaging between users is allowed.
3333         if (empty($CFG->messaging)) {
3334             throw new moodle_exception('disabled', 'message');
3335         }
3337         // Validate params.
3338         $params = [
3339             'userid' => $userid,
3340             'conversationids' => $conversationids,
3341         ];
3342         $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
3344         // Validate context.
3345         $context = context_system::instance();
3346         self::validate_context($context);
3348         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3349         core_user::require_active_user($user);
3351         foreach ($params['conversationids'] as $conversationid) {
3352             if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
3353                 \core_message\api::delete_conversation_by_id($user->id, $conversationid);
3354             } else {
3355                 throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
3356             }
3357         }
3359         return [];
3360     }
3362     /**
3363      * Returns description of method result value.
3364      *
3365      * @return external_description
3366      * @since 3.6
3367      */
3368     public static function delete_conversations_by_id_returns() {
3369         return new external_warnings();
3370     }
3372     /**
3373      * Returns description of method parameters
3374      *
3375      * @return external_function_parameters
3376      * @since 3.1
3377      */
3378     public static function delete_message_parameters() {
3379         return new external_function_parameters(
3380             array(
3381                 'messageid' => new external_value(PARAM_INT, 'The message id'),
3382                 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
3383                 'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
3384             )
3385         );
3386     }
3388     /**
3389      * Deletes a message
3390      *
3391      * @param  int $messageid the message id
3392      * @param  int $userid the user id of who we want to delete the message for
3393      * @param  bool $read if is a message read (default to true)
3394      * @return external_description
3395      * @throws moodle_exception
3396      * @since 3.1
3397      */
3398     public static function delete_message($messageid, $userid, $read = true) {
3399         global $CFG;
3401         // Check if private messaging between users is allowed.
3402         if (empty($CFG->messaging)) {
3403             throw new moodle_exception('disabled', 'message');
3404         }
3406         // Warnings array, it can be empty at the end but is mandatory.
3407         $warnings = array();
3409         // Validate params.
3410         $params = array(
3411             'messageid' => $messageid,
3412             'userid' => $userid,
3413             'read' => $read
3414         );
3415         $params = self::validate_parameters(self::delete_message_parameters(), $params);
3417         // Validate context.
3418         $context = context_system::instance();
3419         self::validate_context($context);
3421         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
3422         core_user::require_active_user($user);
3424         if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
3425             $status = \core_message\api::delete_message($user->id, $params['messageid']);
3426         } else {
3427             throw new moodle_exception('You do not have permission to delete this message');
3428         }
3430         $results = array(
3431             'status' => $status,
3432             'warnings' => $warnings
3433         );