Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

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