Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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