Merge branch 'MDL-81456-main' of https://github.com/andrewnicols/moodle
[moodle.git] / message / externallib.php
blob56c40012fd44ff668033947d9f9ccfb5d803b0f4
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.
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/>.
17 /**
18 * External message API
20 * @package core_message
21 * @category external
22 * @copyright 2011 Jerome Mouneyrac
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 use core_external\external_api;
27 use core_external\external_format_value;
28 use core_external\external_function_parameters;
29 use core_external\external_multiple_structure;
30 use core_external\external_single_structure;
31 use core_external\external_value;
32 use core_external\external_warnings;
33 use core_external\util;
35 defined('MOODLE_INTERNAL') || die();
37 require_once($CFG->dirroot . "/message/lib.php");
39 /**
40 * Message external functions
42 * @package core_message
43 * @category external
44 * @copyright 2011 Jerome Mouneyrac
45 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46 * @since Moodle 2.2
48 class core_message_external extends external_api {
49 /**
50 * Returns description of method parameters
52 * @return external_function_parameters
53 * @since Moodle 3.6
55 public static function send_messages_to_conversation_parameters() {
56 return new external_function_parameters(
57 array(
58 'conversationid' => new external_value(PARAM_INT, 'id of the conversation'),
59 'messages' => new external_multiple_structure(
60 new external_single_structure(
61 array(
62 'text' => new external_value(PARAM_RAW, 'the text of the message'),
63 'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
71 /**
72 * Send messages from the current USER to a conversation.
74 * This conversation may be any type of conversation, individual or group.
76 * @param int $conversationid the id of the conversation to which the messages will be sent.
77 * @param array $messages An array of message to send.
78 * @return array the array of messages which were sent (created).
79 * @since Moodle 3.6
81 public static function send_messages_to_conversation(int $conversationid, array $messages = []) {
82 global $CFG, $USER;
84 // Check if messaging is enabled.
85 if (empty($CFG->messaging)) {
86 throw new moodle_exception('disabled', 'message');
89 // Ensure the current user is allowed to run this function.
90 $context = context_system::instance();
91 self::validate_context($context);
93 $params = self::validate_parameters(self::send_messages_to_conversation_parameters(), [
94 'conversationid' => $conversationid,
95 'messages' => $messages
96 ]);
98 // Validate messages content before posting them.
99 foreach ($params['messages'] as $message) {
100 // Check message length.
101 if (strlen($message['text']) > \core_message\api::MESSAGE_MAX_LENGTH) {
102 throw new moodle_exception('errormessagetoolong', 'message');
106 $messages = [];
107 foreach ($params['messages'] as $message) {
108 $createdmessage = \core_message\api::send_message_to_conversation($USER->id, $params['conversationid'], $message['text'],
109 $message['textformat']);
110 $createdmessage->text = message_format_message_text((object) [
111 'smallmessage' => $createdmessage->text,
112 'fullmessageformat' => util::validate_format($message['textformat']),
113 'fullmessagetrust' => $createdmessage->fullmessagetrust
115 $messages[] = $createdmessage;
118 return $messages;
122 * Returns description of method result value.
124 * @return \core_external\external_description
125 * @since Moodle 3.6
127 public static function send_messages_to_conversation_returns() {
128 return new external_multiple_structure(
129 self::get_conversation_message_structure()
135 * Returns description of method parameters
137 * @return external_function_parameters
138 * @since Moodle 2.2
140 public static function send_instant_messages_parameters() {
141 return new external_function_parameters(
142 array(
143 'messages' => new external_multiple_structure(
144 new external_single_structure(
145 array(
146 'touserid' => new external_value(PARAM_INT, 'id of the user to send the private message'),
147 'text' => new external_value(PARAM_RAW, 'the text of the message'),
148 'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
149 '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),
158 * Send private messages from the current USER to other users
160 * @param array $messages An array of message to send.
161 * @return array
162 * @since Moodle 2.2
164 public static function send_instant_messages($messages = array()) {
165 global $CFG, $USER, $DB;
167 // Check if messaging is enabled.
168 if (empty($CFG->messaging)) {
169 throw new moodle_exception('disabled', 'message');
172 // Ensure the current user is allowed to run this function
173 $context = context_system::instance();
174 self::validate_context($context);
175 require_capability('moodle/site:sendmessage', $context);
177 // Ensure the current user is allowed to delete message for everyone.
178 $candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $context);
180 $params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
182 //retrieve all tousers of the messages
183 $receivers = array();
184 foreach($params['messages'] as $message) {
185 $receivers[] = $message['touserid'];
187 list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
188 $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
190 $resultmessages = array();
191 $messageids = array();
192 foreach ($params['messages'] as $message) {
193 $resultmsg = array(); //the infos about the success of the operation
195 // We are going to do some checking.
196 // Code should match /messages/index.php checks.
197 $success = true;
199 // Check the user exists.
200 if (empty($tousers[$message['touserid']])) {
201 $success = false;
202 $errormessage = get_string('touserdoesntexist', 'message', $message['touserid']);
205 // Check message length.
206 if ($success && strlen($message['text']) > \core_message\api::MESSAGE_MAX_LENGTH) {
207 $success = false;
208 $errormessage = get_string('errormessagetoolong', 'message');
211 // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead userid
212 // Check if the recipient can be messaged by the sender.
213 if ($success && !\core_message\api::can_send_message($tousers[$message['touserid']]->id, $USER->id)) {
214 $success = false;
215 $errormessage = get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message['touserid'])));
218 // Now we can send the message (at least try).
219 if ($success) {
220 // TODO MDL-31118 performance improvement - edit the function so we can pass an array instead one touser object.
221 $success = message_post_message($USER, $tousers[$message['touserid']],
222 $message['text'], util::validate_format($message['textformat']));
225 // Build the resultmsg.
226 if (isset($message['clientmsgid'])) {
227 $resultmsg['clientmsgid'] = $message['clientmsgid'];
229 if ($success) {
230 $resultmsg['msgid'] = $success;
231 $resultmsg['timecreated'] = time();
232 $resultmsg['candeletemessagesforallusers'] = $candeletemessagesforallusers;
233 $messageids[] = $success;
234 } else {
235 // WARNINGS: for backward compatibility we return this errormessage.
236 // We should have thrown exceptions as these errors prevent results to be returned.
237 $resultmsg['msgid'] = -1;
238 if (!isset($errormessage)) { // Nobody has set a message error or thrown an exception, let's set it.
239 $errormessage = get_string('messageundeliveredbynotificationsettings', 'error');
241 $resultmsg['errormessage'] = $errormessage;
244 $resultmessages[] = $resultmsg;
247 if (!empty($messageids)) {
248 $messagerecords = $DB->get_records_list(
249 'messages',
250 'id',
251 $messageids,
253 'id, conversationid, smallmessage, fullmessageformat, fullmessagetrust');
254 $resultmessages = array_map(function($resultmessage) use ($messagerecords, $USER) {
255 $id = $resultmessage['msgid'];
256 $resultmessage['conversationid'] = isset($messagerecords[$id]) ? $messagerecords[$id]->conversationid : null;
257 $resultmessage['useridfrom'] = $USER->id;
258 $resultmessage['text'] = message_format_message_text((object) [
259 'smallmessage' => $messagerecords[$id]->smallmessage,
260 'fullmessageformat' => util::validate_format($messagerecords[$id]->fullmessageformat),
261 'fullmessagetrust' => $messagerecords[$id]->fullmessagetrust
263 return $resultmessage;
264 }, $resultmessages);
267 return $resultmessages;
271 * Returns description of method result value
273 * @return \core_external\external_description
274 * @since Moodle 2.2
276 public static function send_instant_messages_returns() {
277 return new external_multiple_structure(
278 new external_single_structure(
279 array(
280 'msgid' => new external_value(PARAM_INT, 'test this to know if it succeeds: id of the created message if it succeeded, -1 when failed'),
281 'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
282 'errormessage' => new external_value(PARAM_TEXT, 'error message - if it failed', VALUE_OPTIONAL),
283 'text' => new external_value(PARAM_RAW, 'The text of the message', VALUE_OPTIONAL),
284 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message', VALUE_OPTIONAL),
285 'conversationid' => new external_value(PARAM_INT, 'The conversation id for this message', VALUE_OPTIONAL),
286 'useridfrom' => new external_value(PARAM_INT, 'The user id who sent the message', VALUE_OPTIONAL),
287 'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
288 'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
295 * Delete contacts parameters description.
297 * @return external_function_parameters
298 * @since Moodle 2.5
300 public static function delete_contacts_parameters() {
301 return new external_function_parameters(
302 array(
303 'userids' => new external_multiple_structure(
304 new external_value(PARAM_INT, 'User ID'),
305 'List of user IDs'
307 'userid' => new external_value(PARAM_INT, 'The id of the user we are deleting the contacts for, 0 for the
308 current user', VALUE_DEFAULT, 0)
314 * Delete contacts.
316 * @param array $userids array of user IDs.
317 * @param int $userid The id of the user we are deleting the contacts for
318 * @return null
319 * @since Moodle 2.5
321 public static function delete_contacts($userids, $userid = 0) {
322 global $CFG, $USER;
324 // Check if messaging is enabled.
325 if (empty($CFG->messaging)) {
326 throw new moodle_exception('disabled', 'message');
329 if (empty($userid)) {
330 $userid = $USER->id;
333 // Validate context.
334 $context = context_system::instance();
335 self::validate_context($context);
337 $params = array('userids' => $userids, 'userid' => $userid);
338 $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
340 $capability = 'moodle/site:manageallmessaging';
341 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
342 throw new required_capability_exception($context, $capability, 'nopermissions', '');
345 foreach ($params['userids'] as $id) {
346 \core_message\api::remove_contact($params['userid'], $id);
349 return null;
353 * Delete contacts return description.
355 * @return \core_external\external_description
356 * @since Moodle 2.5
358 public static function delete_contacts_returns() {
359 return null;
363 * Mute conversations parameters description.
365 * @return external_function_parameters
367 public static function mute_conversations_parameters() {
368 return new external_function_parameters(
370 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
371 'conversationids' => new external_multiple_structure(
372 new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
379 * Mutes conversations.
381 * @param int $userid The id of the user who is blocking
382 * @param array $conversationids The list of conversations being muted
383 * @return \core_external\external_description
385 public static function mute_conversations(int $userid, array $conversationids) {
386 global $CFG, $USER;
388 // Check if messaging is enabled.
389 if (empty($CFG->messaging)) {
390 throw new moodle_exception('disabled', 'message');
393 // Validate context.
394 $context = context_system::instance();
395 self::validate_context($context);
397 $params = ['userid' => $userid, 'conversationids' => $conversationids];
398 $params = self::validate_parameters(self::mute_conversations_parameters(), $params);
400 $capability = 'moodle/site:manageallmessaging';
401 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
402 throw new required_capability_exception($context, $capability, 'nopermissions', '');
405 foreach ($params['conversationids'] as $conversationid) {
406 if (!\core_message\api::is_conversation_muted($params['userid'], $conversationid)) {
407 \core_message\api::mute_conversation($params['userid'], $conversationid);
411 return [];
415 * Mute conversations return description.
417 * @return \core_external\external_description
419 public static function mute_conversations_returns() {
420 return new external_warnings();
424 * Unmute conversations parameters description.
426 * @return external_function_parameters
428 public static function unmute_conversations_parameters() {
429 return new external_function_parameters(
431 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
432 'conversationids' => new external_multiple_structure(
433 new external_value(PARAM_INT, 'id of the conversation', VALUE_REQUIRED)
440 * Unmute conversations.
442 * @param int $userid The id of the user who is unblocking
443 * @param array $conversationids The list of conversations being muted
445 public static function unmute_conversations(int $userid, array $conversationids) {
446 global $CFG, $USER;
448 // Check if messaging is enabled.
449 if (empty($CFG->messaging)) {
450 throw new moodle_exception('disabled', 'message');
453 // Validate context.
454 $context = context_system::instance();
455 self::validate_context($context);
457 $params = ['userid' => $userid, 'conversationids' => $conversationids];
458 $params = self::validate_parameters(self::unmute_conversations_parameters(), $params);
460 $capability = 'moodle/site:manageallmessaging';
461 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
462 throw new required_capability_exception($context, $capability, 'nopermissions', '');
465 foreach ($params['conversationids'] as $conversationid) {
466 \core_message\api::unmute_conversation($params['userid'], $conversationid);
469 return [];
473 * Unmute conversations return description.
475 * @return \core_external\external_description
477 public static function unmute_conversations_returns() {
478 return new external_warnings();
482 * Block user parameters description.
484 * @return external_function_parameters
486 public static function block_user_parameters() {
487 return new external_function_parameters(
489 'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
490 'blockeduserid' => new external_value(PARAM_INT, 'The id of the user being blocked'),
496 * Blocks a user.
498 * @param int $userid The id of the user who is blocking
499 * @param int $blockeduserid The id of the user being blocked
500 * @return \core_external\external_description
502 public static function block_user(int $userid, int $blockeduserid) {
503 global $CFG, $USER;
505 // Check if messaging is enabled.
506 if (empty($CFG->messaging)) {
507 throw new moodle_exception('disabled', 'message');
510 // Validate context.
511 $context = context_system::instance();
512 self::validate_context($context);
514 $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
515 $params = self::validate_parameters(self::block_user_parameters(), $params);
517 $capability = 'moodle/site:manageallmessaging';
518 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
519 throw new required_capability_exception($context, $capability, 'nopermissions', '');
522 // If the blocking is going to be useless then don't do it.
523 if (\core_message\api::can_send_message($userid, $blockeduserid, true)) {
524 return [];
527 if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
528 \core_message\api::block_user($params['userid'], $params['blockeduserid']);
531 return [];
535 * Block user return description.
537 * @return \core_external\external_description
539 public static function block_user_returns() {
540 return new external_warnings();
544 * Unblock user parameters description.
546 * @return external_function_parameters
548 public static function unblock_user_parameters() {
549 return new external_function_parameters(
551 'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
552 'unblockeduserid' => new external_value(PARAM_INT, 'The id of the user being unblocked'),
558 * Unblock user.
560 * @param int $userid The id of the user who is unblocking
561 * @param int $unblockeduserid The id of the user being unblocked
563 public static function unblock_user(int $userid, int $unblockeduserid) {
564 global $CFG, $USER;
566 // Check if messaging is enabled.
567 if (empty($CFG->messaging)) {
568 throw new moodle_exception('disabled', 'message');
571 // Validate context.
572 $context = context_system::instance();
573 self::validate_context($context);
575 $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
576 $params = self::validate_parameters(self::unblock_user_parameters(), $params);
578 $capability = 'moodle/site:manageallmessaging';
579 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
580 throw new required_capability_exception($context, $capability, 'nopermissions', '');
583 \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
585 return [];
589 * Unblock user return description.
591 * @return \core_external\external_description
593 public static function unblock_user_returns() {
594 return new external_warnings();
598 * Returns contact requests parameters description.
600 * @return external_function_parameters
602 public static function get_contact_requests_parameters() {
603 return new external_function_parameters(
605 'userid' => new external_value(PARAM_INT, 'The id of the user we want the requests for'),
606 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
607 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
613 * Handles returning the contact requests for a user.
615 * This also includes the user data necessary to display information
616 * about the user.
618 * It will not include blocked users.
620 * @param int $userid The id of the user we want to get the contact requests for
621 * @param int $limitfrom
622 * @param int $limitnum
624 public static function get_contact_requests(int $userid, int $limitfrom = 0, int $limitnum = 0) {
625 global $CFG, $USER;
627 // Check if messaging is enabled.
628 if (empty($CFG->messaging)) {
629 throw new moodle_exception('disabled', 'message');
632 // Validate context.
633 $context = context_system::instance();
634 self::validate_context($context);
636 $params = [
637 'userid' => $userid,
638 'limitfrom' => $limitfrom,
639 'limitnum' => $limitnum
641 $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
643 $capability = 'moodle/site:manageallmessaging';
644 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
645 throw new required_capability_exception($context, $capability, 'nopermissions', '');
648 return \core_message\api::get_contact_requests($params['userid'], $params['limitfrom'], $params['limitnum']);
652 * Returns the contact requests return description.
654 * @return \core_external\external_description
656 public static function get_contact_requests_returns() {
657 return new external_multiple_structure(
658 self::get_conversation_member_structure()
663 * Returns the number of contact requests the user has received parameters description.
665 * @return external_function_parameters
667 public static function get_received_contact_requests_count_parameters() {
668 return new external_function_parameters(
669 array(
670 'userid' => new external_value(PARAM_INT, 'The id of the user we want to return the number of ' .
671 'received contact requests for', VALUE_REQUIRED),
677 * Returns the number of contact requests the user has received.
679 * @param int $userid The ID of the user we want to return the number of received contact requests for
680 * @return external_value
682 public static function get_received_contact_requests_count(int $userid) {
683 global $CFG, $USER;
685 // Check if messaging is enabled.
686 if (empty($CFG->messaging)) {
687 throw new moodle_exception('disabled', 'message');
690 // Validate context.
691 $context = context_system::instance();
692 self::validate_context($context);
694 $params = [
695 'userid' => $userid,
697 $params = self::validate_parameters(self::get_received_contact_requests_count_parameters(), $params);
699 $capability = 'moodle/site:manageallmessaging';
700 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
701 throw new required_capability_exception($context, $capability, 'nopermissions', '');
704 return \core_message\api::get_received_contact_requests_count($params['userid']);
708 * Returns the number of contact requests the user has received return description.
710 * @return external_value
712 public static function get_received_contact_requests_count_returns() {
713 return new external_value(PARAM_INT, 'The number of received contact requests');
717 * Returns get conversation members parameters description.
719 * @return external_function_parameters
721 public static function get_conversation_members_parameters() {
722 return new external_function_parameters(
724 'userid' => new external_value(PARAM_INT, 'The id of the user we are performing this action on behalf of'),
725 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation'),
726 'includecontactrequests' => new external_value(PARAM_BOOL, 'Do we want to include contact requests?',
727 VALUE_DEFAULT, false),
728 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Do we want to include privacy info?',
729 VALUE_DEFAULT, false),
730 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
731 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
737 * Returns a list of conversation members.
739 * @param int $userid The user we are returning the conversation members for, used by helper::get_member_info.
740 * @param int $conversationid The id of the conversation
741 * @param bool $includecontactrequests Do we want to include contact requests with this data?
742 * @param bool $includeprivacyinfo Do we want to include privacy info?
743 * @param int $limitfrom
744 * @param int $limitnum
745 * @return array
747 public static function get_conversation_members(int $userid, int $conversationid, bool $includecontactrequests = false,
748 bool $includeprivacyinfo = false, int $limitfrom = 0, int $limitnum = 0) {
749 global $CFG, $USER;
751 // Check if messaging is enabled.
752 if (empty($CFG->messaging)) {
753 throw new moodle_exception('disabled', 'message');
756 // Validate context.
757 $context = context_system::instance();
758 self::validate_context($context);
760 $params = [
761 'userid' => $userid,
762 'conversationid' => $conversationid,
763 'includecontactrequests' => $includecontactrequests,
764 'includeprivacyinfo' => $includeprivacyinfo,
765 'limitfrom' => $limitfrom,
766 'limitnum' => $limitnum
768 $params = self::validate_parameters(self::get_conversation_members_parameters(), $params);
770 $capability = 'moodle/site:manageallmessaging';
771 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
772 throw new required_capability_exception($context, $capability, 'nopermissions', '');
775 // The user needs to be a part of the conversation before querying who the members are.
776 if (!\core_message\api::is_user_in_conversation($params['userid'], $params['conversationid'])) {
777 throw new moodle_exception('You are not a member of this conversation.');
780 return \core_message\api::get_conversation_members($params['userid'], $params['conversationid'], $params['includecontactrequests'],
781 $params['includeprivacyinfo'], $params['limitfrom'], $params['limitnum']);
785 * Returns the get conversation members return description.
787 * @return \core_external\external_description
789 public static function get_conversation_members_returns() {
790 return new external_multiple_structure(
791 self::get_conversation_member_structure()
796 * Creates a contact request parameters description.
798 * @return external_function_parameters
800 public static function create_contact_request_parameters() {
801 return new external_function_parameters(
803 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
804 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
810 * Creates a contact request.
812 * @param int $userid The id of the user who is creating the contact request
813 * @param int $requesteduserid The id of the user being requested
815 public static function create_contact_request(int $userid, int $requesteduserid) {
816 global $CFG, $USER;
818 // Check if messaging is enabled.
819 if (empty($CFG->messaging)) {
820 throw new moodle_exception('disabled', 'message');
823 // Validate context.
824 $context = context_system::instance();
825 self::validate_context($context);
827 $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
828 $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
830 $capability = 'moodle/site:manageallmessaging';
831 if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
832 throw new required_capability_exception($context, $capability, 'nopermissions', '');
835 $result = [
836 'warnings' => []
839 if (!\core_message\api::can_create_contact($params['userid'], $params['requesteduserid'])) {
840 $result['warnings'][] = [
841 'item' => 'user',
842 'itemid' => $params['requesteduserid'],
843 'warningcode' => 'cannotcreatecontactrequest',
844 'message' => 'You are unable to create a contact request for this user'
846 } else {
847 if ($requests = \core_message\api::get_contact_requests_between_users($params['userid'], $params['requesteduserid'])) {
848 // There should only ever be one but just in case there are multiple then we can return the first.
849 $result['request'] = array_shift($requests);
850 } else {
851 $result['request'] = \core_message\api::create_contact_request($params['userid'], $params['requesteduserid']);
855 return $result;
859 * Creates a contact request return description.
861 * @return \core_external\external_description
863 public static function create_contact_request_returns() {
864 return new external_single_structure(
865 array(
866 'request' => new external_single_structure(
867 array(
868 'id' => new external_value(PARAM_INT, 'Message id'),
869 'userid' => new external_value(PARAM_INT, 'User from id'),
870 'requesteduserid' => new external_value(PARAM_INT, 'User to id'),
871 'timecreated' => new external_value(PARAM_INT, 'Time created'),
873 'request record',
874 VALUE_OPTIONAL
876 'warnings' => new external_warnings()
882 * Confirm a contact request parameters description.
884 * @return external_function_parameters
886 public static function confirm_contact_request_parameters() {
887 return new external_function_parameters(
889 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
890 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
896 * Confirm a contact request.
898 * @param int $userid The id of the user who is creating the contact request
899 * @param int $requesteduserid The id of the user being requested
901 public static function confirm_contact_request(int $userid, int $requesteduserid) {
902 global $CFG, $USER;
904 // Check if messaging is enabled.
905 if (empty($CFG->messaging)) {
906 throw new moodle_exception('disabled', 'message');
909 // Validate context.
910 $context = context_system::instance();
911 self::validate_context($context);
913 $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
914 $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
916 $capability = 'moodle/site:manageallmessaging';
917 if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
918 throw new required_capability_exception($context, $capability, 'nopermissions', '');
921 \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
923 return [];
927 * Confirm a contact request return description.
929 * @return \core_external\external_description
931 public static function confirm_contact_request_returns() {
932 return new external_warnings();
936 * Declines a contact request parameters description.
938 * @return external_function_parameters
940 public static function decline_contact_request_parameters() {
941 return new external_function_parameters(
943 'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
944 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
950 * Declines a contact request.
952 * @param int $userid The id of the user who is creating the contact request
953 * @param int $requesteduserid The id of the user being requested
955 public static function decline_contact_request(int $userid, int $requesteduserid) {
956 global $CFG, $USER;
958 // Check if messaging is enabled.
959 if (empty($CFG->messaging)) {
960 throw new moodle_exception('disabled', 'message');
963 // Validate context.
964 $context = context_system::instance();
965 self::validate_context($context);
967 $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
968 $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
970 $capability = 'moodle/site:manageallmessaging';
971 if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
972 throw new required_capability_exception($context, $capability, 'nopermissions', '');
975 \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
977 return [];
981 * Declines a contact request return description.
983 * @return \core_external\external_description
985 public static function decline_contact_request_returns() {
986 return new external_warnings();
990 * Return the structure of a message area contact.
992 * @return external_single_structure
993 * @since Moodle 3.2
995 private static function get_messagearea_contact_structure() {
996 return new external_single_structure(
997 array(
998 'userid' => new external_value(PARAM_INT, 'The user\'s id'),
999 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1000 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1001 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1002 'ismessaging' => new external_value(PARAM_BOOL, 'If we are messaging the user'),
1003 'sentfromcurrentuser' => new external_value(PARAM_BOOL, 'Was the last message sent from the current user?'),
1004 'lastmessage' => new external_value(PARAM_NOTAGS, 'The user\'s last message'),
1005 'lastmessagedate' => new external_value(PARAM_INT, 'Timestamp for last message', VALUE_DEFAULT, null),
1006 'messageid' => new external_value(PARAM_INT, 'The unique search message id', VALUE_DEFAULT, null),
1007 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1008 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1009 'isread' => new external_value(PARAM_BOOL, 'If the user has read the message'),
1010 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1011 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1012 VALUE_DEFAULT, null),
1013 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation', VALUE_DEFAULT, null),
1019 * Return the structure of a conversation.
1021 * @return external_single_structure
1022 * @since Moodle 3.6
1024 private static function get_conversation_structure() {
1025 return new external_single_structure(
1026 array(
1027 'id' => new external_value(PARAM_INT, 'The conversation id'),
1028 'name' => new external_value(PARAM_RAW, 'The conversation name, if set', VALUE_DEFAULT, null),
1029 'subname' => new external_value(PARAM_RAW, 'A subtitle for the conversation name, if set', VALUE_DEFAULT, null),
1030 'imageurl' => new external_value(PARAM_URL, 'A link to the conversation picture, if set', VALUE_DEFAULT, null),
1031 'type' => new external_value(PARAM_INT, 'The type of the conversation (1=individual,2=group,3=self)'),
1032 'membercount' => new external_value(PARAM_INT, 'Total number of conversation members'),
1033 'ismuted' => new external_value(PARAM_BOOL, 'If the user muted this conversation'),
1034 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this conversation as a favourite'),
1035 'isread' => new external_value(PARAM_BOOL, 'If the user has read all messages in the conversation'),
1036 'unreadcount' => new external_value(PARAM_INT, 'The number of unread messages in this conversation',
1037 VALUE_DEFAULT, null),
1038 'members' => new external_multiple_structure(
1039 self::get_conversation_member_structure()
1041 'messages' => new external_multiple_structure(
1042 self::get_conversation_message_structure()
1044 'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
1045 'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
1051 * Return the structure of a conversation member.
1053 * @return external_single_structure
1054 * @since Moodle 3.6
1056 private static function get_conversation_member_structure() {
1057 $result = [
1058 'id' => new external_value(PARAM_INT, 'The user id'),
1059 'fullname' => new external_value(PARAM_NOTAGS, 'The user\'s name'),
1060 'profileurl' => new external_value(PARAM_URL, 'The link to the user\'s profile page'),
1061 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL'),
1062 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL'),
1063 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
1064 'showonlinestatus' => new external_value(PARAM_BOOL, 'Show the user\'s online status?'),
1065 'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
1066 'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'),
1067 'isdeleted' => new external_value(PARAM_BOOL, 'Is the user deleted?'),
1068 'canmessageevenifblocked' => new external_value(PARAM_BOOL,
1069 'If the user can still message even if they get blocked'),
1070 'canmessage' => new external_value(PARAM_BOOL, 'If the user can be messaged'),
1071 'requirescontact' => new external_value(PARAM_BOOL, 'If the user requires to be contacts'),
1074 $result['contactrequests'] = new external_multiple_structure(
1075 new external_single_structure(
1077 'id' => new external_value(PARAM_INT, 'The id of the contact request'),
1078 'userid' => new external_value(PARAM_INT, 'The id of the user who created the contact request'),
1079 'requesteduserid' => new external_value(PARAM_INT, 'The id of the user confirming the request'),
1080 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the contact request'),
1082 ), 'The contact requests', VALUE_OPTIONAL
1085 $result['conversations'] = new external_multiple_structure(new external_single_structure(
1086 array(
1087 'id' => new external_value(PARAM_INT, 'Conversations id'),
1088 'type' => new external_value(PARAM_INT, 'Conversation type: private or public'),
1089 'name' => new external_value(PARAM_RAW, 'Multilang compatible conversation name'. VALUE_OPTIONAL),
1090 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the conversation'),
1091 ), 'information about conversation', VALUE_OPTIONAL),
1092 'Conversations between users', VALUE_OPTIONAL
1095 return new external_single_structure(
1096 $result
1101 * Return the structure of a message area message.
1103 * @return external_single_structure
1104 * @since Moodle 3.6
1106 private static function get_conversation_message_structure() {
1107 return new external_single_structure(
1108 array(
1109 'id' => new external_value(PARAM_INT, 'The id of the message'),
1110 'useridfrom' => new external_value(PARAM_INT, 'The id of the user who sent the message'),
1111 'text' => new external_value(PARAM_RAW, 'The text of the message'),
1112 'timecreated' => new external_value(PARAM_INT, 'The timecreated timestamp for the message'),
1118 * Get messagearea message search users parameters.
1120 * @return external_function_parameters
1121 * @since 3.6
1123 public static function message_search_users_parameters() {
1124 return new external_function_parameters(
1125 array(
1126 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1127 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1128 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1129 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1135 * Get search users results.
1137 * @param int $userid The id of the user who is performing the search
1138 * @param string $search The string being searched
1139 * @param int $limitfrom
1140 * @param int $limitnum
1141 * @return array
1142 * @throws moodle_exception
1143 * @since 3.6
1145 public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) {
1146 global $USER;
1148 $systemcontext = context_system::instance();
1150 $params = array(
1151 'userid' => $userid,
1152 'search' => $search,
1153 'limitfrom' => $limitfrom,
1154 'limitnum' => $limitnum
1156 $params = self::validate_parameters(self::message_search_users_parameters(), $params);
1157 self::validate_context($systemcontext);
1159 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1160 throw new moodle_exception('You do not have permission to perform this action.');
1163 list($contacts, $noncontacts) = \core_message\api::message_search_users(
1164 $params['userid'],
1165 $params['search'],
1166 $params['limitfrom'],
1167 $params['limitnum']);
1169 return array('contacts' => $contacts, 'noncontacts' => $noncontacts);
1173 * Get messagearea message search users returns.
1175 * @return external_single_structure
1176 * @since 3.2
1178 public static function message_search_users_returns() {
1179 return new external_single_structure(
1180 array(
1181 'contacts' => new external_multiple_structure(
1182 self::get_conversation_member_structure()
1184 'noncontacts' => new external_multiple_structure(
1185 self::get_conversation_member_structure()
1192 * Get messagearea search messages parameters.
1194 * @return external_function_parameters
1195 * @since 3.2
1197 public static function data_for_messagearea_search_messages_parameters() {
1198 return new external_function_parameters(
1199 array(
1200 'userid' => new external_value(PARAM_INT, 'The id of the user who is performing the search'),
1201 'search' => new external_value(PARAM_RAW, 'The string being searched'),
1202 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1203 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1209 * Get messagearea search messages results.
1211 * @param int $userid The id of the user who is performing the search
1212 * @param string $search The string being searched
1213 * @param int $limitfrom
1214 * @param int $limitnum
1215 * @return stdClass
1216 * @throws moodle_exception
1217 * @since 3.2
1219 public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
1220 global $CFG, $USER;
1222 // Check if messaging is enabled.
1223 if (empty($CFG->messaging)) {
1224 throw new moodle_exception('disabled', 'message');
1227 $systemcontext = context_system::instance();
1229 $params = array(
1230 'userid' => $userid,
1231 'search' => $search,
1232 'limitfrom' => $limitfrom,
1233 'limitnum' => $limitnum
1236 $params = self::validate_parameters(self::data_for_messagearea_search_messages_parameters(), $params);
1237 self::validate_context($systemcontext);
1239 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1240 throw new moodle_exception('You do not have permission to perform this action.');
1243 $messages = \core_message\api::search_messages(
1244 $params['userid'],
1245 $params['search'],
1246 $params['limitfrom'],
1247 $params['limitnum']
1250 $data = new \stdClass();
1251 $data->contacts = [];
1252 foreach ($messages as $message) {
1253 $contact = new \stdClass();
1254 $contact->userid = $message->userid;
1255 $contact->fullname = $message->fullname;
1256 $contact->profileimageurl = $message->profileimageurl;
1257 $contact->profileimageurlsmall = $message->profileimageurlsmall;
1258 $contact->messageid = $message->messageid;
1259 $contact->ismessaging = $message->ismessaging;
1260 $contact->sentfromcurrentuser = false;
1261 if ($message->lastmessage) {
1262 if ($message->userid !== $message->useridfrom) {
1263 $contact->sentfromcurrentuser = true;
1265 $contact->lastmessage = shorten_text($message->lastmessage, 60);
1266 } else {
1267 $contact->lastmessage = null;
1269 $contact->lastmessagedate = $message->lastmessagedate;
1270 $contact->showonlinestatus = is_null($message->isonline) ? false : true;
1271 $contact->isonline = $message->isonline;
1272 $contact->isblocked = $message->isblocked;
1273 $contact->isread = $message->isread;
1274 $contact->unreadcount = $message->unreadcount;
1275 $contact->conversationid = $message->conversationid;
1277 $data->contacts[] = $contact;
1280 return $data;
1284 * Get messagearea search messages returns.
1286 * @return external_single_structure
1287 * @since 3.2
1289 public static function data_for_messagearea_search_messages_returns() {
1290 return new external_single_structure(
1291 array(
1292 'contacts' => new external_multiple_structure(
1293 self::get_messagearea_contact_structure()
1300 * Get conversations parameters.
1302 * @return external_function_parameters
1303 * @since 3.6
1305 public static function get_conversations_parameters() {
1306 return new external_function_parameters(
1307 array(
1308 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1309 'limitfrom' => new external_value(PARAM_INT, 'The offset to start at', VALUE_DEFAULT, 0),
1310 'limitnum' => new external_value(PARAM_INT, 'Limit number of conversations to this', VALUE_DEFAULT, 0),
1311 'type' => new external_value(PARAM_INT, 'Filter by type', VALUE_DEFAULT, null),
1312 'favourites' => new external_value(PARAM_BOOL, 'Whether to restrict the results to contain NO favourite
1313 conversations (false), ONLY favourite conversation (true), or ignore any restriction altogether (null)',
1314 VALUE_DEFAULT, null),
1315 'mergeself' => new external_value(PARAM_BOOL, 'Whether to include self-conversations (true) or ONLY private
1316 conversations (false) when private conversations are requested.',
1317 VALUE_DEFAULT, false),
1323 * Get the list of conversations for the user.
1325 * @param int $userid The id of the user who is performing the search
1326 * @param int $limitfrom
1327 * @param int $limitnum
1328 * @param int|null $type
1329 * @param bool|null $favourites
1330 * @param bool $mergeself whether to include self-conversations (true) or ONLY private conversations (false)
1331 * when private conversations are requested.
1332 * @return stdClass
1333 * @throws \moodle_exception if the messaging feature is disabled on the site.
1334 * @since 3.2
1336 public static function get_conversations($userid, $limitfrom = 0, $limitnum = 0, int $type = null, bool $favourites = null,
1337 bool $mergeself = false) {
1338 global $CFG, $USER;
1340 // All the standard BL checks.
1341 if (empty($CFG->messaging)) {
1342 throw new moodle_exception('disabled', 'message');
1345 $params = array(
1346 'userid' => $userid,
1347 'limitfrom' => $limitfrom,
1348 'limitnum' => $limitnum,
1349 'type' => $type,
1350 'favourites' => $favourites,
1351 'mergeself' => $mergeself
1353 $params = self::validate_parameters(self::get_conversations_parameters(), $params);
1355 $systemcontext = context_system::instance();
1356 self::validate_context($systemcontext);
1358 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1359 throw new moodle_exception('You do not have permission to perform this action.');
1362 $conversations = \core_message\api::get_conversations(
1363 $params['userid'],
1364 $params['limitfrom'],
1365 $params['limitnum'],
1366 $params['type'],
1367 $params['favourites'],
1368 $params['mergeself']
1371 return (object) ['conversations' => $conversations];
1375 * Get conversations returns.
1377 * @return external_single_structure
1378 * @since 3.6
1380 public static function get_conversations_returns() {
1381 return new external_single_structure(
1383 'conversations' => new external_multiple_structure(
1384 self::get_conversation_structure(true)
1391 * Get conversation parameters.
1393 * @return external_function_parameters
1395 public static function get_conversation_parameters() {
1396 return new external_function_parameters(
1397 array(
1398 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1399 'conversationid' => new external_value(PARAM_INT, 'The id of the conversation to fetch'),
1400 'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1401 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1402 'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1403 'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1404 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1405 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1406 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1412 * Get a single conversation.
1414 * @param int $userid The user id to get the conversation for
1415 * @param int $conversationid The id of the conversation to fetch
1416 * @param bool $includecontactrequests Should contact requests be included between members
1417 * @param bool $includeprivacyinfo Should privacy info be included between members
1418 * @param int $memberlimit Limit number of members to load
1419 * @param int $memberoffset Offset members by this amount
1420 * @param int $messagelimit Limit number of messages to load
1421 * @param int $messageoffset Offset the messages
1422 * @param bool $newestmessagesfirst Order messages by newest first
1423 * @return stdClass
1424 * @throws \moodle_exception if the messaging feature is disabled on the site.
1426 public static function get_conversation(
1427 int $userid,
1428 int $conversationid,
1429 bool $includecontactrequests = false,
1430 bool $includeprivacyinfo = false,
1431 int $memberlimit = 0,
1432 int $memberoffset = 0,
1433 int $messagelimit = 0,
1434 int $messageoffset = 0,
1435 bool $newestmessagesfirst = true
1437 global $CFG, $DB, $USER;
1439 // All the standard BL checks.
1440 if (empty($CFG->messaging)) {
1441 throw new moodle_exception('disabled', 'message');
1444 $params = [
1445 'userid' => $userid,
1446 'conversationid' => $conversationid,
1447 'includecontactrequests' => $includecontactrequests,
1448 'includeprivacyinfo' => $includeprivacyinfo,
1449 'memberlimit' => $memberlimit,
1450 'memberoffset' => $memberoffset,
1451 'messagelimit' => $messagelimit,
1452 'messageoffset' => $messageoffset,
1453 'newestmessagesfirst' => $newestmessagesfirst
1455 self::validate_parameters(self::get_conversation_parameters(), $params);
1457 $systemcontext = context_system::instance();
1458 self::validate_context($systemcontext);
1460 $conversation = \core_message\api::get_conversation(
1461 $params['userid'],
1462 $params['conversationid'],
1463 $params['includecontactrequests'],
1464 $params['includeprivacyinfo'],
1465 $params['memberlimit'],
1466 $params['memberoffset'],
1467 $params['messagelimit'],
1468 $params['messageoffset'],
1469 $params['newestmessagesfirst']
1472 if ($conversation) {
1473 return $conversation;
1474 } else {
1475 // We have to throw an exception here because the external functions annoyingly
1476 // don't accept null to be returned for a single structure.
1477 throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1482 * Get conversation returns.
1484 * @return external_single_structure
1486 public static function get_conversation_returns() {
1487 return self::get_conversation_structure();
1491 * Get conversation parameters.
1493 * @return external_function_parameters
1495 public static function get_conversation_between_users_parameters() {
1496 return new external_function_parameters(
1497 array(
1498 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing conversations for'),
1499 'otheruserid' => new external_value(PARAM_INT, 'The other user id'),
1500 'includecontactrequests' => new external_value(PARAM_BOOL, 'Include contact requests in the members'),
1501 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'Include privacy info in the members'),
1502 'memberlimit' => new external_value(PARAM_INT, 'Limit for number of members', VALUE_DEFAULT, 0),
1503 'memberoffset' => new external_value(PARAM_INT, 'Offset for member list', VALUE_DEFAULT, 0),
1504 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1505 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1506 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1512 * Get a single conversation between users.
1514 * @param int $userid The user id to get the conversation for
1515 * @param int $otheruserid The other user id
1516 * @param bool $includecontactrequests Should contact requests be included between members
1517 * @param bool $includeprivacyinfo Should privacy info be included between members
1518 * @param int $memberlimit Limit number of members to load
1519 * @param int $memberoffset Offset members by this amount
1520 * @param int $messagelimit Limit number of messages to load
1521 * @param int $messageoffset Offset the messages
1522 * @param bool $newestmessagesfirst Order messages by newest first
1523 * @return stdClass
1524 * @throws \moodle_exception if the messaging feature is disabled on the site.
1526 public static function get_conversation_between_users(
1527 int $userid,
1528 int $otheruserid,
1529 bool $includecontactrequests = false,
1530 bool $includeprivacyinfo = false,
1531 int $memberlimit = 0,
1532 int $memberoffset = 0,
1533 int $messagelimit = 0,
1534 int $messageoffset = 0,
1535 bool $newestmessagesfirst = true
1537 global $CFG, $DB, $USER;
1539 // All the standard BL checks.
1540 if (empty($CFG->messaging)) {
1541 throw new moodle_exception('disabled', 'message');
1544 $params = [
1545 'userid' => $userid,
1546 'otheruserid' => $otheruserid,
1547 'includecontactrequests' => $includecontactrequests,
1548 'includeprivacyinfo' => $includeprivacyinfo,
1549 'memberlimit' => $memberlimit,
1550 'memberoffset' => $memberoffset,
1551 'messagelimit' => $messagelimit,
1552 'messageoffset' => $messageoffset,
1553 'newestmessagesfirst' => $newestmessagesfirst
1555 self::validate_parameters(self::get_conversation_between_users_parameters(), $params);
1557 $systemcontext = context_system::instance();
1558 self::validate_context($systemcontext);
1560 $conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']]);
1561 $conversation = null;
1563 if ($conversationid) {
1564 $conversation = \core_message\api::get_conversation(
1565 $params['userid'],
1566 $conversationid,
1567 $params['includecontactrequests'],
1568 $params['includeprivacyinfo'],
1569 $params['memberlimit'],
1570 $params['memberoffset'],
1571 $params['messagelimit'],
1572 $params['messageoffset'],
1573 $params['newestmessagesfirst']
1577 if ($conversation) {
1578 return $conversation;
1579 } else {
1580 // We have to throw an exception here because the external functions annoyingly
1581 // don't accept null to be returned for a single structure.
1582 throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1587 * Get conversation returns.
1589 * @return external_single_structure
1591 public static function get_conversation_between_users_returns() {
1592 return self::get_conversation_structure(true);
1596 * Get self-conversation parameters.
1598 * @return external_function_parameters
1600 public static function get_self_conversation_parameters() {
1601 return new external_function_parameters(
1602 array(
1603 'userid' => new external_value(PARAM_INT, 'The id of the user who we are viewing self-conversations for'),
1604 'messagelimit' => new external_value(PARAM_INT, 'Limit for number of messages', VALUE_DEFAULT, 100),
1605 'messageoffset' => new external_value(PARAM_INT, 'Offset for messages list', VALUE_DEFAULT, 0),
1606 'newestmessagesfirst' => new external_value(PARAM_BOOL, 'Order messages by newest first', VALUE_DEFAULT, true)
1612 * Get a single self-conversation.
1614 * @param int $userid The user id to get the self-conversation for
1615 * @param int $messagelimit Limit number of messages to load
1616 * @param int $messageoffset Offset the messages
1617 * @param bool $newestmessagesfirst Order messages by newest first
1618 * @return stdClass
1619 * @throws \moodle_exception if the messaging feature is disabled on the site.
1620 * @since Moodle 3.7
1622 public static function get_self_conversation(
1623 int $userid,
1624 int $messagelimit = 0,
1625 int $messageoffset = 0,
1626 bool $newestmessagesfirst = true
1628 global $CFG;
1630 // All the standard BL checks.
1631 if (empty($CFG->messaging)) {
1632 throw new moodle_exception('disabled', 'message');
1635 $params = [
1636 'userid' => $userid,
1637 'messagelimit' => $messagelimit,
1638 'messageoffset' => $messageoffset,
1639 'newestmessagesfirst' => $newestmessagesfirst
1641 self::validate_parameters(self::get_self_conversation_parameters(), $params);
1643 $systemcontext = context_system::instance();
1644 self::validate_context($systemcontext);
1646 $conversation = \core_message\api::get_self_conversation($params['userid']);
1648 if ($conversation) {
1649 $conversation = \core_message\api::get_conversation(
1650 $params['userid'],
1651 $conversation->id,
1652 false,
1653 false,
1656 $params['messagelimit'],
1657 $params['messageoffset'],
1658 $params['newestmessagesfirst']
1662 if ($conversation) {
1663 return $conversation;
1664 } else {
1665 // We have to throw an exception here because the external functions annoyingly
1666 // don't accept null to be returned for a single structure.
1667 throw new \moodle_exception('errorconversationdoesnotexist', 'message');
1672 * Get conversation returns.
1674 * @return external_single_structure
1676 public static function get_self_conversation_returns() {
1677 return self::get_conversation_structure();
1681 * The conversation messages parameters.
1683 * @return external_function_parameters
1684 * @since 3.6
1686 public static function get_conversation_messages_parameters() {
1687 return new external_function_parameters(
1688 array(
1689 'currentuserid' => new external_value(PARAM_INT, 'The current user\'s id'),
1690 'convid' => new external_value(PARAM_INT, 'The conversation id'),
1691 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1692 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0),
1693 'newest' => new external_value(PARAM_BOOL, 'Newest first?', VALUE_DEFAULT, false),
1694 'timefrom' => new external_value(PARAM_INT,
1695 'The timestamp from which the messages were created', VALUE_DEFAULT, 0),
1701 * Get conversation messages.
1703 * @param int $currentuserid The current user's id.
1704 * @param int $convid The conversation id.
1705 * @param int $limitfrom Return a subset of records, starting at this point (optional).
1706 * @param int $limitnum Return a subset comprising this many records in total (optional, required if $limitfrom is set).
1707 * @param bool $newest True for getting first newest messages, false otherwise.
1708 * @param int $timefrom The time from the conversation messages to get.
1709 * @return array The messages and members who have sent some of these messages.
1710 * @throws moodle_exception
1711 * @since 3.6
1713 public static function get_conversation_messages(int $currentuserid, int $convid, int $limitfrom = 0, int $limitnum = 0,
1714 bool $newest = false, int $timefrom = 0) {
1715 global $CFG, $USER;
1717 // Check if messaging is enabled.
1718 if (empty($CFG->messaging)) {
1719 throw new moodle_exception('disabled', 'message');
1722 $systemcontext = context_system::instance();
1724 $params = array(
1725 'currentuserid' => $currentuserid,
1726 'convid' => $convid,
1727 'limitfrom' => $limitfrom,
1728 'limitnum' => $limitnum,
1729 'newest' => $newest,
1730 'timefrom' => $timefrom,
1732 $params = self::validate_parameters(self::get_conversation_messages_parameters(), $params);
1733 self::validate_context($systemcontext);
1735 if (($USER->id != $params['currentuserid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1736 throw new moodle_exception('You do not have permission to perform this action.');
1739 // Check that the user belongs to the conversation.
1740 if (!\core_message\api::is_user_in_conversation($params['currentuserid'], $params['convid'])) {
1741 throw new moodle_exception('User is not part of conversation.');
1744 $sort = $newest ? 'timecreated DESC' : 'timecreated ASC';
1746 // We need to enforce a one second delay on messages to avoid race conditions of current
1747 // messages still being sent.
1749 // There is a chance that we could request messages before the current time's
1750 // second has elapsed and while other messages are being sent in that same second. In which
1751 // case those messages will be lost.
1753 // Instead we ignore the current time in the result set to ensure that second is allowed to finish.
1754 $timeto = empty($params['timefrom']) ? 0 : time() - 1;
1756 // No requesting messages from the current time, as stated above.
1757 if ($params['timefrom'] == time()) {
1758 $messages = [];
1759 } else {
1760 $messages = \core_message\api::get_conversation_messages(
1761 $params['currentuserid'],
1762 $params['convid'],
1763 $params['limitfrom'],
1764 $params['limitnum'],
1765 $sort,
1766 $params['timefrom'],
1767 $timeto);
1770 return $messages;
1774 * The messagearea messages return structure.
1776 * @return external_single_structure
1777 * @since 3.6
1779 public static function get_conversation_messages_returns() {
1780 return new external_single_structure(
1781 array(
1782 'id' => new external_value(PARAM_INT, 'The conversation id'),
1783 'members' => new external_multiple_structure(
1784 self::get_conversation_member_structure()
1786 'messages' => new external_multiple_structure(
1787 self::get_conversation_message_structure()
1794 * The user contacts return parameters.
1796 * @return external_function_parameters
1798 public static function get_user_contacts_parameters() {
1799 return new external_function_parameters(
1800 array(
1801 'userid' => new external_value(PARAM_INT, 'The id of the user who we retrieving the contacts for'),
1802 'limitfrom' => new external_value(PARAM_INT, 'Limit from', VALUE_DEFAULT, 0),
1803 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 0)
1809 * Get user contacts.
1811 * @param int $userid The id of the user who we are viewing conversations for
1812 * @param int $limitfrom
1813 * @param int $limitnum
1814 * @return array
1815 * @throws moodle_exception
1817 public static function get_user_contacts(int $userid, int $limitfrom = 0, int $limitnum = 0) {
1818 global $CFG, $USER;
1820 // Check if messaging is enabled.
1821 if (empty($CFG->messaging)) {
1822 throw new moodle_exception('disabled', 'message');
1825 $systemcontext = context_system::instance();
1827 $params = array(
1828 'userid' => $userid,
1829 'limitfrom' => $limitfrom,
1830 'limitnum' => $limitnum
1832 $params = self::validate_parameters(self::get_user_contacts_parameters(), $params);
1833 self::validate_context($systemcontext);
1835 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
1836 throw new moodle_exception('You do not have permission to perform this action.');
1839 return \core_message\api::get_user_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
1843 * The user contacts return structure.
1845 * @return external_multiple_structure
1847 public static function get_user_contacts_returns() {
1848 return new external_multiple_structure(
1849 self::get_conversation_member_structure()
1854 * Search contacts parameters description.
1856 * @return external_function_parameters
1857 * @since Moodle 2.5
1859 public static function search_contacts_parameters() {
1860 return new external_function_parameters(
1861 array(
1862 'searchtext' => new external_value(PARAM_CLEAN, 'String the user\'s fullname has to match to be found'),
1863 'onlymycourses' => new external_value(PARAM_BOOL, 'Limit search to the user\'s courses',
1864 VALUE_DEFAULT, false)
1870 * Search contacts.
1872 * @param string $searchtext query string.
1873 * @param bool $onlymycourses limit the search to the user's courses only.
1874 * @return \core_external\external_description
1875 * @since Moodle 2.5
1877 public static function search_contacts($searchtext, $onlymycourses = false) {
1878 global $CFG, $USER, $PAGE;
1879 require_once($CFG->dirroot . '/user/lib.php');
1881 // Check if messaging is enabled.
1882 if (empty($CFG->messaging)) {
1883 throw new moodle_exception('disabled', 'message');
1886 require_once($CFG->libdir . '/enrollib.php');
1888 $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses);
1889 $params = self::validate_parameters(self::search_contacts_parameters(), $params);
1891 // Extra validation, we do not allow empty queries.
1892 if ($params['searchtext'] === '') {
1893 throw new moodle_exception('querystringcannotbeempty');
1896 $courseids = array();
1897 if ($params['onlymycourses']) {
1898 $mycourses = enrol_get_my_courses(array('id'));
1899 foreach ($mycourses as $mycourse) {
1900 $courseids[] = $mycourse->id;
1902 } else {
1903 $courseids[] = SITEID;
1906 // Retrieving the users matching the query.
1907 $users = message_search_users($courseids, $params['searchtext']);
1908 $results = array();
1909 foreach ($users as $user) {
1910 $results[$user->id] = $user;
1913 // Reorganising information.
1914 foreach ($results as &$user) {
1915 $newuser = array(
1916 'id' => $user->id,
1917 'fullname' => fullname($user)
1920 // Avoid undefined property notice as phone not specified.
1921 $user->phone1 = null;
1922 $user->phone2 = null;
1924 $userpicture = new user_picture($user);
1925 $userpicture->size = 1; // Size f1.
1926 $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
1927 $userpicture->size = 0; // Size f2.
1928 $newuser['profileimageurlsmall'] = $userpicture->get_url($PAGE)->out(false);
1930 $user = $newuser;
1933 return $results;
1937 * Search contacts return description.
1939 * @return \core_external\external_description
1940 * @since Moodle 2.5
1942 public static function search_contacts_returns() {
1943 return new external_multiple_structure(
1944 new external_single_structure(
1945 array(
1946 'id' => new external_value(PARAM_INT, 'User ID'),
1947 'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
1948 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL),
1949 'profileimageurlsmall' => new external_value(PARAM_URL, 'Small user picture URL', VALUE_OPTIONAL)
1952 'List of contacts'
1957 * Get messages parameters description.
1959 * @return external_function_parameters
1960 * @since 2.8
1962 public static function get_messages_parameters() {
1963 return new external_function_parameters(
1964 array(
1965 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
1966 'useridfrom' => new external_value(
1967 PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
1968 VALUE_DEFAULT, 0),
1969 'type' => new external_value(
1970 PARAM_ALPHA, 'type of message to return, expected values are: notifications, conversations and both',
1971 VALUE_DEFAULT, 'both'),
1972 'read' => new external_value(PARAM_INT, '1 for getting read messages, 0 for unread, 2 for both',
1973 VALUE_DEFAULT, 1),
1974 'newestfirst' => new external_value(
1975 PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
1976 VALUE_DEFAULT, true),
1977 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
1978 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
1984 * Get messages function implementation.
1986 * @since 2.8
1987 * @throws invalid_parameter_exception
1988 * @throws moodle_exception
1989 * @param int $useridto the user id who received the message
1990 * @param int $useridfrom the user id who send the message. -10 or -20 for no-reply or support user
1991 * @param string $type type of message to return, expected values: notifications, conversations and both
1992 * @param int $read 1 for getting read messages, 0 for unread, 2 for both
1993 * @param bool $newestfirst true for ordering by newest first, false for oldest first
1994 * @param int $limitfrom limit from
1995 * @param int $limitnum limit num
1996 * @return \core_external\external_description
1998 public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = MESSAGE_GET_READ,
1999 $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
2000 global $CFG, $USER, $PAGE;
2002 $warnings = array();
2004 $params = array(
2005 'useridto' => $useridto,
2006 'useridfrom' => $useridfrom,
2007 'type' => $type,
2008 'read' => $read,
2009 'newestfirst' => $newestfirst,
2010 'limitfrom' => $limitfrom,
2011 'limitnum' => $limitnum
2014 $params = self::validate_parameters(self::get_messages_parameters(), $params);
2016 $context = context_system::instance();
2017 self::validate_context($context);
2019 $useridto = $params['useridto'];
2020 $useridfrom = $params['useridfrom'];
2021 $type = $params['type'];
2022 $read = $params['read'];
2023 $newestfirst = $params['newestfirst'];
2024 $limitfrom = $params['limitfrom'];
2025 $limitnum = $params['limitnum'];
2027 $allowedvalues = array('notifications', 'conversations', 'both');
2028 if (!in_array($type, $allowedvalues)) {
2029 throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
2030 'allowed values are: ' . implode(',', $allowedvalues));
2033 // Check if private messaging between users is allowed.
2034 if (empty($CFG->messaging)) {
2035 // If we are retreiving only conversations, and messaging is disabled, throw an exception.
2036 if ($type == "conversations") {
2037 throw new moodle_exception('disabled', 'message');
2039 if ($type == "both") {
2040 $warning = array();
2041 $warning['item'] = 'message';
2042 $warning['itemid'] = $USER->id;
2043 $warning['warningcode'] = '1';
2044 $warning['message'] = 'Private messages (conversations) are not enabled in this site.
2045 Only notifications will be returned';
2046 $warnings[] = $warning;
2050 if (!empty($useridto)) {
2051 if (core_user::is_real_user($useridto)) {
2052 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2053 } else {
2054 throw new moodle_exception('invaliduser');
2058 if (!empty($useridfrom)) {
2059 // We use get_user here because the from user can be the noreply or support user.
2060 $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2063 // Check if the current user is the sender/receiver or just a privileged user.
2064 if ($useridto != $USER->id and $useridfrom != $USER->id and
2065 !has_capability('moodle/site:readallmessages', $context)) {
2066 throw new moodle_exception('accessdenied', 'admin');
2069 // Which type of messages to retrieve.
2070 $notifications = -1;
2071 if ($type != 'both') {
2072 $notifications = ($type == 'notifications') ? 1 : 0;
2075 $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2076 $sort = "mr.timecreated $orderdirection";
2078 if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2079 $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2081 // In some cases, we don't need to get the to/from user objects from the sql query.
2082 $userfromfullname = '';
2083 $usertofullname = '';
2085 // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
2086 if (!empty($useridto)) {
2087 $usertofullname = fullname($userto, $canviewfullname);
2088 // The user from may or may not be filled.
2089 if (!empty($useridfrom)) {
2090 $userfromfullname = fullname($userfrom, $canviewfullname);
2092 } else {
2093 // If the useridto field is empty, the useridfrom must be filled.
2094 $userfromfullname = fullname($userfrom, $canviewfullname);
2096 foreach ($messages as $mid => $message) {
2098 if (!$message->notification) {
2099 // Do not return deleted messages.
2100 if (($useridto == $USER->id and $message->timeusertodeleted) or
2101 ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
2102 unset($messages[$mid]);
2103 continue;
2105 } else {
2106 // Return iconurl for notifications.
2107 if (!isset($output)) {
2108 $output = $PAGE->get_renderer('core');
2111 if (!empty($message->component) && substr($message->component, 0, 4) == 'mod_') {
2112 $iconurl = $output->image_url('monologo', $message->component);
2113 } else {
2114 $iconurl = $output->image_url('i/marker', 'core');
2117 $message->iconurl = clean_param($iconurl->out(), PARAM_URL);
2120 // We need to get the user from the query.
2121 if (empty($userfromfullname)) {
2122 // Check for non-reply and support users.
2123 if (core_user::is_real_user($message->useridfrom)) {
2124 $user = new stdClass();
2125 $user = username_load_fields_from_object($user, $message, 'userfrom');
2126 $message->userfromfullname = fullname($user, $canviewfullname);
2127 } else {
2128 $user = core_user::get_user($message->useridfrom);
2129 $message->userfromfullname = fullname($user, $canviewfullname);
2131 } else {
2132 $message->userfromfullname = $userfromfullname;
2135 // We need to get the user from the query.
2136 if (empty($usertofullname)) {
2137 $user = new stdClass();
2138 $user = username_load_fields_from_object($user, $message, 'userto');
2139 $message->usertofullname = fullname($user, $canviewfullname);
2140 } else {
2141 $message->usertofullname = $usertofullname;
2144 // Clean subject of html.
2145 $message->subject = clean_param($message->subject, PARAM_TEXT);
2146 $message->text = message_format_message_text($message);
2147 $messages[$mid] = (array) $message;
2151 $results = array(
2152 'messages' => $messages,
2153 'warnings' => $warnings
2156 return $results;
2160 * Get messages return description.
2162 * @return external_single_structure
2163 * @since 2.8
2165 public static function get_messages_returns() {
2166 return new external_single_structure(
2167 array(
2168 'messages' => new external_multiple_structure(
2169 new external_single_structure(
2170 array(
2171 'id' => new external_value(PARAM_INT, 'Message id'),
2172 'useridfrom' => new external_value(PARAM_INT, 'User from id'),
2173 'useridto' => new external_value(PARAM_INT, 'User to id'),
2174 'subject' => new external_value(PARAM_TEXT, 'The message subject'),
2175 'text' => new external_value(PARAM_RAW, 'The message text formated'),
2176 'fullmessage' => new external_value(PARAM_RAW, 'The message'),
2177 'fullmessageformat' => new external_format_value('fullmessage'),
2178 'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
2179 'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
2180 'notification' => new external_value(PARAM_INT, 'Is a notification?'),
2181 'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
2182 'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
2183 'timecreated' => new external_value(PARAM_INT, 'Time created'),
2184 'timeread' => new external_value(PARAM_INT, 'Time read'),
2185 'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
2186 'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name'),
2187 'component' => new external_value(PARAM_TEXT, 'The component that generated the notification',
2188 VALUE_OPTIONAL),
2189 'eventtype' => new external_value(PARAM_TEXT, 'The type of notification', VALUE_OPTIONAL),
2190 'customdata' => new external_value(PARAM_RAW, 'Custom data to be passed to the message processor.
2191 The data here is serialised using json_encode().', VALUE_OPTIONAL),
2192 'iconurl' => new external_value(PARAM_URL, 'URL for icon, only for notifications.', VALUE_OPTIONAL),
2193 ), 'message'
2196 'warnings' => new external_warnings()
2202 * Mark all notifications as read parameters description.
2204 * @return external_function_parameters
2205 * @since 3.2
2207 public static function mark_all_notifications_as_read_parameters() {
2208 return new external_function_parameters(
2209 array(
2210 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2211 'useridfrom' => new external_value(
2212 PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2213 VALUE_DEFAULT, 0),
2214 'timecreatedto' => new external_value(
2215 PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
2216 VALUE_DEFAULT, 0),
2222 * Mark all notifications as read function.
2224 * @since 3.2
2225 * @throws invalid_parameter_exception
2226 * @throws moodle_exception
2227 * @param int $useridto the user id who received the message
2228 * @param int $useridfrom the user id who send the message. -10 or -20 for no-reply or support user
2229 * @param int $timecreatedto mark message created before this time as read, 0 for all messages
2230 * @return \core_external\external_description
2232 public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
2233 global $USER;
2235 $params = self::validate_parameters(
2236 self::mark_all_notifications_as_read_parameters(),
2237 array(
2238 'useridto' => $useridto,
2239 'useridfrom' => $useridfrom,
2240 'timecreatedto' => $timecreatedto,
2244 $context = context_system::instance();
2245 self::validate_context($context);
2247 $useridto = $params['useridto'];
2248 $useridfrom = $params['useridfrom'];
2249 $timecreatedto = $params['timecreatedto'];
2251 if (!empty($useridto)) {
2252 if (core_user::is_real_user($useridto)) {
2253 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2254 } else {
2255 throw new moodle_exception('invaliduser');
2259 if (!empty($useridfrom)) {
2260 // We use get_user here because the from user can be the noreply or support user.
2261 $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2264 // Check if the current user is the sender/receiver or just a privileged user.
2265 if ($useridto != $USER->id and $useridfrom != $USER->id and
2266 // The deleteanymessage cap seems more reasonable here than readallmessages.
2267 !has_capability('moodle/site:deleteanymessage', $context)) {
2268 throw new moodle_exception('accessdenied', 'admin');
2271 \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
2273 return true;
2277 * Mark all notifications as read return description.
2279 * @return external_single_structure
2280 * @since 3.2
2282 public static function mark_all_notifications_as_read_returns() {
2283 return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
2287 * Get unread conversations count parameters description.
2289 * @return external_function_parameters
2290 * @since 3.2
2292 public static function get_unread_conversations_count_parameters() {
2293 return new external_function_parameters(
2294 array(
2295 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2301 * Get unread messages count function.
2303 * @since 3.2
2304 * @throws invalid_parameter_exception
2305 * @throws moodle_exception
2306 * @param int $useridto the user id who received the message
2307 * @return \core_external\external_description
2309 public static function get_unread_conversations_count($useridto) {
2310 global $USER, $CFG;
2312 // Check if messaging is enabled.
2313 if (empty($CFG->messaging)) {
2314 throw new moodle_exception('disabled', 'message');
2317 $params = self::validate_parameters(
2318 self::get_unread_conversations_count_parameters(),
2319 array('useridto' => $useridto)
2322 $context = context_system::instance();
2323 self::validate_context($context);
2325 $useridto = $params['useridto'];
2327 if (!empty($useridto)) {
2328 if (core_user::is_real_user($useridto)) {
2329 $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2330 } else {
2331 throw new moodle_exception('invaliduser');
2333 } else {
2334 $useridto = $USER->id;
2337 // Check if the current user is the receiver or just a privileged user.
2338 if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
2339 throw new moodle_exception('accessdenied', 'admin');
2342 return \core_message\api::count_unread_conversations($userto);
2346 * Get unread conversations count return description.
2348 * @return external_single_structure
2349 * @since 3.2
2351 public static function get_unread_conversations_count_returns() {
2352 return new external_value(PARAM_INT, 'The count of unread messages for the user');
2356 * Get blocked users parameters description.
2358 * @return external_function_parameters
2359 * @since 2.9
2361 public static function get_blocked_users_parameters() {
2362 return new external_function_parameters(
2363 array(
2364 'userid' => new external_value(PARAM_INT,
2365 'the user whose blocked users we want to retrieve',
2366 VALUE_REQUIRED),
2372 * Retrieve a list of users blocked
2374 * @param int $userid the user whose blocked users we want to retrieve
2375 * @return \core_external\external_description
2376 * @since 2.9
2378 public static function get_blocked_users($userid) {
2379 global $CFG, $USER, $PAGE;
2381 // Warnings array, it can be empty at the end but is mandatory.
2382 $warnings = array();
2384 // Validate params.
2385 $params = array(
2386 'userid' => $userid
2388 $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2389 $userid = $params['userid'];
2391 // Validate context.
2392 $context = context_system::instance();
2393 self::validate_context($context);
2395 // Check if private messaging between users is allowed.
2396 if (empty($CFG->messaging)) {
2397 throw new moodle_exception('disabled', 'message');
2400 $user = core_user::get_user($userid, '*', MUST_EXIST);
2401 core_user::require_active_user($user);
2403 // Check if we have permissions for retrieve the information.
2404 $capability = 'moodle/site:manageallmessaging';
2405 if (($USER->id != $userid) && !has_capability($capability, $context)) {
2406 throw new required_capability_exception($context, $capability, 'nopermissions', '');
2409 // Now, we can get safely all the blocked users.
2410 $users = \core_message\api::get_blocked_users($user->id);
2412 $blockedusers = array();
2413 foreach ($users as $user) {
2414 $newuser = array(
2415 'id' => $user->id,
2416 'fullname' => fullname($user),
2419 $userpicture = new user_picture($user);
2420 $userpicture->size = 1; // Size f1.
2421 $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2423 $blockedusers[] = $newuser;
2426 $results = array(
2427 'users' => $blockedusers,
2428 'warnings' => $warnings
2430 return $results;
2434 * Get blocked users return description.
2436 * @return external_single_structure
2437 * @since 2.9
2439 public static function get_blocked_users_returns() {
2440 return new external_single_structure(
2441 array(
2442 'users' => new external_multiple_structure(
2443 new external_single_structure(
2444 array(
2445 'id' => new external_value(PARAM_INT, 'User ID'),
2446 'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2447 'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
2450 'List of blocked users'
2452 'warnings' => new external_warnings()
2458 * Returns description of method parameters
2460 * @return external_function_parameters
2461 * @since 2.9
2463 public static function mark_message_read_parameters() {
2464 return new external_function_parameters(
2465 array(
2466 'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
2467 'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
2468 VALUE_DEFAULT, 0)
2474 * Mark a single message as read, trigger message_viewed event
2476 * @param int $messageid id of the message (in the message table)
2477 * @param int $timeread timestamp for when the message should be marked read
2478 * @return \core_external\external_description
2479 * @throws invalid_parameter_exception
2480 * @throws moodle_exception
2481 * @since 2.9
2483 public static function mark_message_read($messageid, $timeread) {
2484 global $CFG, $DB, $USER;
2486 // Check if private messaging between users is allowed.
2487 if (empty($CFG->messaging)) {
2488 throw new moodle_exception('disabled', 'message');
2491 // Warnings array, it can be empty at the end but is mandatory.
2492 $warnings = array();
2494 // Validate params.
2495 $params = array(
2496 'messageid' => $messageid,
2497 'timeread' => $timeread
2499 $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2501 if (empty($params['timeread'])) {
2502 $timeread = time();
2503 } else {
2504 $timeread = $params['timeread'];
2507 // Validate context.
2508 $context = context_system::instance();
2509 self::validate_context($context);
2511 $sql = "SELECT m.*, mcm.userid as useridto
2512 FROM {messages} m
2513 INNER JOIN {message_conversations} mc
2514 ON m.conversationid = mc.id
2515 INNER JOIN {message_conversation_members} mcm
2516 ON mcm.conversationid = mc.id
2517 LEFT JOIN {message_user_actions} mua
2518 ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
2519 WHERE mua.id is NULL
2520 AND mcm.userid != m.useridfrom
2521 AND m.id = ?";
2522 $messageparams = [];
2523 $messageparams[] = $USER->id;
2524 $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
2525 $messageparams[] = $params['messageid'];
2526 $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
2528 if ($message->useridto != $USER->id) {
2529 throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
2532 \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2534 $results = array(
2535 'messageid' => $message->id,
2536 'warnings' => $warnings
2538 return $results;
2542 * Returns description of method result value
2544 * @return \core_external\external_description
2545 * @since 2.9
2547 public static function mark_message_read_returns() {
2548 return new external_single_structure(
2549 array(
2550 'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
2551 'warnings' => new external_warnings()
2557 * Returns description of method parameters
2559 * @return external_function_parameters
2561 public static function mark_notification_read_parameters() {
2562 return new external_function_parameters(
2563 array(
2564 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2565 'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
2566 VALUE_DEFAULT, 0)
2572 * Mark a single notification as read.
2574 * This will trigger a 'notification_viewed' event.
2576 * @param int $notificationid id of the notification
2577 * @param int $timeread timestamp for when the notification should be marked read
2578 * @return \core_external\external_description
2579 * @throws invalid_parameter_exception
2580 * @throws moodle_exception
2582 public static function mark_notification_read($notificationid, $timeread) {
2583 global $CFG, $DB, $USER;
2585 // Warnings array, it can be empty at the end but is mandatory.
2586 $warnings = array();
2588 // Validate params.
2589 $params = array(
2590 'notificationid' => $notificationid,
2591 'timeread' => $timeread
2593 $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
2595 if (empty($params['timeread'])) {
2596 $timeread = time();
2597 } else {
2598 $timeread = $params['timeread'];
2601 // Validate context.
2602 $context = context_system::instance();
2603 self::validate_context($context);
2605 $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
2607 if ($notification->useridto != $USER->id) {
2608 throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
2609 'notification as read');
2612 \core_message\api::mark_notification_as_read($notification, $timeread);
2614 $results = array(
2615 'notificationid' => $notification->id,
2616 'warnings' => $warnings
2619 return $results;
2623 * Returns description of method result value
2625 * @return \core_external\external_description
2627 public static function mark_notification_read_returns() {
2628 return new external_single_structure(
2629 array(
2630 'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2631 'warnings' => new external_warnings()
2637 * Mark all conversation messages as read parameters description.
2639 * @return external_function_parameters
2640 * @since 3.6
2642 public static function mark_all_conversation_messages_as_read_parameters() {
2643 return new external_function_parameters(
2644 array(
2645 'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
2646 'conversationid' =>
2647 new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
2653 * Mark all conversation messages as read function.
2655 * @param int $userid The user id of who we want to delete the conversation for
2656 * @param int $conversationid The id of the conversations
2657 * @since 3.6
2659 public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
2660 global $CFG;
2662 // Check if messaging is enabled.
2663 if (empty($CFG->messaging)) {
2664 throw new moodle_exception('disabled', 'message');
2667 $params = array(
2668 'userid' => $userid,
2669 'conversationid' => $conversationid,
2671 $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
2673 $context = context_system::instance();
2674 self::validate_context($context);
2676 $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2677 core_user::require_active_user($user);
2679 if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
2680 \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
2681 } else {
2682 throw new moodle_exception('accessdenied', 'admin');
2687 * Mark all conversation messages as read return description.
2689 * @return external_warnings
2690 * @since 3.6
2692 public static function mark_all_conversation_messages_as_read_returns() {
2693 return null;
2697 * Returns description of method parameters.
2699 * @return external_function_parameters
2700 * @since 3.6
2702 public static function delete_conversations_by_id_parameters() {
2703 return new external_function_parameters(
2704 array(
2705 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
2706 'conversationids' => new external_multiple_structure(
2707 new external_value(PARAM_INT, 'The id of the conversation'),
2708 'List of conversation IDs'
2715 * Deletes a conversation.
2717 * @param int $userid The user id of who we want to delete the conversation for
2718 * @param int[] $conversationids The ids of the conversations
2719 * @return array
2720 * @throws moodle_exception
2721 * @since 3.6
2723 public static function delete_conversations_by_id($userid, array $conversationids) {
2724 global $CFG;
2726 // Check if private messaging between users is allowed.
2727 if (empty($CFG->messaging)) {
2728 throw new moodle_exception('disabled', 'message');
2731 // Validate params.
2732 $params = [
2733 'userid' => $userid,
2734 'conversationids' => $conversationids,
2736 $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
2738 // Validate context.
2739 $context = context_system::instance();
2740 self::validate_context($context);
2742 $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2743 core_user::require_active_user($user);
2745 foreach ($params['conversationids'] as $conversationid) {
2746 if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
2747 \core_message\api::delete_conversation_by_id($user->id, $conversationid);
2748 } else {
2749 throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
2753 return [];
2757 * Returns description of method result value.
2759 * @return \core_external\external_description
2760 * @since 3.6
2762 public static function delete_conversations_by_id_returns() {
2763 return new external_warnings();
2767 * Returns description of method parameters
2769 * @return external_function_parameters
2770 * @since 3.1
2772 public static function delete_message_parameters() {
2773 return new external_function_parameters(
2774 array(
2775 'messageid' => new external_value(PARAM_INT, 'The message id'),
2776 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
2777 'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
2783 * Deletes a message
2785 * @param int $messageid the message id
2786 * @param int $userid the user id of who we want to delete the message for
2787 * @param bool $read if is a message read (default to true)
2788 * @return \core_external\external_description
2789 * @throws moodle_exception
2790 * @since 3.1
2792 public static function delete_message($messageid, $userid, $read = true) {
2793 global $CFG;
2795 // Check if private messaging between users is allowed.
2796 if (empty($CFG->messaging)) {
2797 throw new moodle_exception('disabled', 'message');
2800 // Warnings array, it can be empty at the end but is mandatory.
2801 $warnings = array();
2803 // Validate params.
2804 $params = array(
2805 'messageid' => $messageid,
2806 'userid' => $userid,
2807 'read' => $read
2809 $params = self::validate_parameters(self::delete_message_parameters(), $params);
2811 // Validate context.
2812 $context = context_system::instance();
2813 self::validate_context($context);
2815 $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2816 core_user::require_active_user($user);
2818 if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
2819 $status = \core_message\api::delete_message($user->id, $params['messageid']);
2820 } else {
2821 throw new moodle_exception('You do not have permission to delete this message');
2824 $results = array(
2825 'status' => $status,
2826 'warnings' => $warnings
2828 return $results;
2832 * Returns description of method result value
2834 * @return \core_external\external_description
2835 * @since 3.1
2837 public static function delete_message_returns() {
2838 return new external_single_structure(
2839 array(
2840 'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
2841 'warnings' => new external_warnings()
2847 * Returns description of method parameters
2849 * @return external_function_parameters
2850 * @since 3.2
2852 public static function message_processor_config_form_parameters() {
2853 return new external_function_parameters(
2854 array(
2855 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
2856 'name' => new external_value(PARAM_SAFEDIR, 'The name of the message processor'),
2857 'formvalues' => new external_multiple_structure(
2858 new external_single_structure(
2859 array(
2860 'name' => new external_value(PARAM_TEXT, 'name of the form element', VALUE_REQUIRED),
2861 'value' => new external_value(PARAM_RAW, 'value of the form element', VALUE_REQUIRED),
2864 'Config form values',
2865 VALUE_REQUIRED
2872 * Processes a message processor config form.
2874 * @param int $userid the user id
2875 * @param string $name the name of the processor
2876 * @param array $formvalues the form values
2877 * @return \core_external\external_description
2878 * @throws moodle_exception
2879 * @since 3.2
2881 public static function message_processor_config_form($userid, $name, $formvalues) {
2882 global $USER, $CFG;
2884 $params = self::validate_parameters(
2885 self::message_processor_config_form_parameters(),
2886 array(
2887 'userid' => $userid,
2888 'name' => $name,
2889 'formvalues' => $formvalues,
2893 $user = self::validate_preferences_permissions($params['userid']);
2895 $processor = get_message_processor($params['name']);
2896 $preferences = [];
2897 $form = new stdClass();
2899 foreach ($params['formvalues'] as $formvalue) {
2900 // Curly braces to ensure interpretation is consistent between
2901 // php 5 and php 7.
2902 $form->{$formvalue['name']} = $formvalue['value'];
2905 $processor->process_form($form, $preferences);
2907 if (!empty($preferences)) {
2908 set_user_preferences($preferences, $params['userid']);
2913 * Returns description of method result value
2915 * @return \core_external\external_description
2916 * @since 3.2
2918 public static function message_processor_config_form_returns() {
2919 return null;
2923 * Returns description of method parameters
2925 * @return external_function_parameters
2926 * @since 3.2
2928 public static function get_message_processor_parameters() {
2929 return new external_function_parameters(
2930 array(
2931 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user'),
2932 'name' => new external_value(PARAM_SAFEDIR, 'The name of the message processor', VALUE_REQUIRED),
2938 * Get a message processor.
2940 * @param int $userid
2941 * @param string $name the name of the processor
2942 * @return \core_external\external_description
2943 * @throws moodle_exception
2944 * @since 3.2
2946 public static function get_message_processor($userid, $name) {
2947 global $USER, $PAGE, $CFG;
2949 // Check if messaging is enabled.
2950 if (empty($CFG->messaging)) {
2951 throw new moodle_exception('disabled', 'message');
2954 $params = self::validate_parameters(
2955 self::get_message_processor_parameters(),
2956 array(
2957 'userid' => $userid,
2958 'name' => $name,
2962 if (empty($params['userid'])) {
2963 $params['userid'] = $USER->id;
2966 $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2967 core_user::require_active_user($user);
2968 self::validate_context(context_user::instance($params['userid']));
2970 $processor = get_message_processor($params['name']);
2972 $processoroutput = new \core_message\output\processor($processor, $user);
2973 $renderer = $PAGE->get_renderer('core_message');
2975 return $processoroutput->export_for_template($renderer);
2979 * Returns description of method result value
2981 * @return \core_external\external_description
2982 * @since 3.2
2984 public static function get_message_processor_returns() {
2985 return new external_function_parameters(
2986 array(
2987 'systemconfigured' => new external_value(PARAM_BOOL, 'Site configuration status'),
2988 'userconfigured' => new external_value(PARAM_BOOL, 'The user configuration status'),
2994 * Check that the user has enough permission to retrieve message or notifications preferences.
2996 * @param int $userid the user id requesting the preferences
2997 * @return stdClass full user object
2998 * @throws moodle_exception
2999 * @since Moodle 3.2
3001 protected static function validate_preferences_permissions($userid) {
3002 global $USER;
3004 if (empty($userid)) {
3005 $user = $USER;
3006 } else {
3007 $user = core_user::get_user($userid, '*', MUST_EXIST);
3008 core_user::require_active_user($user);
3011 $systemcontext = context_system::instance();
3012 self::validate_context($systemcontext);
3014 // Check access control.
3015 if ($user->id == $USER->id) {
3016 // Editing own message profile.
3017 require_capability('moodle/user:editownmessageprofile', $systemcontext);
3018 } else {
3019 // Teachers, parents, etc.
3020 $personalcontext = context_user::instance($user->id);
3021 require_capability('moodle/user:editmessageprofile', $personalcontext);
3023 return $user;
3027 * Returns a notification or message preference structure.
3029 * @return external_single_structure the structure
3030 * @since Moodle 3.2
3031 * @todo Remove loggedin and loggedoff from processors structure on MDL-73284.
3033 protected static function get_preferences_structure() {
3034 return new external_single_structure(
3035 array(
3036 'userid' => new external_value(PARAM_INT, 'User id'),
3037 'disableall' => new external_value(PARAM_INT, 'Whether all the preferences are disabled'),
3038 'processors' => new external_multiple_structure(
3039 new external_single_structure(
3040 array(
3041 'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3042 'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3043 'hassettings' => new external_value(PARAM_BOOL, 'Whether has settings'),
3044 'contextid' => new external_value(PARAM_INT, 'Context id'),
3045 'userconfigured' => new external_value(PARAM_INT, 'Whether is configured by the user'),
3048 'Config form values'
3050 'components' => new external_multiple_structure(
3051 new external_single_structure(
3052 array(
3053 'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3054 'notifications' => new external_multiple_structure(
3055 new external_single_structure(
3056 array(
3057 'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3058 'preferencekey' => new external_value(PARAM_ALPHANUMEXT, 'Preference key'),
3059 'processors' => new external_multiple_structure(
3060 new external_single_structure(
3061 array(
3062 'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3063 'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3064 'locked' => new external_value(PARAM_BOOL, 'Is locked by admin?'),
3065 'lockedmessage' => new external_value(PARAM_TEXT,
3066 'Text to display if locked', VALUE_OPTIONAL),
3067 'userconfigured' => new external_value(PARAM_INT, 'Is configured?'),
3068 'loggedin' => new external_single_structure(
3069 array(
3070 'name' => new external_value(PARAM_NOTAGS, 'Name'),
3071 'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3072 'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3074 'DEPRECATED ATTRIBUTE -
3075 Kept for backward compatibility, use enabled instead.',
3077 'loggedoff' => new external_single_structure(
3078 array(
3079 'name' => new external_value(PARAM_NOTAGS, 'Name'),
3080 'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3081 'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3083 'DEPRECATED ATTRIBUTE -
3084 Kept for backward compatibility, use enabled instead.',
3086 'enabled' => new external_value(PARAM_BOOL, 'Is enabled?'),
3089 'Processors values for this notification'
3093 'List of notificaitons for the component'
3097 'Available components'
3104 * Returns description of method parameters
3106 * @return external_function_parameters
3107 * @since 3.2
3109 public static function get_user_notification_preferences_parameters() {
3110 return new external_function_parameters(
3111 array(
3112 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3118 * Get the notification preferences for a given user.
3120 * @param int $userid id of the user, 0 for current user
3121 * @return \core_external\external_description
3122 * @throws moodle_exception
3123 * @since 3.2
3125 public static function get_user_notification_preferences($userid = 0) {
3126 global $PAGE;
3128 $params = self::validate_parameters(
3129 self::get_user_notification_preferences_parameters(),
3130 array(
3131 'userid' => $userid,
3134 $user = self::validate_preferences_permissions($params['userid']);
3136 $processors = get_message_processors();
3137 $providers = message_get_providers_for_user($user->id);
3138 $preferences = \core_message\api::get_all_message_preferences($processors, $providers, $user);
3139 $notificationlist = new \core_message\output\preferences\notification_list($processors, $providers, $preferences, $user);
3141 $renderer = $PAGE->get_renderer('core_message');
3143 $result = array(
3144 'warnings' => array(),
3145 'preferences' => $notificationlist->export_for_template($renderer)
3147 return $result;
3151 * Returns description of method result value
3153 * @return \core_external\external_description
3154 * @since 3.2
3156 public static function get_user_notification_preferences_returns() {
3157 return new external_function_parameters(
3158 array(
3159 'preferences' => self::get_preferences_structure(),
3160 'warnings' => new external_warnings(),
3166 * Returns description of method parameters
3168 * @return external_function_parameters
3169 * @since 3.2
3171 public static function get_user_message_preferences_parameters() {
3172 return new external_function_parameters(
3173 array(
3174 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3180 * Get the notification preferences for a given user.
3182 * @param int $userid id of the user, 0 for current user
3183 * @return \core_external\external_description
3184 * @throws moodle_exception
3185 * @since 3.2
3187 public static function get_user_message_preferences($userid = 0) {
3188 global $CFG, $PAGE;
3190 $params = self::validate_parameters(
3191 self::get_user_message_preferences_parameters(),
3192 array(
3193 'userid' => $userid,
3197 $user = self::validate_preferences_permissions($params['userid']);
3199 // Filter out enabled, available system_configured and user_configured processors only.
3200 $readyprocessors = array_filter(get_message_processors(), function($processor) {
3201 return $processor->enabled &&
3202 $processor->configured &&
3203 $processor->object->is_user_configured() &&
3204 // Filter out processors that don't have and message preferences to configure.
3205 $processor->object->has_message_preferences();
3208 $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
3209 return $provider->component === 'moodle';
3211 $preferences = \core_message\api::get_all_message_preferences($readyprocessors, $providers, $user);
3212 $notificationlistoutput = new \core_message\output\preferences\message_notification_list($readyprocessors,
3213 $providers, $preferences, $user);
3215 $renderer = $PAGE->get_renderer('core_message');
3217 $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
3219 $result = array(
3220 'warnings' => array(),
3221 'preferences' => $notificationlistoutput->export_for_template($renderer),
3222 'blocknoncontacts' => \core_message\api::get_user_privacy_messaging_preference($user->id),
3223 'entertosend' => $entertosend
3225 return $result;
3229 * Returns description of method result value
3231 * @return \core_external\external_description
3232 * @since 3.2
3234 public static function get_user_message_preferences_returns() {
3235 return new external_function_parameters(
3236 array(
3237 'preferences' => self::get_preferences_structure(),
3238 'blocknoncontacts' => new external_value(PARAM_INT, 'Privacy messaging setting to define who can message you'),
3239 'entertosend' => new external_value(PARAM_BOOL, 'User preference for using enter to send messages'),
3240 'warnings' => new external_warnings(),
3246 * Returns description of method parameters for the favourite_conversations() method.
3248 * @return external_function_parameters
3250 public static function set_favourite_conversations_parameters() {
3251 return new external_function_parameters(
3252 array(
3253 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3254 'conversations' => new external_multiple_structure(
3255 new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3262 * Favourite a conversation, or list of conversations for a user.
3264 * @param int $userid the id of the user, or 0 for the current user.
3265 * @param array $conversationids the list of conversations ids to favourite.
3266 * @return array
3267 * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3269 public static function set_favourite_conversations(int $userid, array $conversationids) {
3270 global $CFG, $USER;
3272 // All the business logic checks that really shouldn't be in here.
3273 if (empty($CFG->messaging)) {
3274 throw new moodle_exception('disabled', 'message');
3276 $params = [
3277 'userid' => $userid,
3278 'conversations' => $conversationids
3280 $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
3281 $systemcontext = context_system::instance();
3282 self::validate_context($systemcontext);
3284 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3285 throw new moodle_exception('You do not have permission to perform this action.');
3288 foreach ($params['conversations'] as $conversationid) {
3289 \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
3292 return [];
3296 * Return a description of the returns for the create_user_favourite_conversations() method.
3298 * @return \core_external\external_description
3300 public static function set_favourite_conversations_returns() {
3301 return new external_warnings();
3305 * Returns description of method parameters for unfavourite_conversations() method.
3307 * @return external_function_parameters
3309 public static function unset_favourite_conversations_parameters() {
3310 return new external_function_parameters(
3311 array(
3312 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3313 'conversations' => new external_multiple_structure(
3314 new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3321 * Unfavourite a conversation, or list of conversations for a user.
3323 * @param int $userid the id of the user, or 0 for the current user.
3324 * @param array $conversationids the list of conversations ids unset as favourites.
3325 * @return array
3326 * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3328 public static function unset_favourite_conversations(int $userid, array $conversationids) {
3329 global $CFG, $USER;
3331 // All the business logic checks that really shouldn't be in here.
3332 if (empty($CFG->messaging)) {
3333 throw new moodle_exception('disabled', 'message');
3335 $params = [
3336 'userid' => $userid,
3337 'conversations' => $conversationids
3339 $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
3340 $systemcontext = context_system::instance();
3341 self::validate_context($systemcontext);
3343 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3344 throw new moodle_exception('You do not have permission to perform this action.');
3347 foreach ($params['conversations'] as $conversationid) {
3348 \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
3351 return [];
3355 * Unset favourite conversations return description.
3357 * @return \core_external\external_description
3359 public static function unset_favourite_conversations_returns() {
3360 return new external_warnings();
3364 * Returns description of method parameters for get_member_info() method.
3366 * @return external_function_parameters
3368 public static function get_member_info_parameters() {
3369 return new external_function_parameters(
3370 array(
3371 'referenceuserid' => new external_value(PARAM_INT, 'id of the user'),
3372 'userids' => new external_multiple_structure(
3373 new external_value(PARAM_INT, 'id of members to get')
3375 'includecontactrequests' => new external_value(PARAM_BOOL, 'include contact requests in response', VALUE_DEFAULT, false),
3376 'includeprivacyinfo' => new external_value(PARAM_BOOL, 'include privacy info in response', VALUE_DEFAULT, false)
3382 * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
3384 * This is the basic structure used when returning members, and includes information about the relationship between each member
3385 * and the referenceuser, such as a whether the referenceuser has marked the member as a contact, or has blocked them.
3387 * @param int $referenceuserid the id of the user which check contact and blocked status.
3388 * @param array $userids
3389 * @return array the array of objects containing member info.
3390 * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3392 public static function get_member_info(
3393 int $referenceuserid,
3394 array $userids,
3395 bool $includecontactrequests = false,
3396 bool $includeprivacyinfo = false
3398 global $CFG, $USER;
3400 // All the business logic checks that really shouldn't be in here.
3401 if (empty($CFG->messaging)) {
3402 throw new moodle_exception('disabled', 'message');
3404 $params = [
3405 'referenceuserid' => $referenceuserid,
3406 'userids' => $userids,
3407 'includecontactrequests' => $includecontactrequests,
3408 'includeprivacyinfo' => $includeprivacyinfo
3410 $params = self::validate_parameters(self::get_member_info_parameters(), $params);
3411 $systemcontext = context_system::instance();
3412 self::validate_context($systemcontext);
3414 if (($USER->id != $referenceuserid) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3415 throw new moodle_exception('You do not have permission to perform this action.');
3418 return \core_message\helper::get_member_info(
3419 $params['referenceuserid'],
3420 $params['userids'],
3421 $params['includecontactrequests'],
3422 $params['includeprivacyinfo']
3427 * Get member info return description.
3429 * @return \core_external\external_description
3431 public static function get_member_info_returns() {
3432 return new external_multiple_structure(
3433 self::get_conversation_member_structure()
3438 * Returns description of method parameters for get_conversation_counts() method.
3440 * @return external_function_parameters
3442 public static function get_conversation_counts_parameters() {
3443 return new external_function_parameters(
3445 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3451 * Returns an array of conversation counts for the various types of conversations, including favourites.
3453 * Return format:
3455 * 'favourites' => 0,
3456 * 'types' => [
3457 * \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3458 * \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3462 * @param int $userid the id of the user whose counts we are fetching.
3463 * @return array the array of conversation counts, indexed by type.
3464 * @throws moodle_exception if the current user cannot perform this action.
3466 public static function get_conversation_counts(int $userid) {
3467 global $CFG, $USER;
3469 // All the business logic checks that really shouldn't be in here.
3470 if (empty($CFG->messaging)) {
3471 throw new moodle_exception('disabled', 'message');
3474 if (empty($userid)) {
3475 $userid = $USER->id;
3478 $params = ['userid' => $userid];
3479 $params = self::validate_parameters(self::get_conversation_counts_parameters(), $params);
3481 $systemcontext = context_system::instance();
3482 self::validate_context($systemcontext);
3484 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3485 throw new moodle_exception('You do not have permission to perform this action.');
3488 return \core_message\api::get_conversation_counts($params['userid']);
3492 * Get conversation counts return description.
3494 * @return \core_external\external_description
3496 public static function get_conversation_counts_returns() {
3497 return new external_single_structure(
3499 'favourites' => new external_value(PARAM_INT, 'Total number of favourite conversations'),
3500 'types' => new external_single_structure(
3502 \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
3503 'Total number of individual conversations'),
3504 \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
3505 'Total number of group conversations'),
3506 \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
3507 'Total number of self conversations'),
3515 * Returns description of method parameters for get_unread_conversation_counts() method.
3517 * @return external_function_parameters
3519 public static function get_unread_conversation_counts_parameters() {
3520 return new external_function_parameters(
3522 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3528 * Returns an array of unread conversation counts for the various types of conversations, including favourites.
3530 * Return format:
3532 * 'favourites' => 0,
3533 * 'types' => [
3534 * \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3535 * \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3539 * @param int $userid the id of the user whose counts we are fetching.
3540 * @return array the array of unread conversation counts, indexed by type.
3541 * @throws moodle_exception if the current user cannot perform this action.
3543 public static function get_unread_conversation_counts(int $userid) {
3544 global $CFG, $USER;
3546 // All the business logic checks that really shouldn't be in here.
3547 if (empty($CFG->messaging)) {
3548 throw new moodle_exception('disabled', 'message');
3551 if (empty($userid)) {
3552 $userid = $USER->id;
3555 $params = ['userid' => $userid];
3556 $params = self::validate_parameters(self::get_unread_conversation_counts_parameters(), $params);
3558 $systemcontext = context_system::instance();
3559 self::validate_context($systemcontext);
3561 if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3562 throw new moodle_exception('You do not have permission to perform this action.');
3565 return \core_message\api::get_unread_conversation_counts($params['userid']);
3569 * Get unread conversation counts return description.
3571 * @return \core_external\external_description
3573 public static function get_unread_conversation_counts_returns() {
3574 return new external_single_structure(
3576 'favourites' => new external_value(PARAM_INT, 'Total number of unread favourite conversations'),
3577 'types' => new external_single_structure(
3579 \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
3580 'Total number of unread individual conversations'),
3581 \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
3582 'Total number of unread group conversations'),
3583 \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
3584 'Total number of unread self conversations'),
3592 * Returns description of method parameters
3594 * @return external_function_parameters
3595 * @since 3.7
3597 public static function delete_message_for_all_users_parameters() {
3598 return new external_function_parameters(
3599 array(
3600 'messageid' => new external_value(PARAM_INT, 'The message id'),
3601 'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for all users')
3606 * Deletes a message for all users
3608 * @param int $messageid the message id
3609 * @param int $userid the user id of who we want to delete the message for all users, is no longer used.
3610 * @return \core_external\external_description
3611 * @throws moodle_exception
3612 * @since 3.7
3614 public static function delete_message_for_all_users(int $messageid, int $userid) {
3615 global $CFG, $USER;
3617 // Check if private messaging between users is allowed.
3618 if (empty($CFG->messaging)) {
3619 throw new moodle_exception('disabled', 'message');
3622 // Validate params.
3623 $params = array(
3624 'messageid' => $messageid,
3625 'userid' => $userid
3627 $params = self::validate_parameters(self::delete_message_for_all_users_parameters(), $params);
3629 // Validate context.
3630 $context = context_system::instance();
3631 self::validate_context($context);
3633 core_user::require_active_user($USER);
3635 // Checks if a user can delete a message for all users.
3636 if (core_message\api::can_delete_message_for_all_users($USER->id, $params['messageid'])) {
3637 \core_message\api::delete_message_for_all_users($params['messageid']);
3638 } else {
3639 throw new moodle_exception('You do not have permission to delete this message for everyone.');
3642 return [];
3645 * Returns description of method result value
3647 * @return \core_external\external_description
3648 * @since 3.7
3650 public static function delete_message_for_all_users_returns() {
3651 return new external_warnings();