Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

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

   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_INT, '1 for getting read messages, 0 for unread, 2 for both',
1967                      VALUE_DEFAULT, 1),
1968                  'newestfirst' => new external_value(
1969                      PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
1970                      VALUE_DEFAULT, true),
1971                  'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
1972                  'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
1973              )
1974          );
1975      }
1976  
1977      /**
1978       * Get messages function implementation.
1979       *
1980       * @since  2.8
1981       * @throws invalid_parameter_exception
1982       * @throws moodle_exception
1983       * @param  int      $useridto       the user id who received the message
1984       * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
1985       * @param  string   $type           type of message to return, expected values: notifications, conversations and both
1986       * @param  int      $read           1 for getting read messages, 0 for unread, 2 for both
1987       * @param  bool     $newestfirst    true for ordering by newest first, false for oldest first
1988       * @param  int      $limitfrom      limit from
1989       * @param  int      $limitnum       limit num
1990       * @return external_description
1991       */
1992      public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = MESSAGE_GET_READ,
1993                                          $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
1994          global $CFG, $USER, $PAGE;
1995  
1996          $warnings = array();
1997  
1998          $params = array(
1999              'useridto' => $useridto,
2000              'useridfrom' => $useridfrom,
2001              'type' => $type,
2002              'read' => $read,
2003              'newestfirst' => $newestfirst,
2004              'limitfrom' => $limitfrom,
2005              'limitnum' => $limitnum
2006          );
2007  
2008          $params = self::validate_parameters(self::get_messages_parameters(), $params);
2009  
2010          $context = context_system::instance();
2011          self::validate_context($context);
2012  
2013          $useridto = $params['useridto'];
2014          $useridfrom = $params['useridfrom'];
2015          $type = $params['type'];
2016          $read = $params['read'];
2017          $newestfirst = $params['newestfirst'];
2018          $limitfrom = $params['limitfrom'];
2019          $limitnum = $params['limitnum'];
2020  
2021          $allowedvalues = array('notifications', 'conversations', 'both');
2022          if (!in_array($type, $allowedvalues)) {
2023              throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
2024                  'allowed values are: ' . implode(',', $allowedvalues));
2025          }
2026  
2027          // Check if private messaging between users is allowed.
2028          if (empty($CFG->messaging)) {
2029              // If we are retreiving only conversations, and messaging is disabled, throw an exception.
2030              if ($type == "conversations") {
2031                  throw new moodle_exception('disabled', 'message');
2032              }
2033              if ($type == "both") {
2034                  $warning = array();
2035                  $warning['item'] = 'message';
2036                  $warning['itemid'] = $USER->id;
2037                  $warning['warningcode'] = '1';
2038                  $warning['message'] = 'Private messages (conversations) are not enabled in this site.
2039                      Only notifications will be returned';
2040                  $warnings[] = $warning;
2041              }
2042          }
2043  
2044          if (!empty($useridto)) {
2045              if (core_user::is_real_user($useridto)) {
2046                  $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2047              } else {
2048                  throw new moodle_exception('invaliduser');
2049              }
2050          }
2051  
2052          if (!empty($useridfrom)) {
2053              // We use get_user here because the from user can be the noreply or support user.
2054              $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2055          }
2056  
2057          // Check if the current user is the sender/receiver or just a privileged user.
2058          if ($useridto != $USER->id and $useridfrom != $USER->id and
2059               !has_capability('moodle/site:readallmessages', $context)) {
2060              throw new moodle_exception('accessdenied', 'admin');
2061          }
2062  
2063          // Which type of messages to retrieve.
2064          $notifications = -1;
2065          if ($type != 'both') {
2066              $notifications = ($type == 'notifications') ? 1 : 0;
2067          }
2068  
2069          $orderdirection = $newestfirst ? 'DESC' : 'ASC';
2070          $sort = "mr.timecreated $orderdirection";
2071  
2072          if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
2073              $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
2074  
2075              // In some cases, we don't need to get the to/from user objects from the sql query.
2076              $userfromfullname = '';
2077              $usertofullname = '';
2078  
2079              // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
2080              if (!empty($useridto)) {
2081                  $usertofullname = fullname($userto, $canviewfullname);
2082                  // The user from may or may not be filled.
2083                  if (!empty($useridfrom)) {
2084                      $userfromfullname = fullname($userfrom, $canviewfullname);
2085                  }
2086              } else {
2087                  // If the useridto field is empty, the useridfrom must be filled.
2088                  $userfromfullname = fullname($userfrom, $canviewfullname);
2089              }
2090              foreach ($messages as $mid => $message) {
2091  
2092                  if (!$message->notification) {
2093                      // Do not return deleted messages.
2094                      if (($useridto == $USER->id and $message->timeusertodeleted) or
2095                          ($useridfrom == $USER->id and $message->timeuserfromdeleted)) {
2096                          unset($messages[$mid]);
2097                          continue;
2098                      }
2099                  } else {
2100                      // Return iconurl for notifications.
2101                      if (!isset($output)) {
2102                          $output = $PAGE->get_renderer('core');
2103                      }
2104  
2105                      if (!empty($message->component) && substr($message->component, 0, 4) == 'mod_') {
2106                          $iconurl = $output->image_url('monologo', $message->component);
2107                      } else {
2108                          $iconurl = $output->image_url('i/marker', 'core');
2109                      }
2110  
2111                      $message->iconurl = clean_param($iconurl->out(), PARAM_URL);
2112                  }
2113  
2114                  // We need to get the user from the query.
2115                  if (empty($userfromfullname)) {
2116                      // Check for non-reply and support users.
2117                      if (core_user::is_real_user($message->useridfrom)) {
2118                          $user = new stdClass();
2119                          $user = username_load_fields_from_object($user, $message, 'userfrom');
2120                          $message->userfromfullname = fullname($user, $canviewfullname);
2121                      } else {
2122                          $user = core_user::get_user($message->useridfrom);
2123                          $message->userfromfullname = fullname($user, $canviewfullname);
2124                      }
2125                  } else {
2126                      $message->userfromfullname = $userfromfullname;
2127                  }
2128  
2129                  // We need to get the user from the query.
2130                  if (empty($usertofullname)) {
2131                      $user = new stdClass();
2132                      $user = username_load_fields_from_object($user, $message, 'userto');
2133                      $message->usertofullname = fullname($user, $canviewfullname);
2134                  } else {
2135                      $message->usertofullname = $usertofullname;
2136                  }
2137  
2138                  $message->text = message_format_message_text($message);
2139                  $messages[$mid] = (array) $message;
2140              }
2141          }
2142  
2143          $results = array(
2144              'messages' => $messages,
2145              'warnings' => $warnings
2146          );
2147  
2148          return $results;
2149      }
2150  
2151      /**
2152       * Get messages return description.
2153       *
2154       * @return external_single_structure
2155       * @since 2.8
2156       */
2157      public static function get_messages_returns() {
2158          return new external_single_structure(
2159              array(
2160                  'messages' => new external_multiple_structure(
2161                      new external_single_structure(
2162                          array(
2163                              'id' => new external_value(PARAM_INT, 'Message id'),
2164                              'useridfrom' => new external_value(PARAM_INT, 'User from id'),
2165                              'useridto' => new external_value(PARAM_INT, 'User to id'),
2166                              'subject' => new external_value(PARAM_TEXT, 'The message subject'),
2167                              'text' => new external_value(PARAM_RAW, 'The message text formated'),
2168                              'fullmessage' => new external_value(PARAM_RAW, 'The message'),
2169                              'fullmessageformat' => new external_format_value('fullmessage'),
2170                              'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
2171                              'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
2172                              'notification' => new external_value(PARAM_INT, 'Is a notification?'),
2173                              'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
2174                              'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
2175                              'timecreated' => new external_value(PARAM_INT, 'Time created'),
2176                              'timeread' => new external_value(PARAM_INT, 'Time read'),
2177                              'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
2178                              'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name'),
2179                              'component' => new external_value(PARAM_TEXT, 'The component that generated the notification',
2180                                  VALUE_OPTIONAL),
2181                              'eventtype' => new external_value(PARAM_TEXT, 'The type of notification', VALUE_OPTIONAL),
2182                              'customdata' => new external_value(PARAM_RAW, 'Custom data to be passed to the message processor.
2183                                  The data here is serialised using json_encode().', VALUE_OPTIONAL),
2184                              'iconurl' => new external_value(PARAM_URL, 'URL for icon, only for notifications.', VALUE_OPTIONAL),
2185                          ), 'message'
2186                      )
2187                  ),
2188                  'warnings' => new external_warnings()
2189              )
2190          );
2191      }
2192  
2193      /**
2194       * Mark all notifications as read parameters description.
2195       *
2196       * @return external_function_parameters
2197       * @since 3.2
2198       */
2199      public static function mark_all_notifications_as_read_parameters() {
2200          return new external_function_parameters(
2201              array(
2202                  'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2203                  'useridfrom' => new external_value(
2204                      PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
2205                      VALUE_DEFAULT, 0),
2206                  'timecreatedto' => new external_value(
2207                      PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
2208                      VALUE_DEFAULT, 0),
2209              )
2210          );
2211      }
2212  
2213      /**
2214       * Mark all notifications as read function.
2215       *
2216       * @since  3.2
2217       * @throws invalid_parameter_exception
2218       * @throws moodle_exception
2219       * @param  int      $useridto       the user id who received the message
2220       * @param  int      $useridfrom     the user id who send the message. -10 or -20 for no-reply or support user
2221       * @param  int      $timecreatedto  mark message created before this time as read, 0 for all messages
2222       * @return external_description
2223       */
2224      public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
2225          global $USER;
2226  
2227          $params = self::validate_parameters(
2228              self::mark_all_notifications_as_read_parameters(),
2229              array(
2230                  'useridto' => $useridto,
2231                  'useridfrom' => $useridfrom,
2232                  'timecreatedto' => $timecreatedto,
2233              )
2234          );
2235  
2236          $context = context_system::instance();
2237          self::validate_context($context);
2238  
2239          $useridto = $params['useridto'];
2240          $useridfrom = $params['useridfrom'];
2241          $timecreatedto = $params['timecreatedto'];
2242  
2243          if (!empty($useridto)) {
2244              if (core_user::is_real_user($useridto)) {
2245                  $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2246              } else {
2247                  throw new moodle_exception('invaliduser');
2248              }
2249          }
2250  
2251          if (!empty($useridfrom)) {
2252              // We use get_user here because the from user can be the noreply or support user.
2253              $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
2254          }
2255  
2256          // Check if the current user is the sender/receiver or just a privileged user.
2257          if ($useridto != $USER->id and $useridfrom != $USER->id and
2258              // The deleteanymessage cap seems more reasonable here than readallmessages.
2259               !has_capability('moodle/site:deleteanymessage', $context)) {
2260              throw new moodle_exception('accessdenied', 'admin');
2261          }
2262  
2263          \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
2264  
2265          return true;
2266      }
2267  
2268      /**
2269       * Mark all notifications as read return description.
2270       *
2271       * @return external_single_structure
2272       * @since 3.2
2273       */
2274      public static function mark_all_notifications_as_read_returns() {
2275          return new external_value(PARAM_BOOL, 'True if the messages were marked read, false otherwise');
2276      }
2277  
2278      /**
2279       * Get unread conversations count parameters description.
2280       *
2281       * @return external_function_parameters
2282       * @since 3.2
2283       */
2284      public static function get_unread_conversations_count_parameters() {
2285          return new external_function_parameters(
2286              array(
2287                  'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
2288              )
2289          );
2290      }
2291  
2292      /**
2293       * Get unread messages count function.
2294       *
2295       * @since  3.2
2296       * @throws invalid_parameter_exception
2297       * @throws moodle_exception
2298       * @param  int      $useridto       the user id who received the message
2299       * @return external_description
2300       */
2301      public static function get_unread_conversations_count($useridto) {
2302          global $USER, $CFG;
2303  
2304          // Check if messaging is enabled.
2305          if (empty($CFG->messaging)) {
2306              throw new moodle_exception('disabled', 'message');
2307          }
2308  
2309          $params = self::validate_parameters(
2310              self::get_unread_conversations_count_parameters(),
2311              array('useridto' => $useridto)
2312          );
2313  
2314          $context = context_system::instance();
2315          self::validate_context($context);
2316  
2317          $useridto = $params['useridto'];
2318  
2319          if (!empty($useridto)) {
2320              if (core_user::is_real_user($useridto)) {
2321                  $userto = core_user::get_user($useridto, '*', MUST_EXIST);
2322              } else {
2323                  throw new moodle_exception('invaliduser');
2324              }
2325          } else {
2326              $useridto = $USER->id;
2327          }
2328  
2329          // Check if the current user is the receiver or just a privileged user.
2330          if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) {
2331              throw new moodle_exception('accessdenied', 'admin');
2332          }
2333  
2334          return \core_message\api::count_unread_conversations($userto);
2335      }
2336  
2337      /**
2338       * Get unread conversations count return description.
2339       *
2340       * @return external_single_structure
2341       * @since 3.2
2342       */
2343      public static function get_unread_conversations_count_returns() {
2344          return new external_value(PARAM_INT, 'The count of unread messages for the user');
2345      }
2346  
2347      /**
2348       * Get blocked users parameters description.
2349       *
2350       * @return external_function_parameters
2351       * @since 2.9
2352       */
2353      public static function get_blocked_users_parameters() {
2354          return new external_function_parameters(
2355              array(
2356                  'userid' => new external_value(PARAM_INT,
2357                                  'the user whose blocked users we want to retrieve',
2358                                  VALUE_REQUIRED),
2359              )
2360          );
2361      }
2362  
2363      /**
2364       * Retrieve a list of users blocked
2365       *
2366       * @param  int $userid the user whose blocked users we want to retrieve
2367       * @return external_description
2368       * @since 2.9
2369       */
2370      public static function get_blocked_users($userid) {
2371          global $CFG, $USER, $PAGE;
2372  
2373          // Warnings array, it can be empty at the end but is mandatory.
2374          $warnings = array();
2375  
2376          // Validate params.
2377          $params = array(
2378              'userid' => $userid
2379          );
2380          $params = self::validate_parameters(self::get_blocked_users_parameters(), $params);
2381          $userid = $params['userid'];
2382  
2383          // Validate context.
2384          $context = context_system::instance();
2385          self::validate_context($context);
2386  
2387          // Check if private messaging between users is allowed.
2388          if (empty($CFG->messaging)) {
2389              throw new moodle_exception('disabled', 'message');
2390          }
2391  
2392          $user = core_user::get_user($userid, '*', MUST_EXIST);
2393          core_user::require_active_user($user);
2394  
2395          // Check if we have permissions for retrieve the information.
2396          $capability = 'moodle/site:manageallmessaging';
2397          if (($USER->id != $userid) && !has_capability($capability, $context)) {
2398              throw new required_capability_exception($context, $capability, 'nopermissions', '');
2399          }
2400  
2401          // Now, we can get safely all the blocked users.
2402          $users = \core_message\api::get_blocked_users($user->id);
2403  
2404          $blockedusers = array();
2405          foreach ($users as $user) {
2406              $newuser = array(
2407                  'id' => $user->id,
2408                  'fullname' => fullname($user),
2409              );
2410  
2411              $userpicture = new user_picture($user);
2412              $userpicture->size = 1; // Size f1.
2413              $newuser['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
2414  
2415              $blockedusers[] = $newuser;
2416          }
2417  
2418          $results = array(
2419              'users' => $blockedusers,
2420              'warnings' => $warnings
2421          );
2422          return $results;
2423      }
2424  
2425      /**
2426       * Get blocked users return description.
2427       *
2428       * @return external_single_structure
2429       * @since 2.9
2430       */
2431      public static function get_blocked_users_returns() {
2432          return new external_single_structure(
2433              array(
2434                  'users' => new external_multiple_structure(
2435                      new external_single_structure(
2436                          array(
2437                              'id' => new external_value(PARAM_INT, 'User ID'),
2438                              'fullname' => new external_value(PARAM_NOTAGS, 'User full name'),
2439                              'profileimageurl' => new external_value(PARAM_URL, 'User picture URL', VALUE_OPTIONAL)
2440                          )
2441                      ),
2442                      'List of blocked users'
2443                  ),
2444                  'warnings' => new external_warnings()
2445              )
2446          );
2447      }
2448  
2449      /**
2450       * Returns description of method parameters
2451       *
2452       * @return external_function_parameters
2453       * @since 2.9
2454       */
2455      public static function mark_message_read_parameters() {
2456          return new external_function_parameters(
2457              array(
2458                  'messageid' => new external_value(PARAM_INT, 'id of the message in the messages table'),
2459                  'timeread' => new external_value(PARAM_INT, 'timestamp for when the message should be marked read',
2460                      VALUE_DEFAULT, 0)
2461              )
2462          );
2463      }
2464  
2465      /**
2466       * Mark a single message as read, trigger message_viewed event
2467       *
2468       * @param  int $messageid id of the message (in the message table)
2469       * @param  int $timeread timestamp for when the message should be marked read
2470       * @return external_description
2471       * @throws invalid_parameter_exception
2472       * @throws moodle_exception
2473       * @since 2.9
2474       */
2475      public static function mark_message_read($messageid, $timeread) {
2476          global $CFG, $DB, $USER;
2477  
2478          // Check if private messaging between users is allowed.
2479          if (empty($CFG->messaging)) {
2480              throw new moodle_exception('disabled', 'message');
2481          }
2482  
2483          // Warnings array, it can be empty at the end but is mandatory.
2484          $warnings = array();
2485  
2486          // Validate params.
2487          $params = array(
2488              'messageid' => $messageid,
2489              'timeread' => $timeread
2490          );
2491          $params = self::validate_parameters(self::mark_message_read_parameters(), $params);
2492  
2493          if (empty($params['timeread'])) {
2494              $timeread = time();
2495          } else {
2496              $timeread = $params['timeread'];
2497          }
2498  
2499          // Validate context.
2500          $context = context_system::instance();
2501          self::validate_context($context);
2502  
2503          $sql = "SELECT m.*, mcm.userid as useridto
2504                    FROM {messages} m
2505              INNER JOIN {message_conversations} mc
2506                      ON m.conversationid = mc.id
2507              INNER JOIN {message_conversation_members} mcm
2508                      ON mcm.conversationid = mc.id
2509               LEFT JOIN {message_user_actions} mua
2510                      ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
2511                   WHERE mua.id is NULL
2512                     AND mcm.userid != m.useridfrom
2513                     AND m.id = ?";
2514          $messageparams = [];
2515          $messageparams[] = $USER->id;
2516          $messageparams[] = \core_message\api::MESSAGE_ACTION_READ;
2517          $messageparams[] = $params['messageid'];
2518          $message = $DB->get_record_sql($sql, $messageparams, MUST_EXIST);
2519  
2520          if ($message->useridto != $USER->id) {
2521              throw new invalid_parameter_exception('Invalid messageid, you don\'t have permissions to mark this message as read');
2522          }
2523  
2524          \core_message\api::mark_message_as_read($USER->id, $message, $timeread);
2525  
2526          $results = array(
2527              'messageid' => $message->id,
2528              'warnings' => $warnings
2529          );
2530          return $results;
2531      }
2532  
2533      /**
2534       * Returns description of method result value
2535       *
2536       * @return external_description
2537       * @since 2.9
2538       */
2539      public static function mark_message_read_returns() {
2540          return new external_single_structure(
2541              array(
2542                  'messageid' => new external_value(PARAM_INT, 'the id of the message in the messages table'),
2543                  'warnings' => new external_warnings()
2544              )
2545          );
2546      }
2547  
2548      /**
2549       * Returns description of method parameters
2550       *
2551       * @return external_function_parameters
2552       */
2553      public static function mark_notification_read_parameters() {
2554          return new external_function_parameters(
2555              array(
2556                  'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2557                  'timeread' => new external_value(PARAM_INT, 'timestamp for when the notification should be marked read',
2558                      VALUE_DEFAULT, 0)
2559              )
2560          );
2561      }
2562  
2563      /**
2564       * Mark a single notification as read.
2565       *
2566       * This will trigger a 'notification_viewed' event.
2567       *
2568       * @param int $notificationid id of the notification
2569       * @param int $timeread timestamp for when the notification should be marked read
2570       * @return external_description
2571       * @throws invalid_parameter_exception
2572       * @throws moodle_exception
2573       */
2574      public static function mark_notification_read($notificationid, $timeread) {
2575          global $CFG, $DB, $USER;
2576  
2577          // Warnings array, it can be empty at the end but is mandatory.
2578          $warnings = array();
2579  
2580          // Validate params.
2581          $params = array(
2582              'notificationid' => $notificationid,
2583              'timeread' => $timeread
2584          );
2585          $params = self::validate_parameters(self::mark_notification_read_parameters(), $params);
2586  
2587          if (empty($params['timeread'])) {
2588              $timeread = time();
2589          } else {
2590              $timeread = $params['timeread'];
2591          }
2592  
2593          // Validate context.
2594          $context = context_system::instance();
2595          self::validate_context($context);
2596  
2597          $notification = $DB->get_record('notifications', ['id' => $params['notificationid']], '*', MUST_EXIST);
2598  
2599          if ($notification->useridto != $USER->id) {
2600              throw new invalid_parameter_exception('Invalid notificationid, you don\'t have permissions to mark this ' .
2601                  'notification as read');
2602          }
2603  
2604          \core_message\api::mark_notification_as_read($notification, $timeread);
2605  
2606          $results = array(
2607              'notificationid' => $notification->id,
2608              'warnings' => $warnings
2609          );
2610  
2611          return $results;
2612      }
2613  
2614      /**
2615       * Returns description of method result value
2616       *
2617       * @return external_description
2618       */
2619      public static function mark_notification_read_returns() {
2620          return new external_single_structure(
2621              array(
2622                  'notificationid' => new external_value(PARAM_INT, 'id of the notification'),
2623                  'warnings' => new external_warnings()
2624              )
2625          );
2626      }
2627  
2628      /**
2629       * Mark all conversation messages as read parameters description.
2630       *
2631       * @return external_function_parameters
2632       * @since 3.6
2633       */
2634      public static function mark_all_conversation_messages_as_read_parameters() {
2635          return new external_function_parameters(
2636              array(
2637                  'userid' => new external_value(PARAM_INT, 'The user id who who we are marking the messages as read for'),
2638                  'conversationid' =>
2639                      new external_value(PARAM_INT, 'The conversation id who who we are marking the messages as read for')
2640              )
2641          );
2642      }
2643  
2644      /**
2645       * Mark all conversation messages as read function.
2646       *
2647       * @param int $userid The user id of who we want to delete the conversation for
2648       * @param int $conversationid The id of the conversations
2649       * @since 3.6
2650       */
2651      public static function mark_all_conversation_messages_as_read(int $userid, int $conversationid) {
2652          global $CFG;
2653  
2654          // Check if messaging is enabled.
2655          if (empty($CFG->messaging)) {
2656              throw new moodle_exception('disabled', 'message');
2657          }
2658  
2659          $params = array(
2660              'userid' => $userid,
2661              'conversationid' => $conversationid,
2662          );
2663          $params = self::validate_parameters(self::mark_all_conversation_messages_as_read_parameters(), $params);
2664  
2665          $context = context_system::instance();
2666          self::validate_context($context);
2667  
2668          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2669          core_user::require_active_user($user);
2670  
2671          if (\core_message\api::can_mark_all_messages_as_read($params['userid'], $params['conversationid'])) {
2672              \core_message\api::mark_all_messages_as_read($params['userid'], $params['conversationid']);
2673          } else {
2674              throw new moodle_exception('accessdenied', 'admin');
2675          }
2676      }
2677  
2678      /**
2679       * Mark all conversation messages as read return description.
2680       *
2681       * @return external_warnings
2682       * @since 3.6
2683       */
2684      public static function mark_all_conversation_messages_as_read_returns() {
2685          return null;
2686      }
2687  
2688      /**
2689       * Returns description of method parameters.
2690       *
2691       * @return external_function_parameters
2692       * @since 3.6
2693       */
2694      public static function delete_conversations_by_id_parameters() {
2695          return new external_function_parameters(
2696              array(
2697                  'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the conversation for'),
2698                  'conversationids' => new external_multiple_structure(
2699                      new external_value(PARAM_INT, 'The id of the conversation'),
2700                      'List of conversation IDs'
2701                  ),
2702              )
2703          );
2704      }
2705  
2706      /**
2707       * Deletes a conversation.
2708       *
2709       * @param int $userid The user id of who we want to delete the conversation for
2710       * @param int[] $conversationids The ids of the conversations
2711       * @return array
2712       * @throws moodle_exception
2713       * @since 3.6
2714       */
2715      public static function delete_conversations_by_id($userid, array $conversationids) {
2716          global $CFG;
2717  
2718          // Check if private messaging between users is allowed.
2719          if (empty($CFG->messaging)) {
2720              throw new moodle_exception('disabled', 'message');
2721          }
2722  
2723          // Validate params.
2724          $params = [
2725              'userid' => $userid,
2726              'conversationids' => $conversationids,
2727          ];
2728          $params = self::validate_parameters(self::delete_conversations_by_id_parameters(), $params);
2729  
2730          // Validate context.
2731          $context = context_system::instance();
2732          self::validate_context($context);
2733  
2734          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2735          core_user::require_active_user($user);
2736  
2737          foreach ($params['conversationids'] as $conversationid) {
2738              if (\core_message\api::can_delete_conversation($user->id, $conversationid)) {
2739                  \core_message\api::delete_conversation_by_id($user->id, $conversationid);
2740              } else {
2741                  throw new moodle_exception("You do not have permission to delete the conversation '$conversationid'");
2742              }
2743          }
2744  
2745          return [];
2746      }
2747  
2748      /**
2749       * Returns description of method result value.
2750       *
2751       * @return external_description
2752       * @since 3.6
2753       */
2754      public static function delete_conversations_by_id_returns() {
2755          return new external_warnings();
2756      }
2757  
2758      /**
2759       * Returns description of method parameters
2760       *
2761       * @return external_function_parameters
2762       * @since 3.1
2763       */
2764      public static function delete_message_parameters() {
2765          return new external_function_parameters(
2766              array(
2767                  'messageid' => new external_value(PARAM_INT, 'The message id'),
2768                  'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
2769                  'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
2770              )
2771          );
2772      }
2773  
2774      /**
2775       * Deletes a message
2776       *
2777       * @param  int $messageid the message id
2778       * @param  int $userid the user id of who we want to delete the message for
2779       * @param  bool $read if is a message read (default to true)
2780       * @return external_description
2781       * @throws moodle_exception
2782       * @since 3.1
2783       */
2784      public static function delete_message($messageid, $userid, $read = true) {
2785          global $CFG;
2786  
2787          // Check if private messaging between users is allowed.
2788          if (empty($CFG->messaging)) {
2789              throw new moodle_exception('disabled', 'message');
2790          }
2791  
2792          // Warnings array, it can be empty at the end but is mandatory.
2793          $warnings = array();
2794  
2795          // Validate params.
2796          $params = array(
2797              'messageid' => $messageid,
2798              'userid' => $userid,
2799              'read' => $read
2800          );
2801          $params = self::validate_parameters(self::delete_message_parameters(), $params);
2802  
2803          // Validate context.
2804          $context = context_system::instance();
2805          self::validate_context($context);
2806  
2807          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2808          core_user::require_active_user($user);
2809  
2810          if (\core_message\api::can_delete_message($user->id, $params['messageid'])) {
2811              $status = \core_message\api::delete_message($user->id, $params['messageid']);
2812          } else {
2813              throw new moodle_exception('You do not have permission to delete this message');
2814          }
2815  
2816          $results = array(
2817              'status' => $status,
2818              'warnings' => $warnings
2819          );
2820          return $results;
2821      }
2822  
2823      /**
2824       * Returns description of method result value
2825       *
2826       * @return external_description
2827       * @since 3.1
2828       */
2829      public static function delete_message_returns() {
2830          return new external_single_structure(
2831              array(
2832                  'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
2833                  'warnings' => new external_warnings()
2834              )
2835          );
2836      }
2837  
2838      /**
2839       * Returns description of method parameters
2840       *
2841       * @return external_function_parameters
2842       * @since 3.2
2843       */
2844      public static function message_processor_config_form_parameters() {
2845          return new external_function_parameters(
2846              array(
2847                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
2848                  'name' => new external_value(PARAM_SAFEDIR, 'The name of the message processor'),
2849                  'formvalues' => new external_multiple_structure(
2850                      new external_single_structure(
2851                          array(
2852                              'name' => new external_value(PARAM_TEXT, 'name of the form element', VALUE_REQUIRED),
2853                              'value' => new external_value(PARAM_RAW, 'value of the form element', VALUE_REQUIRED),
2854                          )
2855                      ),
2856                      'Config form values',
2857                      VALUE_REQUIRED
2858                  ),
2859              )
2860          );
2861      }
2862  
2863      /**
2864       * Processes a message processor config form.
2865       *
2866       * @param  int $userid the user id
2867       * @param  string $name the name of the processor
2868       * @param  array $formvalues the form values
2869       * @return external_description
2870       * @throws moodle_exception
2871       * @since 3.2
2872       */
2873      public static function message_processor_config_form($userid, $name, $formvalues) {
2874          global $USER, $CFG;
2875  
2876          $params = self::validate_parameters(
2877              self::message_processor_config_form_parameters(),
2878              array(
2879                  'userid' => $userid,
2880                  'name' => $name,
2881                  'formvalues' => $formvalues,
2882              )
2883          );
2884  
2885          $user = self::validate_preferences_permissions($params['userid']);
2886  
2887          $processor = get_message_processor($params['name']);
2888          $preferences = [];
2889          $form = new stdClass();
2890  
2891          foreach ($params['formvalues'] as $formvalue) {
2892              // Curly braces to ensure interpretation is consistent between
2893              // php 5 and php 7.
2894              $form->{$formvalue['name']} = $formvalue['value'];
2895          }
2896  
2897          $processor->process_form($form, $preferences);
2898  
2899          if (!empty($preferences)) {
2900              set_user_preferences($preferences, $params['userid']);
2901          }
2902      }
2903  
2904      /**
2905       * Returns description of method result value
2906       *
2907       * @return external_description
2908       * @since 3.2
2909       */
2910      public static function message_processor_config_form_returns() {
2911          return null;
2912      }
2913  
2914      /**
2915       * Returns description of method parameters
2916       *
2917       * @return external_function_parameters
2918       * @since 3.2
2919       */
2920      public static function get_message_processor_parameters() {
2921          return new external_function_parameters(
2922              array(
2923                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user'),
2924                  'name' => new external_value(PARAM_SAFEDIR, 'The name of the message processor', VALUE_REQUIRED),
2925              )
2926          );
2927      }
2928  
2929      /**
2930       * Get a message processor.
2931       *
2932       * @param int $userid
2933       * @param string $name the name of the processor
2934       * @return external_description
2935       * @throws moodle_exception
2936       * @since 3.2
2937       */
2938      public static function get_message_processor($userid, $name) {
2939          global $USER, $PAGE, $CFG;
2940  
2941          // Check if messaging is enabled.
2942          if (empty($CFG->messaging)) {
2943              throw new moodle_exception('disabled', 'message');
2944          }
2945  
2946          $params = self::validate_parameters(
2947              self::get_message_processor_parameters(),
2948              array(
2949                  'userid' => $userid,
2950                  'name' => $name,
2951              )
2952          );
2953  
2954          if (empty($params['userid'])) {
2955              $params['userid'] = $USER->id;
2956          }
2957  
2958          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2959          core_user::require_active_user($user);
2960          self::validate_context(context_user::instance($params['userid']));
2961  
2962          $processor = get_message_processor($params['name']);
2963  
2964          $processoroutput = new \core_message\output\processor($processor, $user);
2965          $renderer = $PAGE->get_renderer('core_message');
2966  
2967          return $processoroutput->export_for_template($renderer);
2968      }
2969  
2970      /**
2971       * Returns description of method result value
2972       *
2973       * @return external_description
2974       * @since 3.2
2975       */
2976      public static function get_message_processor_returns() {
2977          return new external_function_parameters(
2978              array(
2979                  'systemconfigured' => new external_value(PARAM_BOOL, 'Site configuration status'),
2980                  'userconfigured' => new external_value(PARAM_BOOL, 'The user configuration status'),
2981              )
2982          );
2983      }
2984  
2985      /**
2986       * Check that the user has enough permission to retrieve message or notifications preferences.
2987       *
2988       * @param  int $userid the user id requesting the preferences
2989       * @return stdClass full user object
2990       * @throws moodle_exception
2991       * @since  Moodle 3.2
2992       */
2993      protected static function validate_preferences_permissions($userid) {
2994          global $USER;
2995  
2996          if (empty($userid)) {
2997              $user = $USER;
2998          } else {
2999              $user = core_user::get_user($userid, '*', MUST_EXIST);
3000              core_user::require_active_user($user);
3001          }
3002  
3003          $systemcontext = context_system::instance();
3004          self::validate_context($systemcontext);
3005  
3006          // Check access control.
3007          if ($user->id == $USER->id) {
3008              // Editing own message profile.
3009              require_capability('moodle/user:editownmessageprofile', $systemcontext);
3010          } else {
3011              // Teachers, parents, etc.
3012              $personalcontext = context_user::instance($user->id);
3013              require_capability('moodle/user:editmessageprofile', $personalcontext);
3014          }
3015          return $user;
3016      }
3017  
3018      /**
3019       * Returns a notification or message preference structure.
3020       *
3021       * @return external_single_structure the structure
3022       * @since  Moodle 3.2
3023       * @todo Remove loggedin and loggedoff from processors structure on MDL-73284.
3024       */
3025      protected static function get_preferences_structure() {
3026          return new external_single_structure(
3027              array(
3028                  'userid' => new external_value(PARAM_INT, 'User id'),
3029                  'disableall' => new external_value(PARAM_INT, 'Whether all the preferences are disabled'),
3030                  'processors' => new external_multiple_structure(
3031                      new external_single_structure(
3032                          array(
3033                              'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3034                              'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3035                              'hassettings' => new external_value(PARAM_BOOL, 'Whether has settings'),
3036                              'contextid' => new external_value(PARAM_INT, 'Context id'),
3037                              'userconfigured' => new external_value(PARAM_INT, 'Whether is configured by the user'),
3038                          )
3039                      ),
3040                      'Config form values'
3041                  ),
3042                  'components' => new external_multiple_structure(
3043                      new external_single_structure(
3044                          array(
3045                              'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3046                              'notifications' => new external_multiple_structure(
3047                                  new external_single_structure(
3048                                      array(
3049                                          'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3050                                          'preferencekey' => new external_value(PARAM_ALPHANUMEXT, 'Preference key'),
3051                                          'processors' => new external_multiple_structure(
3052                                              new external_single_structure(
3053                                                  array(
3054                                                      'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3055                                                      'name' => new external_value(PARAM_PLUGIN, 'Processor name'),
3056                                                      'locked' => new external_value(PARAM_BOOL, 'Is locked by admin?'),
3057                                                      'lockedmessage' => new external_value(PARAM_TEXT,
3058                                                          'Text to display if locked', VALUE_OPTIONAL),
3059                                                      'userconfigured' => new external_value(PARAM_INT, 'Is configured?'),
3060                                                      'loggedin' => new external_single_structure(
3061                                                          array(
3062                                                              'name' => new external_value(PARAM_NOTAGS, 'Name'),
3063                                                              'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3064                                                              'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3065                                                          ),
3066                                                          'DEPRECATED ATTRIBUTE -
3067                                                          Kept for backward compatibility, use enabled instead.',
3068                                                      ),
3069                                                      'loggedoff' => new external_single_structure(
3070                                                          array(
3071                                                              'name' => new external_value(PARAM_NOTAGS, 'Name'),
3072                                                              'displayname' => new external_value(PARAM_TEXT, 'Display name'),
3073                                                              'checked' => new external_value(PARAM_BOOL, 'Is checked?'),
3074                                                          ),
3075                                                          'DEPRECATED ATTRIBUTE -
3076                                                          Kept for backward compatibility, use enabled instead.',
3077                                                      ),
3078                                                      'enabled' => new external_value(PARAM_BOOL, 'Is enabled?'),
3079                                                  )
3080                                              ),
3081                                              'Processors values for this notification'
3082                                          ),
3083                                      )
3084                                  ),
3085                                  'List of notificaitons for the component'
3086                              ),
3087                          )
3088                      ),
3089                      'Available components'
3090                  ),
3091              )
3092          );
3093      }
3094  
3095      /**
3096       * Returns description of method parameters
3097       *
3098       * @return external_function_parameters
3099       * @since 3.2
3100       */
3101      public static function get_user_notification_preferences_parameters() {
3102          return new external_function_parameters(
3103              array(
3104                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3105              )
3106          );
3107      }
3108  
3109      /**
3110       * Get the notification preferences for a given user.
3111       *
3112       * @param int $userid id of the user, 0 for current user
3113       * @return external_description
3114       * @throws moodle_exception
3115       * @since 3.2
3116       */
3117      public static function get_user_notification_preferences($userid = 0) {
3118          global $PAGE;
3119  
3120          $params = self::validate_parameters(
3121              self::get_user_notification_preferences_parameters(),
3122              array(
3123                  'userid' => $userid,
3124              )
3125          );
3126          $user = self::validate_preferences_permissions($params['userid']);
3127  
3128          $processors = get_message_processors();
3129          $providers = message_get_providers_for_user($user->id);
3130          $preferences = \core_message\api::get_all_message_preferences($processors, $providers, $user);
3131          $notificationlist = new \core_message\output\preferences\notification_list($processors, $providers, $preferences, $user);
3132  
3133          $renderer = $PAGE->get_renderer('core_message');
3134  
3135          $result = array(
3136              'warnings' => array(),
3137              'preferences' => $notificationlist->export_for_template($renderer)
3138          );
3139          return $result;
3140      }
3141  
3142      /**
3143       * Returns description of method result value
3144       *
3145       * @return external_description
3146       * @since 3.2
3147       */
3148      public static function get_user_notification_preferences_returns() {
3149          return new external_function_parameters(
3150              array(
3151                  'preferences' => self::get_preferences_structure(),
3152                  'warnings' => new external_warnings(),
3153              )
3154          );
3155      }
3156  
3157      /**
3158       * Returns description of method parameters
3159       *
3160       * @return external_function_parameters
3161       * @since 3.2
3162       */
3163      public static function get_user_message_preferences_parameters() {
3164          return new external_function_parameters(
3165              array(
3166                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3167              )
3168          );
3169      }
3170  
3171      /**
3172       * Get the notification preferences for a given user.
3173       *
3174       * @param int $userid id of the user, 0 for current user
3175       * @return external_description
3176       * @throws moodle_exception
3177       * @since 3.2
3178       */
3179      public static function get_user_message_preferences($userid = 0) {
3180          global $CFG, $PAGE;
3181  
3182          $params = self::validate_parameters(
3183              self::get_user_message_preferences_parameters(),
3184              array(
3185                  'userid' => $userid,
3186              )
3187          );
3188  
3189          $user = self::validate_preferences_permissions($params['userid']);
3190  
3191          // Filter out enabled, available system_configured and user_configured processors only.
3192          $readyprocessors = array_filter(get_message_processors(), function($processor) {
3193              return $processor->enabled &&
3194                  $processor->configured &&
3195                  $processor->object->is_user_configured() &&
3196                  // Filter out processors that don't have and message preferences to configure.
3197                  $processor->object->has_message_preferences();
3198          });
3199  
3200          $providers = array_filter(message_get_providers_for_user($user->id), function($provider) {
3201              return $provider->component === 'moodle';
3202          });
3203          $preferences = \core_message\api::get_all_message_preferences($readyprocessors, $providers, $user);
3204          $notificationlistoutput = new \core_message\output\preferences\message_notification_list($readyprocessors,
3205              $providers, $preferences, $user);
3206  
3207          $renderer = $PAGE->get_renderer('core_message');
3208  
3209          $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $user);
3210  
3211          $result = array(
3212              'warnings' => array(),
3213              'preferences' => $notificationlistoutput->export_for_template($renderer),
3214              'blocknoncontacts' => \core_message\api::get_user_privacy_messaging_preference($user->id),
3215              'entertosend' => $entertosend
3216          );
3217          return $result;
3218      }
3219  
3220      /**
3221       * Returns description of method result value
3222       *
3223       * @return external_description
3224       * @since 3.2
3225       */
3226      public static function get_user_message_preferences_returns() {
3227          return new external_function_parameters(
3228              array(
3229                  'preferences' => self::get_preferences_structure(),
3230                  'blocknoncontacts' => new external_value(PARAM_INT, 'Privacy messaging setting to define who can message you'),
3231                  'entertosend' => new external_value(PARAM_BOOL, 'User preference for using enter to send messages'),
3232                  'warnings' => new external_warnings(),
3233              )
3234          );
3235      }
3236  
3237      /**
3238       * Returns description of method parameters for the favourite_conversations() method.
3239       *
3240       * @return external_function_parameters
3241       */
3242      public static function set_favourite_conversations_parameters() {
3243          return new external_function_parameters(
3244              array(
3245                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3246                  'conversations' => new external_multiple_structure(
3247                      new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3248                  )
3249              )
3250          );
3251      }
3252  
3253      /**
3254       * Favourite a conversation, or list of conversations for a user.
3255       *
3256       * @param int $userid the id of the user, or 0 for the current user.
3257       * @param array $conversationids the list of conversations ids to favourite.
3258       * @return array
3259       * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3260       */
3261      public static function set_favourite_conversations(int $userid, array $conversationids) {
3262          global $CFG, $USER;
3263  
3264          // All the business logic checks that really shouldn't be in here.
3265          if (empty($CFG->messaging)) {
3266              throw new moodle_exception('disabled', 'message');
3267          }
3268          $params = [
3269              'userid' => $userid,
3270              'conversations' => $conversationids
3271          ];
3272          $params = self::validate_parameters(self::set_favourite_conversations_parameters(), $params);
3273          $systemcontext = context_system::instance();
3274          self::validate_context($systemcontext);
3275  
3276          if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3277              throw new moodle_exception('You do not have permission to perform this action.');
3278          }
3279  
3280          foreach ($params['conversations'] as $conversationid) {
3281              \core_message\api::set_favourite_conversation($conversationid, $params['userid']);
3282          }
3283  
3284          return [];
3285      }
3286  
3287      /**
3288       * Return a description of the returns for the create_user_favourite_conversations() method.
3289       *
3290       * @return external_description
3291       */
3292      public static function set_favourite_conversations_returns() {
3293          return new external_warnings();
3294      }
3295  
3296      /**
3297       * Returns description of method parameters for unfavourite_conversations() method.
3298       *
3299       * @return external_function_parameters
3300       */
3301      public static function unset_favourite_conversations_parameters() {
3302          return new external_function_parameters(
3303              array(
3304                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0),
3305                  'conversations' => new external_multiple_structure(
3306                      new external_value(PARAM_INT, 'id of the conversation', VALUE_DEFAULT, 0)
3307                  )
3308              )
3309          );
3310      }
3311  
3312      /**
3313       * Unfavourite a conversation, or list of conversations for a user.
3314       *
3315       * @param int $userid the id of the user, or 0 for the current user.
3316       * @param array $conversationids the list of conversations ids unset as favourites.
3317       * @return array
3318       * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3319       */
3320      public static function unset_favourite_conversations(int $userid, array $conversationids) {
3321          global $CFG, $USER;
3322  
3323          // All the business logic checks that really shouldn't be in here.
3324          if (empty($CFG->messaging)) {
3325              throw new moodle_exception('disabled', 'message');
3326          }
3327          $params = [
3328              'userid' => $userid,
3329              'conversations' => $conversationids
3330          ];
3331          $params = self::validate_parameters(self::unset_favourite_conversations_parameters(), $params);
3332          $systemcontext = context_system::instance();
3333          self::validate_context($systemcontext);
3334  
3335          if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3336              throw new moodle_exception('You do not have permission to perform this action.');
3337          }
3338  
3339          foreach ($params['conversations'] as $conversationid) {
3340              \core_message\api::unset_favourite_conversation($conversationid, $params['userid']);
3341          }
3342  
3343          return [];
3344      }
3345  
3346      /**
3347       * Unset favourite conversations return description.
3348       *
3349       * @return external_description
3350       */
3351      public static function unset_favourite_conversations_returns() {
3352          return new external_warnings();
3353      }
3354  
3355      /**
3356       * Returns description of method parameters for get_member_info() method.
3357       *
3358       * @return external_function_parameters
3359       */
3360      public static function get_member_info_parameters() {
3361          return new external_function_parameters(
3362              array(
3363                  'referenceuserid' => new external_value(PARAM_INT, 'id of the user'),
3364                  'userids' => new external_multiple_structure(
3365                      new external_value(PARAM_INT, 'id of members to get')
3366                  ),
3367                  'includecontactrequests' => new external_value(PARAM_BOOL, 'include contact requests in response', VALUE_DEFAULT, false),
3368                  'includeprivacyinfo' => new external_value(PARAM_BOOL, 'include privacy info in response', VALUE_DEFAULT, false)
3369              )
3370          );
3371      }
3372  
3373      /**
3374       * Returns conversation member info for the supplied users, relative to the supplied referenceuserid.
3375       *
3376       * This is the basic structure used when returning members, and includes information about the relationship between each member
3377       * and the referenceuser, such as a whether the referenceuser has marked the member as a contact, or has blocked them.
3378       *
3379       * @param int $referenceuserid the id of the user which check contact and blocked status.
3380       * @param array $userids
3381       * @return array the array of objects containing member info.
3382       * @throws moodle_exception if messaging is disabled or if the user cannot perform the action.
3383       */
3384      public static function get_member_info(
3385          int $referenceuserid,
3386          array $userids,
3387          bool $includecontactrequests = false,
3388          bool $includeprivacyinfo = false
3389      ) {
3390          global $CFG, $USER;
3391  
3392          // All the business logic checks that really shouldn't be in here.
3393          if (empty($CFG->messaging)) {
3394              throw new moodle_exception('disabled', 'message');
3395          }
3396          $params = [
3397              'referenceuserid' => $referenceuserid,
3398              'userids' => $userids,
3399              'includecontactrequests' => $includecontactrequests,
3400              'includeprivacyinfo' => $includeprivacyinfo
3401          ];
3402          $params = self::validate_parameters(self::get_member_info_parameters(), $params);
3403          $systemcontext = context_system::instance();
3404          self::validate_context($systemcontext);
3405  
3406          if (($USER->id != $referenceuserid) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3407              throw new moodle_exception('You do not have permission to perform this action.');
3408          }
3409  
3410          return \core_message\helper::get_member_info(
3411              $params['referenceuserid'],
3412              $params['userids'],
3413              $params['includecontactrequests'],
3414              $params['includeprivacyinfo']
3415          );
3416      }
3417  
3418      /**
3419       * Get member info return description.
3420       *
3421       * @return external_description
3422       */
3423      public static function get_member_info_returns() {
3424          return new external_multiple_structure(
3425              self::get_conversation_member_structure()
3426          );
3427      }
3428  
3429      /**
3430       * Returns description of method parameters for get_conversation_counts() method.
3431       *
3432       * @return external_function_parameters
3433       */
3434      public static function get_conversation_counts_parameters() {
3435          return new external_function_parameters(
3436              [
3437                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3438              ]
3439          );
3440      }
3441  
3442      /**
3443       * Returns an array of conversation counts for the various types of conversations, including favourites.
3444       *
3445       * Return format:
3446       * [
3447       *     'favourites' => 0,
3448       *     'types' => [
3449       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3450       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3451       *      ]
3452       * ]
3453       *
3454       * @param int $userid the id of the user whose counts we are fetching.
3455       * @return array the array of conversation counts, indexed by type.
3456       * @throws moodle_exception if the current user cannot perform this action.
3457       */
3458      public static function get_conversation_counts(int $userid) {
3459          global $CFG, $USER;
3460  
3461          // All the business logic checks that really shouldn't be in here.
3462          if (empty($CFG->messaging)) {
3463              throw new moodle_exception('disabled', 'message');
3464          }
3465  
3466          if (empty($userid)) {
3467              $userid = $USER->id;
3468          }
3469  
3470          $params = ['userid' => $userid];
3471          $params = self::validate_parameters(self::get_conversation_counts_parameters(), $params);
3472  
3473          $systemcontext = context_system::instance();
3474          self::validate_context($systemcontext);
3475  
3476          if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3477              throw new moodle_exception('You do not have permission to perform this action.');
3478          }
3479  
3480          return \core_message\api::get_conversation_counts($params['userid']);
3481      }
3482  
3483      /**
3484       * Get conversation counts return description.
3485       *
3486       * @return external_description
3487       */
3488      public static function get_conversation_counts_returns() {
3489          return new external_single_structure(
3490              [
3491                  'favourites' => new external_value(PARAM_INT, 'Total number of favourite conversations'),
3492                  'types' => new external_single_structure(
3493                      [
3494                          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
3495                              'Total number of individual conversations'),
3496                          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
3497                              'Total number of group conversations'),
3498                          \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
3499                              'Total number of self conversations'),
3500                      ]
3501                  ),
3502              ]
3503          );
3504      }
3505  
3506      /**
3507       * Returns description of method parameters for get_unread_conversation_counts() method.
3508       *
3509       * @return external_function_parameters
3510       */
3511      public static function get_unread_conversation_counts_parameters() {
3512          return new external_function_parameters(
3513              [
3514                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_DEFAULT, 0)
3515              ]
3516          );
3517      }
3518  
3519      /**
3520       * Returns an array of unread conversation counts for the various types of conversations, including favourites.
3521       *
3522       * Return format:
3523       * [
3524       *     'favourites' => 0,
3525       *     'types' => [
3526       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
3527       *          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0
3528       *      ]
3529       * ]
3530       *
3531       * @param int $userid the id of the user whose counts we are fetching.
3532       * @return array the array of unread conversation counts, indexed by type.
3533       * @throws moodle_exception if the current user cannot perform this action.
3534       */
3535      public static function get_unread_conversation_counts(int $userid) {
3536          global $CFG, $USER;
3537  
3538          // All the business logic checks that really shouldn't be in here.
3539          if (empty($CFG->messaging)) {
3540              throw new moodle_exception('disabled', 'message');
3541          }
3542  
3543          if (empty($userid)) {
3544              $userid = $USER->id;
3545          }
3546  
3547          $params = ['userid' => $userid];
3548          $params = self::validate_parameters(self::get_unread_conversation_counts_parameters(), $params);
3549  
3550          $systemcontext = context_system::instance();
3551          self::validate_context($systemcontext);
3552  
3553          if (($USER->id != $params['userid']) && !has_capability('moodle/site:readallmessages', $systemcontext)) {
3554              throw new moodle_exception('You do not have permission to perform this action.');
3555          }
3556  
3557          return \core_message\api::get_unread_conversation_counts($params['userid']);
3558      }
3559  
3560      /**
3561       * Get unread conversation counts return description.
3562       *
3563       * @return external_description
3564       */
3565      public static function get_unread_conversation_counts_returns() {
3566          return new external_single_structure(
3567              [
3568                  'favourites' => new external_value(PARAM_INT, 'Total number of unread favourite conversations'),
3569                  'types' => new external_single_structure(
3570                      [
3571                          \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new external_value(PARAM_INT,
3572                              'Total number of unread individual conversations'),
3573                          \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => new external_value(PARAM_INT,
3574                              'Total number of unread group conversations'),
3575                          \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => new external_value(PARAM_INT,
3576                              'Total number of unread self conversations'),
3577                      ]
3578                  ),
3579              ]
3580          );
3581      }
3582  
3583      /**
3584       * Returns description of method parameters
3585       *
3586       * @return external_function_parameters
3587       * @since 3.7
3588       */
3589      public static function delete_message_for_all_users_parameters() {
3590          return new external_function_parameters(
3591              array(
3592                  'messageid' => new external_value(PARAM_INT, 'The message id'),
3593                  'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for all users')
3594              )
3595          );
3596      }
3597      /**
3598       * Deletes a message for all users
3599       *
3600       * @param  int $messageid the message id
3601       * @param  int $userid the user id of who we want to delete the message for all users, is no longer used.
3602       * @return external_description
3603       * @throws moodle_exception
3604       * @since 3.7
3605       */
3606      public static function delete_message_for_all_users(int $messageid, int $userid) {
3607          global $CFG, $USER;
3608  
3609          // Check if private messaging between users is allowed.
3610          if (empty($CFG->messaging)) {
3611              throw new moodle_exception('disabled', 'message');
3612          }
3613  
3614          // Validate params.
3615          $params = array(
3616              'messageid' => $messageid,
3617              'userid' => $userid
3618          );
3619          $params = self::validate_parameters(self::delete_message_for_all_users_parameters(), $params);
3620  
3621          // Validate context.
3622          $context = context_system::instance();
3623          self::validate_context($context);
3624  
3625          core_user::require_active_user($USER);
3626  
3627          // Checks if a user can delete a message for all users.
3628          if (core_message\api::can_delete_message_for_all_users($USER->id, $params['messageid'])) {
3629              \core_message\api::delete_message_for_all_users($params['messageid']);
3630          } else {
3631              throw new moodle_exception('You do not have permission to delete this message for everyone.');
3632          }
3633  
3634          return [];
3635      }
3636      /**
3637       * Returns description of method result value
3638       *
3639       * @return external_description
3640       * @since 3.7
3641       */
3642      public static function delete_message_for_all_users_returns() {
3643          return new external_warnings();
3644      }
3645  }