Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * External message API
  19   *
  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
  24   */
  25  
  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;
  34  
  35  defined('MOODLE_INTERNAL') || die();
  36  
  37  require_once($CFG->dirroot . "/message/lib.php");
  38  
  39  /**
  40   * Message external functions
  41   *
  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
  47   */
  48  class core_message_external extends external_api {
  49      /**
  50       * Returns description of method parameters
  51       *
  52       * @return external_function_parameters
  53       * @since Moodle 3.6
  54       */
  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),
  64                          )
  65                      )
  66                  )
  67              )
  68          );
  69      }
  70  
  71      /**
  72       * Send messages from the current USER to a conversation.
  73       *
  74       * This conversation may be any type of conversation, individual or group.
  75       *
  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
  80       */
  81      public static function send_messages_to_conversation(int $conversationid, array $messages = []) {
  82          global $CFG, $USER;
  83  
  84          // Check if messaging is enabled.
  85          if (empty($CFG->messaging)) {
  86              throw new moodle_exception('disabled', 'message');
  87          }
  88  
  89          // Ensure the current user is allowed to run this function.
  90          $context = context_system::instance();
  91          self::validate_context($context);
  92  
  93          $params = self::validate_parameters(self::send_messages_to_conversation_parameters(), [
  94              'conversationid' => $conversationid,
  95              'messages' => $messages
  96          ]);
  97  
  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');
 103              }
 104          }
 105  
 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
 114              ]);
 115              $messages[] = $createdmessage;
 116          }
 117  
 118          return $messages;
 119      }
 120  
 121      /**
 122       * Returns description of method result value.
 123       *
 124       * @return \core_external\external_description
 125       * @since Moodle 3.6
 126       */
 127      public static function send_messages_to_conversation_returns() {
 128          return new external_multiple_structure(
 129              self::get_conversation_message_structure()
 130          );
 131      }
 132  
 133  
 134      /**
 135       * Returns description of method parameters
 136       *
 137       * @return external_function_parameters
 138       * @since Moodle 2.2
 139       */
 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),
 150                          )
 151                      )
 152                  )
 153              )
 154          );
 155      }
 156  
 157      /**
 158       * Send private messages from the current USER to other users
 159       *
 160       * @param array $messages An array of message to send.
 161       * @return array
 162       * @since Moodle 2.2
 163       */
 164      public static function send_instant_messages($messages = array()) {
 165          global $CFG, $USER, $DB;
 166  
 167          // Check if messaging is enabled.
 168          if (empty($CFG->messaging)) {
 169              throw new moodle_exception('disabled', 'message');
 170          }
 171  
 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);
 176  
 177          // Ensure the current user is allowed to delete message for everyone.
 178          $candeletemessagesforallusers = has_capability('moodle/site:deleteanymessage', $context);
 179  
 180          $params = self::validate_parameters(self::send_instant_messages_parameters(), array('messages' => $messages));
 181  
 182          //retrieve all tousers of the messages
 183          $receivers = array();
 184          foreach($params['messages'] as $message) {
 185              $receivers[] = $message['touserid'];
 186          }
 187          list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
 188          $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
 189  
 190          $resultmessages = array();
 191          $messageids = array();
 192          foreach ($params['messages'] as $message) {
 193              $resultmsg = array(); //the infos about the success of the operation
 194  
 195              // We are going to do some checking.
 196              // Code should match /messages/index.php checks.
 197              $success = true;
 198  
 199              // Check the user exists.
 200              if (empty($tousers[$message['touserid']])) {
 201                  $success = false;
 202                  $errormessage = get_string('touserdoesntexist', 'message', $message['touserid']);
 203              }
 204  
 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');
 209              }
 210  
 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'])));
 216              }
 217  
 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']));
 223              }
 224  
 225              // Build the resultmsg.
 226              if (isset($message['clientmsgid'])) {
 227                  $resultmsg['clientmsgid'] = $message['clientmsgid'];
 228              }
 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');
 240                  }
 241                  $resultmsg['errormessage'] = $errormessage;
 242              }
 243  
 244              $resultmessages[] = $resultmsg;
 245          }
 246  
 247          if (!empty($messageids)) {
 248              $messagerecords = $DB->get_records_list(
 249                  'messages',
 250                  'id',
 251                  $messageids,
 252                  '',
 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
 262                  ]);
 263                  return $resultmessage;
 264              }, $resultmessages);
 265          }
 266  
 267          return $resultmessages;
 268      }
 269  
 270      /**
 271       * Returns description of method result value
 272       *
 273       * @return \core_external\external_description
 274       * @since Moodle 2.2
 275       */
 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),
 289                  )
 290              )
 291          );
 292      }
 293  
 294      /**
 295       * Delete contacts parameters description.
 296       *
 297       * @return external_function_parameters
 298       * @since Moodle 2.5
 299       */
 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'
 306                  ),
 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)
 309              )
 310          );
 311      }
 312  
 313      /**
 314       * Delete contacts.
 315       *
 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
 320       */
 321      public static function delete_contacts($userids, $userid = 0) {
 322          global $CFG, $USER;
 323  
 324          // Check if messaging is enabled.
 325          if (empty($CFG->messaging)) {
 326              throw new moodle_exception('disabled', 'message');
 327          }
 328  
 329          if (empty($userid)) {
 330              $userid = $USER->id;
 331          }
 332  
 333          // Validate context.
 334          $context = context_system::instance();
 335          self::validate_context($context);
 336  
 337          $params = array('userids' => $userids, 'userid' => $userid);
 338          $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
 339  
 340          $capability = 'moodle/site:manageallmessaging';
 341          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 342              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 343          }
 344  
 345          foreach ($params['userids'] as $id) {
 346              \core_message\api::remove_contact($params['userid'], $id);
 347          }
 348  
 349          return null;
 350      }
 351  
 352      /**
 353       * Delete contacts return description.
 354       *
 355       * @return \core_external\external_description
 356       * @since Moodle 2.5
 357       */
 358      public static function delete_contacts_returns() {
 359          return null;
 360      }
 361  
 362      /**
 363       * Mute conversations parameters description.
 364       *
 365       * @return external_function_parameters
 366       */
 367      public static function mute_conversations_parameters() {
 368          return new external_function_parameters(
 369              [
 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)
 373                  ),
 374              ]
 375          );
 376      }
 377  
 378      /**
 379       * Mutes conversations.
 380       *
 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
 384       */
 385      public static function mute_conversations(int $userid, array $conversationids) {
 386          global $CFG, $USER;
 387  
 388          // Check if messaging is enabled.
 389          if (empty($CFG->messaging)) {
 390              throw new moodle_exception('disabled', 'message');
 391          }
 392  
 393          // Validate context.
 394          $context = context_system::instance();
 395          self::validate_context($context);
 396  
 397          $params = ['userid' => $userid, 'conversationids' => $conversationids];
 398          $params = self::validate_parameters(self::mute_conversations_parameters(), $params);
 399  
 400          $capability = 'moodle/site:manageallmessaging';
 401          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 402              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 403          }
 404  
 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);
 408              }
 409          }
 410  
 411          return [];
 412      }
 413  
 414      /**
 415       * Mute conversations return description.
 416       *
 417       * @return \core_external\external_description
 418       */
 419      public static function mute_conversations_returns() {
 420          return new external_warnings();
 421      }
 422  
 423      /**
 424       * Unmute conversations parameters description.
 425       *
 426       * @return external_function_parameters
 427       */
 428      public static function unmute_conversations_parameters() {
 429          return new external_function_parameters(
 430              [
 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)
 434                  ),
 435              ]
 436          );
 437      }
 438  
 439      /**
 440       * Unmute conversations.
 441       *
 442       * @param int $userid The id of the user who is unblocking
 443       * @param array $conversationids The list of conversations being muted
 444       */
 445      public static function unmute_conversations(int $userid, array $conversationids) {
 446          global $CFG, $USER;
 447  
 448          // Check if messaging is enabled.
 449          if (empty($CFG->messaging)) {
 450              throw new moodle_exception('disabled', 'message');
 451          }
 452  
 453          // Validate context.
 454          $context = context_system::instance();
 455          self::validate_context($context);
 456  
 457          $params = ['userid' => $userid, 'conversationids' => $conversationids];
 458          $params = self::validate_parameters(self::unmute_conversations_parameters(), $params);
 459  
 460          $capability = 'moodle/site:manageallmessaging';
 461          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 462              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 463          }
 464  
 465          foreach ($params['conversationids'] as $conversationid) {
 466              \core_message\api::unmute_conversation($params['userid'], $conversationid);
 467          }
 468  
 469          return [];
 470      }
 471  
 472      /**
 473       * Unmute conversations return description.
 474       *
 475       * @return \core_external\external_description
 476       */
 477      public static function unmute_conversations_returns() {
 478          return new external_warnings();
 479      }
 480  
 481      /**
 482       * Block user parameters description.
 483       *
 484       * @return external_function_parameters
 485       */
 486      public static function block_user_parameters() {
 487          return new external_function_parameters(
 488              [
 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'),
 491              ]
 492          );
 493      }
 494  
 495      /**
 496       * Blocks a user.
 497       *
 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
 501       */
 502      public static function block_user(int $userid, int $blockeduserid) {
 503          global $CFG, $USER;
 504  
 505          // Check if messaging is enabled.
 506          if (empty($CFG->messaging)) {
 507              throw new moodle_exception('disabled', 'message');
 508          }
 509  
 510          // Validate context.
 511          $context = context_system::instance();
 512          self::validate_context($context);
 513  
 514          $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
 515          $params = self::validate_parameters(self::block_user_parameters(), $params);
 516  
 517          $capability = 'moodle/site:manageallmessaging';
 518          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 519              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 520          }
 521  
 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 [];
 525          }
 526  
 527          if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
 528              \core_message\api::block_user($params['userid'], $params['blockeduserid']);
 529          }
 530  
 531          return [];
 532      }
 533  
 534      /**
 535       * Block user return description.
 536       *
 537       * @return \core_external\external_description
 538       */
 539      public static function block_user_returns() {
 540          return new external_warnings();
 541      }
 542  
 543      /**
 544       * Unblock user parameters description.
 545       *
 546       * @return external_function_parameters
 547       */
 548      public static function unblock_user_parameters() {
 549          return new external_function_parameters(
 550              [
 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'),
 553              ]
 554          );
 555      }
 556  
 557      /**
 558       * Unblock user.
 559       *
 560       * @param int $userid The id of the user who is unblocking
 561       * @param int $unblockeduserid The id of the user being unblocked
 562       */
 563      public static function unblock_user(int $userid, int $unblockeduserid) {
 564          global $CFG, $USER;
 565  
 566          // Check if messaging is enabled.
 567          if (empty($CFG->messaging)) {
 568              throw new moodle_exception('disabled', 'message');
 569          }
 570  
 571          // Validate context.
 572          $context = context_system::instance();
 573          self::validate_context($context);
 574  
 575          $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
 576          $params = self::validate_parameters(self::unblock_user_parameters(), $params);
 577  
 578          $capability = 'moodle/site:manageallmessaging';
 579          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 580              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 581          }
 582  
 583          \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
 584  
 585          return [];
 586      }
 587  
 588      /**
 589       * Unblock user return description.
 590       *
 591       * @return \core_external\external_description
 592       */
 593      public static function unblock_user_returns() {
 594          return new external_warnings();
 595      }
 596  
 597      /**
 598       * Returns contact requests parameters description.
 599       *
 600       * @return external_function_parameters
 601       */
 602      public static function get_contact_requests_parameters() {
 603          return new external_function_parameters(
 604              [
 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)
 608              ]
 609          );
 610      }
 611  
 612      /**
 613       * Handles returning the contact requests for a user.
 614       *
 615       * This also includes the user data necessary to display information
 616       * about the user.
 617       *
 618       * It will not include blocked users.
 619       *
 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
 623       */
 624      public static function get_contact_requests(int $userid, int $limitfrom = 0, int $limitnum = 0) {
 625          global $CFG, $USER;
 626  
 627          // Check if messaging is enabled.
 628          if (empty($CFG->messaging)) {
 629              throw new moodle_exception('disabled', 'message');
 630          }
 631  
 632          // Validate context.
 633          $context = context_system::instance();
 634          self::validate_context($context);
 635  
 636          $params = [
 637              'userid' => $userid,
 638              'limitfrom' => $limitfrom,
 639              'limitnum' => $limitnum
 640          ];
 641          $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
 642  
 643          $capability = 'moodle/site:manageallmessaging';
 644          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 645              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 646          }
 647  
 648          return \core_message\api::get_contact_requests($params['userid'], $params['limitfrom'], $params['limitnum']);
 649      }
 650  
 651      /**
 652       * Returns the contact requests return description.
 653       *
 654       * @return \core_external\external_description
 655       */
 656      public static function get_contact_requests_returns() {
 657          return new external_multiple_structure(
 658              self::get_conversation_member_structure()
 659          );
 660      }
 661  
 662      /**
 663       * Returns the number of contact requests the user has received parameters description.
 664       *
 665       * @return external_function_parameters
 666       */
 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),
 672              )
 673          );
 674      }
 675  
 676      /**
 677       * Returns the number of contact requests the user has received.
 678       *
 679       * @param int $userid The ID of the user we want to return the number of received contact requests for
 680       * @return external_value
 681       */
 682      public static function get_received_contact_requests_count(int $userid) {
 683          global $CFG, $USER;
 684  
 685          // Check if messaging is enabled.
 686          if (empty($CFG->messaging)) {
 687              throw new moodle_exception('disabled', 'message');
 688          }
 689  
 690          // Validate context.
 691          $context = context_system::instance();
 692          self::validate_context($context);
 693  
 694          $params = [
 695              'userid' => $userid,
 696          ];
 697          $params = self::validate_parameters(self::get_received_contact_requests_count_parameters(), $params);
 698  
 699          $capability = 'moodle/site:manageallmessaging';
 700          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 701              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 702          }
 703  
 704          return \core_message\api::get_received_contact_requests_count($params['userid']);
 705      }
 706  
 707      /**
 708       * Returns the number of contact requests the user has received return description.
 709       *
 710       * @return external_value
 711       */
 712      public static function get_received_contact_requests_count_returns() {
 713          return new external_value(PARAM_INT, 'The number of received contact requests');
 714      }
 715  
 716      /**
 717       * Returns get conversation members parameters description.
 718       *
 719       * @return external_function_parameters
 720       */
 721      public static function get_conversation_members_parameters() {
 722          return new external_function_parameters(
 723              [
 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)
 732              ]
 733          );
 734      }
 735  
 736      /**
 737       * Returns a list of conversation members.
 738       *
 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
 746       */
 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;
 750  
 751          // Check if messaging is enabled.
 752          if (empty($CFG->messaging)) {
 753              throw new moodle_exception('disabled', 'message');
 754          }
 755  
 756          // Validate context.
 757          $context = context_system::instance();
 758          self::validate_context($context);
 759  
 760          $params = [
 761              'userid' => $userid,
 762              'conversationid' => $conversationid,
 763              'includecontactrequests' => $includecontactrequests,
 764              'includeprivacyinfo' => $includeprivacyinfo,
 765              'limitfrom' => $limitfrom,
 766              'limitnum' => $limitnum
 767          ];
 768          $params = self::validate_parameters(self::get_conversation_members_parameters(), $params);
 769  
 770          $capability = 'moodle/site:manageallmessaging';
 771          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 772              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 773          }
 774  
 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.');
 778          }
 779  
 780          return \core_message\api::get_conversation_members($params['userid'], $params['conversationid'], $params['includecontactrequests'],
 781              $params['includeprivacyinfo'], $params['limitfrom'], $params['limitnum']);
 782      }
 783  
 784      /**
 785       * Returns the get conversation members return description.
 786       *
 787       * @return \core_external\external_description
 788       */
 789      public static function get_conversation_members_returns() {
 790          return new external_multiple_structure(
 791              self::get_conversation_member_structure()
 792          );
 793      }
 794  
 795      /**
 796       * Creates a contact request parameters description.
 797       *
 798       * @return external_function_parameters
 799       */
 800      public static function create_contact_request_parameters() {
 801          return new external_function_parameters(
 802              [
 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')
 805              ]
 806          );
 807      }
 808  
 809      /**
 810       * Creates a contact request.
 811       *
 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
 814       */
 815      public static function create_contact_request(int $userid, int $requesteduserid) {
 816          global $CFG, $USER;
 817  
 818          // Check if messaging is enabled.
 819          if (empty($CFG->messaging)) {
 820              throw new moodle_exception('disabled', 'message');
 821          }
 822  
 823          // Validate context.
 824          $context = context_system::instance();
 825          self::validate_context($context);
 826  
 827          $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
 828          $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
 829  
 830          $capability = 'moodle/site:manageallmessaging';
 831          if (($USER->id != $params['userid']) && !has_capability($capability, $context)) {
 832              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 833          }
 834  
 835          $result = [
 836              'warnings' => []
 837          ];
 838  
 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'
 845              ];
 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']);
 852              }
 853          }
 854  
 855          return $result;
 856      }
 857  
 858      /**
 859       * Creates a contact request return description.
 860       *
 861       * @return \core_external\external_description
 862       */
 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'),
 872                      ),
 873                      'request record',
 874                      VALUE_OPTIONAL
 875                  ),
 876                  'warnings' => new external_warnings()
 877              )
 878          );
 879      }
 880  
 881      /**
 882       * Confirm a contact request parameters description.
 883       *
 884       * @return external_function_parameters
 885       */
 886      public static function confirm_contact_request_parameters() {
 887          return new external_function_parameters(
 888              [
 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')
 891              ]
 892          );
 893      }
 894  
 895      /**
 896       * Confirm a contact request.
 897       *
 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
 900       */
 901      public static function confirm_contact_request(int $userid, int $requesteduserid) {
 902          global $CFG, $USER;
 903  
 904          // Check if messaging is enabled.
 905          if (empty($CFG->messaging)) {
 906              throw new moodle_exception('disabled', 'message');
 907          }
 908  
 909          // Validate context.
 910          $context = context_system::instance();
 911          self::validate_context($context);
 912  
 913          $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
 914          $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
 915  
 916          $capability = 'moodle/site:manageallmessaging';
 917          if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
 918              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 919          }
 920  
 921          \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
 922  
 923          return [];
 924      }
 925  
 926      /**
 927       * Confirm a contact request return description.
 928       *
 929       * @return \core_external\external_description
 930       */
 931      public static function confirm_contact_request_returns() {
 932          return new external_warnings();
 933      }
 934  
 935      /**
 936       * Declines a contact request parameters description.
 937       *
 938       * @return external_function_parameters
 939       */
 940      public static function decline_contact_request_parameters() {
 941          return new external_function_parameters(
 942              [
 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')
 945              ]
 946          );
 947      }
 948  
 949      /**
 950       * Declines a contact request.
 951       *
 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
 954       */
 955      public static function decline_contact_request(int $userid, int $requesteduserid) {
 956          global $CFG, $USER;
 957  
 958          // Check if messaging is enabled.
 959          if (empty($CFG->messaging)) {
 960              throw new moodle_exception('disabled', 'message');
 961          }
 962  
 963          // Validate context.
 964          $context = context_system::instance();
 965          self::validate_context($context);
 966  
 967          $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
 968          $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
 969  
 970          $capability = 'moodle/site:manageallmessaging';
 971          if (($USER->id != $params['requesteduserid']) && !has_capability($capability, $context)) {
 972              throw new required_capability_exception($context, $capability, 'nopermissions', '');
 973          }
 974  
 975          \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
 976  
 977          return [];
 978      }
 979  
 980      /**
 981       * Declines a contact request return description.
 982       *
 983       * @return \core_external\external_description
 984       */
 985      public static function decline_contact_request_returns() {
 986          return new external_warnings();
 987      }
 988  
 989      /**
 990       * Return the structure of a message area contact.
 991       *
 992       * @return external_single_structure
 993       * @since Moodle 3.2
 994       */
 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),
1014              )
1015          );
1016      }
1017  
1018      /**
1019       * Return the structure of a conversation.
1020       *
1021       * @return external_single_structure
1022       * @since Moodle 3.6
1023       */
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()
1040                  ),
1041                  'messages' => new external_multiple_structure(
1042                      self::get_conversation_message_structure()
1043                  ),
1044                  'candeletemessagesforallusers' => new external_value(PARAM_BOOL,
1045                      'If the user can delete messages in the conversation for all users', VALUE_DEFAULT, false),
1046              )
1047          );
1048      }
1049  
1050      /**
1051       * Return the structure of a conversation member.
1052       *
1053       * @return external_single_structure
1054       * @since Moodle 3.6
1055       */
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'),
1072          ];
1073  
1074          $result['contactrequests'] = new external_multiple_structure(
1075              new external_single_structure(
1076                  [
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'),
1081                  ]
1082              ), 'The contact requests', VALUE_OPTIONAL
1083          );
1084  
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
1093          );
1094  
1095          return new external_single_structure(
1096              $result
1097          );
1098      }
1099  
1100      /**
1101       * Return the structure of a message area message.
1102       *
1103       * @return external_single_structure
1104       * @since Moodle 3.6
1105       */
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'),
1113              )
1114          );
1115      }
1116  
1117      /**
1118       * Get messagearea message search users parameters.
1119       *
1120       * @return external_function_parameters
1121       * @since 3.6
1122       */
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),
1130              )
1131          );
1132      }
1133  
1134      /**
1135       * Get search users results.
1136       *
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
1144       */
1145      public static function message_search_users($userid, $search, $limitfrom = 0, $limitnum = 0) {
1146          global $USER;
1147  
1148          $systemcontext = context_system::instance();
1149  
1150          $params = array(
1151              'userid' => $userid,
1152              'search' => $search,
1153              'limitfrom' => $limitfrom,
1154              'limitnum' => $limitnum
1155          );
1156          $params = self::validate_parameters(self::message_search_users_parameters(), $params);
1157          self::validate_context($systemcontext);
1158  
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.');
1161          }
1162  
1163          list($contacts, $noncontacts) = \core_message\api::message_search_users(
1164              $params['userid'],
1165              $params['search'],
1166              $params['limitfrom'],
1167              $params['limitnum']);
1168  
1169          return array('contacts' => $contacts, 'noncontacts' => $noncontacts);
1170      }
1171  
1172      /**
1173       * Get messagearea message search users returns.
1174       *
1175       * @return external_single_structure
1176       * @since 3.2
1177       */
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()
1183                  ),
1184                  'noncontacts' => new external_multiple_structure(
1185                      self::get_conversation_member_structure()
1186                  )
1187              )
1188          );
1189      }
1190  
1191      /**
1192       * Get messagearea search messages parameters.
1193       *
1194       * @return external_function_parameters
1195       * @since 3.2
1196       */
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)
1204              )
1205          );
1206      }
1207  
1208      /**
1209       * Get messagearea search messages results.
1210       *
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
1218       */
1219      public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
1220          global $CFG, $USER;
1221  
1222          // Check if messaging is enabled.
1223          if (empty($CFG->messaging)) {
1224              throw new moodle_exception('disabled', 'message');
1225          }
1226  
1227          $systemcontext = context_system::instance();
1228  
1229          $params = array(
1230              'userid' => $userid,
1231              'search' => $search,
1232              'limitfrom' => $limitfrom,
1233              'limitnum' => $limitnum
1234  
1235          );
1236          $params = self::validate_parameters(self::data_for_messagearea_search_messages_parameters(), $params);
1237          self::validate_context($systemcontext);
1238  
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.');
1241          }
1242  
1243          $messages = \core_message\api::search_messages(
1244              $params['userid'],
1245              $params['search'],
1246              $params['limitfrom'],
1247              $params['limitnum']
1248          );
1249  
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;
1264                  }
1265                  $contact->lastmessage = shorten_text($message->lastmessage, 60);
1266              } else {
1267                  $contact->lastmessage = null;
1268              }
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;
1276  
1277              $data->contacts[] = $contact;
1278          }
1279  
1280          return $data;
1281      }
1282  
1283      /**
1284       * Get messagearea search messages returns.
1285       *
1286       * @return external_single_structure
1287       * @since 3.2
1288       */
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()
1294                  )
1295              )
1296          );
1297      }
1298  
1299      /**
1300       * Get conversations parameters.
1301       *
1302       * @return external_function_parameters
1303       * @since 3.6
1304       */
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),
1318              )
1319          );
1320      }
1321  
1322      /**
1323       * Get the list of conversations for the user.
1324       *
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
1335       */
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;
1339  
1340          // All the standard BL checks.
1341          if (empty($CFG->messaging)) {
1342              throw new moodle_exception('disabled', 'message');
1343          }
1344  
1345          $params = array(
1346              'userid' => $userid,
1347              'limitfrom' => $limitfrom,
1348              'limitnum' => $limitnum,
1349              'type' => $type,
1350              'favourites' => $favourites,
1351              'mergeself' => $mergeself
1352          );
1353          $params = self::validate_parameters(self::get_conversations_parameters(), $params);
1354  
1355          $systemcontext = context_system::instance();
1356          self::validate_context($systemcontext);
1357  
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.');
1360          }
1361  
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']
1369          );
1370  
1371          return (object) ['conversations' => $conversations];
1372      }
1373  
1374      /**
1375       * Get conversations returns.
1376       *
1377       * @return external_single_structure
1378       * @since 3.6
1379       */
1380      public static function get_conversations_returns() {
1381          return new external_single_structure(
1382              [
1383                  'conversations' => new external_multiple_structure(
1384                      self::get_conversation_structure(true)
1385                  )
1386              ]
1387          );
1388      }
1389  
1390      /**
1391       * Get conversation parameters.
1392       *
1393       * @return external_function_parameters
1394       */
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)
1407              )
1408          );
1409      }
1410  
1411      /**
1412       * Get a single conversation.
1413       *
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.
1425       */
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
1436      ) {
1437          global $CFG, $DB, $USER;
1438  
1439          // All the standard BL checks.
1440          if (empty($CFG->messaging)) {
1441              throw new moodle_exception('disabled', 'message');
1442          }
1443  
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
1454          ];
1455          self::validate_parameters(self::get_conversation_parameters(), $params);
1456  
1457          $systemcontext = context_system::instance();
1458          self::validate_context($systemcontext);
1459  
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']
1470          );
1471  
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');
1478          }
1479      }
1480  
1481      /**
1482       * Get conversation returns.
1483       *
1484       * @return external_single_structure
1485       */
1486      public static function get_conversation_returns() {
1487          return self::get_conversation_structure();
1488      }
1489  
1490      /**
1491       * Get conversation parameters.
1492       *
1493       * @return external_function_parameters
1494       */
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)
1507              )
1508          );
1509      }
1510  
1511      /**
1512       * Get a single conversation between users.
1513       *
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.
1525       */
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
1536      ) {
1537          global $CFG, $DB, $USER;
1538  
1539          // All the standard BL checks.
1540          if (empty($CFG->messaging)) {
1541              throw new moodle_exception('disabled', 'message');
1542          }
1543  
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
1554          ];
1555          self::validate_parameters(self::get_conversation_between_users_parameters(), $params);
1556  
1557          $systemcontext = context_system::instance();
1558          self::validate_context($systemcontext);
1559  
1560          $conversationid = \core_message\api::get_conversation_between_users([$params['userid'], $params['otheruserid']]);
1561          $conversation = null;
1562  
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']
1574              );
1575          }
1576  
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');
1583          }
1584      }
1585  
1586      /**
1587       * Get conversation returns.
1588       *
1589       * @return external_single_structure
1590       */
1591      public static function get_conversation_between_users_returns() {
1592          return self::get_conversation_structure(true);
1593      }
1594  
1595      /**
1596       * Get self-conversation parameters.
1597       *
1598       * @return external_function_parameters
1599       */
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)
1607              )
1608          );
1609      }
1610  
1611      /**
1612       * Get a single self-conversation.
1613       *
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
1621       */
1622      public static function get_self_conversation(
1623          int $userid,
1624          int $messagelimit = 0,
1625          int $messageoffset = 0,
1626          bool $newestmessagesfirst = true
1627      ) {
1628          global $CFG;
1629  
1630          // All the standard BL checks.
1631          if (empty($CFG->messaging)) {
1632              throw new moodle_exception('disabled', 'message');
1633          }
1634  
1635          $params = [
1636              'userid' => $userid,
1637              'messagelimit' => $messagelimit,
1638              'messageoffset' => $messageoffset,
1639              'newestmessagesfirst' => $newestmessagesfirst
1640          ];
1641          self::validate_parameters(self::get_self_conversation_parameters(), $params);
1642  
1643          $systemcontext = context_system::instance();
1644          self::validate_context($systemcontext);
1645  
1646          $conversation = \core_message\api::get_self_conversation($params['userid']);
1647  
1648          if ($conversation) {
1649              $conversation = \core_message\api::get_conversation(
1650                  $params['userid'],
1651                  $conversation->id,
1652                  false,
1653                  false,
1654                  0,
1655                  0,
1656                  $params['messagelimit'],
1657                  $params['messageoffset'],
1658                  $params['newestmessagesfirst']
1659              );
1660          }
1661  
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');
1668          }
1669      }
1670  
1671      /**
1672       * Get conversation returns.
1673       *
1674       * @return external_single_structure
1675       */
1676      public static function get_self_conversation_returns() {
1677          return self::get_conversation_structure();
1678      }
1679  
1680      /**
1681       * The conversation messages parameters.
1682       *
1683       * @return external_function_parameters
1684       * @since 3.6
1685       */
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),
1696              )
1697          );
1698      }
1699  
1700      /**
1701       * Get conversation messages.
1702       *
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
1712       */
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;
1716  
1717          // Check if messaging is enabled.
1718          if (empty($CFG->messaging)) {
1719              throw new moodle_exception('disabled', 'message');
1720          }
1721  
1722          $systemcontext = context_system::instance();
1723  
1724          $params = array(
1725              'currentuserid' => $currentuserid,
1726              'convid' => $convid,
1727              'limitfrom' => $limitfrom,
1728              'limitnum' => $limitnum,
1729              'newest' => $newest,
1730              'timefrom' => $timefrom,
1731          );
1732          $params = self::validate_parameters(self::get_conversation_messages_parameters(), $params);
1733          self::validate_context($systemcontext);
1734  
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.');
1737          }
1738  
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.');
1742          }
1743  
1744          $sort = $newest ? 'timecreated DESC' : 'timecreated ASC';
1745  
1746          // We need to enforce a one second delay on messages to avoid race conditions of current
1747          // messages still being sent.
1748          //
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.
1752          //
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;
1755  
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);
1768          }
1769  
1770          return $messages;
1771      }
1772  
1773      /**
1774       * The messagearea messages return structure.
1775       *
1776       * @return external_single_structure
1777       * @since 3.6
1778       */
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()
1785                  ),
1786                  'messages' => new external_multiple_structure(
1787                      self::get_conversation_message_structure()
1788                  ),
1789              )
1790          );
1791      }
1792  
1793      /**
1794       * The user contacts return parameters.
1795       *
1796       * @return external_function_parameters
1797       */
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)
1804              )
1805          );
1806      }
1807  
1808      /**
1809       * Get user contacts.
1810       *
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
1816       */
1817      public static function get_user_contacts(int $userid, int $limitfrom = 0, int $limitnum = 0) {
1818          global $CFG, $USER;
1819  
1820          // Check if messaging is enabled.
1821          if (empty($CFG->messaging)) {
1822              throw new moodle_exception('disabled', 'message');
1823          }
1824  
1825          $systemcontext = context_system::instance();
1826  
1827          $params = array(
1828              'userid' => $userid,
1829              'limitfrom' => $limitfrom,
1830              'limitnum' => $limitnum
1831          );
1832          $params = self::validate_parameters(self::get_user_contacts_parameters(), $params);
1833          self::validate_context($systemcontext);
1834  
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.');
1837          }
1838  
1839          return \core_message\api::get_user_contacts($params['userid'], $params['limitfrom'], $params['limitnum']);
1840      }
1841  
1842      /**
1843       * The user contacts return structure.
1844       *
1845       * @return external_multiple_structure
1846       */
1847      public static function get_user_contacts_returns() {
1848          return new external_multiple_structure(
1849              self::get_conversation_member_structure()
1850          );
1851      }
1852  
1853      /**
1854       * Search contacts parameters description.
1855       *
1856       * @return external_function_parameters
1857       * @since Moodle 2.5
1858       */
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)
1865              )
1866          );
1867      }
1868  
1869      /**
1870       * Search contacts.
1871       *
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
1876       */
1877      public static function search_contacts($searchtext, $onlymycourses = false) {
1878          global $CFG, $USER, $PAGE;
1879          require_once($CFG->dirroot . '/user/lib.php');
1880  
1881          // Check if messaging is enabled.
1882          if (empty($CFG->messaging)) {
1883              throw new moodle_exception('disabled', 'message');
1884          }
1885  
1886          require_once($CFG->libdir . '/enrollib.php');
1887  
1888          $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses);
1889          $params = self::validate_parameters(self::search_contacts_parameters(), $params);
1890  
1891          // Extra validation, we do not allow empty queries.
1892          if ($params['searchtext'] === '') {
1893              throw new moodle_exception('querystringcannotbeempty');
1894          }
1895  
1896          $courseids = array();
1897          if ($params['onlymycourses']) {
1898              $mycourses = enrol_get_my_courses(array('id'));
1899              foreach ($mycourses as $mycourse) {
1900                  $courseids[] = $mycourse->id;
1901              }
1902          } else {
1903              $courseids[] = SITEID;
1904          }
1905  
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;
1911          }
1912  
1913          // Reorganising information.
1914          foreach ($results as &$user) {
1915              $newuser = array(
1916                  'id' => $user->id,
1917                  'fullname' => fullname($user)
1918              );
1919  
1920              // Avoid undefined property notice as phone not specified.
1921              $user->phone1 = null;
1922              $user->phone2 = null;
1923  
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);
1929  
1930              $user = $newuser;
1931          }
1932  
1933          return $results;
1934      }
1935  
1936      /**
1937       * Search contacts return description.
1938       *
1939       * @return \core_external\external_description
1940       * @since Moodle 2.5
1941       */
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)
1950                  )
1951              ),
1952              'List of contacts'
1953          );
1954      }
1955  
1956      /**
1957       * Get messages parameters description.
1958       *
1959       * @return external_function_parameters
1960       * @since 2.8
1961       */
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)
1979              )
1980          );
1981      }
1982  
1983      /**
1984       * Get messages function implementation.
1985       *
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
1997       */
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;
2001  
2002          $warnings = array();
2003  
2004          $params = array(
2005              'useridto' => $useridto,
2006              'useridfrom' => $useridfrom,
2007              'type' => $type,
2008              'read' => $read,
2009              'newestfirst' => $newestfirst,
2010              'limitfrom' => $limitfrom,
2011              'limitnum' => $limitnum
2012          );
2013  
2014          $params = self::validate_parameters(self::get_messages_parameters(), $params);
2015  
2016          $context = context_system::instance();
2017          self::validate_context($context);
2018  
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'];
2026  
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));
2031          }
2032  
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');
2038              }
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;
2047              }
2048          }
2049  
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');
2055              }
2056          }
2057  
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);
2061          }
2062  
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');
2067          }
2068  
2069          // Which type of messages to retrieve.
2070          $notifications = -1;
2071          if ($type != 'both') {
2072              $notifications = ($type == 'notifications') ? 1 : 0;
2073          }
2074  
2075          $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2076          $sort = "mr.timecreated $orderdirection";
2077  
2078          if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2079              $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2080  
2081              // In some cases, we don't need to get the to/from user objects from the sql query.
2082              $userfromfullname = '';
2083              $usertofullname = '';
2084  
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);
2091                  }
2092              } else {
2093                  // If the useridto field is empty, the useridfrom must be filled.
2094                  $userfromfullname = fullname($userfrom, $canviewfullname);
2095              }
2096              foreach ($messages as $mid => $message) {
2097  
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;
2104                      }
2105                  } else {
2106                      // Return iconurl for notifications.
2107                      if (!isset($output)) {
2108                          $output = $PAGE->get_renderer('core');
2109                      }
2110  
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');
2115                      }
2116  
2117                      $message->iconurl = clean_param($iconurl->out(), PARAM_URL);
2118                  }
2119  
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);
2130                      }
2131                  } else {
2132                      $message->userfromfullname = $userfromfullname;
2133                  }
2134  
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;
2142                  }
2143  
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;
2148              }
2149          }
2150  
2151          $results = array(
2152              'messages' => $messages,
2153              'warnings' => $warnings
2154          );
2155  
2156          return $results;
2157      }
2158  
2159      /**
2160       * Get messages return description.
2161       *
2162       * @return external_single_structure
2163       * @since 2.8
2164       */
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'
2194                      )
2195                  ),
2196                  'warnings' => new external_warnings()
2197              )
2198          );
2199      }
2200  
2201      /**
2202       * Mark all notifications as read parameters description.
2203       *
2204       * @return external_function_parameters
2205       * @since 3.2
2206       */
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),
2217              )
2218          );
2219      }
2220  
2221      /**
2222       * Mark all notifications as read function.
2223       *
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
2231       */
2232      public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
2233          global $USER;
2234  
2235          $params = self::validate_parameters(
2236              self::mark_all_notifications_as_read_parameters(),
2237              array(
2238                  'useridto' => $useridto,
2239                  'useridfrom' => $useridfrom,
2240                  'timecreatedto' => $timecreatedto,
2241              )
2242          );
2243  
2244          $context = context_system::instance();
2245          self::validate_context($context);
2246  
2247          $useridto = $params['useridto'];
2248          $useridfrom = $params['useridfrom'];
2249          $timecreatedto = $params['timecreatedto'];
2250  
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');
2256              }
2257          }
2258  
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);
2262          }
2263  
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');
2269          }
2270  
2271          \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
2272  
2273          return true;
2274      }
2275  
2276      /**
2277       * Mark all notifications as read return description.
2278       *
2279       * @return external_single_structure
2280       * @since 3.2
2281       */
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');
2284      }
2285  
2286      /**
2287       * Get unread conversations count parameters description.
2288       *
2289       * @return external_function_parameters
2290       * @since 3.2
2291       */
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),
2296              )
2297          );
2298      }
2299  
2300      /**
2301       * Get unread messages count function.
2302       *
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
2308       */
2309      public static function get_unread_conversations_count($useridto) {
2310          global $USER, $CFG;
2311  
2312          // Check if messaging is enabled.
2313          if (empty($CFG->messaging)) {
2314              throw new moodle_exception('disabled', 'message');
2315          }
2316  
2317          $params = self::validate_parameters(
2318              self::get_unread_conversations_count_parameters(),
2319              array('useridto' => $useridto)
2320          );
2321  
2322          $context = context_system::instance();
2323          self::validate_context($context);
2324  
2325          $useridto = $params['useridto'];
2326  
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');
2332              }
2333          } else {
2334              $useridto = $USER->id;
2335          }
2336  
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');
2340          }
2341  
2342          return \core_message\api::count_unread_conversations($userto);
2343      }
2344  
2345      /**
2346       * Get unread conversations count return description.
2347       *
2348       * @return external_single_structure
2349       * @since 3.2
2350       */
2351      public static function get_unread_conversations_count_returns() {
2352          return new external_value(PARAM_INT, 'The count of unread messages for the user');
2353      }
2354  
2355      /**
2356       * Get blocked users parameters description.
2357       *
2358       * @return external_function_parameters
2359       * @since 2.9
2360       */
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),
2367              )
2368          );
2369      }
2370  
2371      /**
2372       * Retrieve a list of users blocked
2373       *
2374       * @param  int $userid the user whose blocked users we want to retrieve
2375       * @return \core_external\external_description
2376       * @since 2.9
2377       */
2378      public static function get_blocked_users($userid) {
2379          global $CFG, $USER, $PAGE;
2380  
2381          // Warnings array, it can be empty at the end but is mandatory.
2382          $warnings = array();
2383  
2384          // Validate params.
2385          $params = array(
2386              'userid' => $userid
2387          );
2388          $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2389          $userid = $params['userid'];
2390  
2391          // Validate context.
2392          $context = context_system::instance();
2393          self::validate_context($context);
2394  
2395          // Check if private messaging between users is allowed.
2396          if (empty($CFG->messaging)) {
2397              throw new moodle_exception('disabled', 'message');
2398          }
2399  
2400          $user = core_user::get_user($userid, '*', MUST_EXIST);
2401          core_user::require_active_user($user);
2402  
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', '');
2407          }
2408  
2409          // Now, we can get safely all the blocked users.
2410          $users = \core_message\api::get_blocked_users($user->id);
2411  
2412          $blockedusers = array();
2413          foreach ($users as $user) {
2414              $newuser = array(
2415                  'id' => $user->id,
2416                  'fullname' => fullname($user),
2417              );
2418  
2419              $userpicture = new user_picture($user);
2420              $userpicture->size = 1; // Size f1.
2421              $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2422  
2423              $blockedusers[] = $newuser;
2424          }
2425  
2426          $results = array(
2427              'users' => $blockedusers,
2428              'warnings' => $warnings
2429          );
2430          return $results;
2431      }
2432  
2433      /**
2434       * Get blocked users return description.
2435       *
2436       * @return external_single_structure
2437       * @since 2.9
2438       */
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)
2448                          )
2449                      ),
2450                      'List of blocked users'
2451                  ),
2452                  'warnings' => new external_warnings()
2453              )
2454          );
2455      }
2456  
2457      /**
2458       * Returns description of method parameters
2459       *
2460       * @return external_function_parameters
2461       * @since 2.9
2462       */
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)
2469              )
2470          );
2471      }
2472  
2473      /**
2474       * Mark a single message as read, trigger message_viewed event
2475       *
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
2482       */
2483      public static function mark_message_read($messageid, $timeread) {
2484          global $CFG, $DB, $USER;
2485  
2486          // Check if private messaging between users is allowed.
2487          if (empty($CFG->messaging)) {
2488              throw new moodle_exception('disabled', 'message');
2489          }
2490  
2491          // Warnings array, it can be empty at the end but is mandatory.
2492          $warnings = array();
2493  
2494          // Validate params.
2495          $params = array(
2496              'messageid' => $messageid,
2497              'timeread' => $timeread
2498          );
2499          $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2500  
2501          if (empty($params['timeread'])) {
2502              $timeread = time();
2503          } else {
2504              $timeread = $params['timeread'];
2505          }
2506  
2507          // Validate context.
2508          $context = context_system::instance();
2509          self::validate_context($context);
2510  
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);
2527  
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');
2530          }
2531  
2532          \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2533  
2534          $results = array(
2535              'messageid' => $message->id,
2536              'warnings' => $warnings
2537          );
2538          return $results;
2539      }
2540  
2541      /**
2542       * Returns description of method result value
2543       *
2544       * @return \core_external\external_description
2545       * @since 2.9
2546       */
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()
2552              )
2553          );
2554      }
2555  
2556      /**
2557       * Returns description of method parameters
2558       *
2559       * @return external_function_parameters
2560       */
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)
2567              )
2568          );
2569      }
2570  
2571      /**
2572       * Mark a single notification as read.
2573       *
2574       * This will trigger a 'notification_viewed' event.
2575       *
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
2581       */
2582      public static function mark_notification_read($notificationid, $timeread) {
2583          global $CFG, $DB, $USER;
2584  
2585          // Warnings array, it can be empty at the end but is mandatory.
2586          $warnings = array();
2587  
2588          // Validate params.
2589          $params = array(
2590              'notificationid' => $notificationid,
2591              'timeread' => $timeread
2592          );
2593          $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
2594  
2595          if (empty($params['timeread'])) {
2596              $timeread = time();
2597          } else {
2598              $timeread = $params['timeread'];
2599          }
2600  
2601          // Validate context.
2602          $context = context_system::instance();
2603          self::validate_context($context);
2604  
2605          $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
2606  
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');
2610          }
2611  
2612          \core_message\api::mark_notification_as_read($notification, $timeread);
2613  
2614          $results = array(
2615              'notificationid' => $notification->id,
2616              'warnings' => $warnings
2617          );
2618  
2619          return $results;
2620      }
2621  
2622      /**
2623       * Returns description of method result value
2624       *
2625       * @return \core_external\external_description
2626       */
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()
2632              )
2633          );
2634      }
2635  
2636      /**
2637       * Mark all conversation messages as read parameters description.
2638       *
2639       * @return external_function_parameters
2640       * @since 3.6
2641       */
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')
2648              )
2649          );
2650      }
2651  
2652      /**
2653       * Mark all conversation messages as read function.
2654       *
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
2658       */
2659      public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
2660          global $CFG;
2661  
2662          // Check if messaging is enabled.
2663          if (empty($CFG->messaging)) {
2664              throw new moodle_exception('disabled', 'message');
2665          }
2666  
2667          $params = array(
2668              'userid' => $userid,
2669              'conversationid' => $conversationid,
2670          );
2671          $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
2672  
2673          $context = context_system::instance();
2674          self::validate_context($context);
2675  
2676          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2677          core_user::require_active_user($user);
2678  
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');
2683          }
2684      }
2685  
2686      /**
2687       * Mark all conversation messages as read return description.
2688       *
2689       * @return external_warnings
2690       * @since 3.6
2691       */
2692      public static function mark_all_conversation_messages_as_read_returns() {
2693          return null;
2694      }
2695  
2696      /**
2697       * Returns description of method parameters.
2698       *
2699       * @return external_function_parameters
2700       * @since 3.6
2701       */
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'
2709                  ),
2710              )
2711          );
2712      }
2713  
2714      /**
2715       * Deletes a conversation.
2716       *
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
2722       */
2723      public static function delete_conversations_by_id($userid, array $conversationids) {
2724          global $CFG;
2725  
2726          // Check if private messaging between users is allowed.
2727          if (empty($CFG->messaging)) {
2728              throw new moodle_exception('disabled', 'message');
2729          }
2730  
2731          // Validate params.
2732          $params = [
2733              'userid' => $userid,
2734              'conversationids' => $conversationids,
2735          ];
2736          $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
2737  
2738          // Validate context.
2739          $context = context_system::instance();
2740          self::validate_context($context);
2741  
2742          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2743          core_user::require_active_user($user);
2744  
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'");
2750              }
2751          }
2752  
2753          return [];
2754      }
2755  
2756      /**
2757       * Returns description of method result value.
2758       *
2759       * @return \core_external\external_description
2760       * @since 3.6
2761       */
2762      public static function delete_conversations_by_id_returns() {
2763          return new external_warnings();
2764      }
2765  
2766      /**
2767       * Returns description of method parameters
2768       *
2769       * @return external_function_parameters
2770       * @since 3.1
2771       */
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)
2778              )
2779          );
2780      }
2781  
2782      /**
2783       * Deletes a message
2784       *
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
2791       */
2792      public static function delete_message($messageid, $userid, $read = true) {
2793          global $CFG;
2794  
2795          // Check if private messaging between users is allowed.
2796          if (empty($CFG->messaging)) {
2797              throw new moodle_exception('disabled', 'message');
2798          }
2799  
2800          // Warnings array, it can be empty at the end but is mandatory.
2801          $warnings = array();
2802  
2803          // Validate params.
2804          $params = array(
2805              'messageid' => $messageid,
2806              'userid' => $userid,
2807              'read' => $read
2808          );
2809          $params = self::validate_parameters(self::delete_message_parameters(), $params);
2810  
2811          // Validate context.
2812          $context = context_system::instance();
2813          self::validate_context($context);
2814  
2815          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2816          core_user::require_active_user($user);
2817  
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');
2822          }
2823  
2824          $results = array(
2825              'status' => $status,
2826              'warnings' => $warnings
2827          );
2828          return $results;
2829      }
2830  
2831      /**
2832       * Returns description of method result value
2833       *
2834       * @return \core_external\external_description
2835       * @since 3.1
2836       */
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()
2842              )
2843          );
2844      }
2845  
2846      /**
2847       * Returns description of method parameters
2848       *
2849       * @return external_function_parameters
2850       * @since 3.2
2851       */
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),
2862                          )
2863                      ),
2864                      'Config form values',
2865                      VALUE_REQUIRED
2866                  ),
2867              )
2868          );
2869      }
2870  
2871      /**
2872       * Processes a message processor config form.
2873       *
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
2880       */
2881      public static function message_processor_config_form($userid, $name, $formvalues) {
2882          global $USER, $CFG;
2883  
2884          $params = self::validate_parameters(
2885              self::message_processor_config_form_parameters(),
2886              array(
2887                  'userid' => $userid,
2888                  'name' => $name,
2889                  'formvalues' => $formvalues,
2890              )
2891          );
2892  
2893          $user = self::validate_preferences_permissions($params['userid']);
2894  
2895          $processor = get_message_processor($params['name']);
2896          $preferences = [];
2897          $form = new stdClass();
2898  
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'];
2903          }
2904  
2905          $processor->process_form($form, $preferences);
2906  
2907          if (!empty($preferences)) {
2908              set_user_preferences($preferences, $params['userid']);
2909          }
2910      }
2911  
2912      /**
2913       * Returns description of method result value
2914       *
2915       * @return \core_external\external_description
2916       * @since 3.2
2917       */
2918      public static function message_processor_config_form_returns() {
2919          return null;
2920      }
2921  
2922      /**
2923       * Returns description of method parameters
2924       *
2925       * @return external_function_parameters
2926       * @since 3.2
2927       */
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),
2933              )
2934          );
2935      }
2936  
2937      /**
2938       * Get a message processor.
2939       *
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
2945       */
2946      public static function get_message_processor($userid, $name) {
2947          global $USER, $PAGE, $CFG;
2948  
2949          // Check if messaging is enabled.
2950          if (empty($CFG->messaging)) {
2951              throw new moodle_exception('disabled', 'message');
2952          }
2953  
2954          $params = self::validate_parameters(
2955              self::get_message_processor_parameters(),
2956              array(
2957                  'userid' => $userid,
2958                  'name' => $name,
2959              )
2960          );
2961  
2962          if (empty($params['userid'])) {
2963              $params['userid'] = $USER->id;
2964          }
2965  
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']));
2969  
2970          $processor = get_message_processor($params['name']);
2971  
2972          $processoroutput = new \core_message\output\processor($processor, $user);
2973          $renderer = $PAGE->get_renderer('core_message');
2974  
2975          return $processoroutput->export_for_template($renderer);
2976      }
2977  
2978      /**
2979       * Returns description of method result value
2980       *
2981       * @return \core_external\external_description
2982       * @since 3.2
2983       */
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'),
2989              )
2990          );
2991      }
2992  
2993      /**
2994       * Check that the user has enough permission to retrieve message or notifications preferences.
2995       *
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
3000       */
3001      protected static function validate_preferences_permissions($userid) {
3002          global $USER;
3003  
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);
3009          }
3010  
3011          $systemcontext = context_system::instance();
3012          self::validate_context($systemcontext);
3013  
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);
3022          }
3023          return $user;
3024      }
3025  
3026      /**
3027       * Returns a notification or message preference structure.
3028       *
3029       * @return external_single_structure the structure
3030       * @since  Moodle 3.2
3031       * @todo Remove loggedin and loggedoff from processors structure on MDL-73284.
3032       */
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'),
3046                          )
3047                      ),
3048                      'Config form values'
3049                  ),
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?'),
3073                                                          ),
3074                                                          'DEPRECATED ATTRIBUTE -
3075                                                          Kept for backward compatibility, use enabled instead.',
3076                                                      ),
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?'),
3082                                                          ),
3083                                                          'DEPRECATED ATTRIBUTE -
3084                                                          Kept for backward compatibility, use enabled instead.',
3085                                                      ),
3086                                                      'enabled' => new external_value(PARAM_BOOL, 'Is enabled?'),
3087                                                  )
3088                                              ),
3089                                              'Processors values for this notification'
3090                                          ),
3091                                      )
3092                                  ),
3093                                  'List of notificaitons for the component'
3094                              ),
3095                          )
3096                      ),
3097                      'Available components'
3098                  ),
3099              )
3100          );
3101      }
3102  
3103      /**
3104       * Returns description of method parameters
3105       *
3106       * @return external_function_parameters
3107       * @since 3.2
3108       */
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)
3113              )
3114          );
3115      }
3116  
3117      /**
3118       * Get the notification preferences for a given user.
3119       *
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
3124       */
3125      public static function get_user_notification_preferences($userid = 0) {
3126          global $PAGE;
3127  
3128          $params = self::validate_parameters(
3129              self::get_user_notification_preferences_parameters(),
3130              array(
3131                  'userid' => $userid,
3132              )
3133          );
3134          $user = self::validate_preferences_permissions($params['userid']);
3135  
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);
3140  
3141          $renderer = $PAGE->get_renderer('core_message');
3142  
3143          $result = array(
3144              'warnings' => array(),
3145              'preferences' => $notificationlist->export_for_template($renderer)
3146          );
3147          return $result;
3148      }
3149  
3150      /**
3151       * Returns description of method result value
3152       *
3153       * @return \core_external\external_description
3154       * @since 3.2
3155       */
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(),
3161              )
3162          );
3163      }
3164  
3165      /**
3166       * Returns description of method parameters
3167       *
3168       * @return external_function_parameters
3169       * @since 3.2
3170       */
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)
3175              )
3176          );
3177      }
3178  
3179      /**
3180       * Get the notification preferences for a given user.
3181       *
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
3186       */
3187      public static function get_user_message_preferences($userid = 0) {
3188          global $CFG, $PAGE;
3189  
3190          $params = self::validate_parameters(
3191              self::get_user_message_preferences_parameters(),
3192              array(
3193                  'userid' => $userid,
3194              )
3195          );
3196  
3197          $user = self::validate_preferences_permissions($params['userid']);
3198  
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();
3206          });
3207  
3208          $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
3209              return $provider->component === 'moodle';
3210          });
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);
3214  
3215          $renderer = $PAGE->get_renderer('core_message');
3216  
3217          $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
3218  
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
3224          );
3225          return $result;
3226      }
3227  
3228      /**
3229       * Returns description of method result value
3230       *
3231       * @return \core_external\external_description
3232       * @since 3.2
3233       */
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(),
3241              )
3242          );
3243      }
3244  
3245      /**
3246       * Returns description of method parameters for the favourite_conversations() method.
3247       *
3248       * @return external_function_parameters
3249       */
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)
3256                  )
3257              )
3258          );
3259      }
3260  
3261      /**
3262       * Favourite a conversation, or list of conversations for a user.
3263       *
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.
3268       */
3269      public static function set_favourite_conversations(int $userid, array $conversationids) {
3270          global $CFG, $USER;
3271  
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');
3275          }
3276          $params = [
3277              'userid' => $userid,
3278              'conversations' => $conversationids
3279          ];
3280          $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
3281          $systemcontext = context_system::instance();
3282          self::validate_context($systemcontext);
3283  
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.');
3286          }
3287  
3288          foreach ($params['conversations'] as $conversationid) {
3289              \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
3290          }
3291  
3292          return [];
3293      }
3294  
3295      /**
3296       * Return a description of the returns for the create_user_favourite_conversations() method.
3297       *
3298       * @return \core_external\external_description
3299       */
3300      public static function set_favourite_conversations_returns() {
3301          return new external_warnings();
3302      }
3303  
3304      /**
3305       * Returns description of method parameters for unfavourite_conversations() method.
3306       *
3307       * @return external_function_parameters
3308       */
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)
3315                  )
3316              )
3317          );
3318      }
3319  
3320      /**
3321       * Unfavourite a conversation, or list of conversations for a user.
3322       *
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.
3327       */
3328      public static function unset_favourite_conversations(int $userid, array $conversationids) {
3329          global $CFG, $USER;
3330  
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');
3334          }
3335          $params = [
3336              'userid' => $userid,
3337              'conversations' => $conversationids
3338          ];
3339          $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
3340          $systemcontext = context_system::instance();
3341          self::validate_context($systemcontext);
3342  
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.');
3345          }
3346  
3347          foreach ($params['conversations'] as $conversationid) {
3348              \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
3349          }
3350  
3351          return [];
3352      }
3353  
3354      /**
3355       * Unset favourite conversations return description.
3356       *
3357       * @return \core_external\external_description
3358       */
3359      public static function unset_favourite_conversations_returns() {
3360          return new external_warnings();
3361      }
3362  
3363      /**
3364       * Returns description of method parameters for get_member_info() method.
3365       *
3366       * @return external_function_parameters
3367       */
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')
3374                  ),
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)
3377              )
3378          );
3379      }
3380  
3381      /**
3382       * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
3383       *
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.
3386       *
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.
3391       */
3392      public static function get_member_info(
3393          int $referenceuserid,
3394          array $userids,
3395          bool $includecontactrequests = false,
3396          bool $includeprivacyinfo = false
3397      ) {
3398          global $CFG, $USER;
3399  
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');
3403          }
3404          $params = [
3405              'referenceuserid' => $referenceuserid,
3406              'userids' => $userids,
3407              'includecontactrequests' => $includecontactrequests,
3408              'includeprivacyinfo' => $includeprivacyinfo
3409          ];
3410          $params = self::validate_parameters(self::get_member_info_parameters(), $params);
3411          $systemcontext = context_system::instance();
3412          self::validate_context($systemcontext);
3413  
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.');
3416          }
3417  
3418          return \core_message\helper::get_member_info(
3419              $params['referenceuserid'],
3420              $params['userids'],
3421              $params['includecontactrequests'],
3422              $params['includeprivacyinfo']
3423          );
3424      }
3425  
3426      /**
3427       * Get member info return description.
3428       *
3429       * @return \core_external\external_description
3430       */
3431      public static function get_member_info_returns() {
3432          return new external_multiple_structure(
3433              self::get_conversation_member_structure()
3434          );
3435      }
3436  
3437      /**
3438       * Returns description of method parameters for get_conversation_counts() method.
3439       *
3440       * @return external_function_parameters
3441       */
3442      public static function get_conversation_counts_parameters() {
3443          return new external_function_parameters(
3444              [
3445                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3446              ]
3447          );
3448      }
3449  
3450      /**
3451       * Returns an array of conversation counts for the various types of conversations, including favourites.
3452       *
3453       * Return format:
3454       * [
3455       *     'favourites' => 0,
3456       *     'types' => [
3457       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3458       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3459       *      ]
3460       * ]
3461       *
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.
3465       */
3466      public static function get_conversation_counts(int $userid) {
3467          global $CFG, $USER;
3468  
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');
3472          }
3473  
3474          if (empty($userid)) {
3475              $userid = $USER->id;
3476          }
3477  
3478          $params = ['userid' => $userid];
3479          $params = self::validate_parameters(self::get_conversation_counts_parameters(), $params);
3480  
3481          $systemcontext = context_system::instance();
3482          self::validate_context($systemcontext);
3483  
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.');
3486          }
3487  
3488          return \core_message\api::get_conversation_counts($params['userid']);
3489      }
3490  
3491      /**
3492       * Get conversation counts return description.
3493       *
3494       * @return \core_external\external_description
3495       */
3496      public static function get_conversation_counts_returns() {
3497          return new external_single_structure(
3498              [
3499                  'favourites' => new external_value(PARAM_INT, 'Total number of favourite conversations'),
3500                  'types' => new external_single_structure(
3501                      [
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'),
3508                      ]
3509                  ),
3510              ]
3511          );
3512      }
3513  
3514      /**
3515       * Returns description of method parameters for get_unread_conversation_counts() method.
3516       *
3517       * @return external_function_parameters
3518       */
3519      public static function get_unread_conversation_counts_parameters() {
3520          return new external_function_parameters(
3521              [
3522                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3523              ]
3524          );
3525      }
3526  
3527      /**
3528       * Returns an array of unread conversation counts for the various types of conversations, including favourites.
3529       *
3530       * Return format:
3531       * [
3532       *     'favourites' => 0,
3533       *     'types' => [
3534       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3535       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3536       *      ]
3537       * ]
3538       *
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.
3542       */
3543      public static function get_unread_conversation_counts(int $userid) {
3544          global $CFG, $USER;
3545  
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');
3549          }
3550  
3551          if (empty($userid)) {
3552              $userid = $USER->id;
3553          }
3554  
3555          $params = ['userid' => $userid];
3556          $params = self::validate_parameters(self::get_unread_conversation_counts_parameters(), $params);
3557  
3558          $systemcontext = context_system::instance();
3559          self::validate_context($systemcontext);
3560  
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.');
3563          }
3564  
3565          return \core_message\api::get_unread_conversation_counts($params['userid']);
3566      }
3567  
3568      /**
3569       * Get unread conversation counts return description.
3570       *
3571       * @return \core_external\external_description
3572       */
3573      public static function get_unread_conversation_counts_returns() {
3574          return new external_single_structure(
3575              [
3576                  'favourites' => new external_value(PARAM_INT, 'Total number of unread favourite conversations'),
3577                  'types' => new external_single_structure(
3578                      [
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'),
3585                      ]
3586                  ),
3587              ]
3588          );
3589      }
3590  
3591      /**
3592       * Returns description of method parameters
3593       *
3594       * @return external_function_parameters
3595       * @since 3.7
3596       */
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')
3602              )
3603          );
3604      }
3605      /**
3606       * Deletes a message for all users
3607       *
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
3613       */
3614      public static function delete_message_for_all_users(int $messageid, int $userid) {
3615          global $CFG, $USER;
3616  
3617          // Check if private messaging between users is allowed.
3618          if (empty($CFG->messaging)) {
3619              throw new moodle_exception('disabled', 'message');
3620          }
3621  
3622          // Validate params.
3623          $params = array(
3624              'messageid' => $messageid,
3625              'userid' => $userid
3626          );
3627          $params = self::validate_parameters(self::delete_message_for_all_users_parameters(), $params);
3628  
3629          // Validate context.
3630          $context = context_system::instance();
3631          self::validate_context($context);
3632  
3633          core_user::require_active_user($USER);
3634  
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.');
3640          }
3641  
3642          return [];
3643      }
3644      /**
3645       * Returns description of method result value
3646       *
3647       * @return \core_external\external_description
3648       * @since 3.7
3649       */
3650      public static function delete_message_for_all_users_returns() {
3651          return new external_warnings();
3652      }
3653  }