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