Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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   * External message functions unit tests
  19   *
  20   * @package    core_message
  21   * @category   external
  22   * @copyright  2012 Jerome Mouneyrac
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  
  30  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  31  require_once($CFG->dirroot . '/message/externallib.php');
  32  
  33  use \core_message\tests\helper as testhelper;
  34  
  35  class core_message_externallib_testcase extends externallib_advanced_testcase {
  36  
  37      /**
  38       * Tests set up
  39       */
  40      protected function setUp() {
  41          global $CFG;
  42  
  43          require_once($CFG->dirroot . '/message/lib.php');
  44      }
  45  
  46      /**
  47       * Send a fake message.
  48       *
  49       * {@link message_send()} does not support transaction, this function will simulate a message
  50       * sent from a user to another. We should stop using it once {@link message_send()} will support
  51       * transactions. This is not clean at all, this is just used to add rows to the table.
  52       *
  53       * @param stdClass $userfrom user object of the one sending the message.
  54       * @param stdClass $userto user object of the one receiving the message.
  55       * @param string $message message to send.
  56       * @param int $notification is the message a notification.
  57       * @param int $time the time the message was sent
  58       */
  59      protected function send_message($userfrom, $userto, $message = 'Hello world!', $notification = 0, $time = 0) {
  60          global $DB;
  61  
  62          if (empty($time)) {
  63              $time = time();
  64          }
  65  
  66          if ($notification) {
  67              $record = new stdClass();
  68              $record->useridfrom = $userfrom->id;
  69              $record->useridto = $userto->id;
  70              $record->subject = 'No subject';
  71              $record->fullmessage = $message;
  72              $record->smallmessage = $message;
  73              $record->timecreated = $time;
  74  
  75              return $DB->insert_record('notifications', $record);
  76          }
  77  
  78          if (!$conversationid = \core_message\api::get_conversation_between_users([$userfrom->id, $userto->id])) {
  79              $conversation = \core_message\api::create_conversation(
  80                  \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
  81                  [
  82                      $userfrom->id,
  83                      $userto->id
  84                  ]
  85              );
  86              $conversationid = $conversation->id;
  87          }
  88  
  89          // Ok, send the message.
  90          $record = new stdClass();
  91          $record->useridfrom = $userfrom->id;
  92          $record->conversationid = $conversationid;
  93          $record->subject = 'No subject';
  94          $record->fullmessage = $message;
  95          $record->smallmessage = $message;
  96          $record->timecreated = $time;
  97  
  98          return $DB->insert_record('messages', $record);
  99      }
 100  
 101      /**
 102       * Test send_instant_messages.
 103       */
 104      public function test_send_instant_messages() {
 105          global $DB, $USER;
 106  
 107          $this->resetAfterTest();
 108  
 109          // Transactions used in tests, tell phpunit use alternative reset method.
 110          $this->preventResetByRollback();
 111  
 112          $user1 = self::getDataGenerator()->create_user();
 113          $user2 = self::getDataGenerator()->create_user();
 114  
 115          $this->setUser($user1);
 116  
 117          // Create test message data.
 118          $message1 = array();
 119          $message1['touserid'] = $user2->id;
 120          $message1['text'] = 'the message.';
 121          $message1['clientmsgid'] = 4;
 122          $messages = array($message1);
 123  
 124          $sentmessages = core_message_external::send_instant_messages($messages);
 125          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 126          $this->assertEquals(
 127              get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message1['touserid']))),
 128              array_pop($sentmessages)['errormessage']
 129          );
 130  
 131          // Add the user1 as a contact.
 132          \core_message\api::add_contact($user1->id, $user2->id);
 133  
 134          // Send message again. Now it should work properly.
 135          $sentmessages = core_message_external::send_instant_messages($messages);
 136          // We need to execute the return values cleaning process to simulate the web service server.
 137          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 138  
 139          $sentmessage = reset($sentmessages);
 140  
 141          $sql = "SELECT m.*, mcm.userid as useridto
 142                   FROM {messages} m
 143             INNER JOIN {message_conversations} mc
 144                     ON m.conversationid = mc.id
 145             INNER JOIN {message_conversation_members} mcm
 146                     ON mcm.conversationid = mc.id
 147                  WHERE mcm.userid != ?
 148                    AND m.id = ?";
 149          $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 150  
 151          // Confirm that the message was inserted correctly.
 152          $this->assertEquals($themessage->useridfrom, $user1->id);
 153          $this->assertEquals($themessage->useridto, $message1['touserid']);
 154          $this->assertEquals($themessage->smallmessage, $message1['text']);
 155          $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
 156      }
 157  
 158      /**
 159       * Test send_instant_messages with a message text longer than permitted.
 160       */
 161      public function test_send_instant_messages_long_text() {
 162          global $CFG;
 163  
 164          $this->resetAfterTest(true);
 165  
 166          // Transactions used in tests, tell phpunit use alternative reset method.
 167          $this->preventResetByRollback();
 168  
 169          $user1 = self::getDataGenerator()->create_user();
 170          $user2 = self::getDataGenerator()->create_user();
 171  
 172          $this->setUser($user1);
 173  
 174          // Create test message data.
 175          $message1 = [
 176              'touserid' => $user2->id,
 177              'text' => str_repeat("M", \core_message\api::MESSAGE_MAX_LENGTH + 100),
 178              'clientmsgid' => 4,
 179          ];
 180          $messages = [$message1];
 181  
 182          // Add the user1 as a contact.
 183          \core_message\api::add_contact($user1->id, $user2->id);
 184  
 185          $sentmessages = core_message_external::send_instant_messages($messages);
 186          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 187          $this->assertEquals(
 188              get_string('errormessagetoolong', 'message'),
 189              array_pop($sentmessages)['errormessage']
 190          );
 191      }
 192  
 193      /**
 194       * Test send_instant_messages to a user who has blocked you.
 195       */
 196      public function test_send_instant_messages_blocked_user() {
 197          global $DB;
 198  
 199          $this->resetAfterTest();
 200  
 201          // Transactions used in tests, tell phpunit use alternative reset method.
 202          $this->preventResetByRollback();
 203  
 204          $user1 = self::getDataGenerator()->create_user();
 205          $user2 = self::getDataGenerator()->create_user();
 206  
 207          $this->setUser($user1);
 208  
 209          \core_message\api::block_user($user2->id, $user1->id);
 210  
 211          // Create test message data.
 212          $message1 = array();
 213          $message1['touserid'] = $user2->id;
 214          $message1['text'] = 'the message.';
 215          $message1['clientmsgid'] = 4;
 216          $messages = array($message1);
 217  
 218          $sentmessages = core_message_external::send_instant_messages($messages);
 219          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 220  
 221          $sentmessage = reset($sentmessages);
 222  
 223          $this->assertEquals(get_string('usercantbemessaged', 'message', fullname($user2)), $sentmessage['errormessage']);
 224  
 225          $this->assertEquals(0, $DB->count_records('messages'));
 226      }
 227  
 228      /**
 229       * Test send_instant_messages when sending a message to a non-contact who has blocked non-contacts.
 230       */
 231      public function test_send_instant_messages_block_non_contacts() {
 232          global $DB;
 233  
 234          $this->resetAfterTest(true);
 235  
 236          // Transactions used in tests, tell phpunit use alternative reset method.
 237          $this->preventResetByRollback();
 238  
 239          $user1 = self::getDataGenerator()->create_user();
 240          $user2 = self::getDataGenerator()->create_user();
 241  
 242          $this->setUser($user1);
 243  
 244          // Set the user preference so user 2 does not accept messages from non-contacts.
 245          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, $user2);
 246  
 247          // Create test message data.
 248          $message1 = array();
 249          $message1['touserid'] = $user2->id;
 250          $message1['text'] = 'the message.';
 251          $message1['clientmsgid'] = 4;
 252          $messages = array($message1);
 253  
 254          $sentmessages = core_message_external::send_instant_messages($messages);
 255          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 256  
 257          $sentmessage = reset($sentmessages);
 258  
 259          $this->assertEquals(get_string('usercantbemessaged', 'message', fullname($user2)), $sentmessage['errormessage']);
 260  
 261          $this->assertEquals(0, $DB->count_records('messages'));
 262      }
 263  
 264      /**
 265       * Test send_instant_messages when sending a message to a contact who has blocked non-contacts.
 266       */
 267      public function test_send_instant_messages_block_non_contacts_but_am_contact() {
 268          global $DB, $USER;
 269  
 270          $this->resetAfterTest(true);
 271  
 272          // Transactions used in tests, tell phpunit use alternative reset method.
 273          $this->preventResetByRollback();
 274  
 275          $user1 = self::getDataGenerator()->create_user();
 276          $user2 = self::getDataGenerator()->create_user();
 277  
 278          $this->setUser($user1);
 279  
 280          // Set the user preference so user 2 does not accept messages from non-contacts.
 281          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, $user2);
 282  
 283          \core_message\api::add_contact($user1->id, $user2->id);
 284  
 285          // Create test message data.
 286          $message1 = array();
 287          $message1['touserid'] = $user2->id;
 288          $message1['text'] = 'the message.';
 289          $message1['clientmsgid'] = 4;
 290          $messages = array($message1);
 291  
 292          $sentmessages = core_message_external::send_instant_messages($messages);
 293          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 294  
 295          $sentmessage = reset($sentmessages);
 296  
 297          $sql = "SELECT m.*, mcm.userid as useridto
 298                   FROM {messages} m
 299             INNER JOIN {message_conversations} mc
 300                     ON m.conversationid = mc.id
 301             INNER JOIN {message_conversation_members} mcm
 302                     ON mcm.conversationid = mc.id
 303                  WHERE mcm.userid != ?
 304                    AND m.id = ?";
 305          $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 306  
 307          // Confirm that the message was inserted correctly.
 308          $this->assertEquals($themessage->useridfrom, $user1->id);
 309          $this->assertEquals($themessage->useridto, $message1['touserid']);
 310          $this->assertEquals($themessage->smallmessage, $message1['text']);
 311          $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
 312      }
 313  
 314      /**
 315       * Test send_instant_messages with no capabilities
 316       */
 317      public function test_send_instant_messages_no_capability() {
 318          global $DB;
 319  
 320          $this->resetAfterTest(true);
 321  
 322          // Transactions used in tests, tell phpunit use alternative reset method.
 323          $this->preventResetByRollback();
 324  
 325          $user1 = self::getDataGenerator()->create_user();
 326          $user2 = self::getDataGenerator()->create_user();
 327  
 328          $this->setUser($user1);
 329  
 330          // Unset the required capabilities by the external function.
 331          $contextid = context_system::instance()->id;
 332          $userrole = $DB->get_record('role', array('shortname' => 'user'));
 333          $this->unassignUserCapability('moodle/site:sendmessage', $contextid, $userrole->id);
 334  
 335          // Create test message data.
 336          $message1 = array();
 337          $message1['touserid'] = $user2->id;
 338          $message1['text'] = 'the message.';
 339          $message1['clientmsgid'] = 4;
 340          $messages = array($message1);
 341  
 342          $this->expectException('required_capability_exception');
 343          core_message_external::send_instant_messages($messages);
 344      }
 345  
 346      /**
 347       * Test send_instant_messages when messaging is disabled.
 348       */
 349      public function test_send_instant_messages_messaging_disabled() {
 350          global $CFG;
 351  
 352          $this->resetAfterTest(true);
 353  
 354          // Transactions used in tests, tell phpunit use alternative reset method.
 355          $this->preventResetByRollback();
 356  
 357          $user1 = self::getDataGenerator()->create_user();
 358          $user2 = self::getDataGenerator()->create_user();
 359  
 360          $this->setUser($user1);
 361  
 362          // Disable messaging.
 363          $CFG->messaging = 0;
 364  
 365          // Create test message data.
 366          $message1 = array();
 367          $message1['touserid'] = $user2->id;
 368          $message1['text'] = 'the message.';
 369          $message1['clientmsgid'] = 4;
 370          $messages = array($message1);
 371  
 372          $this->expectException('moodle_exception');
 373          core_message_external::send_instant_messages($messages);
 374      }
 375  
 376      /**
 377       * Test create_contacts.
 378       */
 379      public function test_create_contacts() {
 380          $this->resetAfterTest(true);
 381  
 382          $user1 = self::getDataGenerator()->create_user();
 383          $user2 = self::getDataGenerator()->create_user();
 384          $user3 = self::getDataGenerator()->create_user();
 385          $user4 = self::getDataGenerator()->create_user();
 386          $user5 = self::getDataGenerator()->create_user();
 387          $this->setUser($user1);
 388  
 389          // Adding a contact.
 390          $return = core_message_external::create_contacts(array($user2->id));
 391          $this->assertDebuggingCalled();
 392          $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
 393          $this->assertEquals(array(), $return);
 394  
 395          // Adding a contact who is already a contact.
 396          $return = core_message_external::create_contacts(array($user2->id));
 397          $this->assertDebuggingCalled();
 398          $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
 399          $this->assertEquals(array(), $return);
 400  
 401          // Adding multiple contacts.
 402          $return = core_message_external::create_contacts(array($user3->id, $user4->id));
 403          $this->assertDebuggingCalledCount(2);
 404          $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
 405          $this->assertEquals(array(), $return);
 406  
 407          // Adding a non-existing user.
 408          $return = core_message_external::create_contacts(array(99999));
 409          $this->assertDebuggingCalled();
 410          $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
 411          $this->assertCount(1, $return);
 412          $return = array_pop($return);
 413          $this->assertEquals($return['warningcode'], 'contactnotcreated');
 414          $this->assertEquals($return['itemid'], 99999);
 415  
 416          // Adding contacts with valid and invalid parameters.
 417          $return = core_message_external::create_contacts(array($user5->id, 99999));
 418          $this->assertDebuggingCalledCount(2);
 419          $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
 420          $this->assertCount(1, $return);
 421          $return = array_pop($return);
 422          $this->assertEquals($return['warningcode'], 'contactnotcreated');
 423          $this->assertEquals($return['itemid'], 99999);
 424  
 425          // Try to add a contact to another user, should throw an exception.
 426          // All assertions must be added before this point.
 427          $this->expectException('required_capability_exception');
 428          core_message_external::create_contacts(array($user2->id), $user3->id);
 429      }
 430  
 431      /**
 432       * Test delete_contacts.
 433       */
 434      public function test_delete_contacts() {
 435          $this->resetAfterTest(true);
 436  
 437          $user1 = self::getDataGenerator()->create_user();
 438          $user2 = self::getDataGenerator()->create_user();
 439          $user3 = self::getDataGenerator()->create_user();
 440          $user4 = self::getDataGenerator()->create_user();
 441          $user5 = self::getDataGenerator()->create_user();
 442          $user6 = self::getDataGenerator()->create_user();
 443          $this->setUser($user1);
 444  
 445          \core_message\api::add_contact($user1->id, $user3->id);
 446          \core_message\api::add_contact($user1->id, $user4->id);
 447          \core_message\api::add_contact($user1->id, $user5->id);
 448          \core_message\api::add_contact($user1->id, $user6->id);
 449  
 450          // Removing a non-contact.
 451          $return = core_message_external::delete_contacts(array($user2->id));
 452          $this->assertNull($return);
 453  
 454          // Removing one contact.
 455          $return = core_message_external::delete_contacts(array($user3->id));
 456          $this->assertNull($return);
 457  
 458          // Removing multiple contacts.
 459          $return = core_message_external::delete_contacts(array($user4->id, $user5->id));
 460          $this->assertNull($return);
 461  
 462          // Removing contact from unexisting user.
 463          $return = core_message_external::delete_contacts(array(99999));
 464          $this->assertNull($return);
 465  
 466          // Removing mixed valid and invalid data.
 467          $return = core_message_external::delete_contacts(array($user6->id, 99999));
 468          $this->assertNull($return);
 469  
 470          // Try to delete a contact of another user contact list, should throw an exception.
 471          // All assertions must be added before this point.
 472          $this->expectException('required_capability_exception');
 473          core_message_external::delete_contacts(array($user2->id), $user3->id);
 474      }
 475  
 476      /**
 477       * Test block_contacts.
 478       */
 479      public function test_block_contacts() {
 480          $this->resetAfterTest(true);
 481  
 482          $user1 = self::getDataGenerator()->create_user();
 483          $user2 = self::getDataGenerator()->create_user();
 484          $user3 = self::getDataGenerator()->create_user();
 485          $user4 = self::getDataGenerator()->create_user();
 486          $user5 = self::getDataGenerator()->create_user();
 487          $this->setUser($user1);
 488  
 489          \core_message\api::add_contact($user1->id, $user3->id);
 490          \core_message\api::add_contact($user1->id, $user4->id);
 491          \core_message\api::add_contact($user1->id, $user5->id);
 492  
 493          // Blocking a contact.
 494          $return = core_message_external::block_contacts(array($user2->id));
 495          $this->assertDebuggingCalled();
 496          $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
 497          $this->assertEquals(array(), $return);
 498  
 499          // Blocking a contact who is already a contact.
 500          $return = core_message_external::block_contacts(array($user2->id));
 501          $this->assertDebuggingCalled();
 502          $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
 503          $this->assertEquals(array(), $return);
 504  
 505          // Blocking multiple contacts.
 506          $return = core_message_external::block_contacts(array($user3->id, $user4->id));
 507          $this->assertDebuggingCalledCount(2);
 508          $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
 509          $this->assertEquals(array(), $return);
 510  
 511          // Blocking a non-existing user.
 512          $return = core_message_external::block_contacts(array(99999));
 513          $this->assertDebuggingCalled();
 514          $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
 515          $this->assertCount(1, $return);
 516          $return = array_pop($return);
 517          $this->assertEquals($return['warningcode'], 'contactnotblocked');
 518          $this->assertEquals($return['itemid'], 99999);
 519  
 520          // Blocking contacts with valid and invalid parameters.
 521          $return = core_message_external::block_contacts(array($user5->id, 99999));
 522          $this->assertDebuggingCalledCount(2);
 523          $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
 524          $this->assertCount(1, $return);
 525          $return = array_pop($return);
 526          $this->assertEquals($return['warningcode'], 'contactnotblocked');
 527          $this->assertEquals($return['itemid'], 99999);
 528  
 529          // Try to block a contact of another user contact list, should throw an exception.
 530          // All assertions must be added before this point.
 531          $this->expectException('required_capability_exception');
 532          core_message_external::block_contacts(array($user2->id), $user3->id);
 533      }
 534  
 535      /**
 536       * Test unblock_contacts.
 537       */
 538      public function test_unblock_contacts() {
 539          $this->resetAfterTest(true);
 540  
 541          $user1 = self::getDataGenerator()->create_user();
 542          $user2 = self::getDataGenerator()->create_user();
 543          $user3 = self::getDataGenerator()->create_user();
 544          $user4 = self::getDataGenerator()->create_user();
 545          $user5 = self::getDataGenerator()->create_user();
 546          $user6 = self::getDataGenerator()->create_user();
 547          $this->setUser($user1);
 548  
 549          \core_message\api::add_contact($user1->id, $user3->id);
 550          \core_message\api::add_contact($user1->id, $user4->id);
 551          \core_message\api::add_contact($user1->id, $user5->id);
 552          \core_message\api::add_contact($user1->id, $user6->id);
 553  
 554          // Removing a non-contact.
 555          $return = core_message_external::unblock_contacts(array($user2->id));
 556          $this->assertDebuggingCalled();
 557          $this->assertNull($return);
 558  
 559          // Removing one contact.
 560          $return = core_message_external::unblock_contacts(array($user3->id));
 561          $this->assertDebuggingCalled();
 562          $this->assertNull($return);
 563  
 564          // Removing multiple contacts.
 565          $return = core_message_external::unblock_contacts(array($user4->id, $user5->id));
 566          $this->assertDebuggingCalledCount(2);
 567          $this->assertNull($return);
 568  
 569          // Removing contact from unexisting user.
 570          $return = core_message_external::unblock_contacts(array(99999));
 571          $this->assertDebuggingCalled();
 572          $this->assertNull($return);
 573  
 574          // Removing mixed valid and invalid data.
 575          $return = core_message_external::unblock_contacts(array($user6->id, 99999));
 576          $this->assertDebuggingCalledCount(2);
 577          $this->assertNull($return);
 578  
 579          // Try to unblock a contact of another user contact list, should throw an exception.
 580          // All assertions must be added before this point.
 581          $this->expectException('required_capability_exception');
 582          core_message_external::unblock_contacts(array($user2->id), $user3->id);
 583          $this->assertDebuggingCalled();
 584      }
 585  
 586      /**
 587       * Test getting contact requests.
 588       */
 589      public function test_get_contact_requests() {
 590          global $PAGE;
 591  
 592          $this->resetAfterTest();
 593  
 594          $user1 = self::getDataGenerator()->create_user();
 595          $user2 = self::getDataGenerator()->create_user();
 596          $user3 = self::getDataGenerator()->create_user();
 597  
 598          $this->setUser($user1);
 599  
 600          // Block one user, their request should not show up.
 601          \core_message\api::block_user($user1->id, $user3->id);
 602  
 603          \core_message\api::create_contact_request($user2->id, $user1->id);
 604          \core_message\api::create_contact_request($user3->id, $user1->id);
 605  
 606          $requests = core_message_external::get_contact_requests($user1->id);
 607          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 608  
 609          $this->assertCount(1, $requests);
 610  
 611          $request = reset($requests);
 612          $userpicture = new \user_picture($user2);
 613          $profileimageurl = $userpicture->get_url($PAGE)->out(false);
 614  
 615          $this->assertEquals($user2->id, $request['id']);
 616          $this->assertEquals(fullname($user2), $request['fullname']);
 617          $this->assertArrayHasKey('profileimageurl', $request);
 618          $this->assertArrayHasKey('profileimageurlsmall', $request);
 619          $this->assertArrayHasKey('isonline', $request);
 620          $this->assertArrayHasKey('showonlinestatus', $request);
 621          $this->assertArrayHasKey('isblocked', $request);
 622          $this->assertArrayHasKey('iscontact', $request);
 623      }
 624  
 625      /**
 626       * Test the get_contact_requests() function when the user has blocked the sender of the request.
 627       */
 628      public function test_get_contact_requests_blocked_sender() {
 629          $this->resetAfterTest();
 630          $user1 = self::getDataGenerator()->create_user();
 631          $user2 = self::getDataGenerator()->create_user();
 632  
 633          // User1 blocks User2.
 634          \core_message\api::block_user($user1->id, $user2->id);
 635  
 636          // User2 tries to add User1 as a contact.
 637          \core_message\api::create_contact_request($user2->id, $user1->id);
 638  
 639          // Verify we don't see the contact request from the blocked user User2 in the requests for User1.
 640          $this->setUser($user1);
 641          $requests = core_message_external::get_contact_requests($user1->id);
 642          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 643  
 644          $this->assertCount(0, $requests);
 645      }
 646  
 647      /**
 648       * Test getting contact requests when there are none.
 649       */
 650      public function test_get_contact_requests_no_requests() {
 651          $this->resetAfterTest();
 652  
 653          $user1 = self::getDataGenerator()->create_user();
 654  
 655          $this->setUser($user1);
 656  
 657          $requests = core_message_external::get_contact_requests($user1->id);
 658          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 659  
 660          $this->assertEmpty($requests);
 661      }
 662  
 663      /**
 664       * Test getting contact requests with limits.
 665       */
 666      public function test_get_contact_requests_with_limits() {
 667          $this->resetAfterTest();
 668  
 669          $user1 = self::getDataGenerator()->create_user();
 670          $user2 = self::getDataGenerator()->create_user();
 671          $user3 = self::getDataGenerator()->create_user();
 672  
 673          $this->setUser($user1);
 674  
 675          \core_message\api::create_contact_request($user2->id, $user1->id);
 676          \core_message\api::create_contact_request($user3->id, $user1->id);
 677  
 678          $requests = core_message_external::get_contact_requests($user1->id, 0, 1);
 679          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 680  
 681          $this->assertCount(1, $requests);
 682      }
 683  
 684      /**
 685       * Test getting contact requests with messaging disabled.
 686       */
 687      public function test_get_contact_requests_messaging_disabled() {
 688          global $CFG;
 689  
 690          $this->resetAfterTest();
 691  
 692          // Create some skeleton data just so we can call the WS.
 693          $user1 = self::getDataGenerator()->create_user();
 694  
 695          $this->setUser($user1);
 696  
 697          // Disable messaging.
 698          $CFG->messaging = 0;
 699  
 700          // Ensure an exception is thrown.
 701          $this->expectException('moodle_exception');
 702          core_message_external::get_contact_requests($user1->id);
 703      }
 704  
 705      /**
 706       * Test getting contact requests with no permission.
 707       */
 708      public function test_get_contact_requests_no_permission() {
 709          $this->resetAfterTest();
 710  
 711          // Create some skeleton data just so we can call the WS.
 712          $user1 = self::getDataGenerator()->create_user();
 713          $user2 = self::getDataGenerator()->create_user();
 714          $user3 = self::getDataGenerator()->create_user();
 715  
 716          $this->setUser($user3);
 717  
 718          // Ensure an exception is thrown.
 719          $this->expectException('required_capability_exception');
 720          core_message_external::create_contact_request($user1->id, $user2->id);
 721      }
 722  
 723      /**
 724       * Test getting the number of received contact requests.
 725       */
 726      public function test_get_received_contact_requests_count() {
 727          $this->resetAfterTest();
 728  
 729          $user1 = self::getDataGenerator()->create_user();
 730          $user2 = self::getDataGenerator()->create_user();
 731          $user3 = self::getDataGenerator()->create_user();
 732          $user4 = self::getDataGenerator()->create_user();
 733  
 734          $this->setUser($user1);
 735  
 736          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 737          $contactrequestnumber = external_api::clean_returnvalue(
 738              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 739          $this->assertEquals(0, $contactrequestnumber);
 740  
 741          \core_message\api::create_contact_request($user2->id, $user1->id);
 742  
 743          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 744          $contactrequestnumber = external_api::clean_returnvalue(
 745              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 746          $this->assertEquals(1, $contactrequestnumber);
 747  
 748          \core_message\api::create_contact_request($user3->id, $user1->id);
 749  
 750          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 751          $contactrequestnumber = external_api::clean_returnvalue(
 752              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 753          $this->assertEquals(2, $contactrequestnumber);
 754  
 755          \core_message\api::create_contact_request($user1->id, $user4->id);
 756  
 757          // Web service should ignore sent requests.
 758          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 759          $contactrequestnumber = external_api::clean_returnvalue(
 760              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 761          $this->assertEquals(2, $contactrequestnumber);
 762      }
 763  
 764      /**
 765       * Test the get_received_contact_requests_count() function when the user has blocked the sender of the request.
 766       */
 767      public function test_get_received_contact_requests_count_blocked_sender() {
 768          $this->resetAfterTest();
 769          $user1 = self::getDataGenerator()->create_user();
 770          $user2 = self::getDataGenerator()->create_user();
 771  
 772          // User1 blocks User2.
 773          \core_message\api::block_user($user1->id, $user2->id);
 774  
 775          // User2 tries to add User1 as a contact.
 776          \core_message\api::create_contact_request($user2->id, $user1->id);
 777  
 778          // Verify we don't see the contact request from the blocked user User2 in the count for User1.
 779          $this->setUser($user1);
 780          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 781          $contactrequestnumber = external_api::clean_returnvalue(
 782              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 783          $this->assertEquals(0, $contactrequestnumber);
 784      }
 785  
 786      /**
 787       * Test getting the number of received contact requests with no permissions.
 788       */
 789      public function test_get_received_contact_requests_count_no_permission() {
 790          $this->resetAfterTest();
 791  
 792          // Create some skeleton data just so we can call the WS.
 793          $user1 = self::getDataGenerator()->create_user();
 794          $user2 = self::getDataGenerator()->create_user();
 795  
 796          $this->setUser($user2);
 797  
 798          // Ensure an exception is thrown.
 799          $this->expectException('required_capability_exception');
 800          core_message_external::get_received_contact_requests_count($user1->id);
 801      }
 802  
 803      /**
 804       * Test getting the number of received contact requests with messaging disabled.
 805       */
 806      public function test_get_received_contact_requests_count_messaging_disabled() {
 807          global $CFG;
 808  
 809          $this->resetAfterTest();
 810  
 811          // Create some skeleton data just so we can call the WS.
 812          $user1 = self::getDataGenerator()->create_user();
 813  
 814          $this->setUser($user1);
 815  
 816          // Disable messaging.
 817          $CFG->messaging = 0;
 818  
 819          // Ensure an exception is thrown.
 820          $this->expectException('moodle_exception');
 821          core_message_external::get_received_contact_requests_count($user1->id);
 822      }
 823  
 824      /**
 825       * Test creating a contact request.
 826       */
 827      public function test_create_contact_request() {
 828          global $CFG, $DB;
 829  
 830          $this->resetAfterTest();
 831  
 832          $user1 = self::getDataGenerator()->create_user();
 833          $user2 = self::getDataGenerator()->create_user();
 834  
 835          $this->setUser($user1);
 836  
 837          // Allow users to message anyone site-wide.
 838          $CFG->messagingallusers = 1;
 839  
 840          $return = core_message_external::create_contact_request($user1->id, $user2->id);
 841          $return = external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
 842          $this->assertEquals([], $return['warnings']);
 843  
 844          $request = $DB->get_records('message_contact_requests');
 845  
 846          $this->assertCount(1, $request);
 847  
 848          $request = reset($request);
 849  
 850          $this->assertEquals($request->id, $return['request']['id']);
 851          $this->assertEquals($request->userid, $return['request']['userid']);
 852          $this->assertEquals($request->requesteduserid, $return['request']['requesteduserid']);
 853          $this->assertEquals($request->timecreated, $return['request']['timecreated']);
 854      }
 855  
 856      /**
 857       * Test creating a contact request when not allowed.
 858       */
 859      public function test_create_contact_request_not_allowed() {
 860          global $CFG;
 861  
 862          $this->resetAfterTest();
 863  
 864          $user1 = self::getDataGenerator()->create_user();
 865          $user2 = self::getDataGenerator()->create_user();
 866  
 867          $this->setUser($user1);
 868  
 869          $CFG->messagingallusers = 0;
 870  
 871          $return = core_message_external::create_contact_request($user1->id, $user2->id);
 872          $return = external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
 873  
 874          $warning = reset($return['warnings']);
 875  
 876          $this->assertEquals('user', $warning['item']);
 877          $this->assertEquals($user2->id, $warning['itemid']);
 878          $this->assertEquals('cannotcreatecontactrequest', $warning['warningcode']);
 879          $this->assertEquals('You are unable to create a contact request for this user', $warning['message']);
 880      }
 881  
 882      /**
 883       * Test creating a contact request with messaging disabled.
 884       */
 885      public function test_create_contact_request_messaging_disabled() {
 886          global $CFG;
 887  
 888          $this->resetAfterTest();
 889  
 890          // Create some skeleton data just so we can call the WS.
 891          $user1 = self::getDataGenerator()->create_user();
 892          $user2 = self::getDataGenerator()->create_user();
 893  
 894          $this->setUser($user1);
 895  
 896          // Disable messaging.
 897          $CFG->messaging = 0;
 898  
 899          // Ensure an exception is thrown.
 900          $this->expectException('moodle_exception');
 901          core_message_external::create_contact_request($user1->id, $user2->id);
 902      }
 903  
 904      /**
 905       * Test creating a contact request with no permission.
 906       */
 907      public function test_create_contact_request_no_permission() {
 908          $this->resetAfterTest();
 909  
 910          // Create some skeleton data just so we can call the WS.
 911          $user1 = self::getDataGenerator()->create_user();
 912          $user2 = self::getDataGenerator()->create_user();
 913          $user3 = self::getDataGenerator()->create_user();
 914  
 915          $this->setUser($user3);
 916  
 917          // Ensure an exception is thrown.
 918          $this->expectException('required_capability_exception');
 919          core_message_external::create_contact_request($user1->id, $user2->id);
 920      }
 921  
 922      /**
 923       * Test confirming a contact request.
 924       */
 925      public function test_confirm_contact_request() {
 926          global $DB;
 927  
 928          $this->resetAfterTest();
 929  
 930          $user1 = self::getDataGenerator()->create_user();
 931          $user2 = self::getDataGenerator()->create_user();
 932  
 933          $this->setUser($user1);
 934  
 935          \core_message\api::create_contact_request($user1->id, $user2->id);
 936  
 937          $this->setUser($user2);
 938  
 939          $return = core_message_external::confirm_contact_request($user1->id, $user2->id);
 940          $return = external_api::clean_returnvalue(core_message_external::confirm_contact_request_returns(), $return);
 941          $this->assertEquals(array(), $return);
 942  
 943          $this->assertEquals(0, $DB->count_records('message_contact_requests'));
 944  
 945          $contact = $DB->get_records('message_contacts');
 946  
 947          $this->assertCount(1, $contact);
 948  
 949          $contact = reset($contact);
 950  
 951          $this->assertEquals($user1->id, $contact->userid);
 952          $this->assertEquals($user2->id, $contact->contactid);
 953      }
 954  
 955      /**
 956       * Test confirming a contact request with messaging disabled.
 957       */
 958      public function test_confirm_contact_request_messaging_disabled() {
 959          global $CFG;
 960  
 961          $this->resetAfterTest();
 962  
 963          // Create some skeleton data just so we can call the WS.
 964          $user1 = self::getDataGenerator()->create_user();
 965          $user2 = self::getDataGenerator()->create_user();
 966  
 967          $this->setUser($user1);
 968  
 969          // Disable messaging.
 970          $CFG->messaging = 0;
 971  
 972          // Ensure an exception is thrown.
 973          $this->expectException('moodle_exception');
 974          core_message_external::confirm_contact_request($user1->id, $user2->id);
 975      }
 976  
 977      /**
 978       * Test confirming a contact request with no permission.
 979       */
 980      public function test_confirm_contact_request_no_permission() {
 981          $this->resetAfterTest();
 982  
 983          // Create some skeleton data just so we can call the WS.
 984          $user1 = self::getDataGenerator()->create_user();
 985          $user2 = self::getDataGenerator()->create_user();
 986          $user3 = self::getDataGenerator()->create_user();
 987  
 988          $this->setUser($user3);
 989  
 990          // Ensure an exception is thrown.
 991          $this->expectException('required_capability_exception');
 992          core_message_external::confirm_contact_request($user1->id, $user2->id);
 993      }
 994  
 995      /**
 996       * Test declining a contact request.
 997       */
 998      public function test_decline_contact_request() {
 999          global $DB;
1000  
1001          $this->resetAfterTest();
1002  
1003          $user1 = self::getDataGenerator()->create_user();
1004          $user2 = self::getDataGenerator()->create_user();
1005  
1006          $this->setUser($user1);
1007  
1008          \core_message\api::create_contact_request($user1->id, $user2->id);
1009  
1010          $this->setUser($user2);
1011  
1012          $return = core_message_external::decline_contact_request($user1->id, $user2->id);
1013          $return = external_api::clean_returnvalue(core_message_external::decline_contact_request_returns(), $return);
1014          $this->assertEquals(array(), $return);
1015  
1016          $this->assertEquals(0, $DB->count_records('message_contact_requests'));
1017          $this->assertEquals(0, $DB->count_records('message_contacts'));
1018      }
1019  
1020      /**
1021       * Test declining a contact request with messaging disabled.
1022       */
1023      public function test_decline_contact_request_messaging_disabled() {
1024          global $CFG;
1025  
1026          $this->resetAfterTest();
1027  
1028          // Create some skeleton data just so we can call the WS.
1029          $user1 = self::getDataGenerator()->create_user();
1030          $user2 = self::getDataGenerator()->create_user();
1031  
1032          $this->setUser($user1);
1033  
1034          // Disable messaging.
1035          $CFG->messaging = 0;
1036  
1037          // Ensure an exception is thrown.
1038          $this->expectException('moodle_exception');
1039          core_message_external::decline_contact_request($user1->id, $user2->id);
1040      }
1041  
1042      /**
1043       * Test declining a contact request with no permission.
1044       */
1045      public function test_decline_contact_request_no_permission() {
1046          $this->resetAfterTest();
1047  
1048          // Create some skeleton data just so we can call the WS.
1049          $user1 = self::getDataGenerator()->create_user();
1050          $user2 = self::getDataGenerator()->create_user();
1051          $user3 = self::getDataGenerator()->create_user();
1052  
1053          $this->setUser($user3);
1054  
1055          // Ensure an exception is thrown.
1056          $this->expectException('required_capability_exception');
1057          core_message_external::decline_contact_request($user1->id, $user2->id);
1058      }
1059  
1060      /**
1061       * Test muting conversations.
1062       */
1063      public function test_mute_conversations() {
1064          global $DB;
1065  
1066          $this->resetAfterTest(true);
1067  
1068          $user1 = self::getDataGenerator()->create_user();
1069          $user2 = self::getDataGenerator()->create_user();
1070  
1071          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1072              [$user1->id, $user2->id]);
1073  
1074          $this->setUser($user1);
1075  
1076          // Muting a conversation.
1077          $return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
1078          $return = external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
1079          $this->assertEquals(array(), $return);
1080  
1081          // Get list of muted conversations.
1082          $mca = $DB->get_record('message_conversation_actions', []);
1083  
1084          $this->assertEquals($user1->id, $mca->userid);
1085          $this->assertEquals($conversation->id, $mca->conversationid);
1086          $this->assertEquals(\core_message\api::CONVERSATION_ACTION_MUTED, $mca->action);
1087  
1088          // Muting a conversation that is already muted.
1089          $return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
1090          $return = external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
1091          $this->assertEquals(array(), $return);
1092  
1093          $this->assertEquals(1, $DB->count_records('message_conversation_actions'));
1094      }
1095  
1096      /**
1097       * Test muting a conversation with messaging disabled.
1098       */
1099      public function test_mute_conversations_messaging_disabled() {
1100          global $CFG;
1101  
1102          $this->resetAfterTest();
1103  
1104          // Create some skeleton data just so we can call the WS.
1105          $user1 = self::getDataGenerator()->create_user();
1106          $user2 = self::getDataGenerator()->create_user();
1107  
1108          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1109              [$user1->id, $user2->id]);
1110  
1111          $this->setUser($user1);
1112  
1113          // Disable messaging.
1114          $CFG->messaging = 0;
1115  
1116          // Ensure an exception is thrown.
1117          $this->expectException('moodle_exception');
1118          core_message_external::mute_conversations($user1->id, [$conversation->id]);
1119      }
1120  
1121      /**
1122       * Test muting a conversation with no permission.
1123       */
1124      public function test_mute_conversations_no_permission() {
1125          $this->resetAfterTest();
1126  
1127          // Create some skeleton data just so we can call the WS.
1128          $user1 = self::getDataGenerator()->create_user();
1129          $user2 = self::getDataGenerator()->create_user();
1130          $user3 = self::getDataGenerator()->create_user();
1131  
1132          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1133              [$user1->id, $user2->id]);
1134  
1135          $this->setUser($user3);
1136  
1137          // Ensure an exception is thrown.
1138          $this->expectException('required_capability_exception');
1139          core_message_external::mute_conversations($user1->id, [$conversation->id]);
1140      }
1141  
1142      /**
1143       * Test unmuting conversations.
1144       */
1145      public function test_unmute_conversations() {
1146          global $DB;
1147  
1148          $this->resetAfterTest(true);
1149  
1150          $user1 = self::getDataGenerator()->create_user();
1151          $user2 = self::getDataGenerator()->create_user();
1152  
1153          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1154              [$user1->id, $user2->id]);
1155  
1156          $this->setUser($user1);
1157  
1158          // Mute the conversation.
1159          \core_message\api::mute_conversation($user1->id, $conversation->id);
1160  
1161          // Unmuting a conversation.
1162          $return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1163          $return = external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
1164          $this->assertEquals(array(), $return);
1165  
1166          $this->assertEquals(0, $DB->count_records('message_conversation_actions'));
1167  
1168          // Unmuting a conversation which is already unmuted.
1169          $return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1170          $return = external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
1171          $this->assertEquals(array(), $return);
1172  
1173          $this->assertEquals(0, $DB->count_records('message_conversation_actions'));
1174      }
1175  
1176      /**
1177       * Test unmuting a conversation with messaging disabled.
1178       */
1179      public function test_unmute_conversation_messaging_disabled() {
1180          global $CFG;
1181  
1182          $this->resetAfterTest();
1183  
1184          // Create some skeleton data just so we can call the WS.
1185          $user1 = self::getDataGenerator()->create_user();
1186          $user2 = self::getDataGenerator()->create_user();
1187  
1188          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1189              [$user1->id, $user2->id]);
1190  
1191          $this->setUser($user1);
1192  
1193          // Disable messaging.
1194          $CFG->messaging = 0;
1195  
1196          // Ensure an exception is thrown.
1197          $this->expectException('moodle_exception');
1198          core_message_external::unmute_conversations($user1->id, [$user2->id]);
1199      }
1200  
1201      /**
1202       * Test unmuting a conversation with no permission.
1203       */
1204      public function test_unmute_conversation_no_permission() {
1205          $this->resetAfterTest();
1206  
1207          // Create some skeleton data just so we can call the WS.
1208          $user1 = self::getDataGenerator()->create_user();
1209          $user2 = self::getDataGenerator()->create_user();
1210          $user3 = self::getDataGenerator()->create_user();
1211  
1212          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1213              [$user1->id, $user2->id]);
1214  
1215          $this->setUser($user3);
1216  
1217          // Ensure an exception is thrown.
1218          $this->expectException('required_capability_exception');
1219          core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1220      }
1221  
1222      /**
1223       * Test blocking a user.
1224       */
1225      public function test_block_user() {
1226          global $DB;
1227  
1228          $this->resetAfterTest(true);
1229  
1230          $user1 = self::getDataGenerator()->create_user();
1231          $user2 = self::getDataGenerator()->create_user();
1232  
1233          $this->setUser($user1);
1234  
1235          // Blocking a user.
1236          $return = core_message_external::block_user($user1->id, $user2->id);
1237          $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1238          $this->assertEquals(array(), $return);
1239  
1240          // Get list of blocked users.
1241          $record = $DB->get_record('message_users_blocked', []);
1242  
1243          $this->assertEquals($user1->id, $record->userid);
1244          $this->assertEquals($user2->id, $record->blockeduserid);
1245  
1246          // Blocking a user who is already blocked.
1247          $return = core_message_external::block_user($user1->id, $user2->id);
1248          $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1249          $this->assertEquals(array(), $return);
1250  
1251          $this->assertEquals(1, $DB->count_records('message_users_blocked'));
1252      }
1253  
1254      /**
1255       * Test blocking a user.
1256       */
1257      public function test_block_user_when_ineffective() {
1258          global $DB;
1259  
1260          $this->resetAfterTest(true);
1261  
1262          $user1 = self::getDataGenerator()->create_user();
1263          $user2 = self::getDataGenerator()->create_user();
1264  
1265          $this->setUser($user1);
1266  
1267          $authenticateduser = $DB->get_record('role', array('shortname' => 'user'));
1268          assign_capability('moodle/site:messageanyuser', CAP_ALLOW, $authenticateduser->id, context_system::instance(), true);
1269  
1270          // Blocking a user.
1271          $return = core_message_external::block_user($user1->id, $user2->id);
1272          $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1273          $this->assertEquals(array(), $return);
1274  
1275          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1276      }
1277  
1278      /**
1279       * Test blocking a user with messaging disabled.
1280       */
1281      public function test_block_user_messaging_disabled() {
1282          global $CFG;
1283  
1284          $this->resetAfterTest();
1285  
1286          // Create some skeleton data just so we can call the WS.
1287          $user1 = self::getDataGenerator()->create_user();
1288          $user2 = self::getDataGenerator()->create_user();
1289  
1290          $this->setUser($user1);
1291  
1292          // Disable messaging.
1293          $CFG->messaging = 0;
1294  
1295          // Ensure an exception is thrown.
1296          $this->expectException('moodle_exception');
1297          core_message_external::block_user($user1->id, $user2->id);
1298      }
1299  
1300      /**
1301       * Test blocking a user with no permission.
1302       */
1303      public function test_block_user_no_permission() {
1304          $this->resetAfterTest();
1305  
1306          // Create some skeleton data just so we can call the WS.
1307          $user1 = self::getDataGenerator()->create_user();
1308          $user2 = self::getDataGenerator()->create_user();
1309          $user3 = self::getDataGenerator()->create_user();
1310  
1311          $this->setUser($user3);
1312  
1313          // Ensure an exception is thrown.
1314          $this->expectException('required_capability_exception');
1315          core_message_external::block_user($user1->id, $user2->id);
1316      }
1317  
1318      /**
1319       * Test unblocking a user.
1320       */
1321      public function test_unblock_user() {
1322          global $DB;
1323  
1324          $this->resetAfterTest(true);
1325  
1326          $user1 = self::getDataGenerator()->create_user();
1327          $user2 = self::getDataGenerator()->create_user();
1328  
1329          $this->setUser($user1);
1330  
1331          // Block the user.
1332          \core_message\api::block_user($user1->id, $user2->id);
1333  
1334          // Unblocking a user.
1335          $return = core_message_external::unblock_user($user1->id, $user2->id);
1336          $return = external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
1337          $this->assertEquals(array(), $return);
1338  
1339          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1340  
1341          // Unblocking a user who is already unblocked.
1342          $return = core_message_external::unblock_user($user1->id, $user2->id);
1343          $return = external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
1344          $this->assertEquals(array(), $return);
1345  
1346          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1347      }
1348  
1349      /**
1350       * Test unblocking a user with messaging disabled.
1351       */
1352      public function test_unblock_user_messaging_disabled() {
1353          global $CFG;
1354  
1355          $this->resetAfterTest();
1356  
1357          // Create some skeleton data just so we can call the WS.
1358          $user1 = self::getDataGenerator()->create_user();
1359          $user2 = self::getDataGenerator()->create_user();
1360  
1361          $this->setUser($user1);
1362  
1363          // Disable messaging.
1364          $CFG->messaging = 0;
1365  
1366          // Ensure an exception is thrown.
1367          $this->expectException('moodle_exception');
1368          core_message_external::unblock_user($user1->id, $user2->id);
1369      }
1370  
1371      /**
1372       * Test unblocking a user with no permission.
1373       */
1374      public function test_unblock_user_no_permission() {
1375          $this->resetAfterTest();
1376  
1377          // Create some skeleton data just so we can call the WS.
1378          $user1 = self::getDataGenerator()->create_user();
1379          $user2 = self::getDataGenerator()->create_user();
1380          $user3 = self::getDataGenerator()->create_user();
1381  
1382          $this->setUser($user3);
1383  
1384          // Ensure an exception is thrown.
1385          $this->expectException('required_capability_exception');
1386          core_message_external::unblock_user($user1->id, $user2->id);
1387      }
1388  
1389      /**
1390       * Test get_contacts.
1391       */
1392      public function test_get_contacts() {
1393          $this->resetAfterTest(true);
1394  
1395          $user1 = self::getDataGenerator()->create_user();
1396          $user_stranger = self::getDataGenerator()->create_user();
1397          $user_offline1 = self::getDataGenerator()->create_user();
1398          $user_offline2 = self::getDataGenerator()->create_user();
1399          $user_offline3 = self::getDataGenerator()->create_user();
1400          $user_online = new stdClass();
1401          $user_online->lastaccess = time();
1402          $user_online = self::getDataGenerator()->create_user($user_online);
1403          $user_blocked = self::getDataGenerator()->create_user();
1404          $noreplyuser = core_user::get_user(core_user::NOREPLY_USER);
1405  
1406          // Login as user1.
1407          $this->setUser($user1);
1408          \core_message\api::add_contact($user1->id, $user_offline1->id);
1409          \core_message\api::add_contact($user1->id, $user_offline2->id);
1410          \core_message\api::add_contact($user1->id, $user_offline3->id);
1411          \core_message\api::add_contact($user1->id, $user_online->id);
1412  
1413          // User_stranger sends a couple of messages to user1.
1414          $this->send_message($user_stranger, $user1, 'Hello there!');
1415          $this->send_message($user_stranger, $user1, 'How you goin?');
1416          $this->send_message($user_stranger, $user1, 'Cya!');
1417          $this->send_message($noreplyuser, $user1, 'I am not a real user');
1418  
1419          // User_blocked sends a message to user1.
1420          $this->send_message($user_blocked, $user1, 'Here, have some spam.');
1421  
1422          // Retrieve the contacts of the user.
1423          $this->setUser($user1);
1424          $contacts = core_message_external::get_contacts();
1425          $contacts = external_api::clean_returnvalue(core_message_external::get_contacts_returns(), $contacts);
1426          $this->assertCount(3, $contacts['offline']);
1427          $this->assertCount(1, $contacts['online']);
1428          $this->assertCount(3, $contacts['strangers']);
1429          core_message_external::block_contacts(array($user_blocked->id));
1430          $this->assertDebuggingCalled();
1431          $contacts = core_message_external::get_contacts();
1432          $contacts = external_api::clean_returnvalue(core_message_external::get_contacts_returns(), $contacts);
1433          $this->assertCount(3, $contacts['offline']);
1434          $this->assertCount(1, $contacts['online']);
1435          $this->assertCount(2, $contacts['strangers']);
1436  
1437          // Checking some of the fields returned.
1438          $stranger = array_pop($contacts['strangers']);
1439  
1440          $this->assertEquals(core_user::NOREPLY_USER, $stranger['id']);
1441          $this->assertEquals(1, $stranger['unread']);
1442  
1443          // Check that deleted users are not returned.
1444          delete_user($user_offline1);
1445          delete_user($user_stranger);
1446          delete_user($user_online);
1447          $contacts = core_message_external::get_contacts();
1448          $contacts = external_api::clean_returnvalue(core_message_external::get_contacts_returns(), $contacts);
1449          $this->assertCount(2, $contacts['offline']);
1450          $this->assertCount(0, $contacts['online']);
1451          $this->assertCount(1, $contacts['strangers']);
1452      }
1453  
1454      /**
1455       * Test search_contacts.
1456       * @expectedException moodle_exception
1457       */
1458      public function test_search_contacts() {
1459          global $DB;
1460          $this->resetAfterTest(true);
1461  
1462          $course1 = $this->getDataGenerator()->create_course();
1463          $course2 = $this->getDataGenerator()->create_course();
1464  
1465          $user1 = new stdClass();
1466          $user1->firstname = 'X';
1467          $user1->lastname = 'X';
1468          $user1 = $this->getDataGenerator()->create_user($user1);
1469          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
1470          $this->getDataGenerator()->enrol_user($user1->id, $course2->id);
1471  
1472          $user2 = new stdClass();
1473          $user2->firstname = 'Eric';
1474          $user2->lastname = 'Cartman';
1475          $user2 = self::getDataGenerator()->create_user($user2);
1476          $user3 = new stdClass();
1477          $user3->firstname = 'Stan';
1478          $user3->lastname = 'Marsh';
1479          $user3 = self::getDataGenerator()->create_user($user3);
1480          self::getDataGenerator()->enrol_user($user3->id, $course1->id);
1481          $user4 = new stdClass();
1482          $user4->firstname = 'Kyle';
1483          $user4->lastname = 'Broflovski';
1484          $user4 = self::getDataGenerator()->create_user($user4);
1485          $user5 = new stdClass();
1486          $user5->firstname = 'Kenny';
1487          $user5->lastname = 'McCormick';
1488          $user5 = self::getDataGenerator()->create_user($user5);
1489          self::getDataGenerator()->enrol_user($user5->id, $course2->id);
1490  
1491          $this->setUser($user1);
1492  
1493          $results = core_message_external::search_contacts('r');
1494          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1495          $this->assertCount(5, $results); // Users 2 through 5 + admin
1496  
1497          $results = core_message_external::search_contacts('r', true);
1498          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1499          $this->assertCount(2, $results);
1500  
1501          $results = core_message_external::search_contacts('Kyle', false);
1502          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1503          $this->assertCount(1, $results);
1504          $result = reset($results);
1505          $this->assertEquals($user4->id, $result['id']);
1506  
1507          $results = core_message_external::search_contacts('y', false);
1508          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1509          $this->assertCount(2, $results);
1510  
1511          $results = core_message_external::search_contacts('y', true);
1512          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1513          $this->assertCount(1, $results);
1514          $result = reset($results);
1515          $this->assertEquals($user5->id, $result['id']);
1516  
1517          // Empty query, will throw an exception.
1518          $results = core_message_external::search_contacts('');
1519      }
1520  
1521      /**
1522       * Test get_messages.
1523       */
1524      public function test_get_messages() {
1525          global $CFG, $DB;
1526          $this->resetAfterTest(true);
1527  
1528          $this->preventResetByRollback();
1529          // This mark the messages as read!.
1530          $sink = $this->redirectMessages();
1531  
1532          $user1 = self::getDataGenerator()->create_user();
1533          $user2 = self::getDataGenerator()->create_user();
1534          $user3 = self::getDataGenerator()->create_user();
1535  
1536          $course = self::getDataGenerator()->create_course();
1537  
1538          // Send a message from one user to another.
1539          message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
1540          message_post_message($user1, $user3, 'some random text 2', FORMAT_MOODLE);
1541          message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
1542          message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
1543          message_post_message($user3, $user1, 'some random text 5', FORMAT_MOODLE);
1544  
1545          $this->setUser($user1);
1546          // Get read conversations from user1 to user2.
1547          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', true, true, 0, 0);
1548          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1549          $this->assertCount(1, $messages['messages']);
1550  
1551          // Delete the message.
1552          $message = array_shift($messages['messages']);
1553          \core_message\api::delete_message($user1->id, $message['id']);
1554  
1555          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', true, true, 0, 0);
1556          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1557          $this->assertCount(0, $messages['messages']);
1558  
1559          // Get unread conversations from user1 to user2.
1560          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', false, true, 0, 0);
1561          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1562          $this->assertCount(0, $messages['messages']);
1563  
1564          // Get read messages send from user1.
1565          $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
1566          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1567          $this->assertCount(1, $messages['messages']);
1568  
1569          $this->setUser($user2);
1570          // Get read conversations from any user to user2.
1571          $messages = core_message_external::get_messages($user2->id, 0, 'conversations', true, true, 0, 0);
1572          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1573          $this->assertCount(2, $messages['messages']);
1574  
1575          // Conversations from user3 to user2.
1576          $messages = core_message_external::get_messages($user2->id, $user3->id, 'conversations', true, true, 0, 0);
1577          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1578          $this->assertCount(1, $messages['messages']);
1579  
1580          // Delete the message.
1581          $message = array_shift($messages['messages']);
1582          \core_message\api::delete_message($user2->id, $message['id']);
1583  
1584          $messages = core_message_external::get_messages($user2->id, $user3->id, 'conversations', true, true, 0, 0);
1585          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1586          $this->assertCount(0, $messages['messages']);
1587  
1588          $this->setUser($user3);
1589          // Get read notifications received by user3.
1590          $messages = core_message_external::get_messages($user3->id, 0, 'notifications', true, true, 0, 0);
1591          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1592          $this->assertCount(0, $messages['messages']);
1593  
1594          // Now, create some notifications...
1595          // We are creating fake notifications but based on real ones.
1596  
1597          // This one comes from a disabled plugin's provider and therefore is not sent.
1598          $eventdata = new \core\message\message();
1599          $eventdata->courseid          = $course->id;
1600          $eventdata->notification      = 1;
1601          $eventdata->modulename        = 'moodle';
1602          $eventdata->component         = 'enrol_paypal';
1603          $eventdata->name              = 'paypal_enrolment';
1604          $eventdata->userfrom          = get_admin();
1605          $eventdata->userto            = $user1;
1606          $eventdata->subject           = "Moodle: PayPal payment";
1607          $eventdata->fullmessage       = "Your PayPal payment is pending.";
1608          $eventdata->fullmessageformat = FORMAT_PLAIN;
1609          $eventdata->fullmessagehtml   = '';
1610          $eventdata->smallmessage      = '';
1611          message_send($eventdata);
1612          $this->assertDebuggingCalled('Attempt to send msg from a provider enrol_paypal/paypal_enrolment '.
1613              'that is inactive or not allowed for the user id='.$user1->id);
1614  
1615          // This one omits notification = 1.
1616          $message = new \core\message\message();
1617          $message->courseid          = $course->id;
1618          $message->component         = 'enrol_manual';
1619          $message->name              = 'expiry_notification';
1620          $message->userfrom          = $user2;
1621          $message->userto            = $user1;
1622          $message->subject           = 'Test: This is not a notification but otherwise is valid';
1623          $message->fullmessage       = 'Test: Full message';
1624          $message->fullmessageformat = FORMAT_MARKDOWN;
1625          $message->fullmessagehtml   = markdown_to_html($message->fullmessage);
1626          $message->smallmessage      = $message->subject;
1627          $message->contexturlname    = $course->fullname;
1628          $message->contexturl        = (string)new moodle_url('/course/view.php', array('id' => $course->id));
1629          message_send($message);
1630  
1631          $message = new \core\message\message();
1632          $message->courseid          = $course->id;
1633          $message->notification      = 1;
1634          $message->component         = 'enrol_manual';
1635          $message->name              = 'expiry_notification';
1636          $message->userfrom          = $user2;
1637          $message->userto            = $user1;
1638          $message->subject           = 'Enrolment expired';
1639          $message->fullmessage       = 'Enrolment expired blah blah blah';
1640          $message->fullmessageformat = FORMAT_MARKDOWN;
1641          $message->fullmessagehtml   = markdown_to_html($message->fullmessage);
1642          $message->smallmessage      = $message->subject;
1643          $message->contexturlname    = $course->fullname;
1644          $message->contexturl        = (string)new moodle_url('/course/view.php', array('id' => $course->id));
1645          message_send($message);
1646  
1647          $userfrom = core_user::get_noreply_user();
1648          $userfrom->maildisplay = true;
1649          $eventdata = new \core\message\message();
1650          $eventdata->courseid          = $course->id;
1651          $eventdata->component         = 'moodle';
1652          $eventdata->name              = 'badgecreatornotice';
1653          $eventdata->userfrom          = $userfrom;
1654          $eventdata->userto            = $user1;
1655          $eventdata->notification      = 1;
1656          $eventdata->subject           = 'New badge';
1657          $eventdata->fullmessage       = format_text_email($eventdata->subject, FORMAT_HTML);
1658          $eventdata->fullmessageformat = FORMAT_PLAIN;
1659          $eventdata->fullmessagehtml   = $eventdata->subject;
1660          $eventdata->smallmessage      = $eventdata->subject;
1661          message_send($eventdata);
1662  
1663          $eventdata = new \core\message\message();
1664          $eventdata->courseid         = $course->id;
1665          $eventdata->name             = 'submission';
1666          $eventdata->component        = 'mod_feedback';
1667          $eventdata->userfrom         = $user1;
1668          $eventdata->userto           = $user2;
1669          $eventdata->subject          = 'Feedback submitted';
1670          $eventdata->fullmessage      = 'Feedback submitted from an user';
1671          $eventdata->fullmessageformat = FORMAT_PLAIN;
1672          $eventdata->fullmessagehtml  = '<strong>Feedback submitted</strong>';
1673          $eventdata->smallmessage     = '';
1674          $eventdata->customdata         = ['datakey' => 'data'];
1675          message_send($eventdata);
1676  
1677          $this->setUser($user1);
1678          // Get read notifications from any user to user1.
1679          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', true, true, 0, 0);
1680          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1681          $this->assertCount(3, $messages['messages']);
1682  
1683          // Get one read notifications from any user to user1.
1684          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', true, true, 0, 1);
1685          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1686          $this->assertCount(1, $messages['messages']);
1687  
1688          // Get unread notifications from any user to user1.
1689          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', false, true, 0, 0);
1690          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1691          $this->assertCount(0, $messages['messages']);
1692  
1693          // Get read both type of messages from any user to user1.
1694          $messages = core_message_external::get_messages($user1->id, 0, 'both', true, true, 0, 0);
1695          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1696          $this->assertCount(4, $messages['messages']);
1697  
1698          // Get read notifications from no-reply-user to user1.
1699          $messages = core_message_external::get_messages($user1->id, $userfrom->id, 'notifications', true, true, 0, 0);
1700          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1701          $this->assertCount(1, $messages['messages']);
1702  
1703          // Get notifications send by user1 to any user.
1704          $messages = core_message_external::get_messages(0, $user1->id, 'notifications', true, true, 0, 0);
1705          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1706          $this->assertCount(1, $messages['messages']);
1707          // Check we receive custom data as a unserialisable json.
1708          $this->assertObjectHasAttribute('datakey', json_decode($messages['messages'][0]['customdata']));
1709          $this->assertEquals('mod_feedback', $messages['messages'][0]['component']);
1710          $this->assertEquals('submission', $messages['messages'][0]['eventtype']);
1711  
1712          // Test warnings.
1713          $CFG->messaging = 0;
1714  
1715          $messages = core_message_external::get_messages(0, $user1->id, 'both', true, true, 0, 0);
1716          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1717          $this->assertCount(1, $messages['warnings']);
1718  
1719          // Test exceptions.
1720  
1721          // Messaging disabled.
1722          try {
1723              $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
1724              $this->fail('Exception expected due messaging disabled.');
1725          } catch (moodle_exception $e) {
1726              $this->assertEquals('disabled', $e->errorcode);
1727          }
1728  
1729          $CFG->messaging = 1;
1730  
1731          // Invalid users.
1732          try {
1733              $messages = core_message_external::get_messages(0, 0, 'conversations', true, true, 0, 0);
1734              $this->fail('Exception expected due invalid users.');
1735          } catch (moodle_exception $e) {
1736              $this->assertEquals('accessdenied', $e->errorcode);
1737          }
1738  
1739          // Invalid user ids.
1740          try {
1741              $messages = core_message_external::get_messages(2500, 0, 'conversations', true, true, 0, 0);
1742              $this->fail('Exception expected due invalid users.');
1743          } catch (moodle_exception $e) {
1744              $this->assertEquals('invaliduser', $e->errorcode);
1745          }
1746  
1747          // Invalid users (permissions).
1748          $this->setUser($user2);
1749          try {
1750              $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
1751              $this->fail('Exception expected due invalid user.');
1752          } catch (moodle_exception $e) {
1753              $this->assertEquals('accessdenied', $e->errorcode);
1754          }
1755  
1756      }
1757  
1758      /**
1759       * Test get_messages where we want all messages from a user, sent to any user.
1760       */
1761      public function test_get_messages_useridto_all() {
1762          $this->resetAfterTest(true);
1763  
1764          $user1 = self::getDataGenerator()->create_user();
1765          $user2 = self::getDataGenerator()->create_user();
1766          $user3 = self::getDataGenerator()->create_user();
1767  
1768          $this->setUser($user1);
1769  
1770          // Send a message from user 1 to two other users.
1771          $this->send_message($user1, $user2, 'some random text 1', 0, 1);
1772          $this->send_message($user1, $user3, 'some random text 2', 0, 2);
1773  
1774          // Get messages sent from user 1.
1775          $messages = core_message_external::get_messages(0, $user1->id, 'conversations', false, false, 0, 0);
1776          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1777  
1778          // Confirm the data is correct.
1779          $messages = $messages['messages'];
1780          $this->assertCount(2, $messages);
1781  
1782          $message1 = array_shift($messages);
1783          $message2 = array_shift($messages);
1784  
1785          $this->assertEquals($user1->id, $message1['useridfrom']);
1786          $this->assertEquals($user2->id, $message1['useridto']);
1787  
1788          $this->assertEquals($user1->id, $message2['useridfrom']);
1789          $this->assertEquals($user3->id, $message2['useridto']);
1790      }
1791  
1792      /**
1793       * Test get_messages where we want all messages to a user, sent by any user.
1794       */
1795      public function test_get_messages_useridfrom_all() {
1796          $this->resetAfterTest();
1797  
1798          $user1 = self::getDataGenerator()->create_user();
1799          $user2 = self::getDataGenerator()->create_user();
1800          $user3 = self::getDataGenerator()->create_user();
1801  
1802          $this->setUser($user1);
1803  
1804          // Send a message to user 1 from two other users.
1805          $this->send_message($user2, $user1, 'some random text 1', 0, 1);
1806          $this->send_message($user3, $user1, 'some random text 2', 0, 2);
1807  
1808          // Get messages sent to user 1.
1809          $messages = core_message_external::get_messages($user1->id, 0, 'conversations', false, false, 0, 0);
1810          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1811  
1812          // Confirm the data is correct.
1813          $messages = $messages['messages'];
1814          $this->assertCount(2, $messages);
1815  
1816          $message1 = array_shift($messages);
1817          $message2 = array_shift($messages);
1818  
1819          $this->assertEquals($user2->id, $message1['useridfrom']);
1820          $this->assertEquals($user1->id, $message1['useridto']);
1821  
1822          $this->assertEquals($user3->id, $message2['useridfrom']);
1823          $this->assertEquals($user1->id, $message2['useridto']);
1824      }
1825  
1826      /**
1827       * Test get_blocked_users.
1828       */
1829      public function test_get_blocked_users() {
1830          $this->resetAfterTest(true);
1831  
1832          $user1 = self::getDataGenerator()->create_user();
1833          $userstranger = self::getDataGenerator()->create_user();
1834          $useroffline1 = self::getDataGenerator()->create_user();
1835          $useroffline2 = self::getDataGenerator()->create_user();
1836          $userblocked = self::getDataGenerator()->create_user();
1837  
1838          // Login as user1.
1839          $this->setUser($user1);
1840  
1841          \core_message\api::add_contact($user1->id, $useroffline1->id);
1842          \core_message\api::add_contact($user1->id, $useroffline2->id);
1843  
1844          // The userstranger sends a couple of messages to user1.
1845          $this->send_message($userstranger, $user1, 'Hello there!');
1846          $this->send_message($userstranger, $user1, 'How you goin?');
1847  
1848          // The userblocked sends a message to user1.
1849          // Note that this user is not blocked at this point.
1850          $this->send_message($userblocked, $user1, 'Here, have some spam.');
1851  
1852          // Retrieve the list of blocked users.
1853          $this->setUser($user1);
1854          $blockedusers = core_message_external::get_blocked_users($user1->id);
1855          $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1856          $this->assertCount(0, $blockedusers['users']);
1857  
1858          // Block the $userblocked and retrieve again the list.
1859          core_message_external::block_contacts(array($userblocked->id));
1860          $this->assertDebuggingCalled();
1861          $blockedusers = core_message_external::get_blocked_users($user1->id);
1862          $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1863          $this->assertCount(1, $blockedusers['users']);
1864  
1865          // Remove the $userblocked and check that the list now is empty.
1866          delete_user($userblocked);
1867          $blockedusers = core_message_external::get_blocked_users($user1->id);
1868          $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1869          $this->assertCount(0, $blockedusers['users']);
1870      }
1871  
1872      /**
1873       * Test mark_message_read.
1874       */
1875      public function test_mark_message_read() {
1876          $this->resetAfterTest(true);
1877  
1878          $user1 = self::getDataGenerator()->create_user();
1879          $user2 = self::getDataGenerator()->create_user();
1880          $user3 = self::getDataGenerator()->create_user();
1881  
1882          // Login as user1.
1883          $this->setUser($user1);
1884          \core_message\api::add_contact($user1->id, $user2->id);
1885          \core_message\api::add_contact($user1->id, $user3->id);
1886  
1887          // The user2 sends a couple of messages to user1.
1888          $this->send_message($user2, $user1, 'Hello there!');
1889          $this->send_message($user2, $user1, 'How you goin?');
1890          $this->send_message($user3, $user1, 'How you goin?');
1891          $this->send_message($user3, $user2, 'How you goin?');
1892  
1893          // Retrieve all messages sent by user2 (they are currently unread).
1894          $lastmessages = message_get_messages($user1->id, $user2->id, 0, false);
1895  
1896          $messageids = array();
1897          foreach ($lastmessages as $m) {
1898              $messageid = core_message_external::mark_message_read($m->id, time());
1899              $messageids[] = external_api::clean_returnvalue(core_message_external::mark_message_read_returns(), $messageid);
1900          }
1901  
1902          // Retrieve all messages sent (they are currently read).
1903          $lastmessages = message_get_messages($user1->id, $user2->id, 0, true);
1904          $this->assertCount(2, $lastmessages);
1905          $this->assertArrayHasKey($messageids[0]['messageid'], $lastmessages);
1906          $this->assertArrayHasKey($messageids[1]['messageid'], $lastmessages);
1907  
1908          // Retrieve all messages sent by any user (that are currently unread).
1909          $lastmessages = message_get_messages($user1->id, 0, 0, false);
1910          $this->assertCount(1, $lastmessages);
1911  
1912          // Invalid message ids.
1913          try {
1914              $messageid = core_message_external::mark_message_read(1337, time());
1915              $this->fail('Exception expected due invalid messageid.');
1916          } catch (dml_missing_record_exception $e) {
1917              $this->assertEquals('invalidrecordunknown', $e->errorcode);
1918          }
1919  
1920          // A message to a different user.
1921          $lastmessages = message_get_messages($user2->id, $user3->id, 0, false);
1922          $messageid = array_pop($lastmessages)->id;
1923          try {
1924              $messageid = core_message_external::mark_message_read($messageid, time());
1925              $this->fail('Exception expected due invalid messageid.');
1926          } catch (invalid_parameter_exception $e) {
1927              $this->assertEquals('invalidparameter', $e->errorcode);
1928          }
1929      }
1930  
1931      /**
1932       * Test mark_notification_read.
1933       */
1934      public function test_mark_notification_read() {
1935          $this->resetAfterTest(true);
1936  
1937          $user1 = self::getDataGenerator()->create_user();
1938          $user2 = self::getDataGenerator()->create_user();
1939          $user3 = self::getDataGenerator()->create_user();
1940  
1941          // Login as user1.
1942          $this->setUser($user1);
1943          \core_message\api::add_contact($user1->id, $user2->id);
1944          \core_message\api::add_contact($user1->id, $user3->id);
1945  
1946          // The user2 sends a couple of notifications to user1.
1947          $this->send_message($user2, $user1, 'Hello there!', 1);
1948          $this->send_message($user2, $user1, 'How you goin?', 1);
1949          $this->send_message($user3, $user1, 'How you goin?', 1);
1950          $this->send_message($user3, $user2, 'How you goin?', 1);
1951  
1952          // Retrieve all notifications sent by user2 (they are currently unread).
1953          $lastnotifications = message_get_messages($user1->id, $user2->id, 1, false);
1954  
1955          $notificationids = array();
1956          foreach ($lastnotifications as $n) {
1957              $notificationid = core_message_external::mark_notification_read($n->id, time());
1958              $notificationids[] = external_api::clean_returnvalue(core_message_external::mark_notification_read_returns(),
1959                  $notificationid);
1960          }
1961  
1962          // Retrieve all notifications sent (they are currently read).
1963          $lastnotifications = message_get_messages($user1->id, $user2->id, 1, true);
1964          $this->assertCount(2, $lastnotifications);
1965          $this->assertArrayHasKey($notificationids[1]['notificationid'], $lastnotifications);
1966          $this->assertArrayHasKey($notificationids[0]['notificationid'], $lastnotifications);
1967  
1968          // Retrieve all notifications sent by any user (that are currently unread).
1969          $lastnotifications = message_get_messages($user1->id, 0, 1, false);
1970          $this->assertCount(1, $lastnotifications);
1971  
1972          // Invalid notification ids.
1973          try {
1974              $notificationid = core_message_external::mark_notification_read(1337, time());
1975              $this->fail('Exception expected due invalid notificationid.');
1976          } catch (dml_missing_record_exception $e) {
1977              $this->assertEquals('invalidrecord', $e->errorcode);
1978          }
1979  
1980          // A notification to a different user.
1981          $lastnotifications = message_get_messages($user2->id, $user3->id, 1, false);
1982          $notificationid = array_pop($lastnotifications)->id;
1983          try {
1984              $notificationid = core_message_external::mark_notification_read($notificationid, time());
1985              $this->fail('Exception expected due invalid notificationid.');
1986          } catch (invalid_parameter_exception $e) {
1987              $this->assertEquals('invalidparameter', $e->errorcode);
1988          }
1989      }
1990  
1991      /**
1992       * Test delete_message.
1993       */
1994      public function test_delete_message() {
1995          global $DB;
1996          $this->resetAfterTest(true);
1997  
1998          $user1 = self::getDataGenerator()->create_user();
1999          $user2 = self::getDataGenerator()->create_user();
2000          $user3 = self::getDataGenerator()->create_user();
2001          $user4 = self::getDataGenerator()->create_user();
2002  
2003          // Login as user1.
2004          $this->setUser($user1);
2005          \core_message\api::add_contact($user1->id, $user2->id);
2006          \core_message\api::add_contact($user1->id, $user3->id);
2007  
2008          // User user1 does not interchange messages with user3.
2009          $m1to2 = message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
2010          $m2to3 = message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
2011          $m3to2 = message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
2012          $m3to4 = message_post_message($user3, $user4, 'some random text 4', FORMAT_MOODLE);
2013  
2014          // Retrieve all messages sent by user2 (they are currently unread).
2015          $lastmessages = message_get_messages($user1->id, $user2->id, 0, false);
2016  
2017          // Delete a message not read, as a user from.
2018          $result = core_message_external::delete_message($m1to2, $user1->id, false);
2019          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
2020          $this->assertTrue($result['status']);
2021          $this->assertCount(0, $result['warnings']);
2022          $mua = $DB->get_record('message_user_actions', array('messageid' => $m1to2, 'userid' => $user1->id));
2023          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua->action);
2024  
2025          // Try to delete the same message again.
2026          $result = core_message_external::delete_message($m1to2, $user1->id, false);
2027          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
2028          $this->assertFalse($result['status']);
2029  
2030          // Try to delete a message that does not belong to me.
2031          try {
2032              $messageid = core_message_external::delete_message($m2to3, $user3->id, false);
2033              $this->fail('Exception expected due invalid messageid.');
2034          } catch (moodle_exception $e) {
2035              $this->assertEquals('You do not have permission to delete this message', $e->errorcode);
2036          }
2037  
2038          $this->setUser($user3);
2039          // Delete a message not read, as a user to.
2040          $result = core_message_external::delete_message($m2to3, $user3->id, false);
2041          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
2042          $this->assertTrue($result['status']);
2043          $this->assertCount(0, $result['warnings']);
2044          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m2to3, 'userid' => $user3->id,
2045              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
2046  
2047          // Delete a message read.
2048          $message = $DB->get_record('messages', ['id' => $m3to2]);
2049          \core_message\api::mark_message_as_read($user3->id, $message, time());
2050          $result = core_message_external::delete_message($m3to2, $user3->id);
2051          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
2052          $this->assertTrue($result['status']);
2053          $this->assertCount(0, $result['warnings']);
2054          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m3to2, 'userid' => $user3->id,
2055              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
2056  
2057          // Invalid message ids.
2058          try {
2059              $result = core_message_external::delete_message(-1, $user1->id);
2060              $this->fail('Exception expected due invalid messageid.');
2061          } catch (dml_missing_record_exception $e) {
2062              $this->assertEquals('invalidrecord', $e->errorcode);
2063          }
2064  
2065          // Invalid user.
2066          try {
2067              $result = core_message_external::delete_message($m1to2, -1, false);
2068              $this->fail('Exception expected due invalid user.');
2069          } catch (moodle_exception $e) {
2070              $this->assertEquals('invaliduser', $e->errorcode);
2071          }
2072  
2073          // Not active user.
2074          delete_user($user2);
2075          try {
2076              $result = core_message_external::delete_message($m1to2, $user2->id, false);
2077              $this->fail('Exception expected due invalid user.');
2078          } catch (moodle_exception $e) {
2079              $this->assertEquals('userdeleted', $e->errorcode);
2080          }
2081  
2082          // Now, as an admin, try to delete any message.
2083          $this->setAdminUser();
2084          $result = core_message_external::delete_message($m3to4, $user4->id, false);
2085          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
2086          $this->assertTrue($result['status']);
2087          $this->assertCount(0, $result['warnings']);
2088          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m3to4, 'userid' => $user4->id,
2089              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
2090  
2091      }
2092  
2093      public function test_mark_all_notifications_as_read_invalid_user_exception() {
2094          $this->resetAfterTest(true);
2095  
2096          $this->expectException('moodle_exception');
2097          core_message_external::mark_all_notifications_as_read(-2132131, 0);
2098      }
2099  
2100      public function test_mark_all_notifications_as_read_access_denied_exception() {
2101          $this->resetAfterTest(true);
2102  
2103          $sender = $this->getDataGenerator()->create_user();
2104          $user = $this->getDataGenerator()->create_user();
2105  
2106          $this->setUser($user);
2107          $this->expectException('moodle_exception');
2108          core_message_external::mark_all_notifications_as_read($sender->id, 0);
2109      }
2110  
2111      public function test_mark_all_notifications_as_read_missing_from_user_exception() {
2112          $this->resetAfterTest(true);
2113  
2114          $sender = $this->getDataGenerator()->create_user();
2115  
2116          $this->setUser($sender);
2117          $this->expectException('moodle_exception');
2118          core_message_external::mark_all_notifications_as_read($sender->id, 99999);
2119      }
2120  
2121      public function test_mark_all_notifications_as_read() {
2122          global $DB;
2123  
2124          $this->resetAfterTest(true);
2125  
2126          $sender1 = $this->getDataGenerator()->create_user();
2127          $sender2 = $this->getDataGenerator()->create_user();
2128          $sender3 = $this->getDataGenerator()->create_user();
2129          $recipient = $this->getDataGenerator()->create_user();
2130  
2131          $this->setUser($recipient);
2132  
2133          $this->send_message($sender1, $recipient, 'Notification', 1);
2134          $this->send_message($sender1, $recipient, 'Notification', 1);
2135          $this->send_message($sender2, $recipient, 'Notification', 1);
2136          $this->send_message($sender2, $recipient, 'Notification', 1);
2137          $this->send_message($sender3, $recipient, 'Notification', 1);
2138          $this->send_message($sender3, $recipient, 'Notification', 1);
2139  
2140          core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id);
2141          $readnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', [$recipient->id]);
2142          $unreadnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NULL', [$recipient->id]);
2143  
2144          $this->assertCount(2, $readnotifications);
2145          $this->assertCount(4, $unreadnotifications);
2146  
2147          core_message_external::mark_all_notifications_as_read($recipient->id, 0);
2148          $readnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', [$recipient->id]);
2149          $unreadnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NULL', [$recipient->id]);
2150  
2151          $this->assertCount(6, $readnotifications);
2152          $this->assertCount(0, $unreadnotifications);
2153      }
2154  
2155      public function test_mark_all_notifications_as_read_time_created_to() {
2156          global $DB;
2157  
2158          $this->resetAfterTest(true);
2159  
2160          $sender1 = $this->getDataGenerator()->create_user();
2161          $sender2 = $this->getDataGenerator()->create_user();
2162  
2163          $recipient = $this->getDataGenerator()->create_user();
2164          $this->setUser($recipient);
2165  
2166          // Record messages as sent on one second intervals.
2167          $time = time();
2168  
2169          $this->send_message($sender1, $recipient, 'Message 1', 1, $time);
2170          $this->send_message($sender2, $recipient, 'Message 2', 1, $time + 1);
2171          $this->send_message($sender1, $recipient, 'Message 3', 1, $time + 2);
2172          $this->send_message($sender2, $recipient, 'Message 4', 1, $time + 3);
2173  
2174          // Mark notifications sent from sender1 up until the second message; should only mark the first notification as read.
2175          core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id, $time + 1);
2176  
2177          $params = [$recipient->id];
2178  
2179          $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
2180          $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
2181  
2182          // Mark all notifications as read from any sender up to the time the third message was sent.
2183          core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 2);
2184  
2185          $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
2186          $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
2187  
2188          // Mark all notifications as read from any sender with a time after all messages were sent.
2189          core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 10);
2190  
2191          $this->assertEquals(4, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
2192          $this->assertEquals(0, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
2193      }
2194  
2195      /**
2196       * Test get_user_notification_preferences
2197       */
2198      public function test_get_user_notification_preferences() {
2199          $this->resetAfterTest(true);
2200  
2201          $user = self::getDataGenerator()->create_user();
2202          $this->setUser($user);
2203  
2204          // Set a couple of preferences to test.
2205          set_user_preference('message_provider_mod_assign_assign_notification_loggedin', 'popup', $user);
2206          set_user_preference('message_provider_mod_assign_assign_notification_loggedoff', 'email', $user);
2207  
2208          $prefs = core_message_external::get_user_notification_preferences();
2209          $prefs = external_api::clean_returnvalue(core_message_external::get_user_notification_preferences_returns(), $prefs);
2210          // Check processors.
2211          $this->assertGreaterThanOrEqual(2, count($prefs['preferences']['processors']));
2212          $this->assertEquals($user->id, $prefs['preferences']['userid']);
2213  
2214          // Check components.
2215          $this->assertGreaterThanOrEqual(8, count($prefs['preferences']['components']));
2216  
2217          // Check some preferences that we previously set.
2218          $found = 0;
2219          foreach ($prefs['preferences']['components'] as $component) {
2220              foreach ($component['notifications'] as $prefdata) {
2221                  if ($prefdata['preferencekey'] != 'message_provider_mod_assign_assign_notification') {
2222                      continue;
2223                  }
2224                  foreach ($prefdata['processors'] as $processor) {
2225                      if ($processor['name'] == 'popup') {
2226                          $this->assertTrue($processor['loggedin']['checked']);
2227                          $found++;
2228                      } else if ($processor['name'] == 'email') {
2229                          $this->assertTrue($processor['loggedoff']['checked']);
2230                          $found++;
2231                      }
2232                  }
2233              }
2234          }
2235          $this->assertEquals(2, $found);
2236      }
2237  
2238      /**
2239       * Test get_user_notification_preferences permissions
2240       */
2241      public function test_get_user_notification_preferences_permissions() {
2242          $this->resetAfterTest(true);
2243  
2244          $user = self::getDataGenerator()->create_user();
2245          $otheruser = self::getDataGenerator()->create_user();
2246          $this->setUser($user);
2247  
2248          $this->expectException('moodle_exception');
2249          $prefs = core_message_external::get_user_notification_preferences($otheruser->id);
2250      }
2251  
2252      /**
2253       * Tests searching users in a course.
2254       */
2255      public function test_data_for_messagearea_search_users_in_course() {
2256          $this->resetAfterTest(true);
2257  
2258          // Create some users.
2259          $user1 = new stdClass();
2260          $user1->firstname = 'User';
2261          $user1->lastname = 'One';
2262          $user1 = self::getDataGenerator()->create_user($user1);
2263  
2264          // The person doing the search.
2265          $this->setUser($user1);
2266  
2267          // Set the second user's status to online by setting their last access to now.
2268          $user2 = new stdClass();
2269          $user2->firstname = 'User';
2270          $user2->lastname = 'Two';
2271          $user2->lastaccess = time();
2272          $user2 = self::getDataGenerator()->create_user($user2);
2273  
2274          // Block the second user.
2275          \core_message\api::block_user($user1->id, $user2->id);
2276  
2277          $user3 = new stdClass();
2278          $user3->firstname = 'User';
2279          $user3->lastname = 'Three';
2280          $user3 = self::getDataGenerator()->create_user($user3);
2281  
2282          // Create a course.
2283          $course1 = new stdClass();
2284          $course1->fullname = 'Course';
2285          $course1->shortname = 'One';
2286          $course1 = $this->getDataGenerator()->create_course();
2287  
2288          // Enrol the user we are doing the search for and one user in the course.
2289          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
2290          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
2291  
2292          // Perform a search.
2293          $result = core_message_external::data_for_messagearea_search_users_in_course($user1->id, $course1->id, 'User');
2294  
2295          // We need to execute the return values cleaning process to simulate the web service.
2296          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_users_in_course_returns(),
2297              $result);
2298  
2299          // Check that we only retrieved a user that was enrolled, and that the user performing the search was not returned.
2300          $users = $result['contacts'];
2301          $this->assertCount(1, $users);
2302  
2303          $user = $users[0];
2304          $this->assertEquals($user2->id, $user['userid']);
2305          $this->assertEquals(fullname($user2), $user['fullname']);
2306          $this->assertFalse($user['ismessaging']);
2307          $this->assertFalse($user['sentfromcurrentuser']);
2308          $this->assertNull($user['lastmessage']);
2309          $this->assertNull($user['messageid']);
2310          $this->assertNull($user['isonline']);
2311          $this->assertFalse($user['isread']);
2312          $this->assertTrue($user['isblocked']);
2313          $this->assertNull($user['unreadcount']);
2314      }
2315  
2316      /**
2317       * Tests searching users in course as another user.
2318       */
2319      public function test_data_for_messagearea_search_users_in_course_as_other_user() {
2320          $this->resetAfterTest(true);
2321  
2322          // The person doing the search for another user.
2323          $this->setAdminUser();
2324  
2325          // Create some users.
2326          $user1 = new stdClass();
2327          $user1->firstname = 'User';
2328          $user1->lastname = 'One';
2329          $user1 = self::getDataGenerator()->create_user($user1);
2330  
2331          $user2 = new stdClass();
2332          $user2->firstname = 'User';
2333          $user2->lastname = 'Two';
2334          $user2 = self::getDataGenerator()->create_user($user2);
2335  
2336          $user3 = new stdClass();
2337          $user3->firstname = 'User';
2338          $user3->lastname = 'Three';
2339          $user3 = self::getDataGenerator()->create_user($user3);
2340  
2341          // Create a course.
2342          $course1 = new stdClass();
2343          $course1->fullname = 'Course';
2344          $course1->shortname = 'One';
2345          $course1 = $this->getDataGenerator()->create_course();
2346  
2347          // Enrol the user we are doing the search for and one user in the course.
2348          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
2349          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
2350  
2351          // Perform a search.
2352          $result = core_message_external::data_for_messagearea_search_users_in_course($user1->id, $course1->id, 'User');
2353  
2354          // We need to execute the return values cleaning process to simulate the web service server.
2355          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_users_in_course_returns(),
2356              $result);
2357  
2358          // Check that we got the user enrolled, and that the user we are performing the search on behalf of was not returned.
2359          $users = $result['contacts'];
2360          $this->assertCount(1, $users);
2361  
2362          $user = $users[0];
2363          $this->assertEquals($user2->id, $user['userid']);
2364          $this->assertEquals(fullname($user2), $user['fullname']);
2365          $this->assertFalse($user['ismessaging']);
2366          $this->assertFalse($user['sentfromcurrentuser']);
2367          $this->assertNull($user['lastmessage']);
2368          $this->assertNull($user['messageid']);
2369          $this->assertFalse($user['isonline']);
2370          $this->assertFalse($user['isread']);
2371          $this->assertFalse($user['isblocked']);
2372          $this->assertNull($user['unreadcount']);
2373      }
2374  
2375      /**
2376       * Tests searching users in course as another user without the proper capabilities.
2377       */
2378      public function test_data_for_messagearea_search_users_in_course_as_other_user_without_cap() {
2379          $this->resetAfterTest(true);
2380  
2381          // Create some users.
2382          $user1 = self::getDataGenerator()->create_user();
2383          $user2 = self::getDataGenerator()->create_user();
2384  
2385          // The person doing the search for another user.
2386          $this->setUser($user1);
2387  
2388          // Create a course.
2389          $course = $this->getDataGenerator()->create_course();
2390  
2391          // Ensure an exception is thrown.
2392          $this->expectException('moodle_exception');
2393          core_message_external::data_for_messagearea_search_users_in_course($user2->id, $course->id, 'User');
2394          $this->assertDebuggingCalled();
2395      }
2396  
2397      /**
2398       * Tests searching users in course with messaging disabled.
2399       */
2400      public function test_data_for_messagearea_search_users_in_course_messaging_disabled() {
2401          global $CFG;
2402  
2403          $this->resetAfterTest(true);
2404  
2405          // Create some skeleton data just so we can call the WS..
2406          $user = self::getDataGenerator()->create_user();
2407          $course = $this->getDataGenerator()->create_course();
2408  
2409          // The person doing the search for another user.
2410          $this->setUser($user);
2411  
2412          // Disable messaging.
2413          $CFG->messaging = 0;
2414  
2415          // Ensure an exception is thrown.
2416          $this->expectException('moodle_exception');
2417          core_message_external::data_for_messagearea_search_users_in_course($user->id, $course->id, 'User');
2418          $this->assertDebuggingCalled();
2419      }
2420  
2421      /**
2422       * Tests searching users.
2423       */
2424      public function test_data_for_messagearea_search_users() {
2425          $this->resetAfterTest(true);
2426  
2427          // Create some users.
2428          $user1 = new stdClass();
2429          $user1->firstname = 'User';
2430          $user1->lastname = 'One';
2431          $user1 = self::getDataGenerator()->create_user($user1);
2432  
2433          // Set as the user performing the search.
2434          $this->setUser($user1);
2435  
2436          $user2 = new stdClass();
2437          $user2->firstname = 'User search';
2438          $user2->lastname = 'Two';
2439          $user2 = self::getDataGenerator()->create_user($user2);
2440  
2441          $user3 = new stdClass();
2442          $user3->firstname = 'User search';
2443          $user3->lastname = 'Three';
2444          $user3 = self::getDataGenerator()->create_user($user3);
2445  
2446          $user4 = new stdClass();
2447          $user4->firstname = 'User';
2448          $user4->lastname = 'Four';
2449          $user4 = self::getDataGenerator()->create_user($user4);
2450  
2451          $user5 = new stdClass();
2452          $user5->firstname = 'User search';
2453          $user5->lastname = 'Five';
2454          $user5 = self::getDataGenerator()->create_user($user5);
2455  
2456          $user6 = new stdClass();
2457          $user6->firstname = 'User';
2458          $user6->lastname = 'Six';
2459          $user6 = self::getDataGenerator()->create_user($user6);
2460  
2461          // Create some courses.
2462          $course1 = new stdClass();
2463          $course1->fullname = 'Course search';
2464          $course1->shortname = 'One';
2465          $course1 = $this->getDataGenerator()->create_course($course1);
2466  
2467          $course2 = new stdClass();
2468          $course2->fullname = 'Course';
2469          $course2->shortname = 'Two';
2470          $course2 = $this->getDataGenerator()->create_course($course2);
2471  
2472          $course3 = new stdClass();
2473          $course3->fullname = 'Course';
2474          $course3->shortname = 'Three search';
2475          $course3 = $this->getDataGenerator()->create_course($course3);
2476  
2477          $course4 = new stdClass();
2478          $course4->fullname = 'Course Four';
2479          $course4->shortname = 'CF100';
2480          $course4 = $this->getDataGenerator()->create_course($course4);
2481  
2482          $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
2483          $this->getDataGenerator()->enrol_user($user1->id, $course2->id, 'student');
2484          $this->getDataGenerator()->enrol_user($user1->id, $course3->id, 'student');
2485  
2486          // Add some users as contacts.
2487          \core_message\api::add_contact($user1->id, $user2->id);
2488          \core_message\api::add_contact($user1->id, $user3->id);
2489          \core_message\api::add_contact($user1->id, $user4->id);
2490  
2491          // Perform a search $CFG->messagingallusers setting enabled.
2492          set_config('messagingallusers', 1);
2493          $result = core_message_external::data_for_messagearea_search_users($user1->id, 'search');
2494  
2495          // We need to execute the return values cleaning process to simulate the web service server.
2496          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_users_returns(),
2497              $result);
2498  
2499          // Confirm that we returns contacts, courses and non-contacts.
2500          $contacts = $result['contacts'];
2501          $courses = $result['courses'];
2502          $noncontacts = $result['noncontacts'];
2503  
2504          // Check that we retrieved the correct contacts.
2505          $this->assertCount(2, $contacts);
2506          $this->assertEquals($user3->id, $contacts[0]['userid']);
2507          $this->assertEquals($user2->id, $contacts[1]['userid']);
2508  
2509          // Check that we retrieved the correct courses.
2510          $this->assertCount(2, $courses);
2511          $this->assertEquals($course3->id, $courses[0]['id']);
2512          $this->assertEquals($course1->id, $courses[1]['id']);
2513  
2514          // Check that we retrieved the correct non-contacts.
2515          $this->assertCount(1, $noncontacts);
2516          $this->assertEquals($user5->id, $noncontacts[0]['userid']);
2517      }
2518  
2519      /**
2520       * Tests searching users as another user.
2521       */
2522      public function test_data_for_messagearea_search_users_as_other_user() {
2523          $this->resetAfterTest(true);
2524  
2525          // The person doing the search.
2526          $this->setAdminUser();
2527  
2528          // Create some users.
2529          $user1 = new stdClass();
2530          $user1->firstname = 'User';
2531          $user1->lastname = 'One';
2532          $user1 = self::getDataGenerator()->create_user($user1);
2533  
2534          $user2 = new stdClass();
2535          $user2->firstname = 'User search';
2536          $user2->lastname = 'Two';
2537          $user2 = self::getDataGenerator()->create_user($user2);
2538  
2539          $user3 = new stdClass();
2540          $user3->firstname = 'User search';
2541          $user3->lastname = 'Three';
2542          $user3 = self::getDataGenerator()->create_user($user3);
2543  
2544          $user4 = new stdClass();
2545          $user4->firstname = 'User';
2546          $user4->lastname = 'Four';
2547          $user4 = self::getDataGenerator()->create_user($user4);
2548  
2549          $user5 = new stdClass();
2550          $user5->firstname = 'User search';
2551          $user5->lastname = 'Five';
2552          $user5 = self::getDataGenerator()->create_user($user5);
2553  
2554          $user6 = new stdClass();
2555          $user6->firstname = 'User';
2556          $user6->lastname = 'Six';
2557          $user6 = self::getDataGenerator()->create_user($user6);
2558  
2559          // Create some courses.
2560          $course1 = new stdClass();
2561          $course1->fullname = 'Course search';
2562          $course1->shortname = 'One';
2563          $course1 = $this->getDataGenerator()->create_course($course1);
2564  
2565          $course2 = new stdClass();
2566          $course2->fullname = 'Course';
2567          $course2->shortname = 'Two';
2568          $course2 = $this->getDataGenerator()->create_course($course2);
2569  
2570          $course3 = new stdClass();
2571          $course3->fullname = 'Course';
2572          $course3->shortname = 'Three search';
2573          $course3 = $this->getDataGenerator()->create_course($course3);
2574  
2575          // Add some users as contacts.
2576          \core_message\api::add_contact($user1->id, $user2->id);
2577          \core_message\api::add_contact($user1->id, $user3->id);
2578          \core_message\api::add_contact($user1->id, $user4->id);
2579  
2580          // Perform a search $CFG->messagingallusers setting enabled.
2581          set_config('messagingallusers', 1);
2582          $result = core_message_external::data_for_messagearea_search_users($user1->id, 'search');
2583  
2584          // We need to execute the return values cleaning process to simulate the web service server.
2585          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_users_returns(),
2586              $result);
2587  
2588          // Confirm that we returns contacts, courses and non-contacts.
2589          $contacts = $result['contacts'];
2590          $courses = $result['courses'];
2591          $noncontacts = $result['noncontacts'];
2592  
2593          // Check that we retrieved the correct contacts.
2594          $this->assertCount(2, $contacts);
2595          $this->assertEquals($user3->id, $contacts[0]['userid']);
2596          $this->assertEquals($user2->id, $contacts[1]['userid']);
2597  
2598          // Check that we retrieved the correct courses.
2599          $this->assertCount(0, $courses);
2600  
2601          // Check that we retrieved the correct non-contacts.
2602          $this->assertCount(1, $noncontacts);
2603          $this->assertEquals($user5->id, $noncontacts[0]['userid']);
2604      }
2605  
2606      /**
2607       * Tests searching users as another user without the proper capabilities.
2608       */
2609      public function test_data_for_messagearea_search_users_as_other_user_without_cap() {
2610          $this->resetAfterTest(true);
2611  
2612          // Create some users.
2613          $user1 = self::getDataGenerator()->create_user();
2614          $user2 = self::getDataGenerator()->create_user();
2615  
2616          // The person doing the search for another user.
2617          $this->setUser($user1);
2618  
2619          // Ensure an exception is thrown.
2620          $this->expectException('moodle_exception');
2621          core_message_external::data_for_messagearea_search_users($user2->id, 'User');
2622          $this->assertDebuggingCalled();
2623      }
2624  
2625      /**
2626       * Tests searching users with messaging disabled.
2627       */
2628      public function test_data_for_messagearea_search_users_messaging_disabled() {
2629          global $CFG;
2630  
2631          $this->resetAfterTest(true);
2632  
2633          // Create some skeleton data just so we can call the WS.
2634          $user = self::getDataGenerator()->create_user();
2635  
2636          // The person doing the search.
2637          $this->setUser($user);
2638  
2639          // Disable messaging.
2640          $CFG->messaging = 0;
2641  
2642          // Ensure an exception is thrown.
2643          $this->expectException('moodle_exception');
2644          core_message_external::data_for_messagearea_search_users($user->id, 'User');
2645          $this->assertDebuggingCalled();
2646      }
2647  
2648      /**
2649       * Tests searching for users when site-wide messaging is disabled.
2650       *
2651       * This test verifies that any contacts are returned, as well as any non-contacts whose profile we can view.
2652       * If checks this by placing some users in the same course, where default caps would permit a user to view another user's
2653       * profile.
2654       */
2655      public function test_message_search_users_messagingallusers_disabled() {
2656          global $DB;
2657          $this->resetAfterTest();
2658  
2659          // Create some users.
2660          $users = [];
2661          foreach (range(1, 8) as $i) {
2662              $user = new stdClass();
2663              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2664              $user->lastname = $i;
2665              $user = $this->getDataGenerator()->create_user($user);
2666              $users[$i] = $user;
2667          }
2668  
2669          // Enrol a few users in the same course, but leave them as non-contacts.
2670          $course1 = $this->getDataGenerator()->create_course();
2671          $course2 = $this->getDataGenerator()->create_course();
2672  
2673          $this->setAdminUser();
2674          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2675          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2676          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2677  
2678          // Add some other users as contacts.
2679          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2680          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2681          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2682  
2683          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
2684          $this->getDataGenerator()->enrol_user($users[8]->id, $course2->id, 'editingteacher');
2685          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2686          set_config('coursecontact', $teacherrole->id);
2687  
2688          // Create individual conversations between some users, one contact and one non-contact.
2689          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2690              [$users[1]->id, $users[2]->id]);
2691          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2692              [$users[6]->id, $users[1]->id]);
2693  
2694          // Create a group conversation between 4 users, including a contact and a non-contact.
2695          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2696              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id], 'Project chat');
2697  
2698          // Set as the user performing the search.
2699          $this->setUser($users[1]);
2700  
2701          // Perform a search with $CFG->messagingallusers disabled.
2702          set_config('messagingallusers', 0);
2703          $result = core_message_external::message_search_users($users[1]->id, 'search');
2704          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2705  
2706          // Confirm that we returns contacts and non-contacts.
2707          $this->assertArrayHasKey('contacts', $result);
2708          $this->assertArrayHasKey('noncontacts', $result);
2709          $contacts = $result['contacts'];
2710          $noncontacts = $result['noncontacts'];
2711  
2712          // Check that we retrieved the correct contacts.
2713          $this->assertCount(2, $contacts);
2714          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2715          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2716  
2717          // Verify the correct conversations were returned for the contacts.
2718          $this->assertCount(2, $contacts[0]['conversations']);
2719          // We can't rely on the ordering of conversations within the results, so sort by id first.
2720          usort($contacts[0]['conversations'], function($a, $b) {
2721              return $a['id'] < $b['id'];
2722          });
2723          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]['conversations'][0]['type']);
2724          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]['conversations'][1]['type']);
2725  
2726          $this->assertCount(0, $contacts[1]['conversations']);
2727  
2728          // Check that we retrieved the correct non-contacts.
2729          // When site wide messaging is disabled, we expect to see only those users who we share a course with and whose profiles
2730          // are visible in that course. This excludes users like course contacts.
2731          $this->assertCount(3, $noncontacts);
2732          // Self-conversation first.
2733          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2734          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2735          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2736  
2737          // Verify the correct conversations were returned for the non-contacts.
2738          $this->assertCount(1, $noncontacts[1]['conversations']);
2739          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $noncontacts[1]['conversations'][0]['type']);
2740  
2741          $this->assertCount(1, $noncontacts[2]['conversations']);
2742          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]['conversations'][0]['type']);
2743      }
2744  
2745      /**
2746       * Tests searching for users when site-wide messaging is enabled.
2747       *
2748       * This test verifies that any contacts are returned, as well as any non-contacts, regardless of whether the searching user
2749       * can view their respective profile.
2750       */
2751      public function test_message_search_users_messagingallusers_enabled() {
2752          global $DB;
2753          $this->resetAfterTest();
2754  
2755          // Create some users.
2756          $users = [];
2757          foreach (range(1, 9) as $i) {
2758              $user = new stdClass();
2759              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2760              $user->lastname = $i;
2761              $user = $this->getDataGenerator()->create_user($user);
2762              $users[$i] = $user;
2763          }
2764  
2765          // Enrol a few users in the same course, but leave them as non-contacts.
2766          $course1 = $this->getDataGenerator()->create_course();
2767          $course2 = $this->getDataGenerator()->create_course();
2768  
2769          $this->setAdminUser();
2770          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2771          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2772          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2773  
2774          // Add some other users as contacts.
2775          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2776          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2777          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2778  
2779          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
2780          $this->getDataGenerator()->enrol_user($users[9]->id, $course2->id, 'editingteacher');
2781          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2782          set_config('coursecontact', $teacherrole->id);
2783  
2784          // Create individual conversations between some users, one contact and one non-contact.
2785          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2786              [$users[1]->id, $users[2]->id]);
2787          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2788              [$users[6]->id, $users[1]->id]);
2789  
2790          // Create a group conversation between 5 users, including a contact and a non-contact, and a user NOT in a shared course.
2791          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2792              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id, $users[8]->id], 'Project chat');
2793  
2794          // Set as the user performing the search.
2795          $this->setUser($users[1]);
2796  
2797          // Perform a search with $CFG->messagingallusers enabled.
2798          set_config('messagingallusers', 1);
2799          $result = core_message_external::message_search_users($users[1]->id, 'search');
2800          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2801  
2802          // Confirm that we returns contacts and non-contacts.
2803          $this->assertArrayHasKey('contacts', $result);
2804          $this->assertArrayHasKey('noncontacts', $result);
2805          $contacts = $result['contacts'];
2806          $noncontacts = $result['noncontacts'];
2807  
2808          // Check that we retrieved the correct contacts.
2809          $this->assertCount(2, $contacts);
2810          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2811          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2812  
2813          // Verify the correct conversations were returned for the contacts.
2814          $this->assertCount(2, $contacts[0]['conversations']);
2815          // We can't rely on the ordering of conversations within the results, so sort by id first.
2816          usort($contacts[0]['conversations'], function($a, $b) {
2817              return $a['id'] < $b['id'];
2818          });
2819          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]['conversations'][0]['type']);
2820          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]['conversations'][1]['type']);
2821  
2822          $this->assertCount(0, $contacts[1]['conversations']);
2823  
2824          // Check that we retrieved the correct non-contacts.
2825          // If site wide messaging is enabled, we expect to be able to search for any users whose profiles we can view.
2826          // In this case, as a student, that's the course contact for course2 and those noncontacts sharing a course with user1.
2827          $this->assertCount(4, $noncontacts);
2828          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2829          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2830          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2831          $this->assertEquals($users[9]->id, $noncontacts[3]['id']);
2832  
2833          // Verify the correct conversations were returned for the non-contacts.
2834          $this->assertCount(1, $noncontacts[1]['conversations']);
2835          $this->assertCount(1, $noncontacts[2]['conversations']);
2836          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $noncontacts[1]['conversations'][0]['type']);
2837          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]['conversations'][0]['type']);
2838          $this->assertCount(0, $noncontacts[3]['conversations']);
2839      }
2840  
2841      /**
2842       * Verify searching for users find themselves when they have self-conversations.
2843       */
2844      public function test_message_search_users_self_conversations() {
2845          $this->resetAfterTest();
2846  
2847          // Create some users.
2848          $user1 = new stdClass();
2849          $user1->firstname = 'User';
2850          $user1->lastname = 'One';
2851          $user1 = $this->getDataGenerator()->create_user($user1);
2852          $user2 = new stdClass();
2853          $user2->firstname = 'User';
2854          $user2->lastname = 'Two';
2855          $user2 = $this->getDataGenerator()->create_user($user2);
2856  
2857          // Get self-conversation for user1.
2858          $sc1 = \core_message\api::get_self_conversation($user1->id);
2859          testhelper::send_fake_message_to_conversation($user1, $sc1->id, 'Hi myself!');
2860  
2861          // Perform a search as user1.
2862          $this->setUser($user1);
2863          $result = core_message_external::message_search_users($user1->id, 'One');
2864          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2865  
2866          // Check results are empty.
2867          $this->assertCount(0, $result['contacts']);
2868          $this->assertCount(1, $result['noncontacts']);
2869      }
2870  
2871      /**
2872       * Verify searching for users works even if no matching users from either contacts, or non-contacts can be found.
2873       */
2874      public function test_message_search_users_with_empty_result() {
2875          $this->resetAfterTest();
2876  
2877          // Create some users, but make sure neither will match the search term.
2878          $user1 = new stdClass();
2879          $user1->firstname = 'User';
2880          $user1->lastname = 'One';
2881          $user1 = $this->getDataGenerator()->create_user($user1);
2882          $user2 = new stdClass();
2883          $user2->firstname = 'User';
2884          $user2->lastname = 'Two';
2885          $user2 = $this->getDataGenerator()->create_user($user2);
2886  
2887          // Perform a search as user1.
2888          $this->setUser($user1);
2889          $result = core_message_external::message_search_users($user1->id, 'search');
2890          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2891  
2892          // Check results are empty.
2893          $this->assertCount(0, $result['contacts']);
2894          $this->assertCount(0, $result['noncontacts']);
2895      }
2896  
2897      /**
2898       * Test verifying that limits and offsets work for both the contacts and non-contacts return data.
2899       */
2900      public function test_message_search_users_limit_offset() {
2901          $this->resetAfterTest();
2902  
2903          // Create 20 users.
2904          $users = [];
2905          foreach (range(1, 20) as $i) {
2906              $user = new stdClass();
2907              $user->firstname = "User search";
2908              $user->lastname = $i;
2909              $user = $this->getDataGenerator()->create_user($user);
2910              $users[$i] = $user;
2911          }
2912  
2913          // Enrol the first 8 users in the same course, but leave them as non-contacts.
2914          $this->setAdminUser();
2915          $course1 = $this->getDataGenerator()->create_course();
2916          foreach (range(1, 8) as $i) {
2917              $this->getDataGenerator()->enrol_user($users[$i]->id, $course1->id);
2918          }
2919  
2920          // Add 5 users, starting at the 11th user, as contacts for user1.
2921          foreach (range(11, 15) as $i) {
2922              \core_message\api::add_contact($users[1]->id, $users[$i]->id);
2923          }
2924  
2925          // Set as the user performing the search.
2926          $this->setUser($users[1]);
2927  
2928          // Search using a limit of 3.
2929          // This tests the case where we have more results than the limit for both contacts and non-contacts.
2930          $result = core_message_external::message_search_users($users[1]->id, 'search', 0, 3);
2931          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2932          $contacts = $result['contacts'];
2933          $noncontacts = $result['noncontacts'];
2934  
2935          // Check that we retrieved the correct contacts.
2936          $this->assertCount(3, $contacts);
2937          $this->assertEquals($users[11]->id, $contacts[0]['id']);
2938          $this->assertEquals($users[12]->id, $contacts[1]['id']);
2939          $this->assertEquals($users[13]->id, $contacts[2]['id']);
2940  
2941          // Check that we retrieved the correct non-contacts.
2942          // Consider first conversation is self-conversation.
2943          $this->assertCount(3, $noncontacts);
2944          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2945          $this->assertEquals($users[2]->id, $noncontacts[1]['id']);
2946          $this->assertEquals($users[3]->id, $noncontacts[2]['id']);
2947  
2948          // Now, offset to get the next batch of results.
2949          // We expect to see 2 contacts, and 3 non-contacts.
2950          $result = core_message_external::message_search_users($users[1]->id, 'search', 3, 3);
2951          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2952          $contacts = $result['contacts'];
2953          $noncontacts = $result['noncontacts'];
2954          $this->assertCount(2, $contacts);
2955          $this->assertEquals($users[14]->id, $contacts[0]['id']);
2956          $this->assertEquals($users[15]->id, $contacts[1]['id']);
2957  
2958          $this->assertCount(3, $noncontacts);
2959          $this->assertEquals($users[4]->id, $noncontacts[0]['id']);
2960          $this->assertEquals($users[5]->id, $noncontacts[1]['id']);
2961          $this->assertEquals($users[6]->id, $noncontacts[2]['id']);
2962  
2963          // Now, offset to get the next batch of results.
2964          // We expect to see 0 contacts, and 2 non-contacts.
2965          $result = core_message_external::message_search_users($users[1]->id, 'search', 6, 3);
2966          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2967          $contacts = $result['contacts'];
2968          $noncontacts = $result['noncontacts'];
2969          $this->assertCount(0, $contacts);
2970  
2971          $this->assertCount(2, $noncontacts);
2972          $this->assertEquals($users[7]->id, $noncontacts[0]['id']);
2973          $this->assertEquals($users[8]->id, $noncontacts[1]['id']);
2974      }
2975  
2976      /**
2977       * Tests searching users as another user having the 'moodle/user:viewdetails' capability.
2978       */
2979      public function test_message_search_users_with_cap() {
2980          $this->resetAfterTest();
2981          global $DB;
2982  
2983          // Create some users.
2984          $users = [];
2985          foreach (range(1, 8) as $i) {
2986              $user = new stdClass();
2987              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2988              $user->lastname = $i;
2989              $user = $this->getDataGenerator()->create_user($user);
2990              $users[$i] = $user;
2991          }
2992  
2993          // Enrol a few users in the same course, but leave them as non-contacts.
2994          $course1 = $this->getDataGenerator()->create_course();
2995          $this->setAdminUser();
2996          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2997          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2998          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2999  
3000          // Add some other users as contacts.
3001          \core_message\api::add_contact($users[1]->id, $users[2]->id);
3002          \core_message\api::add_contact($users[3]->id, $users[1]->id);
3003          \core_message\api::add_contact($users[1]->id, $users[4]->id);
3004  
3005          // Set as the user performing the search.
3006          $this->setUser($users[1]);
3007  
3008          // Grant the authenticated user role the capability 'user:viewdetails' at site context.
3009          $authenticatedrole = $DB->get_record('role', ['shortname' => 'user'], '*', MUST_EXIST);
3010          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $authenticatedrole->id, context_system::instance());
3011  
3012          // Perform a search with $CFG->messagingallusers disabled.
3013          set_config('messagingallusers', 0);
3014          $result = core_message_external::message_search_users($users[1]->id, 'search');
3015          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
3016          $contacts = $result['contacts'];
3017          $noncontacts = $result['noncontacts'];
3018  
3019          // Check that we retrieved the correct contacts.
3020          $this->assertCount(2, $contacts);
3021          $this->assertEquals($users[2]->id, $contacts[0]['id']);
3022          $this->assertEquals($users[3]->id, $contacts[1]['id']);
3023  
3024          // Check that we retrieved the correct non-contacts.
3025          // Site-wide messaging is disabled, so we expect to be able to search for any users whose profile we can view.
3026          // Consider first conversations is self-conversation.
3027          $this->assertCount(3, $noncontacts);
3028          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
3029          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
3030          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
3031      }
3032  
3033      /**
3034       * Tests searching users as another user without the 'moodle/user:viewdetails' capability.
3035       */
3036      public function test_message_search_users_without_cap() {
3037          $this->resetAfterTest();
3038  
3039          // Create some users.
3040          $user1 = $this->getDataGenerator()->create_user();
3041          $user2 = $this->getDataGenerator()->create_user();
3042  
3043          // The person doing the search for another user.
3044          $this->setUser($user1);
3045  
3046          // Ensure an exception is thrown.
3047          $this->expectException('moodle_exception');
3048          core_message_external::message_search_users($user2->id, 'User');
3049          $this->assertDebuggingCalled();
3050      }
3051  
3052      /**
3053       * Tests searching users with messaging disabled.
3054       */
3055      public function test_message_search_users_messaging_disabled() {
3056          $this->resetAfterTest();
3057  
3058          // Create some skeleton data just so we can call the WS.
3059          $user = $this->getDataGenerator()->create_user();
3060  
3061          // The person doing the search.
3062          $this->setUser($user);
3063  
3064          // Disable messaging.
3065          set_config('messaging', 0);
3066  
3067          // Ensure an exception is thrown.
3068          $this->expectException('moodle_exception');
3069          core_message_external::message_search_users($user->id, 'User');
3070      }
3071  
3072      /**
3073       * Tests searching messages.
3074       */
3075      public function test_messagearea_search_messages() {
3076          $this->resetAfterTest(true);
3077  
3078          // Create some users.
3079          $user1 = self::getDataGenerator()->create_user();
3080          $user2 = self::getDataGenerator()->create_user();
3081  
3082          // The person doing the search.
3083          $this->setUser($user1);
3084  
3085          // Send some messages back and forth.
3086          $time = time();
3087          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3088          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3089          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3090          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3091          $convid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3092  
3093          // Perform a search.
3094          $result = core_message_external::data_for_messagearea_search_messages($user1->id, 'o');
3095  
3096          // We need to execute the return values cleaning process to simulate the web service server.
3097          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_messages_returns(), $result);
3098  
3099          // Confirm the data is correct.
3100          $messages = $result['contacts'];
3101          $this->assertCount(2, $messages);
3102  
3103          $message1 = $messages[0];
3104          $message2 = $messages[1];
3105  
3106          $this->assertEquals($user2->id, $message1['userid']);
3107          $this->assertEquals(fullname($user2), $message1['fullname']);
3108          $this->assertTrue($message1['ismessaging']);
3109          $this->assertFalse($message1['sentfromcurrentuser']);
3110          $this->assertEquals('Word.', $message1['lastmessage']);
3111          $this->assertNotEmpty($message1['messageid']);
3112          $this->assertNull($message1['isonline']);
3113          $this->assertFalse($message1['isread']);
3114          $this->assertFalse($message1['isblocked']);
3115          $this->assertNull($message1['unreadcount']);
3116          $this->assertEquals($convid, $message1['conversationid']);
3117  
3118          $this->assertEquals($user2->id, $message2['userid']);
3119          $this->assertEquals(fullname($user2), $message2['fullname']);
3120          $this->assertTrue($message2['ismessaging']);
3121          $this->assertTrue($message2['sentfromcurrentuser']);
3122          $this->assertEquals('Yo!', $message2['lastmessage']);
3123          $this->assertNotEmpty($message2['messageid']);
3124          $this->assertNull($message2['isonline']);
3125          $this->assertTrue($message2['isread']);
3126          $this->assertFalse($message2['isblocked']);
3127          $this->assertNull($message2['unreadcount']);
3128          $this->assertEquals($convid, $message2['conversationid']);
3129      }
3130  
3131      /**
3132       * Tests searching messages as another user.
3133       */
3134      public function test_messagearea_search_messages_as_other_user() {
3135          $this->resetAfterTest(true);
3136  
3137          // The person doing the search.
3138          $this->setAdminUser();
3139  
3140          // Create some users.
3141          $user1 = self::getDataGenerator()->create_user();
3142          $user2 = self::getDataGenerator()->create_user();
3143  
3144          // Send some messages back and forth.
3145          $time = time();
3146          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3147          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3148          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3149          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3150  
3151          // Perform a search.
3152          $result = core_message_external::data_for_messagearea_search_messages($user1->id, 'o');
3153  
3154          // We need to execute the return values cleaning process to simulate the web service server.
3155          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_messages_returns(),
3156              $result);
3157  
3158          // Confirm the data is correct.
3159          $messages = $result['contacts'];
3160          $this->assertCount(2, $messages);
3161  
3162          $message1 = $messages[0];
3163          $message2 = $messages[1];
3164  
3165          $this->assertEquals($user2->id, $message1['userid']);
3166          $this->assertEquals(fullname($user2), $message1['fullname']);
3167          $this->assertTrue($message1['ismessaging']);
3168          $this->assertFalse($message1['sentfromcurrentuser']);
3169          $this->assertEquals('Word.', $message1['lastmessage']);
3170          $this->assertNotEmpty($message1['messageid']);
3171          $this->assertFalse($message1['isonline']);
3172          $this->assertFalse($message1['isread']);
3173          $this->assertFalse($message1['isblocked']);
3174          $this->assertNull($message1['unreadcount']);
3175  
3176          $this->assertEquals($user2->id, $message2['userid']);
3177          $this->assertEquals(fullname($user2), $message2['fullname']);
3178          $this->assertTrue($message2['ismessaging']);
3179          $this->assertTrue($message2['sentfromcurrentuser']);
3180          $this->assertEquals('Yo!', $message2['lastmessage']);
3181          $this->assertNotEmpty($message2['messageid']);
3182          $this->assertFalse($message2['isonline']);
3183          $this->assertTrue($message2['isread']);
3184          $this->assertFalse($message2['isblocked']);
3185          $this->assertNull($message2['unreadcount']);
3186      }
3187  
3188      /**
3189       * Tests searching messages as another user without the proper capabilities.
3190       */
3191      public function test_messagearea_search_messages_as_other_user_without_cap() {
3192          $this->resetAfterTest(true);
3193  
3194          // Create some users.
3195          $user1 = self::getDataGenerator()->create_user();
3196          $user2 = self::getDataGenerator()->create_user();
3197  
3198          // The person doing the search for another user.
3199          $this->setUser($user1);
3200  
3201          // Ensure an exception is thrown.
3202          $this->expectException('moodle_exception');
3203          core_message_external::data_for_messagearea_search_messages($user2->id, 'Search');
3204      }
3205  
3206      /**
3207       * Tests searching messages with messaging disabled
3208       */
3209      public function test_messagearea_search_messages_messaging_disabled() {
3210          global $CFG;
3211  
3212          $this->resetAfterTest(true);
3213  
3214          // Create some skeleton data just so we can call the WS.
3215          $user = self::getDataGenerator()->create_user();
3216  
3217          // The person doing the search .
3218          $this->setUser($user);
3219  
3220          // Disable messaging.
3221          $CFG->messaging = 0;
3222  
3223          // Ensure an exception is thrown.
3224          $this->expectException('moodle_exception');
3225          core_message_external::data_for_messagearea_search_messages($user->id, 'Search');
3226      }
3227  
3228      /**
3229       * Tests retrieving conversations.
3230       */
3231      public function test_messagearea_conversations() {
3232          $this->resetAfterTest(true);
3233  
3234          // Create some users.
3235          $user1 = self::getDataGenerator()->create_user();
3236          $user2 = self::getDataGenerator()->create_user();
3237          $user3 = self::getDataGenerator()->create_user();
3238          $user4 = self::getDataGenerator()->create_user();
3239  
3240          // The person retrieving the conversations.
3241          $this->setUser($user1);
3242  
3243          // Send some messages back and forth, have some different conversations with different users.
3244          $time = time();
3245          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3246          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3247          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3248          $messageid1 = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3249  
3250          $this->send_message($user1, $user3, 'Booyah', 0, $time + 4);
3251          $this->send_message($user3, $user1, 'Whaaat?', 0, $time + 5);
3252          $this->send_message($user1, $user3, 'Nothing.', 0, $time + 6);
3253          $messageid2 = $this->send_message($user3, $user1, 'Cool.', 0, $time + 7);
3254  
3255          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 8);
3256          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 9);
3257          $messageid3 = $this->send_message($user1, $user4, 'Dope.', 0, $time + 10);
3258  
3259          // Retrieve the conversations.
3260          $result = core_message_external::data_for_messagearea_conversations($user1->id);
3261  
3262          // We need to execute the return values cleaning process to simulate the web service server.
3263          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_conversations_returns(),
3264              $result);
3265  
3266          // Confirm the data is correct.
3267          $messages = $result['contacts'];
3268          $this->assertCount(3, $messages);
3269  
3270          $message1 = $messages[0];
3271          $message2 = $messages[1];
3272          $message3 = $messages[2];
3273  
3274          $this->assertEquals($user4->id, $message1['userid']);
3275          $this->assertTrue($message1['ismessaging']);
3276          $this->assertTrue($message1['sentfromcurrentuser']);
3277          $this->assertEquals('Dope.', $message1['lastmessage']);
3278          $this->assertEquals($messageid3, $message1['messageid']);
3279          $this->assertNull($message1['isonline']);
3280          $this->assertFalse($message1['isread']);
3281          $this->assertFalse($message1['isblocked']);
3282          $this->assertEquals(1, $message1['unreadcount']);
3283  
3284          $this->assertEquals($user3->id, $message2['userid']);
3285          $this->assertTrue($message2['ismessaging']);
3286          $this->assertFalse($message2['sentfromcurrentuser']);
3287          $this->assertEquals('Cool.', $message2['lastmessage']);
3288          $this->assertEquals($messageid2, $message2['messageid']);
3289          $this->assertNull($message2['isonline']);
3290          $this->assertFalse($message2['isread']);
3291          $this->assertFalse($message2['isblocked']);
3292          $this->assertEquals(2, $message2['unreadcount']);
3293  
3294          $this->assertEquals($user2->id, $message3['userid']);
3295          $this->assertTrue($message3['ismessaging']);
3296          $this->assertFalse($message3['sentfromcurrentuser']);
3297          $this->assertEquals('Word.', $message3['lastmessage']);
3298          $this->assertEquals($messageid1, $message3['messageid']);
3299          $this->assertNull($message3['isonline']);
3300          $this->assertFalse($message3['isread']);
3301          $this->assertFalse($message3['isblocked']);
3302          $this->assertEquals(2, $message3['unreadcount']);
3303      }
3304  
3305      /**
3306       * Tests retrieving conversations as another user.
3307       */
3308      public function test_messagearea_conversations_as_other_user() {
3309          $this->resetAfterTest(true);
3310  
3311          // Set as admin.
3312          $this->setAdminUser();
3313  
3314          // Create some users.
3315          $user1 = self::getDataGenerator()->create_user();
3316          $user2 = self::getDataGenerator()->create_user();
3317          $user3 = self::getDataGenerator()->create_user();
3318          $user4 = self::getDataGenerator()->create_user();
3319  
3320          // Send some messages back and forth, have some different conversations with different users.
3321          $time = time();
3322          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3323          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3324          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3325          $messageid1 = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3326  
3327          $this->send_message($user1, $user3, 'Booyah', 0, $time + 4);
3328          $this->send_message($user3, $user1, 'Whaaat?', 0, $time + 5);
3329          $this->send_message($user1, $user3, 'Nothing.', 0, $time + 6);
3330          $messageid2 = $this->send_message($user3, $user1, 'Cool.', 0, $time + 7);
3331  
3332          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?', 0, $time + 8);
3333          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.', 0, $time + 9);
3334          $messageid3 = $this->send_message($user1, $user4, 'Dope.', 0, $time + 10);
3335  
3336          // Retrieve the conversations.
3337          $result = core_message_external::data_for_messagearea_conversations($user1->id);
3338  
3339          // We need to execute the return values cleaning process to simulate the web service server.
3340          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_conversations_returns(),
3341              $result);
3342  
3343          // Confirm the data is correct.
3344          $messages = $result['contacts'];
3345          $this->assertCount(3, $messages);
3346  
3347          $message1 = $messages[0];
3348          $message2 = $messages[1];
3349          $message3 = $messages[2];
3350  
3351          $this->assertEquals($user4->id, $message1['userid']);
3352          $this->assertTrue($message1['ismessaging']);
3353          $this->assertTrue($message1['sentfromcurrentuser']);
3354          $this->assertEquals('Dope.', $message1['lastmessage']);
3355          $this->assertEquals($messageid3, $message1['messageid']);
3356          $this->assertFalse($message1['isonline']);
3357          $this->assertFalse($message1['isread']);
3358          $this->assertFalse($message1['isblocked']);
3359          $this->assertEquals(1, $message1['unreadcount']);
3360  
3361          $this->assertEquals($user3->id, $message2['userid']);
3362          $this->assertTrue($message2['ismessaging']);
3363          $this->assertFalse($message2['sentfromcurrentuser']);
3364          $this->assertEquals('Cool.', $message2['lastmessage']);
3365          $this->assertEquals($messageid2, $message2['messageid']);
3366          $this->assertFalse($message2['isonline']);
3367          $this->assertFalse($message2['isread']);
3368          $this->assertFalse($message2['isblocked']);
3369          $this->assertEquals(2, $message2['unreadcount']);
3370  
3371          $this->assertEquals($user2->id, $message3['userid']);
3372          $this->assertTrue($message3['ismessaging']);
3373          $this->assertFalse($message3['sentfromcurrentuser']);
3374          $this->assertEquals('Word.', $message3['lastmessage']);
3375          $this->assertEquals($messageid1, $message3['messageid']);
3376          $this->assertFalse($message3['isonline']);
3377          $this->assertFalse($message3['isread']);
3378          $this->assertFalse($message3['isblocked']);
3379          $this->assertEquals(2, $message3['unreadcount']);
3380      }
3381  
3382      /**
3383       * Tests retrieving conversations as another user without the proper capabilities.
3384       */
3385      public function test_messagearea_conversations_as_other_user_without_cap() {
3386          $this->resetAfterTest(true);
3387  
3388          // Create some users.
3389          $user1 = self::getDataGenerator()->create_user();
3390          $user2 = self::getDataGenerator()->create_user();
3391  
3392          // The person retrieving the conversations for another user.
3393          $this->setUser($user1);
3394  
3395          // Ensure an exception is thrown.
3396          $this->expectException('moodle_exception');
3397          core_message_external::data_for_messagearea_conversations($user2->id);
3398      }
3399  
3400      /**
3401       * Tests retrieving conversations with messaging disabled.
3402       */
3403      public function test_messagearea_conversations_messaging_disabled() {
3404          global $CFG;
3405  
3406          $this->resetAfterTest(true);
3407  
3408          // Create some skeleton data just so we can call the WS.
3409          $user = self::getDataGenerator()->create_user();
3410  
3411          // The person retrieving the conversations.
3412          $this->setUser($user);
3413  
3414          // Disable messaging.
3415          $CFG->messaging = 0;
3416  
3417          // Ensure an exception is thrown.
3418          $this->expectException('moodle_exception');
3419          core_message_external::data_for_messagearea_conversations($user->id);
3420      }
3421  
3422      /**
3423       * Tests retrieving contacts.
3424       */
3425      public function test_messagearea_contacts() {
3426          $this->resetAfterTest(true);
3427  
3428          // Create some users.
3429          $user1 = self::getDataGenerator()->create_user();
3430  
3431          // Set as the user.
3432          $this->setUser($user1);
3433  
3434          $user2 = new stdClass();
3435          $user2->firstname = 'User';
3436          $user2->lastname = 'A';
3437          $user2 = self::getDataGenerator()->create_user($user2);
3438  
3439          $user3 = new stdClass();
3440          $user3->firstname = 'User';
3441          $user3->lastname = 'B';
3442          $user3 = self::getDataGenerator()->create_user($user3);
3443  
3444          $user4 = new stdClass();
3445          $user4->firstname = 'User';
3446          $user4->lastname = 'C';
3447          $user4 = self::getDataGenerator()->create_user($user4);
3448  
3449          $user5 = new stdClass();
3450          $user5->firstname = 'User';
3451          $user5->lastname = 'D';
3452          $user5 = self::getDataGenerator()->create_user($user5);
3453  
3454          // Add some users as contacts.
3455          \core_message\api::add_contact($user1->id, $user2->id);
3456          \core_message\api::add_contact($user1->id, $user3->id);
3457          \core_message\api::add_contact($user1->id, $user4->id);
3458  
3459          // Retrieve the contacts.
3460          $result = core_message_external::data_for_messagearea_contacts($user1->id);
3461  
3462          // We need to execute the return values cleaning process to simulate the web service server.
3463          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_contacts_returns(),
3464              $result);
3465  
3466          // Confirm the data is correct.
3467          $contacts = $result['contacts'];
3468          usort($contacts, ['static', 'sort_contacts']);
3469          $this->assertCount(3, $contacts);
3470  
3471          $contact1 = $contacts[0];
3472          $contact2 = $contacts[1];
3473          $contact3 = $contacts[2];
3474  
3475          $this->assertEquals($user2->id, $contact1['userid']);
3476          $this->assertFalse($contact1['ismessaging']);
3477          $this->assertFalse($contact1['sentfromcurrentuser']);
3478          $this->assertNull($contact1['lastmessage']);
3479          $this->assertNull($contact1['messageid']);
3480          $this->assertNull($contact1['isonline']);
3481          $this->assertFalse($contact1['isread']);
3482          $this->assertFalse($contact1['isblocked']);
3483          $this->assertNull($contact1['unreadcount']);
3484  
3485          $this->assertEquals($user3->id, $contact2['userid']);
3486          $this->assertFalse($contact2['ismessaging']);
3487          $this->assertFalse($contact2['sentfromcurrentuser']);
3488          $this->assertNull($contact2['lastmessage']);
3489          $this->assertNull($contact2['messageid']);
3490          $this->assertNull($contact2['isonline']);
3491          $this->assertFalse($contact2['isread']);
3492          $this->assertFalse($contact2['isblocked']);
3493          $this->assertNull($contact2['unreadcount']);
3494  
3495          $this->assertEquals($user4->id, $contact3['userid']);
3496          $this->assertFalse($contact3['ismessaging']);
3497          $this->assertFalse($contact3['sentfromcurrentuser']);
3498          $this->assertNull($contact3['lastmessage']);
3499          $this->assertNull($contact3['messageid']);
3500          $this->assertNull($contact3['isonline']);
3501          $this->assertFalse($contact3['isread']);
3502          $this->assertFalse($contact3['isblocked']);
3503          $this->assertNull($contact3['unreadcount']);
3504      }
3505  
3506      /**
3507       * Tests retrieving contacts as another user.
3508       */
3509      public function test_messagearea_contacts_as_other_user() {
3510          $this->resetAfterTest(true);
3511  
3512          $this->setAdminUser();
3513  
3514          // Create some users.
3515          $user1 = self::getDataGenerator()->create_user();
3516  
3517          $user2 = new stdClass();
3518          $user2->firstname = 'User';
3519          $user2->lastname = 'A';
3520          $user2 = self::getDataGenerator()->create_user($user2);
3521  
3522          $user3 = new stdClass();
3523          $user3->firstname = 'User';
3524          $user3->lastname = 'B';
3525          $user3 = self::getDataGenerator()->create_user($user3);
3526  
3527          $user4 = new stdClass();
3528          $user4->firstname = 'User';
3529          $user4->lastname = 'C';
3530          $user4 = self::getDataGenerator()->create_user($user4);
3531  
3532          $user5 = new stdClass();
3533          $user5->firstname = 'User';
3534          $user5->lastname = 'D';
3535          $user5 = self::getDataGenerator()->create_user($user5);
3536  
3537          // Add some users as contacts.
3538          \core_message\api::add_contact($user1->id, $user2->id);
3539          \core_message\api::add_contact($user1->id, $user3->id);
3540          \core_message\api::add_contact($user1->id, $user4->id);
3541  
3542          // Retrieve the contacts.
3543          $result = core_message_external::data_for_messagearea_contacts($user1->id);
3544  
3545          // We need to execute the return values cleaning process to simulate the web service server.
3546          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_contacts_returns(),
3547              $result);
3548  
3549          // Confirm the data is correct.
3550          $contacts = $result['contacts'];
3551          usort($contacts, ['static', 'sort_contacts']);
3552          $this->assertCount(3, $contacts);
3553  
3554          $contact1 = $contacts[0];
3555          $contact2 = $contacts[1];
3556          $contact3 = $contacts[2];
3557  
3558          $this->assertEquals($user2->id, $contact1['userid']);
3559          $this->assertFalse($contact1['ismessaging']);
3560          $this->assertFalse($contact1['sentfromcurrentuser']);
3561          $this->assertNull($contact1['lastmessage']);
3562          $this->assertNull($contact1['messageid']);
3563          $this->assertFalse($contact1['isonline']);
3564          $this->assertFalse($contact1['isread']);
3565          $this->assertFalse($contact1['isblocked']);
3566          $this->assertNull($contact1['unreadcount']);
3567  
3568          $this->assertEquals($user3->id, $contact2['userid']);
3569          $this->assertFalse($contact2['ismessaging']);
3570          $this->assertFalse($contact2['sentfromcurrentuser']);
3571          $this->assertNull($contact2['lastmessage']);
3572          $this->assertNull($contact2['messageid']);
3573          $this->assertFalse($contact2['isonline']);
3574          $this->assertFalse($contact2['isread']);
3575          $this->assertFalse($contact2['isblocked']);
3576          $this->assertNull($contact2['unreadcount']);
3577  
3578          $this->assertEquals($user4->id, $contact3['userid']);
3579          $this->assertFalse($contact3['ismessaging']);
3580          $this->assertFalse($contact3['sentfromcurrentuser']);
3581          $this->assertNull($contact3['lastmessage']);
3582          $this->assertNull($contact3['messageid']);
3583          $this->assertFalse($contact3['isonline']);
3584          $this->assertFalse($contact3['isread']);
3585          $this->assertFalse($contact3['isblocked']);
3586          $this->assertNull($contact3['unreadcount']);
3587      }
3588  
3589      /**
3590       * Tests retrieving contacts as another user without the proper capabilities.
3591       */
3592      public function test_messagearea_contacts_as_other_user_without_cap() {
3593          $this->resetAfterTest(true);
3594  
3595          // Create some users.
3596          $user1 = self::getDataGenerator()->create_user();
3597          $user2 = self::getDataGenerator()->create_user();
3598  
3599          // The person retrieving the contacts for another user.
3600          $this->setUser($user1);
3601  
3602          // Perform the WS call and ensure an exception is thrown.
3603          $this->expectException('moodle_exception');
3604          core_message_external::data_for_messagearea_contacts($user2->id);
3605      }
3606  
3607      /**
3608       * Tests retrieving contacts with messaging disabled.
3609       */
3610      public function test_messagearea_contacts_messaging_disabled() {
3611          global $CFG;
3612  
3613          $this->resetAfterTest(true);
3614  
3615          // Create some skeleton data just so we can call the WS.
3616          $user = self::getDataGenerator()->create_user();
3617  
3618          // The person retrieving the contacts.
3619          $this->setUser($user);
3620  
3621          // Disable messaging.
3622          $CFG->messaging = 0;
3623  
3624          // Perform the WS call and ensure we are shown that it is disabled.
3625          $this->expectException('moodle_exception');
3626          core_message_external::data_for_messagearea_contacts($user->id);
3627      }
3628  
3629      /**
3630       * Tests retrieving contacts.
3631       */
3632      public function test_get_user_contacts() {
3633          $this->resetAfterTest(true);
3634  
3635          // Create some users.
3636          $user1 = self::getDataGenerator()->create_user();
3637  
3638          // Set as the user.
3639          $this->setUser($user1);
3640  
3641          $user2 = new stdClass();
3642          $user2->firstname = 'User';
3643          $user2->lastname = 'A';
3644          $user2 = self::getDataGenerator()->create_user($user2);
3645  
3646          $user3 = new stdClass();
3647          $user3->firstname = 'User';
3648          $user3->lastname = 'B';
3649          $user3 = self::getDataGenerator()->create_user($user3);
3650  
3651          $user4 = new stdClass();
3652          $user4->firstname = 'User';
3653          $user4->lastname = 'C';
3654          $user4 = self::getDataGenerator()->create_user($user4);
3655  
3656          $user5 = new stdClass();
3657          $user5->firstname = 'User';
3658          $user5->lastname = 'D';
3659          $user5 = self::getDataGenerator()->create_user($user5);
3660  
3661          // Add some users as contacts.
3662          \core_message\api::add_contact($user1->id, $user2->id);
3663          \core_message\api::add_contact($user1->id, $user3->id);
3664          \core_message\api::add_contact($user1->id, $user4->id);
3665  
3666          // Retrieve the contacts.
3667          $result = core_message_external::get_user_contacts($user1->id);
3668  
3669          // We need to execute the return values cleaning process to simulate the web service server.
3670          $result = external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(),
3671              $result);
3672  
3673          // Confirm the data is correct.
3674          $contacts = $result;
3675          usort($contacts, ['static', 'sort_contacts_id']);
3676          $this->assertCount(3, $contacts);
3677  
3678          $contact1 = array_shift($contacts);
3679          $contact2 = array_shift($contacts);
3680          $contact3 = array_shift($contacts);
3681  
3682          $this->assertEquals($user2->id, $contact1['id']);
3683          $this->assertEquals(fullname($user2), $contact1['fullname']);
3684          $this->assertTrue($contact1['iscontact']);
3685  
3686          $this->assertEquals($user3->id, $contact2['id']);
3687          $this->assertEquals(fullname($user3), $contact2['fullname']);
3688          $this->assertTrue($contact2['iscontact']);
3689  
3690          $this->assertEquals($user4->id, $contact3['id']);
3691          $this->assertEquals(fullname($user4), $contact3['fullname']);
3692          $this->assertTrue($contact3['iscontact']);
3693      }
3694  
3695      /**
3696       * Tests retrieving contacts as another user.
3697       */
3698      public function test_get_user_contacts_as_other_user() {
3699          $this->resetAfterTest(true);
3700  
3701          $this->setAdminUser();
3702  
3703          // Create some users.
3704          $user1 = self::getDataGenerator()->create_user();
3705  
3706          $user2 = new stdClass();
3707          $user2->firstname = 'User';
3708          $user2->lastname = 'A';
3709          $user2 = self::getDataGenerator()->create_user($user2);
3710  
3711          $user3 = new stdClass();
3712          $user3->firstname = 'User';
3713          $user3->lastname = 'B';
3714          $user3 = self::getDataGenerator()->create_user($user3);
3715  
3716          $user4 = new stdClass();
3717          $user4->firstname = 'User';
3718          $user4->lastname = 'C';
3719          $user4 = self::getDataGenerator()->create_user($user4);
3720  
3721          $user5 = new stdClass();
3722          $user5->firstname = 'User';
3723          $user5->lastname = 'D';
3724          $user5 = self::getDataGenerator()->create_user($user5);
3725  
3726          // Add some users as contacts.
3727          \core_message\api::add_contact($user1->id, $user2->id);
3728          \core_message\api::add_contact($user1->id, $user3->id);
3729          \core_message\api::add_contact($user1->id, $user4->id);
3730  
3731          // Retrieve the contacts.
3732          $result = core_message_external::get_user_contacts($user1->id);
3733  
3734          // We need to execute the return values cleaning process to simulate the web service server.
3735          $result = external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(),
3736              $result);
3737  
3738          // Confirm the data is correct.
3739          $contacts = $result;
3740          usort($contacts, ['static', 'sort_contacts_id']);
3741          $this->assertCount(3, $contacts);
3742  
3743          $contact1 = array_shift($contacts);
3744          $contact2 = array_shift($contacts);
3745          $contact3 = array_shift($contacts);
3746  
3747          $this->assertEquals($user2->id, $contact1['id']);
3748          $this->assertEquals(fullname($user2), $contact1['fullname']);
3749          $this->assertTrue($contact1['iscontact']);
3750  
3751          $this->assertEquals($user3->id, $contact2['id']);
3752          $this->assertEquals(fullname($user3), $contact2['fullname']);
3753          $this->assertTrue($contact2['iscontact']);
3754  
3755          $this->assertEquals($user4->id, $contact3['id']);
3756          $this->assertEquals(fullname($user4), $contact3['fullname']);
3757          $this->assertTrue($contact3['iscontact']);
3758      }
3759  
3760      /**
3761       * Tests retrieving contacts as another user without the proper capabilities.
3762       */
3763      public function test_get_user_contacts_as_other_user_without_cap() {
3764          $this->resetAfterTest(true);
3765  
3766          // Create some users.
3767          $user1 = self::getDataGenerator()->create_user();
3768          $user2 = self::getDataGenerator()->create_user();
3769  
3770          // The person retrieving the contacts for another user.
3771          $this->setUser($user1);
3772  
3773          // Perform the WS call and ensure an exception is thrown.
3774          $this->expectException('moodle_exception');
3775          core_message_external::get_user_contacts($user2->id);
3776      }
3777  
3778      /**
3779       * Tests retrieving contacts with messaging disabled.
3780       */
3781      public function test_get_user_contacts_messaging_disabled() {
3782          global $CFG;
3783  
3784          $this->resetAfterTest(true);
3785  
3786          // Create some skeleton data just so we can call the WS.
3787          $user = self::getDataGenerator()->create_user();
3788  
3789          // The person retrieving the contacts.
3790          $this->setUser($user);
3791  
3792          // Disable messaging.
3793          $CFG->messaging = 0;
3794  
3795          // Perform the WS call and ensure we are shown that it is disabled.
3796          $this->expectException('moodle_exception');
3797          core_message_external::get_user_contacts($user->id);
3798      }
3799  
3800      /**
3801       * Test getting contacts when there are no results.
3802       */
3803      public function test_get_user_contacts_no_results() {
3804          $this->resetAfterTest();
3805  
3806          $user1 = self::getDataGenerator()->create_user();
3807  
3808          $this->setUser($user1);
3809  
3810          $requests = core_message_external::get_user_contacts($user1->id);
3811          $requests = external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(), $requests);
3812  
3813          $this->assertEmpty($requests);
3814      }
3815  
3816      /**
3817       * Tests retrieving messages.
3818       */
3819      public function test_messagearea_messages() {
3820          $this->resetAfterTest(true);
3821  
3822          // Create some users.
3823          $user1 = self::getDataGenerator()->create_user();
3824          $user2 = self::getDataGenerator()->create_user();
3825  
3826          // The person asking for the messages.
3827          $this->setUser($user1);
3828  
3829          // Send some messages back and forth.
3830          $time = time();
3831          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3832          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3833          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3834          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3835  
3836          // Retrieve the messages.
3837          $result = core_message_external::data_for_messagearea_messages($user1->id, $user2->id);
3838  
3839          // We need to execute the return values cleaning process to simulate the web service server.
3840          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_messages_returns(),
3841              $result);
3842  
3843          // Check the results are correct.
3844          $this->assertTrue($result['iscurrentuser']);
3845          $this->assertEquals($user1->id, $result['currentuserid']);
3846          $this->assertEquals($user2->id, $result['otheruserid']);
3847          $this->assertEquals(fullname($user2), $result['otheruserfullname']);
3848          $this->assertNull($result['isonline']);
3849  
3850          // Confirm the message data is correct.
3851          $messages = $result['messages'];
3852          $this->assertCount(4, $messages);
3853  
3854          $message1 = $messages[0];
3855          $message2 = $messages[1];
3856          $message3 = $messages[2];
3857          $message4 = $messages[3];
3858  
3859          $this->assertEquals($user1->id, $message1['useridfrom']);
3860          $this->assertEquals($user2->id, $message1['useridto']);
3861          $this->assertTrue($message1['displayblocktime']);
3862          $this->assertContains('Yo!', $message1['text']);
3863  
3864          $this->assertEquals($user2->id, $message2['useridfrom']);
3865          $this->assertEquals($user1->id, $message2['useridto']);
3866          $this->assertFalse($message2['displayblocktime']);
3867          $this->assertContains('Sup mang?', $message2['text']);
3868  
3869          $this->assertEquals($user1->id, $message3['useridfrom']);
3870          $this->assertEquals($user2->id, $message3['useridto']);
3871          $this->assertFalse($message3['displayblocktime']);
3872          $this->assertContains('Writing PHPUnit tests!', $message3['text']);
3873  
3874          $this->assertEquals($user2->id, $message4['useridfrom']);
3875          $this->assertEquals($user1->id, $message4['useridto']);
3876          $this->assertFalse($message4['displayblocktime']);
3877          $this->assertContains('Word.', $message4['text']);
3878      }
3879  
3880      /**
3881       * Tests retrieving messages.
3882       */
3883      public function test_messagearea_messages_timefrom() {
3884          $this->resetAfterTest(true);
3885  
3886          // Create some users.
3887          $user1 = self::getDataGenerator()->create_user();
3888          $user2 = self::getDataGenerator()->create_user();
3889  
3890          // The person asking for the messages.
3891          $this->setUser($user1);
3892  
3893          // Send some messages back and forth.
3894          $time = time();
3895          $this->send_message($user1, $user2, 'Message 1', 0, $time - 4);
3896          $this->send_message($user2, $user1, 'Message 2', 0, $time - 3);
3897          $this->send_message($user1, $user2, 'Message 3', 0, $time - 2);
3898          $this->send_message($user2, $user1, 'Message 4', 0, $time - 1);
3899  
3900          // Retrieve the messages from $time - 3, which should be the 3 most recent messages.
3901          $result = core_message_external::data_for_messagearea_messages($user1->id, $user2->id, 0, 0, false, $time - 3);
3902  
3903          // We need to execute the return values cleaning process to simulate the web service server.
3904          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_messages_returns(),
3905              $result);
3906  
3907          // Confirm the message data is correct. We shouldn't get 'Message 1' back.
3908          $messages = $result['messages'];
3909          $this->assertCount(3, $messages);
3910  
3911          $message1 = $messages[0];
3912          $message2 = $messages[1];
3913          $message3 = $messages[2];
3914  
3915          $this->assertContains('Message 2', $message1['text']);
3916          $this->assertContains('Message 3', $message2['text']);
3917          $this->assertContains('Message 4', $message3['text']);
3918      }
3919  
3920      /**
3921       * Tests retrieving messages as another user.
3922       */
3923      public function test_messagearea_messages_as_other_user() {
3924          $this->resetAfterTest(true);
3925  
3926          // Set as admin.
3927          $this->setAdminUser();
3928  
3929          // Create some users.
3930          $user1 = self::getDataGenerator()->create_user();
3931          $user2 = self::getDataGenerator()->create_user();
3932  
3933          // Send some messages back and forth.
3934          $time = time();
3935          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3936          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3937          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3938          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3939  
3940          // Retrieve the messages.
3941          $result = core_message_external::data_for_messagearea_messages($user1->id, $user2->id);
3942  
3943          // We need to execute the return values cleaning process to simulate the web service server.
3944          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_messages_returns(),
3945              $result);
3946  
3947          // Check the results are correct.
3948          $this->assertFalse($result['iscurrentuser']);
3949          $this->assertEquals($user1->id, $result['currentuserid']);
3950          $this->assertEquals($user2->id, $result['otheruserid']);
3951          $this->assertEquals(fullname($user2), $result['otheruserfullname']);
3952          $this->assertFalse($result['isonline']);
3953  
3954          // Confirm the message data is correct.
3955          $messages = $result['messages'];
3956          $this->assertCount(4, $messages);
3957  
3958          $message1 = $messages[0];
3959          $message2 = $messages[1];
3960          $message3 = $messages[2];
3961          $message4 = $messages[3];
3962  
3963          $this->assertEquals($user1->id, $message1['useridfrom']);
3964          $this->assertEquals($user2->id, $message1['useridto']);
3965          $this->assertTrue($message1['displayblocktime']);
3966          $this->assertContains('Yo!', $message1['text']);
3967  
3968          $this->assertEquals($user2->id, $message2['useridfrom']);
3969          $this->assertEquals($user1->id, $message2['useridto']);
3970          $this->assertFalse($message2['displayblocktime']);
3971          $this->assertContains('Sup mang?', $message2['text']);
3972  
3973          $this->assertEquals($user1->id, $message3['useridfrom']);
3974          $this->assertEquals($user2->id, $message3['useridto']);
3975          $this->assertFalse($message3['displayblocktime']);
3976          $this->assertContains('Writing PHPUnit tests!', $message3['text']);
3977  
3978          $this->assertEquals($user2->id, $message4['useridfrom']);
3979          $this->assertEquals($user1->id, $message4['useridto']);
3980          $this->assertFalse($message4['displayblocktime']);
3981          $this->assertContains('Word.', $message4['text']);
3982      }
3983  
3984      /**
3985       * Tests retrieving messages as another user without the proper capabilities.
3986       */
3987      public function test_messagearea_messages_as_other_user_without_cap() {
3988          $this->resetAfterTest(true);
3989  
3990          // Create some users.
3991          $user1 = self::getDataGenerator()->create_user();
3992          $user2 = self::getDataGenerator()->create_user();
3993          $user3 = self::getDataGenerator()->create_user();
3994  
3995          // The person asking for the messages for another user.
3996          $this->setUser($user1);
3997  
3998          // Ensure an exception is thrown.
3999          $this->expectException('moodle_exception');
4000          core_message_external::data_for_messagearea_messages($user2->id, $user3->id);
4001      }
4002  
4003      /**
4004       * Tests retrieving messages with messaging disabled.
4005       */
4006      public function test_messagearea_messages_messaging_disabled() {
4007          global $CFG;
4008  
4009          $this->resetAfterTest(true);
4010  
4011          // Create some skeleton data just so we can call the WS.
4012          $user1 = self::getDataGenerator()->create_user();
4013          $user2 = self::getDataGenerator()->create_user();
4014  
4015          // The person asking for the messages for another user.
4016          $this->setUser($user1);
4017  
4018          // Disable messaging.
4019          $CFG->messaging = 0;
4020  
4021          // Ensure an exception is thrown.
4022          $this->expectException('moodle_exception');
4023          core_message_external::data_for_messagearea_messages($user1->id, $user2->id);
4024      }
4025  
4026      /**
4027       * Tests get_conversation_messages for retrieving messages.
4028       */
4029      public function test_get_conversation_messages() {
4030          $this->resetAfterTest(true);
4031  
4032          // Create some users.
4033          $user1 = self::getDataGenerator()->create_user();
4034          $user2 = self::getDataGenerator()->create_user();
4035          $user3 = self::getDataGenerator()->create_user();
4036          $user4 = self::getDataGenerator()->create_user();
4037          $user5 = self::getDataGenerator()->create_user();
4038  
4039          // Create group conversation.
4040          $conversation = \core_message\api::create_conversation(
4041              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4042              [$user1->id, $user2->id, $user3->id, $user4->id]
4043          );
4044  
4045          // The person asking for the messages.
4046          $this->setUser($user1);
4047  
4048          // Send some messages back and forth.
4049          $time = time();
4050          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time);
4051          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Sup mang?', $time + 1);
4052          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Writing PHPUnit tests!', $time + 2);
4053          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 3);
4054  
4055          // Retrieve the messages.
4056          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id);
4057  
4058          // We need to execute the return values cleaning process to simulate the web service server.
4059          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
4060              $result);
4061  
4062          // Check the results are correct.
4063          $this->assertEquals($conversation->id, $result['id']);
4064  
4065          // Confirm the members data is correct.
4066          $members = $result['members'];
4067          $this->assertCount(3, $members);
4068          $membersid = [$members[0]['id'], $members[1]['id'], $members[2]['id']];
4069          $this->assertContains($user1->id, $membersid);
4070          $this->assertContains($user2->id, $membersid);
4071          $this->assertContains($user3->id, $membersid);
4072  
4073          $membersfullnames = [$members[0]['fullname'], $members[1]['fullname'], $members[2]['fullname']];
4074          $this->assertContains(fullname($user1), $membersfullnames);
4075          $this->assertContains(fullname($user2), $membersfullnames);
4076          $this->assertContains(fullname($user3), $membersfullnames);
4077  
4078          // Confirm the messages data is correct.
4079          $messages = $result['messages'];
4080          $this->assertCount(4, $messages);
4081  
4082          $message1 = $messages[0];
4083          $message2 = $messages[1];
4084          $message3 = $messages[2];
4085          $message4 = $messages[3];
4086  
4087          $this->assertEquals($user1->id, $message1['useridfrom']);
4088          $this->assertContains('Yo!', $message1['text']);
4089  
4090          $this->assertEquals($user3->id, $message2['useridfrom']);
4091          $this->assertContains('Sup mang?', $message2['text']);
4092  
4093          $this->assertEquals($user2->id, $message3['useridfrom']);
4094          $this->assertContains('Writing PHPUnit tests!', $message3['text']);
4095  
4096          $this->assertEquals($user1->id, $message4['useridfrom']);
4097          $this->assertContains('Word.', $message4['text']);
4098      }
4099  
4100      /**
4101       * Tests get_conversation_messages for retrieving messages using timefrom parameter.
4102       */
4103      public function test_get_conversation_messages_timefrom() {
4104          $this->resetAfterTest(true);
4105  
4106          // Create some users.
4107          $user1 = self::getDataGenerator()->create_user();
4108          $user2 = self::getDataGenerator()->create_user();
4109          $user3 = self::getDataGenerator()->create_user();
4110          $user4 = self::getDataGenerator()->create_user();
4111  
4112          // Create group conversation.
4113          $conversation = \core_message\api::create_conversation(
4114              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4115              [$user1->id, $user2->id, $user3->id]
4116          );
4117  
4118          // The person asking for the messages.
4119          $this->setUser($user1);
4120  
4121          // Send some messages back and forth.
4122          $time = time();
4123          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time - 4);
4124          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time - 3);
4125          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 3', $time - 2);
4126          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 4', $time - 1);
4127  
4128          // Retrieve the messages from $time - 3, which should be the 3 most recent messages.
4129          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id, 0, 0, false, $time - 3);
4130  
4131          // We need to execute the return values cleaning process to simulate the web service server.
4132          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
4133              $result);
4134  
4135          // Check the results are correct.
4136          $this->assertEquals($conversation->id, $result['id']);
4137  
4138          // Confirm the messages data is correct.
4139          $messages = $result['messages'];
4140          $this->assertCount(3, $messages);
4141  
4142          $message1 = $messages[0];
4143          $message2 = $messages[1];
4144          $message3 = $messages[2];
4145  
4146          $this->assertContains('Message 2', $message1['text']);
4147          $this->assertContains('Message 3', $message2['text']);
4148          $this->assertContains('Message 4', $message3['text']);
4149  
4150          // Confirm the members data is correct.
4151          $members = $result['members'];
4152          $this->assertCount(1, $members);
4153          $this->assertEquals($user2->id, $members[0]['id']);
4154      }
4155  
4156      /**
4157       * Tests get_conversation_messages for retrieving messages as another user.
4158       */
4159      public function test_get_conversation_messages_as_other_user() {
4160          $this->resetAfterTest(true);
4161  
4162          // Set as admin.
4163          $this->setAdminUser();
4164  
4165          // Create some users.
4166          $user1 = self::getDataGenerator()->create_user();
4167          $user2 = self::getDataGenerator()->create_user();
4168          $user3 = self::getDataGenerator()->create_user();
4169          $user4 = self::getDataGenerator()->create_user();
4170  
4171          // Create group conversation.
4172          $conversation = \core_message\api::create_conversation(
4173              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4174              [$user1->id, $user2->id, $user3->id, $user4->id]
4175          );
4176  
4177          // Send some messages back and forth.
4178          $time = time();
4179          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time);
4180          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Sup mang?', $time + 1);
4181          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Writing PHPUnit tests!', $time + 2);
4182          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 3);
4183  
4184          // Retrieve the messages.
4185          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id);
4186  
4187          // We need to execute the return values cleaning process to simulate the web service server.
4188          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
4189              $result);
4190  
4191          // Check the results are correct.
4192          $this->assertEquals($conversation->id, $result['id']);
4193  
4194          // Confirm the members data is correct.
4195          $members = $result['members'];
4196          $this->assertCount(3, $members);
4197          $membersid = [$members[0]['id'], $members[1]['id'], $members[2]['id']];
4198          $this->assertContains($user1->id, $membersid);
4199          $this->assertContains($user2->id, $membersid);
4200          $this->assertContains($user3->id, $membersid);
4201  
4202          // Confirm the message data is correct.
4203          $messages = $result['messages'];
4204          $this->assertCount(4, $messages);
4205  
4206          $message1 = $messages[0];
4207          $message2 = $messages[1];
4208          $message3 = $messages[2];
4209          $message4 = $messages[3];
4210  
4211          $this->assertEquals($user1->id, $message1['useridfrom']);
4212          $this->assertContains('Yo!', $message1['text']);
4213  
4214          $this->assertEquals($user3->id, $message2['useridfrom']);
4215          $this->assertContains('Sup mang?', $message2['text']);
4216  
4217          $this->assertEquals($user2->id, $message3['useridfrom']);
4218          $this->assertContains('Writing PHPUnit tests!', $message3['text']);
4219  
4220          $this->assertEquals($user1->id, $message4['useridfrom']);
4221          $this->assertContains('Word.', $message4['text']);
4222      }
4223  
4224      /**
4225       * Tests get_conversation_messages for retrieving messages as another user without the proper capabilities.
4226       */
4227      public function test_get_conversation_messages_as_other_user_without_cap() {
4228          $this->resetAfterTest(true);
4229  
4230          // Create some users.
4231          $user1 = self::getDataGenerator()->create_user();
4232          $user2 = self::getDataGenerator()->create_user();
4233          $user3 = self::getDataGenerator()->create_user();
4234          $user4 = self::getDataGenerator()->create_user();
4235  
4236          // Create group conversation.
4237          $conversation = \core_message\api::create_conversation(
4238              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4239              [$user1->id, $user2->id, $user3->id, $user4->id]
4240          );
4241  
4242          // The person asking for the messages for another user.
4243          $this->setUser($user1);
4244  
4245          // Ensure an exception is thrown.
4246          $this->expectException('moodle_exception');
4247          core_message_external::get_conversation_messages($user2->id, $conversation->id);
4248      }
4249  
4250      /**
4251       * Tests get_conversation_messages for retrieving messages as another user not in the conversation.
4252       */
4253      public function test_get_conversation_messages_as_user_not_in_conversation() {
4254          $this->resetAfterTest(true);
4255  
4256          // Create some users.
4257          $user1 = self::getDataGenerator()->create_user();
4258          $user2 = self::getDataGenerator()->create_user();
4259          $user3 = self::getDataGenerator()->create_user(); // Not in group.
4260  
4261          // Create group conversation.
4262          $conversation = \core_message\api::create_conversation(
4263              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4264              [$user1->id, $user2->id]
4265          );
4266  
4267          // The person asking for the messages for a conversation he does not belong to.
4268          $this->setUser($user3);
4269  
4270          // Ensure an exception is thrown.
4271          $this->expectExceptionMessage('User is not part of conversation.');
4272          core_message_external::get_conversation_messages($user3->id, $conversation->id);
4273      }
4274  
4275      /**
4276       * Tests get_conversation_messages for retrieving messages with messaging disabled.
4277       */
4278      public function test_get_conversation_messages_messaging_disabled() {
4279          $this->resetAfterTest(true);
4280  
4281          // Create some skeleton data just so we can call the WS.
4282          $user1 = self::getDataGenerator()->create_user();
4283          $user2 = self::getDataGenerator()->create_user();
4284          $user3 = self::getDataGenerator()->create_user();
4285          $user4 = self::getDataGenerator()->create_user();
4286  
4287          // Create group conversation.
4288          $conversation = \core_message\api::create_conversation(
4289              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4290              [$user1->id, $user2->id, $user3->id, $user4->id]
4291          );
4292  
4293          // The person asking for the messages for another user.
4294          $this->setUser($user1);
4295  
4296          // Disable messaging.
4297          set_config('messaging', 0);
4298  
4299          // Ensure an exception is thrown.
4300          $this->expectException('moodle_exception');
4301          core_message_external::get_conversation_messages($user1->id, $conversation->id);
4302      }
4303  
4304      /**
4305       * Tests retrieving most recent message.
4306       */
4307      public function test_messagearea_get_most_recent_message() {
4308          $this->resetAfterTest(true);
4309  
4310          // Create some users.
4311          $user1 = self::getDataGenerator()->create_user();
4312          $user2 = self::getDataGenerator()->create_user();
4313  
4314          // The person doing the search.
4315          $this->setUser($user1);
4316  
4317          // Send some messages back and forth.
4318          $time = time();
4319          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4320          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4321          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4322          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4323  
4324          // Get the most recent message.
4325          $result = core_message_external::data_for_messagearea_get_most_recent_message($user1->id, $user2->id);
4326  
4327          // We need to execute the return values cleaning process to simulate the web service server.
4328          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_get_most_recent_message_returns(),
4329              $result);
4330  
4331          // Check the results are correct.
4332          $this->assertEquals($user2->id, $result['useridfrom']);
4333          $this->assertEquals($user1->id, $result['useridto']);
4334          $this->assertContains('Word.', $result['text']);
4335      }
4336  
4337      /**
4338       * Tests retrieving most recent message as another user.
4339       */
4340      public function test_messagearea_get_most_recent_message_as_other_user() {
4341          $this->resetAfterTest(true);
4342  
4343          // The person doing the search.
4344          $this->setAdminUser();
4345  
4346          // Create some users.
4347          $user1 = self::getDataGenerator()->create_user();
4348          $user2 = self::getDataGenerator()->create_user();
4349  
4350          // Send some messages back and forth.
4351          $time = time();
4352          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4353          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4354          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4355          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4356  
4357          // Get the most recent message.
4358          $result = core_message_external::data_for_messagearea_get_most_recent_message($user1->id, $user2->id);
4359  
4360          // We need to execute the return values cleaning process to simulate the web service server.
4361          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_get_most_recent_message_returns(),
4362              $result);
4363  
4364          // Check the results are correct.
4365          $this->assertEquals($user2->id, $result['useridfrom']);
4366          $this->assertEquals($user1->id, $result['useridto']);
4367          $this->assertContains('Word.', $result['text']);
4368      }
4369  
4370      /**
4371       * Tests retrieving most recent message as another user without the proper capabilities.
4372       */
4373      public function test_messagearea_get_most_recent_message_as_other_user_without_cap() {
4374          $this->resetAfterTest(true);
4375  
4376          // Create some users.
4377          $user1 = self::getDataGenerator()->create_user();
4378          $user2 = self::getDataGenerator()->create_user();
4379          $user3 = self::getDataGenerator()->create_user();
4380  
4381          // The person asking for the most recent message for another user.
4382          $this->setUser($user1);
4383  
4384          // Ensure an exception is thrown.
4385          $this->expectException('moodle_exception');
4386          core_message_external::data_for_messagearea_get_most_recent_message($user2->id, $user3->id);
4387      }
4388  
4389      /**
4390       * Tests retrieving most recent message with messaging disabled.
4391       */
4392      public function test_messagearea_get_most_recent_message_messaging_disabled() {
4393          global $CFG;
4394  
4395          $this->resetAfterTest(true);
4396  
4397          // Create some skeleton data just so we can call the WS.
4398          $user1 = self::getDataGenerator()->create_user();
4399          $user2 = self::getDataGenerator()->create_user();
4400  
4401          // The person asking for the most recent message.
4402          $this->setUser($user1);
4403  
4404          // Disable messaging.
4405          $CFG->messaging = 0;
4406  
4407          // Ensure an exception is thrown.
4408          $this->expectException('moodle_exception');
4409          core_message_external::data_for_messagearea_get_most_recent_message($user1->id, $user2->id);
4410      }
4411  
4412      /**
4413       * Tests retrieving a user's profile.
4414       */
4415      public function test_messagearea_get_profile() {
4416          $this->resetAfterTest(true);
4417  
4418          // Create some users.
4419          $user1 = self::getDataGenerator()->create_user();
4420          $user2 = self::getDataGenerator()->create_user();
4421  
4422          // The person asking for the profile information.
4423          $this->setUser($user1);
4424  
4425          // Get the profile.
4426          $result = core_message_external::data_for_messagearea_get_profile($user1->id, $user2->id);
4427  
4428          // We need to execute the return values cleaning process to simulate the web service server.
4429          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_get_profile_returns(),
4430              $result);
4431  
4432          $this->assertEquals($user2->id, $result['userid']);
4433          $this->assertEmpty($result['email']);
4434          $this->assertEmpty($result['country']);
4435          $this->assertEmpty($result['city']);
4436          $this->assertEquals(fullname($user2), $result['fullname']);
4437          $this->assertNull($result['isonline']);
4438          $this->assertFalse($result['isblocked']);
4439          $this->assertFalse($result['iscontact']);
4440      }
4441  
4442      /**
4443       * Tests retrieving a user's profile as another user.
4444       */
4445      public function test_messagearea_profile_as_other_user() {
4446          $this->resetAfterTest(true);
4447  
4448          // The person asking for the profile information.
4449          $this->setAdminUser();
4450  
4451          // Create some users.
4452          $user1 = self::getDataGenerator()->create_user();
4453  
4454          $user2 = new stdClass();
4455          $user2->country = 'AU';
4456          $user2->city = 'Perth';
4457          $user2 = self::getDataGenerator()->create_user($user2);
4458  
4459          // Get the profile.
4460          $result = core_message_external::data_for_messagearea_get_profile($user1->id, $user2->id);
4461  
4462          // We need to execute the return values cleaning process to simulate the web service server.
4463          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_get_profile_returns(),
4464              $result);
4465  
4466          $this->assertEquals($user2->id, $result['userid']);
4467          $this->assertEquals($user2->email, $result['email']);
4468          $this->assertEquals(get_string($user2->country, 'countries'), $result['country']);
4469          $this->assertEquals($user2->city, $result['city']);
4470          $this->assertEquals(fullname($user2), $result['fullname']);
4471          $this->assertFalse($result['isonline']);
4472          $this->assertFalse($result['isblocked']);
4473          $this->assertFalse($result['iscontact']);
4474      }
4475  
4476      /**
4477       * Tests retrieving a user's profile as another user without the proper capabilities.
4478       */
4479      public function test_messagearea_profile_as_other_user_without_cap() {
4480          $this->resetAfterTest(true);
4481  
4482          // Create some users.
4483          $user1 = self::getDataGenerator()->create_user();
4484          $user2 = self::getDataGenerator()->create_user();
4485          $user3 = self::getDataGenerator()->create_user();
4486  
4487          // The person asking for the profile information for another user.
4488          $this->setUser($user1);
4489  
4490          // Ensure an exception is thrown.
4491          $this->expectException('moodle_exception');
4492          core_message_external::data_for_messagearea_get_profile($user2->id, $user3->id);
4493      }
4494  
4495      /**
4496       * Tests retrieving a user's profile with messaging disabled.
4497       */
4498      public function test_messagearea_profile_messaging_disabled() {
4499          global $CFG;
4500  
4501          $this->resetAfterTest(true);
4502  
4503          // Create some skeleton data just so we can call the WS.
4504          $user1 = self::getDataGenerator()->create_user();
4505          $user2 = self::getDataGenerator()->create_user();
4506  
4507          // The person asking for the profile information.
4508          $this->setUser($user1);
4509  
4510          // Disable messaging.
4511          $CFG->messaging = 0;
4512  
4513          // Ensure an exception is thrown.
4514          $this->expectException('moodle_exception');
4515          core_message_external::data_for_messagearea_get_profile($user1->id, $user2->id);
4516      }
4517  
4518      /**
4519       * Test marking all message as read with an invalid user.
4520       */
4521      public function test_mark_all_messages_as_read_invalid_user_exception() {
4522          $this->resetAfterTest(true);
4523  
4524          $this->expectException('moodle_exception');
4525          core_message_external::mark_all_messages_as_read(-2132131, 0);
4526      }
4527  
4528      /**
4529       * Test marking all message as read without proper access.
4530       */
4531      public function test_mark_all_messages_as_read_access_denied_exception() {
4532          $this->resetAfterTest(true);
4533  
4534          $sender = $this->getDataGenerator()->create_user();
4535          $user = $this->getDataGenerator()->create_user();
4536  
4537          $this->setUser($user);
4538          $this->expectException('moodle_exception');
4539          core_message_external::mark_all_messages_as_read($sender->id, 0);
4540      }
4541  
4542      /**
4543       * Test marking all message as read with missing from user.
4544       */
4545      public function test_mark_all_messages_as_read_missing_from_user_exception() {
4546          $this->resetAfterTest(true);
4547  
4548          $sender = $this->getDataGenerator()->create_user();
4549  
4550          $this->setUser($sender);
4551          $this->expectException('moodle_exception');
4552          core_message_external::mark_all_messages_as_read($sender->id, 99999);
4553      }
4554  
4555      /**
4556       * Test marking all message as read.
4557       */
4558      public function test_mark_all_messages_as_read() {
4559          global $DB;
4560  
4561          $this->resetAfterTest(true);
4562  
4563          $sender1 = $this->getDataGenerator()->create_user();
4564          $sender2 = $this->getDataGenerator()->create_user();
4565          $sender3 = $this->getDataGenerator()->create_user();
4566          $recipient = $this->getDataGenerator()->create_user();
4567  
4568          $this->setUser($recipient);
4569  
4570          $this->send_message($sender1, $recipient, 'Message');
4571          $this->send_message($sender1, $recipient, 'Message');
4572          $this->send_message($sender2, $recipient, 'Message');
4573          $this->send_message($sender2, $recipient, 'Message');
4574          $this->send_message($sender3, $recipient, 'Message');
4575          $this->send_message($sender3, $recipient, 'Message');
4576  
4577          core_message_external::mark_all_messages_as_read($recipient->id, $sender1->id);
4578          $this->assertEquals(2, $DB->count_records('message_user_actions'));
4579  
4580          core_message_external::mark_all_messages_as_read($recipient->id, 0);
4581          $this->assertEquals(6, $DB->count_records('message_user_actions'));
4582      }
4583  
4584      /**
4585       * Test marking all conversation messages as read with an invalid user.
4586       */
4587      public function test_mark_all_conversation_messages_as_read_invalid_user_exception() {
4588          $this->resetAfterTest(true);
4589  
4590          $user1 = self::getDataGenerator()->create_user();
4591          $user2 = self::getDataGenerator()->create_user();
4592  
4593          // Send some messages back and forth.
4594          $time = time();
4595          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4596          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4597          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4598          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4599  
4600          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
4601  
4602          $this->expectException('moodle_exception');
4603          core_message_external::mark_all_conversation_messages_as_read(-2132131, $conversationid);
4604      }
4605  
4606      /**
4607       * Test marking all conversation messages as read without proper access.
4608       */
4609      public function test_mark_all_conversation_messages_as_read_access_denied_exception() {
4610          $this->resetAfterTest(true);
4611  
4612          $user1 = self::getDataGenerator()->create_user();
4613          $user2 = self::getDataGenerator()->create_user();
4614          $user3 = self::getDataGenerator()->create_user();
4615  
4616          // Send some messages back and forth.
4617          $time = time();
4618          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4619          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4620          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4621          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4622  
4623          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
4624  
4625          // User 3 is not in the conversation.
4626          $this->expectException('moodle_exception');
4627          core_message_external::mark_all_conversation_messages_as_read($user3->id, $conversationid);
4628      }
4629  
4630      /**
4631       * Test marking all conversation messages as read for another user.
4632       */
4633      public function test_mark_all_conversation_messages_as_read_wrong_user() {
4634          $this->resetAfterTest(true);
4635  
4636          $user1 = self::getDataGenerator()->create_user();
4637          $user2 = self::getDataGenerator()->create_user();
4638  
4639          // Send some messages back and forth.
4640          $time = time();
4641          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4642          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4643          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4644          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4645  
4646          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
4647  
4648          // Can't mark the messages as read for user 2.
4649          $this->setUser($user1);
4650          $this->expectException('moodle_exception');
4651          core_message_external::mark_all_conversation_messages_as_read($user2->id, $conversationid);
4652      }
4653  
4654      /**
4655       * Test marking all conversation messages as admin.
4656       */
4657      public function test_mark_all_conversation_messages_as_admin() {
4658          global $DB;
4659  
4660          $this->resetAfterTest(true);
4661  
4662          $user1 = self::getDataGenerator()->create_user();
4663          $user2 = self::getDataGenerator()->create_user();
4664  
4665          // Send some messages back and forth.
4666          $time = time();
4667          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4668          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4669          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4670          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4671  
4672          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
4673  
4674          // Admin can do anything.
4675          $this->setAdminUser();
4676          core_message_external::mark_all_conversation_messages_as_read($user2->id, $conversationid);
4677          $this->assertEquals(2, $DB->count_records('message_user_actions'));
4678      }
4679  
4680      /**
4681       * Test marking all conversation messages.
4682       */
4683      public function test_mark_all_conversation_messages_as_read() {
4684          global $DB;
4685  
4686          $this->resetAfterTest(true);
4687  
4688          $user1 = self::getDataGenerator()->create_user();
4689          $user2 = self::getDataGenerator()->create_user();
4690  
4691          // Send some messages back and forth.
4692          $time = time();
4693          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4694          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4695          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4696          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4697  
4698          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
4699  
4700          // We are the user we want to mark the messages for and we are in the conversation, all good.
4701          $this->setUser($user1);
4702          core_message_external::mark_all_conversation_messages_as_read($user1->id, $conversationid);
4703          $this->assertEquals(2, $DB->count_records('message_user_actions'));
4704      }
4705  
4706      /**
4707       * Test getting unread conversation count.
4708       */
4709      public function test_get_unread_conversations_count() {
4710          $this->resetAfterTest(true);
4711  
4712          // Create some users.
4713          $user1 = self::getDataGenerator()->create_user();
4714          $user2 = self::getDataGenerator()->create_user();
4715          $user3 = self::getDataGenerator()->create_user();
4716          $user4 = self::getDataGenerator()->create_user();
4717  
4718          // The person wanting the conversation count.
4719          $this->setUser($user1);
4720  
4721          // Send some messages back and forth, have some different conversations with different users.
4722          $this->send_message($user1, $user2, 'Yo!');
4723          $this->send_message($user2, $user1, 'Sup mang?');
4724          $this->send_message($user1, $user2, 'Writing PHPUnit tests!');
4725          $this->send_message($user2, $user1, 'Word.');
4726  
4727          $this->send_message($user1, $user3, 'Booyah');
4728          $this->send_message($user3, $user1, 'Whaaat?');
4729          $this->send_message($user1, $user3, 'Nothing.');
4730          $this->send_message($user3, $user1, 'Cool.');
4731  
4732          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
4733          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
4734          $this->send_message($user1, $user4, 'Dope.');
4735  
4736          // Get the unread conversation count.
4737          $result = core_message_external::get_unread_conversations_count($user1->id);
4738  
4739          // We need to execute the return values cleaning process to simulate the web service server.
4740          $result = external_api::clean_returnvalue(core_message_external::get_unread_conversations_count_returns(),
4741              $result);
4742  
4743          $this->assertEquals(3, $result);
4744      }
4745  
4746      /**
4747       * Test getting unread conversation count as other user.
4748       */
4749      public function test_get_unread_conversations_count_as_other_user() {
4750          $this->resetAfterTest(true);
4751  
4752          // The person wanting the conversation count.
4753          $this->setAdminUser();
4754  
4755          // Create some users.
4756          $user1 = self::getDataGenerator()->create_user();
4757          $user2 = self::getDataGenerator()->create_user();
4758          $user3 = self::getDataGenerator()->create_user();
4759          $user4 = self::getDataGenerator()->create_user();
4760  
4761          // Send some messages back and forth, have some different conversations with different users.
4762          $this->send_message($user1, $user2, 'Yo!');
4763          $this->send_message($user2, $user1, 'Sup mang?');
4764          $this->send_message($user1, $user2, 'Writing PHPUnit tests!');
4765          $this->send_message($user2, $user1, 'Word.');
4766  
4767          $this->send_message($user1, $user3, 'Booyah');
4768          $this->send_message($user3, $user1, 'Whaaat?');
4769          $this->send_message($user1, $user3, 'Nothing.');
4770          $this->send_message($user3, $user1, 'Cool.');
4771  
4772          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
4773          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
4774          $this->send_message($user1, $user4, 'Dope.');
4775  
4776          // Get the unread conversation count.
4777          $result = core_message_external::get_unread_conversations_count($user1->id);
4778  
4779          // We need to execute the return values cleaning process to simulate the web service server.
4780          $result = external_api::clean_returnvalue(core_message_external::get_unread_conversations_count_returns(),
4781              $result);
4782  
4783          $this->assertEquals(3, $result);
4784      }
4785  
4786      /**
4787       * Test getting unread conversation count as other user without proper capability.
4788       */
4789      public function test_get_unread_conversations_count_as_other_user_without_cap() {
4790          $this->resetAfterTest(true);
4791  
4792          // Create some users.
4793          $user1 = self::getDataGenerator()->create_user();
4794          $user2 = self::getDataGenerator()->create_user();
4795  
4796          // The person wanting the conversation count.
4797          $this->setUser($user1);
4798  
4799          // Ensure an exception is thrown.
4800          $this->expectException('moodle_exception');
4801          core_message_external::get_unread_conversations_count($user2->id);
4802      }
4803  
4804      /**
4805       * Test deleting conversation.
4806       */
4807      public function test_delete_conversation() {
4808          global $DB;
4809  
4810          $this->resetAfterTest(true);
4811  
4812          // Create some users.
4813          $user1 = self::getDataGenerator()->create_user();
4814          $user2 = self::getDataGenerator()->create_user();
4815  
4816          // The person wanting to delete the conversation.
4817          $this->setUser($user1);
4818  
4819          // Send some messages back and forth.
4820          $time = time();
4821          $m1id = $this->send_message($user1, $user2, 'Yo!', 0, $time);
4822          $m2id = $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4823          $m3id = $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4824          $m4id = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4825  
4826          // Delete the conversation.
4827          core_message_external::delete_conversation($user1->id, $user2->id);
4828  
4829          $muas = $DB->get_records('message_user_actions', array(), 'timecreated ASC');
4830          $this->assertCount(4, $muas);
4831          // Sort by id.
4832          ksort($muas);
4833  
4834          $mua1 = array_shift($muas);
4835          $mua2 = array_shift($muas);
4836          $mua3 = array_shift($muas);
4837          $mua4 = array_shift($muas);
4838  
4839          $this->assertEquals($user1->id, $mua1->userid);
4840          $this->assertEquals($m1id, $mua1->messageid);
4841          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
4842  
4843          $this->assertEquals($user1->id, $mua2->userid);
4844          $this->assertEquals($m2id, $mua2->messageid);
4845          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
4846  
4847          $this->assertEquals($user1->id, $mua3->userid);
4848          $this->assertEquals($m3id, $mua3->messageid);
4849          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
4850  
4851          $this->assertEquals($user1->id, $mua4->userid);
4852          $this->assertEquals($m4id, $mua4->messageid);
4853          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua4->action);
4854      }
4855  
4856      /**
4857       * Test deleting conversation as other user.
4858       */
4859      public function test_delete_conversation_as_other_user() {
4860          global $DB;
4861  
4862          $this->resetAfterTest(true);
4863  
4864          $this->setAdminUser();
4865  
4866          // Create some users.
4867          $user1 = self::getDataGenerator()->create_user();
4868          $user2 = self::getDataGenerator()->create_user();
4869  
4870          // Send some messages back and forth.
4871          $time = time();
4872          $m1id = $this->send_message($user1, $user2, 'Yo!', 0, $time);
4873          $m2id = $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4874          $m3id = $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4875          $m4id = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4876  
4877          // Delete the conversation.
4878          core_message_external::delete_conversation($user1->id, $user2->id);
4879  
4880          $muas = $DB->get_records('message_user_actions', array(), 'timecreated ASC');
4881          $this->assertCount(4, $muas);
4882          // Sort by id.
4883          ksort($muas);
4884  
4885          $mua1 = array_shift($muas);
4886          $mua2 = array_shift($muas);
4887          $mua3 = array_shift($muas);
4888          $mua4 = array_shift($muas);
4889  
4890          $this->assertEquals($user1->id, $mua1->userid);
4891          $this->assertEquals($m1id, $mua1->messageid);
4892          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
4893  
4894          $this->assertEquals($user1->id, $mua2->userid);
4895          $this->assertEquals($m2id, $mua2->messageid);
4896          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
4897  
4898          $this->assertEquals($user1->id, $mua3->userid);
4899          $this->assertEquals($m3id, $mua3->messageid);
4900          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
4901  
4902          $this->assertEquals($user1->id, $mua4->userid);
4903          $this->assertEquals($m4id, $mua4->messageid);
4904          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua4->action);
4905      }
4906  
4907      /**
4908       * Test deleting conversation as other user without proper capability.
4909       */
4910      public function test_delete_conversation_as_other_user_without_cap() {
4911          $this->resetAfterTest(true);
4912  
4913          // Create some users.
4914          $user1 = self::getDataGenerator()->create_user();
4915          $user2 = self::getDataGenerator()->create_user();
4916          $user3 = self::getDataGenerator()->create_user();
4917  
4918          // Send some messages back and forth.
4919          $time = time();
4920          $this->send_message($user1, $user2, 'Yo!', 0, $time);
4921          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4922          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4923          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4924  
4925          // The person wanting to delete the conversation.
4926          $this->setUser($user3);
4927  
4928          // Ensure an exception is thrown.
4929          $this->expectException('moodle_exception');
4930          core_message_external::delete_conversation($user1->id, $user2->id);
4931      }
4932  
4933      /**
4934       * Test deleting conversation with messaging disabled.
4935       */
4936      public function test_delete_conversation_messaging_disabled() {
4937          global $CFG;
4938  
4939          $this->resetAfterTest(true);
4940  
4941          // Create some users.
4942          $user1 = self::getDataGenerator()->create_user();
4943          $user2 = self::getDataGenerator()->create_user();
4944  
4945          // The person wanting to delete the conversation.
4946          $this->setUser($user1);
4947  
4948          // Disable messaging.
4949          $CFG->messaging = 0;
4950  
4951          // Ensure an exception is thrown.
4952          $this->expectException('moodle_exception');
4953          core_message_external::delete_conversation($user1->id, $user2->id);
4954      }
4955  
4956      /**
4957       * Test deleting conversations.
4958       */
4959      public function test_delete_conversations_by_id() {
4960          global $DB;
4961  
4962          $this->resetAfterTest(true);
4963  
4964          // Create some users.
4965          $user1 = self::getDataGenerator()->create_user();
4966          $user2 = self::getDataGenerator()->create_user();
4967  
4968          // The person wanting to delete the conversation.
4969          $this->setUser($user1);
4970  
4971          // Send some messages back and forth.
4972          $time = time();
4973          $m1id = $this->send_message($user1, $user2, 'Yo!', 0, $time);
4974          $m2id = $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
4975          $m3id = $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
4976          $m4id = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
4977  
4978          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
4979  
4980          // Delete the conversation.
4981          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
4982  
4983          $muas = $DB->get_records('message_user_actions', array(), 'timecreated ASC');
4984          $this->assertCount(4, $muas);
4985          // Sort by id.
4986          ksort($muas);
4987  
4988          $mua1 = array_shift($muas);
4989          $mua2 = array_shift($muas);
4990          $mua3 = array_shift($muas);
4991          $mua4 = array_shift($muas);
4992  
4993          $this->assertEquals($user1->id, $mua1->userid);
4994          $this->assertEquals($m1id, $mua1->messageid);
4995          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
4996  
4997          $this->assertEquals($user1->id, $mua2->userid);
4998          $this->assertEquals($m2id, $mua2->messageid);
4999          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
5000  
5001          $this->assertEquals($user1->id, $mua3->userid);
5002          $this->assertEquals($m3id, $mua3->messageid);
5003          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
5004  
5005          $this->assertEquals($user1->id, $mua4->userid);
5006          $this->assertEquals($m4id, $mua4->messageid);
5007          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua4->action);
5008      }
5009  
5010      /**
5011       * Test deleting conversations as other user.
5012       */
5013      public function test_delete_conversations_by_id_as_other_user() {
5014          global $DB;
5015  
5016          $this->resetAfterTest(true);
5017  
5018          $this->setAdminUser();
5019  
5020          // Create some users.
5021          $user1 = self::getDataGenerator()->create_user();
5022          $user2 = self::getDataGenerator()->create_user();
5023  
5024          // Send some messages back and forth.
5025          $time = time();
5026          $m1id = $this->send_message($user1, $user2, 'Yo!', 0, $time);
5027          $m2id = $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5028          $m3id = $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5029          $m4id = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
5030  
5031          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5032  
5033          // Delete the conversation.
5034          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
5035  
5036          $muas = $DB->get_records('message_user_actions', array(), 'timecreated ASC');
5037          $this->assertCount(4, $muas);
5038          // Sort by id.
5039          ksort($muas);
5040  
5041          $mua1 = array_shift($muas);
5042          $mua2 = array_shift($muas);
5043          $mua3 = array_shift($muas);
5044          $mua4 = array_shift($muas);
5045  
5046          $this->assertEquals($user1->id, $mua1->userid);
5047          $this->assertEquals($m1id, $mua1->messageid);
5048          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
5049  
5050          $this->assertEquals($user1->id, $mua2->userid);
5051          $this->assertEquals($m2id, $mua2->messageid);
5052          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
5053  
5054          $this->assertEquals($user1->id, $mua3->userid);
5055          $this->assertEquals($m3id, $mua3->messageid);
5056          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
5057  
5058          $this->assertEquals($user1->id, $mua4->userid);
5059          $this->assertEquals($m4id, $mua4->messageid);
5060          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua4->action);
5061      }
5062  
5063      /**
5064       * Test deleting conversations as other user without proper capability.
5065       */
5066      public function test_delete_conversations_by_id_as_other_user_without_cap() {
5067          $this->resetAfterTest(true);
5068  
5069          // Create some users.
5070          $user1 = self::getDataGenerator()->create_user();
5071          $user2 = self::getDataGenerator()->create_user();
5072          $user3 = self::getDataGenerator()->create_user();
5073  
5074          // Send some messages back and forth.
5075          $time = time();
5076          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5077          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5078          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5079          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
5080  
5081          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5082  
5083          // The person wanting to delete the conversation.
5084          $this->setUser($user3);
5085  
5086          // Ensure an exception is thrown.
5087          $this->expectException('moodle_exception');
5088          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
5089      }
5090  
5091      /**
5092       * Test deleting conversations with messaging disabled.
5093       */
5094      public function test_delete_conversations_by_id_messaging_disabled() {
5095          global $CFG;
5096  
5097          $this->resetAfterTest(true);
5098  
5099          // Create some users.
5100          $user1 = self::getDataGenerator()->create_user();
5101          $user2 = self::getDataGenerator()->create_user();
5102  
5103          // Send some messages back and forth.
5104          $time = time();
5105          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5106          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5107          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5108          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
5109  
5110          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5111  
5112          // The person wanting to delete the conversation.
5113          $this->setUser($user1);
5114  
5115          // Disable messaging.
5116          $CFG->messaging = 0;
5117  
5118          // Ensure an exception is thrown.
5119          $this->expectException('moodle_exception');
5120          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
5121      }
5122  
5123      /**
5124       * Test get message processor.
5125       */
5126      public function test_get_message_processor() {
5127          $this->resetAfterTest(true);
5128  
5129          // Create a user.
5130          $user1 = self::getDataGenerator()->create_user();
5131  
5132          // Set you as the user.
5133          $this->setUser($user1);
5134  
5135          // Get the message processors.
5136          $result = core_message_external::get_message_processor($user1->id, 'popup');
5137  
5138          // We need to execute the return values cleaning process to simulate the web service server.
5139          $result = external_api::clean_returnvalue(core_message_external::get_message_processor_returns(), $result);
5140  
5141          $this->assertNotEmpty($result['systemconfigured']);
5142          $this->assertNotEmpty($result['userconfigured']);
5143      }
5144  
5145      /**
5146       * Test get_user_notification_preferences
5147       */
5148      public function test_get_user_message_preferences() {
5149          $this->resetAfterTest(true);
5150  
5151          $user = self::getDataGenerator()->create_user();
5152          $this->setUser($user);
5153  
5154          // Enable site-wide messagging privacy setting. The user will be able to receive messages from everybody.
5155          set_config('messagingallusers', true);
5156  
5157          // Set a couple of preferences to test.
5158          set_user_preference('message_provider_moodle_instantmessage_loggedin', 'email', $user);
5159          set_user_preference('message_provider_moodle_instantmessage_loggedoff', 'email', $user);
5160          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_SITE, $user);
5161  
5162          $prefs = core_message_external::get_user_message_preferences();
5163          $prefs = external_api::clean_returnvalue(core_message_external::get_user_message_preferences_returns(), $prefs);
5164          $this->assertEquals($user->id, $prefs['preferences']['userid']);
5165  
5166          // Check components.
5167          $this->assertCount(1, $prefs['preferences']['components']);
5168          $this->assertEquals(\core_message\api::MESSAGE_PRIVACY_SITE, $prefs['blocknoncontacts']);
5169  
5170          // Check some preferences that we previously set.
5171          $found = false;
5172          foreach ($prefs['preferences']['components'] as $component) {
5173              foreach ($component['notifications'] as $prefdata) {
5174                  if ($prefdata['preferencekey'] != 'message_provider_moodle_instantmessage') {
5175                      continue;
5176                  }
5177                  foreach ($prefdata['processors'] as $processor) {
5178                      if ($processor['name'] == 'email') {
5179                          $this->assertTrue($processor['loggedin']['checked']);
5180                          $this->assertTrue($processor['loggedoff']['checked']);
5181                          $found = true;
5182                      }
5183                  }
5184              }
5185          }
5186          $this->assertTrue($found);
5187      }
5188  
5189      /**
5190       * Test get_user_message_preferences permissions
5191       */
5192      public function test_get_user_message_preferences_permissions() {
5193          $this->resetAfterTest(true);
5194  
5195          $user = self::getDataGenerator()->create_user();
5196          $otheruser = self::getDataGenerator()->create_user();
5197          $this->setUser($user);
5198  
5199          $this->expectException('moodle_exception');
5200          $prefs = core_message_external::get_user_message_preferences($otheruser->id);
5201      }
5202  
5203      /**
5204       * Comparison function for sorting contacts.
5205       *
5206       * @param array $a
5207       * @param array $b
5208       * @return bool
5209       */
5210      protected static function sort_contacts($a, $b) {
5211          return $a['userid'] > $b['userid'];
5212      }
5213  
5214      /**
5215       * Comparison function for sorting contacts.
5216       *
5217       * @param array $a
5218       * @param array $b
5219       * @return bool
5220       */
5221      protected static function sort_contacts_id($a, $b) {
5222          return $a['id'] > $b['id'];
5223      }
5224  
5225      /**
5226       * Test verifying that conversations can be marked as favourite conversations.
5227       */
5228      public function test_set_favourite_conversations_basic() {
5229          $this->resetAfterTest();
5230  
5231          $user1 = self::getDataGenerator()->create_user();
5232          $user2 = self::getDataGenerator()->create_user();
5233          $user3 = self::getDataGenerator()->create_user();
5234          $user4 = self::getDataGenerator()->create_user();
5235  
5236          $this->setUser($user1);
5237  
5238          // Now, create some conversations.
5239          $time = time();
5240          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5241          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5242          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5243  
5244          $this->send_message($user1, $user3, 'Booyah');
5245          $this->send_message($user3, $user1, 'Whaaat?');
5246          $this->send_message($user1, $user3, 'Nothing.');
5247  
5248          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
5249          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
5250  
5251          // Favourite 2 conversations as user 1.
5252          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5253          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
5254          $result = core_message_external::set_favourite_conversations($user1->id, [$conversation1, $conversation2]);
5255  
5256          // We need to execute the return values cleaning process to simulate the web service server.
5257          $result = external_api::clean_returnvalue(core_message_external::set_favourite_conversations_returns(), $result);
5258          $this->assertCount(0, $result);
5259      }
5260  
5261      /**
5262       * Test confirming that a user can't favourite a conversation on behalf of another user.
5263       */
5264      public function test_set_favourite_conversations_another_users_conversation() {
5265          $this->resetAfterTest();
5266  
5267          $user1 = self::getDataGenerator()->create_user();
5268          $user2 = self::getDataGenerator()->create_user();
5269          $user3 = self::getDataGenerator()->create_user();
5270  
5271          $this->setUser($user3);
5272  
5273          // Now, create some conversations.
5274          $time = time();
5275          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5276          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5277          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5278  
5279          $this->send_message($user1, $user3, 'Booyah');
5280          $this->send_message($user3, $user1, 'Whaaat?');
5281          $this->send_message($user1, $user3, 'Nothing.');
5282  
5283          // Try to favourite conversation 1 for user 2, as user3.
5284          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5285          $this->expectException(\moodle_exception::class);
5286          $result = core_message_external::set_favourite_conversations($user2->id, [$conversation1]);
5287      }
5288  
5289      /**
5290       * Test confirming that a user can't mark a conversation as their own favourite if it's a conversation they're not a member of.
5291       */
5292      public function test_set_favourite_conversations_non_member() {
5293          $this->resetAfterTest();
5294  
5295          $user1 = self::getDataGenerator()->create_user();
5296          $user2 = self::getDataGenerator()->create_user();
5297          $user3 = self::getDataGenerator()->create_user();
5298  
5299          $this->setUser($user3);
5300  
5301          // Now, create some conversations.
5302          $time = time();
5303          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5304          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5305          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5306  
5307          $this->send_message($user1, $user3, 'Booyah');
5308          $this->send_message($user3, $user1, 'Whaaat?');
5309          $this->send_message($user1, $user3, 'Nothing.');
5310  
5311          // Try to favourite conversation 1 as user 3.
5312          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5313          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
5314          $this->expectException(\moodle_exception::class);
5315          $result = core_message_external::set_favourite_conversations($user3->id, [$conversation1]);
5316      }
5317  
5318      /**
5319       * Test confirming that a user can't favourite a non-existent conversation.
5320       */
5321      public function test_set_favourite_conversations_non_existent_conversation() {
5322          $this->resetAfterTest();
5323  
5324          $user1 = self::getDataGenerator()->create_user();
5325          $this->setUser($user1);
5326  
5327          // Try to favourite a non-existent conversation.
5328          $this->expectException(\moodle_exception::class);
5329          $result = core_message_external::set_favourite_conversations($user1->id, [0]);
5330      }
5331  
5332      /**
5333       * Test confirming that a user can unset a favourite conversation, or list of favourite conversations.
5334       */
5335      public function test_unset_favourite_conversations_basic() {
5336          $this->resetAfterTest();
5337  
5338          $user1 = self::getDataGenerator()->create_user();
5339          $user2 = self::getDataGenerator()->create_user();
5340          $user3 = self::getDataGenerator()->create_user();
5341          $user4 = self::getDataGenerator()->create_user();
5342  
5343          $this->setUser($user1);
5344  
5345          // Now, create some conversations.
5346          $time = time();
5347          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5348          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5349          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5350  
5351          $this->send_message($user1, $user3, 'Booyah');
5352          $this->send_message($user3, $user1, 'Whaaat?');
5353          $this->send_message($user1, $user3, 'Nothing.');
5354  
5355          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
5356          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
5357  
5358          // Favourite 2 conversations as user 1.
5359          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5360          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
5361          \core_message\api::set_favourite_conversation($conversation1, $user1->id);
5362          \core_message\api::set_favourite_conversation($conversation2, $user1->id);
5363          // Consider first conversations is self-conversation.
5364          $this->assertCount(3, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
5365  
5366          // Unset favourite self-conversation.
5367          $selfconversation = \core_message\api::get_self_conversation($user1->id);
5368          $result = core_message_external::unset_favourite_conversations($user1->id, [$selfconversation->id]);
5369          $this->assertCount(2, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
5370  
5371          // Now, using the web service, unset the favourite conversations.
5372          $result = core_message_external::unset_favourite_conversations($user1->id, [$conversation1, $conversation2]);
5373  
5374          // We need to execute the return values cleaning process to simulate the web service server.
5375          $result = external_api::clean_returnvalue(core_message_external::unset_favourite_conversations_returns(), $result);
5376          $this->assertCount(0, $result);
5377      }
5378  
5379      /**
5380       * Test confirming that a user can't unfavourite a conversation for another user.
5381       */
5382      public function test_unset_favourite_conversations_another_users_conversation() {
5383          $this->resetAfterTest();
5384  
5385          $user1 = self::getDataGenerator()->create_user();
5386          $user2 = self::getDataGenerator()->create_user();
5387          $user3 = self::getDataGenerator()->create_user();
5388  
5389          $this->setUser($user3);
5390  
5391          // Now, create some conversations.
5392          $time = time();
5393          $this->send_message($user1, $user2, 'Yo!', 0, $time);
5394          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
5395          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
5396  
5397          $this->send_message($user1, $user3, 'Booyah');
5398          $this->send_message($user3, $user1, 'Whaaat?');
5399          $this->send_message($user1, $user3, 'Nothing.');
5400  
5401          // Favourite conversation 1 for user1. The current user ($USER) isn't checked for this action.
5402          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5403          \core_message\api::set_favourite_conversation($conversation1, $user1->id);
5404          // Consider first conversations is self-conversation.
5405          $this->assertCount(2, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
5406  
5407          // Try to unfavourite conversation 1 for user 2, as user3.
5408          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
5409          $this->expectException(\moodle_exception::class);
5410          $result = core_message_external::unset_favourite_conversations($user2->id, [$conversation1]);
5411      }
5412  
5413      /**
5414       * Test confirming that a user can't unfavourite a non-existent conversation.
5415       */
5416      public function test_unset_favourite_conversations_non_existent_conversation() {
5417          $this->resetAfterTest();
5418  
5419          $user1 = self::getDataGenerator()->create_user();
5420          $this->setUser($user1);
5421  
5422          // Try to unfavourite a non-existent conversation.
5423          $this->expectException(\moodle_exception::class);
5424          $result = core_message_external::unset_favourite_conversations($user1->id, [0]);
5425      }
5426  
5427      /**
5428       * Helper to seed the database with initial state.
5429       */
5430      protected function create_conversation_test_data() {
5431          // Create some users.
5432          $user1 = self::getDataGenerator()->create_user();
5433          $user2 = self::getDataGenerator()->create_user();
5434          $user3 = self::getDataGenerator()->create_user();
5435          $user4 = self::getDataGenerator()->create_user();
5436  
5437          $time = 1;
5438  
5439          // Create some conversations. We want:
5440          // 1) At least one of each type (group, individual) of which user1 IS a member and DID send the most recent message.
5441          // 2) At least one of each type (group, individual) of which user1 IS a member and DID NOT send the most recent message.
5442          // 3) At least one of each type (group, individual) of which user1 IS NOT a member.
5443          // 4) At least two group conversation having 0 messages, of which user1 IS a member (To confirm conversationid ordering).
5444          // 5) At least one group conversation having 0 messages, of which user1 IS NOT a member.
5445  
5446          // Individual conversation, user1 is a member, last message from other user.
5447          $ic1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5448              [$user1->id, $user2->id]);
5449          testhelper::send_fake_message_to_conversation($user1, $ic1->id, 'Message 1', $time);
5450          testhelper::send_fake_message_to_conversation($user2, $ic1->id, 'Message 2', $time + 1);
5451  
5452          // Individual conversation, user1 is a member, last message from user1.
5453          $ic2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5454              [$user1->id, $user3->id]);
5455          testhelper::send_fake_message_to_conversation($user3, $ic2->id, 'Message 3', $time + 2);
5456          testhelper::send_fake_message_to_conversation($user1, $ic2->id, 'Message 4', $time + 3);
5457  
5458          // Individual conversation, user1 is not a member.
5459          $ic3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5460              [$user2->id, $user3->id]);
5461          testhelper::send_fake_message_to_conversation($user2, $ic3->id, 'Message 5', $time + 4);
5462          testhelper::send_fake_message_to_conversation($user3, $ic3->id, 'Message 6', $time + 5);
5463  
5464          // Group conversation, user1 is not a member.
5465          $gc1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5466              [$user2->id, $user3->id, $user4->id], 'Project discussions');
5467          testhelper::send_fake_message_to_conversation($user2, $gc1->id, 'Message 7', $time + 6);
5468          testhelper::send_fake_message_to_conversation($user4, $gc1->id, 'Message 8', $time + 7);
5469  
5470          // Group conversation, user1 is a member, last message from another user.
5471          $gc2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5472              [$user1->id, $user3->id, $user4->id], 'Group chat');
5473          testhelper::send_fake_message_to_conversation($user1, $gc2->id, 'Message 9', $time + 8);
5474          testhelper::send_fake_message_to_conversation($user3, $gc2->id, 'Message 10', $time + 9);
5475          testhelper::send_fake_message_to_conversation($user4, $gc2->id, 'Message 11', $time + 10);
5476  
5477          // Group conversation, user1 is a member, last message from user1.
5478          $gc3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5479              [$user1->id, $user2->id, $user3->id, $user4->id], 'Group chat again!');
5480          testhelper::send_fake_message_to_conversation($user4, $gc3->id, 'Message 12', $time + 11);
5481          testhelper::send_fake_message_to_conversation($user3, $gc3->id, 'Message 13', $time + 12);
5482          testhelper::send_fake_message_to_conversation($user1, $gc3->id, 'Message 14', $time + 13);
5483  
5484          // Empty group conversations (x2), user1 is a member.
5485          $gc4 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5486              [$user1->id, $user2->id, $user3->id], 'Empty group');
5487          $gc5 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5488              [$user1->id, $user2->id, $user4->id], 'Another empty group');
5489  
5490          // Empty group conversation, user1 is NOT a member.
5491          $gc6 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5492              [$user2->id, $user3->id, $user4->id], 'Empty group 3');
5493  
5494          return [$user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, $gc1, $gc2, $gc3, $gc4, $gc5, $gc6];
5495      }
5496  
5497      /**
5498       * Test confirming the basic use of get_conversations, with no limits, nor type or favourite restrictions.
5499       */
5500      public function test_get_conversations_no_restrictions() {
5501          $this->resetAfterTest(true);
5502  
5503          // Get a bunch of conversations, some group, some individual and in different states.
5504          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
5505              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
5506  
5507          // The user making the request.
5508          $this->setUser($user1);
5509  
5510          // Get all conversations for user1.
5511          $result = core_message_external::get_conversations($user1->id);
5512          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5513          $conversations = $result['conversations'];
5514  
5515          $selfconversation = \core_message\api::get_self_conversation($user1->id);
5516  
5517          // Verify there are 7 conversations: 2 individual, 2 group with message, 2 group without messages
5518          // and a self-conversation.
5519          // The conversations with the most recent messages should be listed first, followed by the most newly created
5520          // conversations without messages.
5521          $this->assertCount(7, $conversations);
5522          $this->assertEquals($gc3->id, $conversations[0]['id']);
5523          $this->assertEquals($gc2->id, $conversations[1]['id']);
5524          $this->assertEquals($ic2->id, $conversations[2]['id']);
5525          $this->assertEquals($ic1->id, $conversations[3]['id']);
5526          $this->assertEquals($gc5->id, $conversations[4]['id']);
5527          $this->assertEquals($gc4->id, $conversations[5]['id']);
5528          $this->assertEquals($selfconversation->id, $conversations[6]['id']);
5529  
5530          foreach ($conversations as $conv) {
5531              $this->assertArrayHasKey('id', $conv);
5532              $this->assertArrayHasKey('name', $conv);
5533              $this->assertArrayHasKey('subname', $conv);
5534              $this->assertArrayHasKey('imageurl', $conv);
5535              $this->assertArrayHasKey('type', $conv);
5536              $this->assertArrayHasKey('membercount', $conv);
5537              $this->assertArrayHasKey('isfavourite', $conv);
5538              $this->assertArrayHasKey('isread', $conv);
5539              $this->assertArrayHasKey('unreadcount', $conv);
5540              $this->assertArrayHasKey('members', $conv);
5541              foreach ($conv['members'] as $member) {
5542                  $this->assertArrayHasKey('id', $member);
5543                  $this->assertArrayHasKey('fullname', $member);
5544                  $this->assertArrayHasKey('profileimageurl', $member);
5545                  $this->assertArrayHasKey('profileimageurlsmall', $member);
5546                  $this->assertArrayHasKey('isonline', $member);
5547                  $this->assertArrayHasKey('showonlinestatus', $member);
5548                  $this->assertArrayHasKey('isblocked', $member);
5549                  $this->assertArrayHasKey('iscontact', $member);
5550                  $this->assertArrayHasKey('isdeleted', $member);
5551                  $this->assertArrayHasKey('canmessage', $member);
5552                  $this->assertArrayHasKey('requirescontact', $member);
5553                  $this->assertArrayHasKey('contactrequests', $member);
5554              }
5555              $this->assertArrayHasKey('messages', $conv);
5556              foreach ($conv['messages'] as $message) {
5557                  $this->assertArrayHasKey('id', $message);
5558                  $this->assertArrayHasKey('useridfrom', $message);
5559                  $this->assertArrayHasKey('text', $message);
5560                  $this->assertArrayHasKey('timecreated', $message);
5561              }
5562          }
5563      }
5564  
5565      /**
5566       * Test verifying that html format messages are supported, and that message_format_message_text() is being called appropriately.
5567       */
5568      public function test_get_conversations_message_format() {
5569          $this->resetAfterTest();
5570  
5571          global $DB;
5572          // Create some users.
5573          $user1 = self::getDataGenerator()->create_user();
5574          $user2 = self::getDataGenerator()->create_user();
5575  
5576          // Create conversation.
5577          $conversation = \core_message\api::create_conversation(
5578              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5579              [$user1->id, $user2->id]
5580          );
5581  
5582          // Send some messages back and forth.
5583          $time = 1;
5584          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 1);
5585          $mid = testhelper::send_fake_message_to_conversation($user1, $conversation->id, '<a href="#">A link</a>', $time + 2);
5586          $message = $DB->get_record('messages', ['id' => $mid]);
5587  
5588          // The user in scope.
5589          $this->setUser($user1);
5590  
5591          // Verify the format of the html message.
5592          $expectedmessagetext = message_format_message_text($message);
5593          $result = core_message_external::get_conversations($user1->id);
5594          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5595          $conversations = $result['conversations'];
5596          $messages = $conversations[0]['messages'];
5597          $this->assertEquals($expectedmessagetext, $messages[0]['text']);
5598      }
5599  
5600      /**
5601       * Tests retrieving conversations with a limit and offset to ensure pagination works correctly.
5602       */
5603      public function test_get_conversations_limit_offset() {
5604          $this->resetAfterTest(true);
5605  
5606          // Get a bunch of conversations, some group, some individual and in different states.
5607          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
5608              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
5609  
5610          // The user making the request.
5611          $this->setUser($user1);
5612  
5613          // Get all conversations for user1.
5614          $result = core_message_external::get_conversations($user1->id, 0, 1);
5615          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5616          $conversations = $result['conversations'];
5617  
5618          // Verify the first conversation.
5619          $this->assertCount(1, $conversations);
5620          $conversation = array_shift($conversations);
5621          $this->assertEquals($gc3->id, $conversation['id']);
5622  
5623          // Verify the next conversation.
5624          $result = core_message_external::get_conversations($user1->id, 1, 1);
5625          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5626          $conversations = $result['conversations'];
5627          $this->assertCount(1, $conversations);
5628          $this->assertEquals($gc2->id, $conversations[0]['id']);
5629  
5630          // Verify the next conversation.
5631          $result = core_message_external::get_conversations($user1->id, 2, 1);
5632          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5633          $conversations = $result['conversations'];
5634          $this->assertCount(1, $conversations);
5635          $this->assertEquals($ic2->id, $conversations[0]['id']);
5636  
5637          // Skip one and get both empty conversations.
5638          $result = core_message_external::get_conversations($user1->id, 4, 2);
5639          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5640          $conversations = $result['conversations'];
5641          $this->assertCount(2, $conversations);
5642          $this->assertEquals($gc5->id, $conversations[0]['id']);
5643          $this->assertEmpty($conversations[0]['messages']);
5644          $this->assertEquals($gc4->id, $conversations[1]['id']);
5645          $this->assertEmpty($conversations[1]['messages']);
5646  
5647          // Ask for an offset that doesn't exist and verify no conversations are returned.
5648          $conversations = \core_message\api::get_conversations($user1->id, 10, 1);
5649          $this->assertCount(0, $conversations);
5650      }
5651  
5652      /**
5653       * Test verifying the type filtering behaviour of the get_conversations external method.
5654       */
5655      public function test_get_conversations_type_filter() {
5656          $this->resetAfterTest(true);
5657  
5658          // Get a bunch of conversations, some group, some individual and in different states.
5659          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
5660              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
5661  
5662          // The user making the request.
5663          $this->setUser($user1);
5664  
5665          // Verify we can ask for only individual conversations.
5666          $result = core_message_external::get_conversations($user1->id, 0, 20,
5667              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL);
5668          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5669          $conversations = $result['conversations'];
5670          $this->assertCount(2, $conversations);
5671  
5672          // Verify we can ask for only group conversations.
5673          $result = core_message_external::get_conversations($user1->id, 0, 20,
5674              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP);
5675          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5676          $conversations = $result['conversations'];
5677          $this->assertCount(4, $conversations);
5678  
5679          // Verify an exception is thrown if an unrecognized type is specified.
5680          $this->expectException(\moodle_exception::class);
5681          core_message_external::get_conversations($user1->id, 0, 20, 0);
5682      }
5683  
5684      /**
5685       * Tests retrieving conversations when a 'self' conversation exists.
5686       */
5687      public function test_get_conversations_self_conversations() {
5688          global $DB;
5689          $this->resetAfterTest();
5690  
5691          // Create a conversation between one user and themself.
5692          $user1 = self::getDataGenerator()->create_user();
5693          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
5694              [$user1->id]);
5695          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Test message to self!');
5696  
5697          // Verify we are in a 'self' conversation state.
5698          $members = $DB->get_records('message_conversation_members', ['conversationid' => $conversation->id]);
5699          $this->assertCount(1, $members);
5700          $member = array_pop($members);
5701          $this->assertEquals($user1->id, $member->userid);
5702  
5703          // Verify this conversation is returned by the method.
5704          $this->setUser($user1);
5705          $result = core_message_external::get_conversations($user1->id, 0, 20);
5706          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5707          $conversations = $result['conversations'];
5708          $this->assertCount(1, $conversations);
5709      }
5710  
5711      /**
5712       * Tests retrieving conversations when a conversation contains a deleted user.
5713       */
5714      public function test_get_conversations_deleted_user() {
5715          $this->resetAfterTest(true);
5716  
5717          // Get a bunch of conversations, some group, some individual and in different states.
5718          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
5719              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
5720  
5721          // The user making the request.
5722          $this->setUser($user1);
5723  
5724          $selfconversation = \core_message\api::get_self_conversation($user1->id);
5725  
5726          // Delete the second user and retrieve the conversations.
5727          // We should have 6 still, as conversations with soft-deleted users are still returned.
5728          // Group conversations are also present, albeit with less members.
5729          delete_user($user2);
5730          $result = core_message_external::get_conversations($user1->id);
5731          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5732          $conversations = $result['conversations'];
5733          $this->assertCount(7, $conversations);
5734          $this->assertEquals($gc3->id, $conversations[0]['id']);
5735          $this->assertcount(1, $conversations[0]['members']);
5736          $this->assertEquals($gc2->id, $conversations[1]['id']);
5737          $this->assertcount(1, $conversations[1]['members']);
5738          $this->assertEquals($ic2->id, $conversations[2]['id']);
5739          $this->assertEquals($ic1->id, $conversations[3]['id']);
5740          $this->assertEquals($gc5->id, $conversations[4]['id']);
5741          $this->assertEquals($gc4->id, $conversations[5]['id']);
5742          $this->assertEquals($selfconversation->id, $conversations[6]['id']);
5743  
5744          // Delete a user from a group conversation where that user had sent the most recent message.
5745          // This user will still be present in the members array, as will the message in the messages array.
5746          delete_user($user4);
5747          $result = core_message_external::get_conversations($user1->id);
5748          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5749          $conversations = $result['conversations'];
5750          $this->assertCount(7, $conversations);
5751          $this->assertEquals($gc2->id, $conversations[1]['id']);
5752          $this->assertcount(1, $conversations[1]['members']);
5753          $this->assertEquals($user4->id, $conversations[1]['members'][0]['id']);
5754          $this->assertcount(1, $conversations[1]['messages']);
5755          $this->assertEquals($user4->id, $conversations[1]['messages'][0]['useridfrom']);
5756  
5757          // Delete the third user and retrieve the conversations.
5758          // We should have 7 still (including self-conversation), as conversations with soft-deleted users are still returned.
5759          // Group conversations are also present, albeit with less members.
5760          delete_user($user3);
5761          $result = core_message_external::get_conversations($user1->id);
5762          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5763          $conversations = $result['conversations'];
5764          $this->assertCount(7, $conversations);
5765          $this->assertEquals($gc3->id, $conversations[0]['id']);
5766          $this->assertcount(1, $conversations[0]['members']);
5767          $this->assertEquals($gc2->id, $conversations[1]['id']);
5768          $this->assertcount(1, $conversations[1]['members']);
5769          $this->assertEquals($ic2->id, $conversations[2]['id']);
5770          $this->assertEquals($ic1->id, $conversations[3]['id']);
5771          $this->assertEquals($gc5->id, $conversations[4]['id']);
5772          $this->assertEquals($gc4->id, $conversations[5]['id']);
5773          $this->assertEquals($selfconversation->id, $conversations[6]['id']);
5774      }
5775  
5776      /**
5777       * Tests retrieving conversations when a conversation contains a deleted from the database user.
5778       */
5779      public function test_get_conversations_deleted_user_from_database() {
5780          global $DB;
5781  
5782          $this->resetAfterTest();
5783  
5784          $user1 = self::getDataGenerator()->create_user();
5785          $user2 = self::getDataGenerator()->create_user();
5786          $user3 = self::getDataGenerator()->create_user();
5787  
5788          $conversation1 = \core_message\api::create_conversation(
5789              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5790              [
5791                  $user1->id,
5792                  $user2->id
5793              ],
5794              'Individual conversation 1'
5795          );
5796  
5797          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'A');
5798          testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'B');
5799          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'C');
5800  
5801          $conversation2 = \core_message\api::create_conversation(
5802              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5803              [
5804                  $user1->id,
5805                  $user3->id
5806              ],
5807              'Individual conversation 2'
5808          );
5809  
5810          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'A');
5811          testhelper::send_fake_message_to_conversation($user3, $conversation2->id, 'B');
5812          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'C');
5813  
5814          $this->setUser($user1);
5815  
5816          // Delete the second user (from DB as well as this could happen in the past).
5817          delete_user($user2);
5818          $DB->delete_records('user', ['id' => $user2->id]);
5819          $result = core_message_external::get_conversations($user1->id, 0, 20, 1, false);
5820          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5821  
5822          $conversation = $result['conversations'];
5823  
5824          $this->assertCount(1, $conversation);
5825  
5826          $conversation = reset($conversation);
5827  
5828          $this->assertEquals($conversation2->id, $conversation['id']);
5829      }
5830  
5831      /**
5832       * Test verifying the behaviour of get_conversations() when fetching favourite conversations.
5833       */
5834      public function test_get_conversations_favourite_conversations() {
5835          $this->resetAfterTest(true);
5836  
5837          // Get a bunch of conversations, some group, some individual and in different states.
5838          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
5839              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
5840  
5841          // The user making the request.
5842          $this->setUser($user1);
5843  
5844          // Unset favourite self-conversation.
5845          $selfconversation = \core_message\api::get_self_conversation($user1->id);
5846          \core_message\api::unset_favourite_conversation($selfconversation->id, $user1->id);
5847  
5848          // Try to get ONLY favourite conversations, when no favourites exist.
5849          $result = core_message_external::get_conversations($user1->id, 0, 20, null, true);
5850          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5851          $conversations = $result['conversations'];
5852          $this->assertEquals([], $conversations);
5853  
5854          // Try to get NO favourite conversations, when no favourites exist.
5855          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
5856          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5857          $conversations = $result['conversations'];
5858          // Consider first conversations is self-conversation.
5859          $this->assertCount(7, $conversations);
5860  
5861          // Mark a few conversations as favourites.
5862          \core_message\api::set_favourite_conversation($ic1->id, $user1->id);
5863          \core_message\api::set_favourite_conversation($gc2->id, $user1->id);
5864          \core_message\api::set_favourite_conversation($gc5->id, $user1->id);
5865  
5866          // Get the conversations, first with no restrictions, confirming the favourite status of the conversations.
5867          $result = core_message_external::get_conversations($user1->id);
5868          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5869          $conversations = $result['conversations'];
5870          $this->assertCount(7, $conversations);
5871          foreach ($conversations as $conv) {
5872              if (in_array($conv['id'], [$ic1->id, $gc2->id, $gc5->id])) {
5873                  $this->assertTrue($conv['isfavourite']);
5874              }
5875          }
5876  
5877          // Now, get ONLY favourite conversations.
5878          $result = core_message_external::get_conversations($user1->id, 0, 20, null, true);
5879          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5880          $conversations = $result['conversations'];
5881          $this->assertCount(3, $conversations);
5882          foreach ($conversations as $conv) {
5883              $this->assertTrue($conv['isfavourite']);
5884          }
5885  
5886          // Now, try ONLY favourites of type 'group'.
5887          $conversations = \core_message\api::get_conversations($user1->id, 0, 20,
5888              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, true);
5889          $this->assertCount(2, $conversations);
5890          foreach ($conversations as $conv) {
5891              $this->assertTrue($conv->isfavourite);
5892          }
5893  
5894          // And NO favourite conversations.
5895          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
5896          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5897          $conversations = $result['conversations'];
5898          $this->assertCount(4, $conversations);
5899          foreach ($conversations as $conv) {
5900              $this->assertFalse($conv['isfavourite']);
5901          }
5902      }
5903  
5904      /**
5905       * Test verifying that group linked conversations are returned and contain a subname matching the course name.
5906       */
5907      public function test_get_conversations_group_linked() {
5908          $this->resetAfterTest();
5909          global $CFG, $DB;
5910  
5911          // Create some users.
5912          $user1 = self::getDataGenerator()->create_user();
5913          $user2 = self::getDataGenerator()->create_user();
5914          $user3 = self::getDataGenerator()->create_user();
5915  
5916          $course1 = $this->getDataGenerator()->create_course();
5917  
5918          // Create a group with a linked conversation.
5919          $this->setAdminUser();
5920          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
5921          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
5922          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
5923          $group1 = $this->getDataGenerator()->create_group([
5924              'courseid' => $course1->id,
5925              'enablemessaging' => 1,
5926              'picturepath' => $CFG->dirroot . '/lib/tests/fixtures/gd-logo.png'
5927          ]);
5928  
5929          // Add users to group1.
5930          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
5931          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
5932  
5933          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
5934          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5935          $conversations = $result['conversations'];
5936  
5937          $this->assertEquals(2, $conversations[0]['membercount']);
5938          $this->assertEquals($course1->shortname, $conversations[0]['subname']);
5939          $groupimageurl = get_group_picture_url($group1, $group1->courseid, true);
5940          $this->assertEquals($groupimageurl, $conversations[0]['imageurl']);
5941  
5942          // Now, disable the conversation linked to the group and verify it's no longer returned.
5943          $DB->set_field('message_conversations', 'enabled', 0, ['id' => $conversations[0]['id']]);
5944          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
5945          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5946          $conversations = $result['conversations'];
5947          $this->assertCount(0, $conversations);
5948      }
5949  
5950      /**
5951       * Test that group conversations containing MathJax don't break the WebService.
5952       */
5953      public function test_get_conversations_group_with_mathjax() {
5954          $this->resetAfterTest(true);
5955          $this->setAdminUser();
5956  
5957          // Enable MathJax filter in content and headings.
5958          $this->configure_filters([
5959              ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true],
5960          ]);
5961  
5962          // Create some users, a course and a group with a linked conversation.
5963          $user1 = self::getDataGenerator()->create_user();
5964          $user2 = self::getDataGenerator()->create_user();
5965  
5966          $coursename = 'Course $$(a+b)=2$$';
5967          $groupname = 'Group $$(a+b)=2$$';
5968          $course1 = $this->getDataGenerator()->create_course(['shortname' => $coursename]);
5969  
5970          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
5971          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
5972          $group1 = $this->getDataGenerator()->create_group([
5973              'name' => $groupname,
5974              'courseid' => $course1->id,
5975              'enablemessaging' => 1,
5976          ]);
5977  
5978          // Add users to group1.
5979          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
5980          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
5981  
5982          // Call the WebService.
5983          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
5984          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
5985          $conversations = $result['conversations'];
5986  
5987          // Format original data.
5988          $coursecontext = \context_course::instance($course1->id);
5989          $coursename = external_format_string($coursename, $coursecontext->id);
5990          $groupname = external_format_string($groupname, $coursecontext->id);
5991  
5992          $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $conversations[0]['name']);
5993          $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $conversations[0]['subname']);
5994          $this->assertEquals($groupname, $conversations[0]['name']);
5995          $this->assertEquals($coursename, $conversations[0]['subname']);
5996      }
5997  
5998      /**
5999       * Test verifying get_conversations when there are users in a group and/or individual conversation. The reason this
6000       * test is performed is because we do not need as much data for group conversations (saving DB calls), so we want
6001       * to confirm this happens.
6002       */
6003      public function test_get_conversations_user_in_group_and_individual_chat() {
6004          $this->resetAfterTest();
6005  
6006          $user1 = self::getDataGenerator()->create_user();
6007          $user2 = self::getDataGenerator()->create_user();
6008          $user3 = self::getDataGenerator()->create_user();
6009  
6010          $conversation = \core_message\api::create_conversation(
6011              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6012              [
6013                  $user1->id,
6014                  $user2->id
6015              ],
6016              'Individual conversation'
6017          );
6018  
6019          testhelper::send_fake_message_to_conversation($user1, $conversation->id);
6020  
6021          $conversation = \core_message\api::create_conversation(
6022              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
6023              [
6024                  $user1->id,
6025                  $user2->id,
6026              ],
6027              'Group conversation'
6028          );
6029  
6030          testhelper::send_fake_message_to_conversation($user1, $conversation->id);
6031  
6032          \core_message\api::create_contact_request($user1->id, $user2->id);
6033          \core_message\api::create_contact_request($user1->id, $user3->id);
6034  
6035          $this->setUser($user2);
6036          $result = core_message_external::get_conversations($user2->id);
6037          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
6038          $conversations = $result['conversations'];
6039  
6040          $groupconversation = array_shift($conversations);
6041          $individualconversation = array_shift($conversations);
6042  
6043          $this->assertEquals('Group conversation', $groupconversation['name']);
6044          $this->assertEquals('Individual conversation', $individualconversation['name']);
6045  
6046          $this->assertCount(1, $groupconversation['members']);
6047          $this->assertCount(1, $individualconversation['members']);
6048  
6049          $groupmember = reset($groupconversation['members']);
6050          $this->assertNull($groupmember['requirescontact']);
6051          $this->assertNull($groupmember['canmessage']);
6052          $this->assertEmpty($groupmember['contactrequests']);
6053  
6054          $individualmember = reset($individualconversation['members']);
6055          $this->assertNotNull($individualmember['requirescontact']);
6056          $this->assertNotNull($individualmember['canmessage']);
6057          $this->assertNotEmpty($individualmember['contactrequests']);
6058      }
6059  
6060      /**
6061       * Test verifying get_conversations identifies if a conversation is muted or not.
6062       */
6063      public function test_get_conversations_some_muted() {
6064          $this->resetAfterTest();
6065  
6066          // Create some users.
6067          $user1 = self::getDataGenerator()->create_user();
6068          $user2 = self::getDataGenerator()->create_user();
6069          $user3 = self::getDataGenerator()->create_user();
6070  
6071          $conversation1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6072              [$user1->id, $user2->id]);
6073          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 1');
6074          testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'Message 2');
6075          \core_message\api::mute_conversation($user1->id, $conversation1->id);
6076  
6077          $conversation2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6078              [$user1->id, $user3->id]);
6079          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Message 1');
6080          testhelper::send_fake_message_to_conversation($user2, $conversation2->id, 'Message 2');
6081  
6082          $conversation3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
6083              [$user1->id, $user2->id]);
6084          \core_message\api::mute_conversation($user1->id, $conversation3->id);
6085  
6086          $conversation4 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
6087              [$user1->id, $user3->id]);
6088  
6089          $this->setUser($user1);
6090          $result = core_message_external::get_conversations($user1->id);
6091          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
6092          $conversations = $result['conversations'];
6093  
6094          usort($conversations, function($first, $second){
6095              return $first['id'] > $second['id'];
6096          });
6097  
6098          $selfconversation = array_shift($conversations);
6099          $conv1 = array_shift($conversations);
6100          $conv2 = array_shift($conversations);
6101          $conv3 = array_shift($conversations);
6102          $conv4 = array_shift($conversations);
6103  
6104          $this->assertTrue($conv1['ismuted']);
6105          $this->assertFalse($conv2['ismuted']);
6106          $this->assertTrue($conv3['ismuted']);
6107          $this->assertFalse($conv4['ismuted']);
6108      }
6109  
6110      /**
6111       * Test returning members in a conversation with no contact requests.
6112       */
6113      public function test_get_conversation_members_messaging_disabled() {
6114          global $CFG;
6115  
6116          $this->resetAfterTest();
6117  
6118          $CFG->messaging = 0;
6119  
6120          $this->expectException('moodle_exception');
6121          core_message_external::get_conversation_members(1, 2);
6122      }
6123  
6124      /**
6125       * Test returning members in a conversation with no contact requests.
6126       */
6127      public function test_get_conversation_members_wrong_user() {
6128          $this->resetAfterTest();
6129  
6130          $user1 = self::getDataGenerator()->create_user();
6131          $user2 = self::getDataGenerator()->create_user();
6132  
6133          $this->setUser($user2);
6134  
6135          $this->expectException('moodle_exception');
6136          core_message_external::get_conversation_members($user1->id, 2);
6137      }
6138  
6139      /**
6140       * Test returning members in a conversation with no contact requests.
6141       */
6142      public function test_get_conversation_members() {
6143          $this->resetAfterTest();
6144  
6145          $lastaccess = new stdClass();
6146          $lastaccess->lastaccess = time();
6147  
6148          $user1 = self::getDataGenerator()->create_user($lastaccess);
6149          $user2 = self::getDataGenerator()->create_user();
6150          $user3 = self::getDataGenerator()->create_user();
6151  
6152          // This user will not be in the conversation, but a contact request will exist for them.
6153          $user4 = self::getDataGenerator()->create_user();
6154  
6155          // Add some contact requests.
6156          \core_message\api::create_contact_request($user1->id, $user3->id);
6157          \core_message\api::create_contact_request($user1->id, $user4->id);
6158          \core_message\api::create_contact_request($user2->id, $user3->id);
6159  
6160          // User 1 and 2 are already contacts.
6161          \core_message\api::add_contact($user1->id, $user2->id);
6162  
6163          // User 1 has blocked user 3.
6164          \core_message\api::block_user($user1->id, $user3->id);
6165  
6166          $conversation = \core_message\api::create_conversation(
6167              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
6168              [
6169                  $user1->id,
6170                  $user2->id,
6171                  $user3->id
6172              ]
6173          );
6174          $conversationid = $conversation->id;
6175  
6176          $this->setAdminUser();
6177  
6178          $members = core_message_external::get_conversation_members($user1->id, $conversationid, false);
6179          external_api::clean_returnvalue(core_message_external::get_conversation_members_returns(), $members);
6180  
6181          // Sort them by id.
6182          ksort($members);
6183          $this->assertCount(3, $members);
6184          $member1 = array_shift($members);
6185          $member2 = array_shift($members);
6186          $member3 = array_shift($members);
6187  
6188          // Confirm the standard fields are OK.
6189          $this->assertEquals($user1->id, $member1->id);
6190          $this->assertEquals(fullname($user1), $member1->fullname);
6191          $this->assertEquals(true, $member1->isonline);
6192          $this->assertEquals(true, $member1->showonlinestatus);
6193          $this->assertEquals(false, $member1->iscontact);
6194          $this->assertEquals(false, $member1->isblocked);
6195          $this->assertObjectHasAttribute('contactrequests', $member1);
6196          $this->assertEmpty($member1->contactrequests);
6197  
6198          $this->assertEquals($user2->id, $member2->id);
6199          $this->assertEquals(fullname($user2), $member2->fullname);
6200          $this->assertEquals(false, $member2->isonline);
6201          $this->assertEquals(true, $member2->showonlinestatus);
6202          $this->assertEquals(true, $member2->iscontact);
6203          $this->assertEquals(false, $member2->isblocked);
6204          $this->assertObjectHasAttribute('contactrequests', $member2);
6205          $this->assertEmpty($member2->contactrequests);
6206  
6207          $this->assertEquals($user3->id, $member3->id);
6208          $this->assertEquals(fullname($user3), $member3->fullname);
6209          $this->assertEquals(false, $member3->isonline);
6210          $this->assertEquals(true, $member3->showonlinestatus);
6211          $this->assertEquals(false, $member3->iscontact);
6212          $this->assertEquals(true, $member3->isblocked);
6213          $this->assertObjectHasAttribute('contactrequests', $member3);
6214          $this->assertEmpty($member3->contactrequests);
6215      }
6216  
6217      /**
6218       * Test returning members in a conversation with contact requests.
6219       */
6220      public function test_get_conversation_members_with_contact_requests() {
6221          $this->resetAfterTest();
6222  
6223          $lastaccess = new stdClass();
6224          $lastaccess->lastaccess = time();
6225  
6226          $user1 = self::getDataGenerator()->create_user($lastaccess);
6227          $user2 = self::getDataGenerator()->create_user();
6228          $user3 = self::getDataGenerator()->create_user();
6229  
6230          // This user will not be in the conversation, but a contact request will exist for them.
6231          $user4 = self::getDataGenerator()->create_user();
6232  
6233          // Add some contact requests.
6234          \core_message\api::create_contact_request($user1->id, $user2->id);
6235          \core_message\api::create_contact_request($user1->id, $user3->id);
6236          \core_message\api::create_contact_request($user1->id, $user4->id);
6237          \core_message\api::create_contact_request($user2->id, $user3->id);
6238  
6239          // User 1 and 2 are already contacts.
6240          \core_message\api::add_contact($user1->id, $user2->id);
6241          // User 1 has blocked user 3.
6242          \core_message\api::block_user($user1->id, $user3->id);
6243  
6244          $conversation = \core_message\api::create_conversation(
6245              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
6246              [
6247                  $user1->id,
6248                  $user2->id,
6249                  $user3->id
6250              ]
6251          );
6252          $conversationid = $conversation->id;
6253  
6254          $this->setAdminUser();
6255  
6256          $members = core_message_external::get_conversation_members($user1->id, $conversationid, true);
6257          external_api::clean_returnvalue(core_message_external::get_conversation_members_returns(), $members);
6258  
6259          // Sort them by id.
6260          ksort($members);
6261          $this->assertCount(3, $members);
6262          $member1 = array_shift($members);
6263          $member2 = array_shift($members);
6264          $member3 = array_shift($members);
6265  
6266          // Confirm the standard fields are OK.
6267          $this->assertEquals($user1->id, $member1->id);
6268          $this->assertEquals(fullname($user1), $member1->fullname);
6269          $this->assertEquals(true, $member1->isonline);
6270          $this->assertEquals(true, $member1->showonlinestatus);
6271          $this->assertEquals(false, $member1->iscontact);
6272          $this->assertEquals(false, $member1->isblocked);
6273          $this->assertCount(2, $member1->contactrequests);
6274  
6275          $this->assertEquals($user2->id, $member2->id);
6276          $this->assertEquals(fullname($user2), $member2->fullname);
6277          $this->assertEquals(false, $member2->isonline);
6278          $this->assertEquals(true, $member2->showonlinestatus);
6279          $this->assertEquals(true, $member2->iscontact);
6280          $this->assertEquals(false, $member2->isblocked);
6281          $this->assertCount(1, $member2->contactrequests);
6282  
6283          $this->assertEquals($user3->id, $member3->id);
6284          $this->assertEquals(fullname($user3), $member3->fullname);
6285          $this->assertEquals(false, $member3->isonline);
6286          $this->assertEquals(true, $member3->showonlinestatus);
6287          $this->assertEquals(false, $member3->iscontact);
6288          $this->assertEquals(true, $member3->isblocked);
6289          $this->assertCount(1, $member3->contactrequests);
6290  
6291          // Confirm the contact requests are OK.
6292          $request1 = array_shift($member1->contactrequests);
6293          $request2 = array_shift($member1->contactrequests);
6294  
6295          $this->assertEquals($user1->id, $request1->userid);
6296          $this->assertEquals($user2->id, $request1->requesteduserid);
6297  
6298          $this->assertEquals($user1->id, $request2->userid);
6299          $this->assertEquals($user3->id, $request2->requesteduserid);
6300  
6301          $request1 = array_shift($member2->contactrequests);
6302  
6303          $this->assertEquals($user1->id, $request1->userid);
6304          $this->assertEquals($user2->id, $request1->requesteduserid);
6305  
6306          $request1 = array_shift($member3->contactrequests);
6307  
6308          $this->assertEquals($user1->id, $request1->userid);
6309          $this->assertEquals($user3->id, $request1->requesteduserid);
6310      }
6311  
6312      /**
6313       * Test returning members in a conversation when you are not a member.
6314       */
6315      public function test_get_conversation_members_not_a_member() {
6316          $this->resetAfterTest();
6317  
6318          $user1 = self::getDataGenerator()->create_user();
6319          $user2 = self::getDataGenerator()->create_user();
6320  
6321          // This user will not be in the conversation.
6322          $user3 = self::getDataGenerator()->create_user();
6323  
6324          $conversation = \core_message\api::create_conversation(
6325              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
6326              [
6327                  $user1->id,
6328                  $user2->id,
6329              ]
6330          );
6331          $conversationid = $conversation->id;
6332  
6333          $this->setUser($user3);
6334  
6335          $this->expectException('moodle_exception');
6336          core_message_external::get_conversation_members($user3->id, $conversationid);
6337      }
6338  
6339      /**
6340       * Test verifying multiple messages can be sent to an individual conversation.
6341       */
6342      public function test_send_messages_to_conversation_individual() {
6343          $this->resetAfterTest(true);
6344  
6345          // Get a bunch of conversations, some group, some individual and in different states.
6346          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
6347              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
6348  
6349          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
6350          $course1 = $this->getDataGenerator()->create_course();
6351          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
6352          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
6353          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
6354          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
6355  
6356          // The user making the request.
6357          $this->setUser($user1);
6358  
6359          // Try to send a message as user1 to a conversation user1 is a a part of.
6360          $messages = [
6361              [
6362                  'text' => 'a message from user 1',
6363                  'textformat' => FORMAT_MOODLE
6364              ],
6365              [
6366                  'text' => 'another message from user 1',
6367                  'textformat' => FORMAT_MOODLE
6368              ],
6369          ];
6370          // Redirect messages.
6371          // This marks messages as read, but we can still observe and verify the number of conversation recipients,
6372          // based on the message_viewed events generated as part of marking the message as read for each user.
6373          $this->preventResetByRollback();
6374          $sink = $this->redirectMessages();
6375          $writtenmessages = core_message_external::send_messages_to_conversation($ic1->id, $messages);
6376  
6377          external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages);
6378  
6379          $this->assertCount(2, $writtenmessages);
6380          $this->assertObjectHasAttribute('id', $writtenmessages[0]);
6381          $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom);
6382          $this->assertEquals('<p>a message from user 1</p>', $writtenmessages[0]->text);
6383          $this->assertNotEmpty($writtenmessages[0]->timecreated);
6384  
6385          $this->assertObjectHasAttribute('id', $writtenmessages[1]);
6386          $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom);
6387          $this->assertEquals('<p>another message from user 1</p>', $writtenmessages[1]->text);
6388          $this->assertNotEmpty($writtenmessages[1]->timecreated);
6389      }
6390  
6391      /**
6392       * Test verifying multiple messages can be sent to an group conversation.
6393       */
6394      public function test_send_messages_to_conversation_group() {
6395          $this->resetAfterTest(true);
6396  
6397          // Get a bunch of conversations, some group, some individual and in different states.
6398          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
6399              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
6400  
6401          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
6402          $course1 = $this->getDataGenerator()->create_course();
6403          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
6404          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
6405          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
6406          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
6407  
6408          // The user making the request.
6409          $this->setUser($user1);
6410  
6411          // Try to send a message as user1 to a conversation user1 is a a part of.
6412          $messages = [
6413              [
6414                  'text' => 'a message from user 1 to group conv',
6415                  'textformat' => FORMAT_MOODLE
6416              ],
6417              [
6418                  'text' => 'another message from user 1 to group conv',
6419                  'textformat' => FORMAT_MOODLE
6420              ],
6421          ];
6422          // Redirect messages.
6423          // This marks messages as read, but we can still observe and verify the number of conversation recipients,
6424          // based on the message_viewed events generated as part of marking the message as read for each user.
6425          $this->preventResetByRollback();
6426          $sink = $this->redirectMessages();
6427          $writtenmessages = core_message_external::send_messages_to_conversation($gc2->id, $messages);
6428  
6429          external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages);
6430  
6431          $this->assertCount(2, $writtenmessages);
6432          $this->assertObjectHasAttribute('id', $writtenmessages[0]);
6433          $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom);
6434          $this->assertEquals('<p>a message from user 1 to group conv</p>', $writtenmessages[0]->text);
6435          $this->assertNotEmpty($writtenmessages[0]->timecreated);
6436  
6437          $this->assertObjectHasAttribute('id', $writtenmessages[1]);
6438          $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom);
6439          $this->assertEquals('<p>another message from user 1 to group conv</p>', $writtenmessages[1]->text);
6440          $this->assertNotEmpty($writtenmessages[1]->timecreated);
6441      }
6442  
6443      /**
6444       * Test verifying multiple messages can not be sent to a non existent conversation.
6445       */
6446      public function test_send_messages_to_conversation_non_existent_conversation() {
6447          $this->resetAfterTest(true);
6448  
6449          // Get a bunch of conversations, some group, some individual and in different states.
6450          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
6451              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
6452  
6453          // The user making the request.
6454          $this->setUser($user1);
6455  
6456          // Try to send a message as user1 to a conversation user1 is a a part of.
6457          $messages = [
6458              [
6459                  'text' => 'a message from user 1',
6460                  'textformat' => FORMAT_MOODLE
6461              ],
6462              [
6463                  'text' => 'another message from user 1',
6464                  'textformat' => FORMAT_MOODLE
6465              ],
6466          ];
6467          $this->expectException(\moodle_exception::class);
6468          $writtenmessages = core_message_external::send_messages_to_conversation(0, $messages);
6469      }
6470  
6471      /**
6472       * Test verifying multiple messages can not be sent to a conversation by a non-member.
6473       */
6474      public function test_send_messages_to_conversation_non_member() {
6475          $this->resetAfterTest(true);
6476  
6477          // Get a bunch of conversations, some group, some individual and in different states.
6478          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
6479              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
6480  
6481          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
6482          $course1 = $this->getDataGenerator()->create_course();
6483          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
6484          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
6485          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
6486          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
6487  
6488          // The user making the request. This user is not a member of group conversation 1 (gc1).
6489          $this->setUser($user1);
6490  
6491          // Try to send a message as user1 to a conversation user1 is a a part of.
6492          $messages = [
6493              [
6494                  'text' => 'a message from user 1 to group conv',
6495                  'textformat' => FORMAT_MOODLE
6496              ],
6497              [
6498                  'text' => 'another message from user 1 to group conv',
6499                  'textformat' => FORMAT_MOODLE
6500              ],
6501          ];
6502          $this->expectException(\moodle_exception::class);
6503          $writtenmessages = core_message_external::send_messages_to_conversation($gc1->id, $messages);
6504      }
6505  
6506      /**
6507       * Test verifying a to long message can not be sent to a conversation.
6508       */
6509      public function test_send_messages_to_conversation_long_text() {
6510          $this->resetAfterTest(true);
6511  
6512          // Get a bunch of conversations, some group, some individual and in different states.
6513          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
6514              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
6515  
6516          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
6517          $course1 = $this->getDataGenerator()->create_course();
6518          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
6519          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
6520          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
6521          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
6522  
6523          // The user making the request.
6524          $this->setUser($user1);
6525  
6526          // Try to send a message as user1 to a conversation user1 is a a part of.
6527          $messages = [
6528              [
6529                  'text' => str_repeat("M", \core_message\api::MESSAGE_MAX_LENGTH + 100),
6530                  'textformat' => FORMAT_MOODLE
6531              ],
6532          ];
6533  
6534          $this->expectException(moodle_exception::class);
6535          $writtenmessages = core_message_external::send_messages_to_conversation($gc2->id, $messages);
6536      }
6537  
6538      /**
6539       * Test getting a conversation that doesn't exist.
6540       */
6541      public function test_get_conversation_no_conversation() {
6542          $this->resetAfterTest();
6543  
6544          $user1 = self::getDataGenerator()->create_user();
6545          $user2 = self::getDataGenerator()->create_user();
6546  
6547          $name = 'lol conversation';
6548          $conversation = \core_message\api::create_conversation(
6549              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6550              [
6551                  $user1->id,
6552                  $user2->id,
6553              ],
6554              $name
6555          );
6556          $conversationid = $conversation->id;
6557  
6558          $this->setUser($user1);
6559  
6560          $this->expectException('moodle_exception');
6561          $conv = core_message_external::get_conversation($user1->id, $conversationid + 1);
6562          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6563      }
6564  
6565      /**
6566       * Test verifying that the correct favourite information is returned for a non-linked converastion at user context.
6567       */
6568      public function test_get_conversation_favourited() {
6569          $this->resetAfterTest();
6570  
6571          $user1 = self::getDataGenerator()->create_user();
6572          $user2 = self::getDataGenerator()->create_user();
6573  
6574          // Create a conversation between the 2 users.
6575          $conversation = \core_message\api::create_conversation(
6576              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6577              [
6578                  $user1->id,
6579                  $user2->id,
6580              ],
6581              'An individual conversation'
6582          );
6583  
6584          // Favourite the conversation as user 1 only.
6585          \core_message\api::set_favourite_conversation($conversation->id, $user1->id);
6586  
6587          // Get the conversation for user1 and confirm it's favourited.
6588          $this->setUser($user1);
6589          $conv = core_message_external::get_conversation($user1->id, $conversation->id);
6590          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6591          $this->assertTrue($conv['isfavourite']);
6592  
6593          // Get the conversation for user2 and confirm it's NOT favourited.
6594          $this->setUser($user2);
6595          $conv = core_message_external::get_conversation($user2->id, $conversation->id);
6596          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6597          $this->assertFalse($conv['isfavourite']);
6598      }
6599  
6600      /**
6601       * Test verifying that the correct favourite information is returned for a group-linked conversation at course context.
6602       */
6603      public function test_get_conversation_favourited_group_linked() {
6604          $this->resetAfterTest();
6605          global $DB;
6606  
6607          $user1 = self::getDataGenerator()->create_user();
6608          $user2 = self::getDataGenerator()->create_user();
6609          $user3 = self::getDataGenerator()->create_user();
6610  
6611          $course1 = $this->getDataGenerator()->create_course();
6612          $course1context = \context_course::instance($course1->id);
6613  
6614          // Create a group with a linked conversation and a valid image.
6615          $this->setAdminUser();
6616          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
6617          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
6618          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
6619          $group1 = $this->getDataGenerator()->create_group([
6620              'courseid' => $course1->id,
6621              'enablemessaging' => 1
6622          ]);
6623  
6624          // Add users to group1.
6625          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
6626          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
6627  
6628          // Verify that the conversation is a group linked conversation in the course context.
6629          $conversationrecord = $DB->get_record('message_conversations', ['component' => 'core_group', 'itemtype' => 'groups']);
6630          $this->assertEquals($course1context->id, $conversationrecord->contextid);
6631  
6632          // Favourite the conversation as user 1 only.
6633          \core_message\api::set_favourite_conversation($conversationrecord->id, $user1->id);
6634  
6635          // Get the conversation for user1 and confirm it's favourited.
6636          $this->setUser($user1);
6637          $conv = core_message_external::get_conversation($user1->id, $conversationrecord->id);
6638          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6639          $this->assertTrue($conv['isfavourite']);
6640  
6641          // Get the conversation for user2 and confirm it's NOT favourited.
6642          $this->setUser($user2);
6643          $conv = core_message_external::get_conversation($user2->id, $conversationrecord->id);
6644          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6645          $this->assertFalse($conv['isfavourite']);
6646      }
6647  
6648      /**
6649       * Test getting a conversation with no messages.
6650       */
6651      public function test_get_conversation_no_messages() {
6652          $this->resetAfterTest();
6653  
6654          $user1 = self::getDataGenerator()->create_user();
6655          $user2 = self::getDataGenerator()->create_user();
6656  
6657          $name = 'lol conversation';
6658          $conversation = \core_message\api::create_conversation(
6659              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6660              [
6661                  $user1->id,
6662                  $user2->id,
6663              ],
6664              $name
6665          );
6666          $conversationid = $conversation->id;
6667  
6668          $this->setUser($user1);
6669  
6670          $conv = core_message_external::get_conversation($user1->id, $conversationid);
6671          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6672  
6673          $conv = (array) $conv;
6674          $this->assertEquals($conversationid, $conv['id']);
6675          $this->assertEquals($name, $conv['name']);
6676          $this->assertArrayHasKey('subname', $conv);
6677          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conv['type']);
6678          $this->assertEquals(2, $conv['membercount']);
6679          $this->assertEquals(false, $conv['isfavourite']);
6680          $this->assertEquals(true, $conv['isread']);
6681          $this->assertEquals(0, $conv['unreadcount']);
6682          $this->assertCount(1, $conv['members']);
6683          foreach ($conv['members'] as $member) {
6684              $member = (array) $member;
6685              $this->assertArrayHasKey('id', $member);
6686              $this->assertArrayHasKey('fullname', $member);
6687              $this->assertArrayHasKey('profileimageurl', $member);
6688              $this->assertArrayHasKey('profileimageurlsmall', $member);
6689              $this->assertArrayHasKey('isonline', $member);
6690              $this->assertArrayHasKey('showonlinestatus', $member);
6691              $this->assertArrayHasKey('isblocked', $member);
6692              $this->assertArrayHasKey('iscontact', $member);
6693          }
6694          $this->assertEmpty($conv['messages']);
6695      }
6696  
6697      /**
6698       * Test getting a conversation with messages.
6699       */
6700      public function test_get_conversation_with_messages() {
6701          $this->resetAfterTest();
6702  
6703          $user1 = self::getDataGenerator()->create_user();
6704          $user2 = self::getDataGenerator()->create_user();
6705          $user3 = self::getDataGenerator()->create_user();
6706  
6707          // Some random conversation.
6708          $otherconversation = \core_message\api::create_conversation(
6709              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6710              [
6711                  $user1->id,
6712                  $user3->id,
6713              ]
6714          );
6715  
6716          $conversation = \core_message\api::create_conversation(
6717              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
6718              [
6719                  $user1->id,
6720                  $user2->id,
6721              ]
6722          );
6723          $conversationid = $conversation->id;
6724  
6725          $time = time();
6726          $message1id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'A', $time - 10);
6727          $message2id = testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'B', $time - 5);
6728          $message3id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'C', $time);
6729  
6730          // Add some messages to the other convo to make sure they aren't included.
6731          testhelper::send_fake_message_to_conversation($user1, $otherconversation->id, 'foo');
6732  
6733          $this->setUser($user1);
6734  
6735          // Test newest first.
6736          $conv = core_message_external::get_conversation(
6737              $user1->id,
6738              $conversationid,
6739              false,
6740              false,
6741              0,
6742              0,
6743              0,
6744              0,
6745              true
6746          );
6747          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6748  
6749          $conv = (array) $conv;
6750          $this->assertEquals(false, $conv['isread']);
6751          $this->assertEquals(1, $conv['unreadcount']);
6752          $this->assertCount(3, $conv['messages']);
6753          $this->assertEquals($message3id, $conv['messages'][0]->id);
6754          $this->assertEquals($user1->id, $conv['messages'][0]->useridfrom);
6755          $this->assertEquals($message2id, $conv['messages'][1]->id);
6756          $this->assertEquals($user2->id, $conv['messages'][1]->useridfrom);
6757          $this->assertEquals($message1id, $conv['messages'][2]->id);
6758          $this->assertEquals($user1->id, $conv['messages'][2]->useridfrom);
6759  
6760          // Test newest last.
6761          $conv = core_message_external::get_conversation(
6762              $user1->id,
6763              $conversationid,
6764              false,
6765              false,
6766              0,
6767              0,
6768              0,
6769              0,
6770              false
6771          );
6772          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6773  
6774          $conv = (array) $conv;
6775          $this->assertCount(3, $conv['messages']);
6776          $this->assertEquals($message3id, $conv['messages'][2]->id);
6777          $this->assertEquals($user1->id, $conv['messages'][2]->useridfrom);
6778          $this->assertEquals($message2id, $conv['messages'][1]->id);
6779          $this->assertEquals($user2->id, $conv['messages'][1]->useridfrom);
6780          $this->assertEquals($message1id, $conv['messages'][0]->id);
6781          $this->assertEquals($user1->id, $conv['messages'][0]->useridfrom);
6782  
6783          // Test message offest and limit.
6784          $conv = core_message_external::get_conversation(
6785              $user1->id,
6786              $conversationid,
6787              false,
6788              false,
6789              0,
6790              0,
6791              1,
6792              1,
6793              true
6794          );
6795          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
6796  
6797          $conv = (array) $conv;
6798          $this->assertCount(1, $conv['messages']);
6799          $this->assertEquals($message2id, $conv['messages'][0]->id);
6800          $this->assertEquals($user2->id, $conv['messages'][0]->useridfrom);
6801      }
6802  
6803      /**
6804       * Data provider for test_get_conversation_counts().
6805       */
6806      public function get_conversation_counts_test_cases() {
6807          $typeindividual = \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL;
6808          $typegroup = \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP;
6809          $typeself = \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF;
6810          list($user1, $user2, $user3, $user4, $user5, $user6, $user7, $user8) = [0, 1, 2, 3, 4, 5, 6, 7];
6811          $conversations = [
6812              [
6813                  'type' => $typeindividual,
6814                  'users' => [$user1, $user2],
6815                  'messages' => [$user1, $user2, $user2],
6816                  'favourites' => [$user1],
6817                  'enabled' => null // Individual conversations cannot be disabled.
6818              ],
6819              [
6820                  'type' => $typeindividual,
6821                  'users' => [$user1, $user3],
6822                  'messages' => [$user1, $user3, $user1],
6823                  'favourites' => [],
6824                  'enabled' => null // Individual conversations cannot be disabled.
6825              ],
6826              [
6827                  'type' => $typegroup,
6828                  'users' => [$user1, $user2, $user3, $user4],
6829                  'messages' => [$user1, $user2, $user3, $user4],
6830                  'favourites' => [],
6831                  'enabled' => true
6832              ],
6833              [
6834                  'type' => $typegroup,
6835                  'users' => [$user2, $user3, $user4],
6836                  'messages' => [$user2, $user3, $user4],
6837                  'favourites' => [],
6838                  'enabled' => true
6839              ],
6840              [
6841                  'type' => $typegroup,
6842                  'users' => [$user6, $user7],
6843                  'messages' => [$user6, $user7, $user7],
6844                  'favourites' => [$user6],
6845                  'enabled' => false
6846              ],
6847              [
6848                  'type' => $typeself,
6849                  'users' => [$user8],
6850                  'messages' => [$user8],
6851                  'favourites' => [],
6852                  'enabled' => null // Individual conversations cannot be disabled.
6853              ],
6854          ];
6855  
6856          return [
6857              'No conversations' => [
6858                  'conversationConfigs' => $conversations,
6859                  'deletemessagesuser' => null,
6860                  'deletemessages' => [],
6861                  'arguments' => [$user5],
6862                  'expectedcounts' => ['favourites' => 1, 'types' => [
6863                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
6864                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
6865                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6866                  ]],
6867                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
6868                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
6869                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
6870                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6871                  ]],
6872                  'deletedusers' => []
6873              ],
6874              'No individual conversations, 2 group conversations' => [
6875                  'conversationConfigs' => $conversations,
6876                  'deletemessagesuser' => null,
6877                  'deletemessages' => [],
6878                  'arguments' => [$user4],
6879                  'expectedcounts' => ['favourites' => 1, 'types' => [
6880                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
6881                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
6882                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6883                  ]],
6884                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
6885                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
6886                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
6887                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6888                  ]],
6889                  'deletedusers' => []
6890              ],
6891              '2 individual conversations (one favourited), 1 group conversation' => [
6892                  'conversationConfigs' => $conversations,
6893                  'deletemessagesuser' => null,
6894                  'deletemessages' => [],
6895                  'arguments' => [$user1],
6896                  'expectedcounts' => ['favourites' => 2, 'types' => [
6897                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6898                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6899                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6900                  ]],
6901                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
6902                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6903                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6904                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6905                  ]],
6906                  'deletedusers' => []
6907              ],
6908              '1 individual conversation, 2 group conversations' => [
6909                  'conversationConfigs' => $conversations,
6910                  'deletemessagesuser' => null,
6911                  'deletemessages' => [],
6912                  'arguments' => [$user2],
6913                  'expectedcounts' => ['favourites' => 1, 'types' => [
6914                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6915                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
6916                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6917                  ]],
6918                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
6919                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6920                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
6921                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6922                  ]],
6923                  'deletedusers' => []
6924              ],
6925              '2 group conversations only' => [
6926                  'conversationConfigs' => $conversations,
6927                  'deletemessagesuser' => null,
6928                  'deletemessages' => [],
6929                  'arguments' => [$user4],
6930                  'expectedcounts' => ['favourites' => 1, 'types' => [
6931                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
6932                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
6933                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6934                  ]],
6935                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
6936                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
6937                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
6938                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6939                  ]],
6940                  'deletedusers' => []
6941              ],
6942              'All conversation types, delete a message from individual favourited, messages remaining' => [
6943                  'conversationConfigs' => $conversations,
6944                  'deletemessagesuser' => $user1,
6945                  'deletemessages' => [0],
6946                  'arguments' => [$user1],
6947                  'expectedcounts' => ['favourites' => 2, 'types' => [
6948                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6949                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6950                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6951                  ]],
6952                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
6953                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6954                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6955                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6956                  ]],
6957                  'deletedusers' => []
6958              ],
6959              'All conversation types, delete a message from individual non-favourited, messages remaining' => [
6960                  'conversationConfigs' => $conversations,
6961                  'deletemessagesuser' => $user1,
6962                  'deletemessages' => [3],
6963                  'arguments' => [$user1],
6964                  'expectedcounts' => ['favourites' => 2, 'types' => [
6965                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6966                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6967                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6968                  ]],
6969                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
6970                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6971                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6972                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6973                  ]],
6974                  'deletedusers' => []
6975              ],
6976              'All conversation types, delete all messages from individual favourited, no messages remaining' => [
6977                  'conversationConfigs' => $conversations,
6978                  'deletemessagesuser' => $user1,
6979                  'deletemessages' => [0, 1, 2],
6980                  'arguments' => [$user1],
6981                  'expectedcounts' => ['favourites' => 1, 'types' => [
6982                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6983                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6984                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6985                  ]],
6986                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
6987                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
6988                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
6989                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
6990                  ]],
6991                  'deletedusers' => []
6992              ],
6993              'All conversation types, delete all messages from individual non-favourited, no messages remaining' => [
6994                  'conversationConfigs' => $conversations,
6995                  'deletemessagesuser' => $user1,
6996                  'deletemessages' => [3, 4, 5],
6997                  'arguments' => [$user1],
6998                  'expectedcounts' => ['favourites' => 2, 'types' => [
6999                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7000                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7001                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7002                  ]],
7003                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
7004                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7005                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7006                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7007                  ]],
7008                  'deletedusers' => []
7009              ],
7010              'All conversation types, delete all messages from individual favourited, no messages remaining, different user' => [
7011                  'conversationConfigs' => $conversations,
7012                  'deletemessagesuser' => $user1,
7013                  'deletemessages' => [0, 1, 2],
7014                  'arguments' => [$user2],
7015                  'expectedcounts' => ['favourites' => 1, 'types' => [
7016                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7017                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
7018                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7019                  ]],
7020                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
7021                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7022                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
7023                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7024                  ]],
7025                  'deletedusers' => []
7026              ],
7027              'All conversation types, delete all messages from individual non-favourited, no messages remaining, different user' => [
7028                  'conversationConfigs' => $conversations,
7029                  'deletemessagesuser' => $user1,
7030                  'deletemessages' => [3, 4, 5],
7031                  'arguments' => [$user3],
7032                  'expectedcounts' => ['favourites' => 1, 'types' => [
7033                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7034                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
7035                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7036                  ]],
7037                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
7038                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7039                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
7040                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7041                  ]],
7042                  'deletedusers' => []
7043              ],
7044              'All conversation types, delete some messages from group non-favourited, messages remaining,' => [
7045                  'conversationConfigs' => $conversations,
7046                  'deletemessagesuser' => $user1,
7047                  'deletemessages' => [6, 7],
7048                  'arguments' => [$user1],
7049                  'expectedcounts' => ['favourites' => 2, 'types' => [
7050                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7051                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7052                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7053                  ]],
7054                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
7055                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7056                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7057                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7058                  ]],
7059                  'deletedusers' => []
7060              ],
7061              'All conversation types, delete all messages from group non-favourited, no messages remaining,' => [
7062                  'conversationConfigs' => $conversations,
7063                  'deletemessagesuser' => $user1,
7064                  'deletemessages' => [6, 7, 8, 9],
7065                  'arguments' => [$user1],
7066                  'expectedcounts' => ['favourites' => 2, 'types' => [
7067                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7068                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7069                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7070                  ]],
7071                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
7072                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7073                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7074                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7075                  ]],
7076                  'deletedusers' => []
7077              ],
7078              'All conversation types, another user soft deleted' => [
7079                  'conversationConfigs' => $conversations,
7080                  'deletemessagesuser' => null,
7081                  'deletemessages' => [],
7082                  'arguments' => [$user1],
7083                  'expectedcounts' => ['favourites' => 2, 'types' => [
7084                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7085                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7086                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7087                  ]],
7088                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
7089                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7090                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7091                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7092                  ]],
7093                  'deletedusers' => [$user2]
7094              ],
7095              'All conversation types, all group users soft deleted' => [
7096                  'conversationConfigs' => $conversations,
7097                  'deletemessagesuser' => null,
7098                  'deletemessages' => [],
7099                  'arguments' => [$user1],
7100                  'expectedcounts' => ['favourites' => 2, 'types' => [
7101                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7102                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7103                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7104                  ]],
7105                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
7106                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
7107                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
7108                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7109                  ]],
7110                  'deletedusers' => [$user2, $user3, $user4]
7111              ],
7112              'Group conversation which is disabled, favourited' => [
7113                  'conversationConfigs' => $conversations,
7114                  'deletemessagesuser' => null,
7115                  'deletemessages' => [],
7116                  'arguments' => [$user6],
7117                  'expectedcounts' => ['favourites' => 1, 'types' => [
7118                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7119                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7120                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7121                  ]],
7122                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
7123                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7124                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7125                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7126                  ]],
7127                  'deletedusers' => []
7128              ],
7129              'Group conversation which is disabled, non-favourited' => [
7130                  'conversationConfigs' => $conversations,
7131                  'deletemessagesuser' => null,
7132                  'deletemessages' => [],
7133                  'arguments' => [$user7],
7134                  'expectedcounts' => ['favourites' => 1, 'types' => [
7135                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7136                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7137                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7138                  ]],
7139                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
7140                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7141                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7142                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7143                  ]],
7144                  'deletedusers' => []
7145              ],
7146              'Conversation with self' => [
7147                  'conversationConfigs' => $conversations,
7148                  'deletemessagesuser' => null,
7149                  'deletemessages' => [],
7150                  'arguments' => [$user8],
7151                  'expectedcounts' => ['favourites' => 0, 'types' => [
7152                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7153                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7154                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 1
7155                  ]],
7156                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
7157                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
7158                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
7159                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
7160                  ]],
7161                  'deletedusers' => []
7162              ],
7163          ];
7164      }
7165  
7166      /**
7167       * Test the get_conversation_counts() function.
7168       *
7169       * @dataProvider get_conversation_counts_test_cases()
7170       * @param array $conversationconfigs Conversations to create
7171       * @param int $deletemessagesuser The user who is deleting the messages
7172       * @param array $deletemessages The list of messages to delete (by index)
7173       * @param array $arguments Arguments for the count conversations function
7174       * @param array $expectedcounts the expected conversation counts
7175       * @param array $expectedunreadcounts the expected unread conversation counts
7176       * @param array $deletedusers the array of users to soft delete.
7177       */
7178      public function test_get_conversation_counts(
7179          $conversationconfigs,
7180          $deletemessagesuser,
7181          $deletemessages,
7182          $arguments,
7183          $expectedcounts,
7184          $expectedunreadcounts,
7185          $deletedusers
7186      ) {
7187          $this->resetAfterTest();
7188          $generator = $this->getDataGenerator();
7189          $users = [
7190              $generator->create_user(),
7191              $generator->create_user(),
7192              $generator->create_user(),
7193              $generator->create_user(),
7194              $generator->create_user(),
7195              $generator->create_user(),
7196              $generator->create_user(),
7197              $generator->create_user()
7198          ];
7199  
7200          $deleteuser = !is_null($deletemessagesuser) ? $users[$deletemessagesuser] : null;
7201          $this->setUser($users[$arguments[0]]);
7202          $arguments[0] = $users[$arguments[0]]->id;
7203          $systemcontext = \context_system::instance();
7204          $conversations = [];
7205          $messageids = [];
7206  
7207          foreach ($conversationconfigs as $config) {
7208              $conversation = \core_message\api::create_conversation(
7209                  $config['type'],
7210                  array_map(function($userindex) use ($users) {
7211                      return $users[$userindex]->id;
7212                  }, $config['users']),
7213                  null,
7214                  ($config['enabled'] ?? true)
7215              );
7216  
7217              foreach ($config['messages'] as $userfromindex) {
7218                  $userfrom = $users[$userfromindex];
7219                  $messageids[] = testhelper::send_fake_message_to_conversation($userfrom, $conversation->id);
7220              }
7221  
7222              // Remove the self conversations created by the generator,
7223              // so we can choose to set that ourself and honour the original intention of the test.
7224              $userids = array_map(function($userindex) use ($users) {
7225                  return $users[$userindex]->id;
7226              }, $config['users']);
7227              foreach ($userids as $userid) {
7228                  if ($conversation->type == \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF) {
7229                      \core_message\api::unset_favourite_conversation($conversation->id, $userid);
7230                  }
7231              }
7232  
7233              foreach ($config['favourites'] as $userfromindex) {
7234                  $userfrom = $users[$userfromindex];
7235                  $usercontext = \context_user::instance($userfrom->id);
7236                  $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
7237                  $ufservice->create_favourite('core_message', 'message_conversations', $conversation->id, $systemcontext);
7238              }
7239  
7240              $conversations[] = $conversation;
7241          }
7242  
7243          foreach ($deletemessages as $messageindex) {
7244              \core_message\api::delete_message($deleteuser->id, $messageids[$messageindex]);
7245          }
7246  
7247          foreach ($deletedusers as $deleteduser) {
7248              delete_user($users[$deleteduser]);
7249          }
7250  
7251          $counts = core_message_external::get_conversation_counts(...$arguments);
7252          $counts = external_api::clean_returnvalue(core_message_external::get_conversation_counts_returns(), $counts);
7253  
7254          $this->assertEquals($expectedcounts['favourites'], $counts['favourites']);
7255          $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
7256              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]);
7257          $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP],
7258              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]);
7259          $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF],
7260              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]);
7261      }
7262  
7263      /**
7264       * Test the get_unread_conversation_counts() function.
7265       *
7266       * @dataProvider get_conversation_counts_test_cases
7267       * @param array $conversationconfigs Conversations to create
7268       * @param int $deletemessagesuser The user who is deleting the messages
7269       * @param array $deletemessages The list of messages to delete (by index)
7270       * @param array $arguments Arguments for the count conversations function
7271       * @param array $expectedcounts the expected conversation counts
7272       * @param array $expectedunreadcounts the expected unread conversation counts
7273       * @param array $deletedusers the list of users to soft-delete.
7274       */
7275      public function test_get_unread_conversation_counts(
7276          $conversationconfigs,
7277          $deletemessagesuser,
7278          $deletemessages,
7279          $arguments,
7280          $expectedcounts,
7281          $expectedunreadcounts,
7282          $deletedusers
7283      ) {
7284          $this->resetAfterTest();
7285          $generator = $this->getDataGenerator();
7286          $users = [
7287              $generator->create_user(),
7288              $generator->create_user(),
7289              $generator->create_user(),
7290              $generator->create_user(),
7291              $generator->create_user(),
7292              $generator->create_user(),
7293              $generator->create_user(),
7294              $generator->create_user()
7295          ];
7296  
7297          $deleteuser = !is_null($deletemessagesuser) ? $users[$deletemessagesuser] : null;
7298          $this->setUser($users[$arguments[0]]);
7299          $arguments[0] = $users[$arguments[0]]->id;
7300          $systemcontext = \context_system::instance();
7301          $conversations = [];
7302          $messageids = [];
7303  
7304          foreach ($conversationconfigs as $config) {
7305              $conversation = \core_message\api::create_conversation(
7306                  $config['type'],
7307                  array_map(function($userindex) use ($users) {
7308                      return $users[$userindex]->id;
7309                  }, $config['users']),
7310                  null,
7311                  ($config['enabled'] ?? true)
7312              );
7313  
7314              foreach ($config['messages'] as $userfromindex) {
7315                  $userfrom = $users[$userfromindex];
7316                  $messageids[] = testhelper::send_fake_message_to_conversation($userfrom, $conversation->id);
7317              }
7318  
7319              foreach ($config['favourites'] as $userfromindex) {
7320                  $userfrom = $users[$userfromindex];
7321                  $usercontext = \context_user::instance($userfrom->id);
7322                  $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
7323                  $ufservice->create_favourite('core_message', 'message_conversations', $conversation->id, $systemcontext);
7324              }
7325  
7326              $conversations[] = $conversation;
7327          }
7328  
7329          foreach ($deletemessages as $messageindex) {
7330              \core_message\api::delete_message($deleteuser->id, $messageids[$messageindex]);
7331          }
7332  
7333          foreach ($deletedusers as $deleteduser) {
7334              delete_user($users[$deleteduser]);
7335          }
7336  
7337          $counts = core_message_external::get_unread_conversation_counts(...$arguments);
7338          $counts = external_api::clean_returnvalue(core_message_external::get_unread_conversation_counts_returns(), $counts);
7339  
7340          $this->assertEquals($expectedunreadcounts['favourites'], $counts['favourites']);
7341          $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
7342              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]);
7343          $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP],
7344              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]);
7345          $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF],
7346              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]);
7347      }
7348  
7349      /**
7350       * Test delete_message for all users.
7351       */
7352      public function test_delete_message_for_all_users() {
7353          global $DB;
7354  
7355          $this->resetAfterTest(true);
7356  
7357          // Create fake data to test it.
7358          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
7359  
7360          // Send message as user1 to group conversation.
7361          $messageid1 = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
7362          $messageid2 = testhelper::send_fake_message_to_conversation($user2, $convgroup->id);
7363  
7364          // User1 deletes the first message for all users of group conversation.
7365          // First, we have to allow user1 (Teacher) can delete messages for all users.
7366          $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
7367          assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, context_system::instance());
7368  
7369          $this->setUser($user1);
7370  
7371          // Now, user1 deletes message for all users.
7372          $return = core_message_external::delete_message_for_all_users($messageid1, $user1->id);
7373          $return = external_api::clean_returnvalue(core_message_external::delete_message_for_all_users_returns(), $return);
7374          // Check if everything is ok.
7375          $this->assertEquals(array(), $return);
7376  
7377          // Check we have 3 records on message_user_actions with the mark MESSAGE_ACTION_DELETED.
7378          $muas = $DB->get_records('message_user_actions', array('messageid' => $messageid1), 'userid ASC');
7379          $this->assertCount(3, $muas);
7380          $mua1 = array_shift($muas);
7381          $mua2 = array_shift($muas);
7382          $mua3 = array_shift($muas);
7383  
7384          $this->assertEquals($user1->id, $mua1->userid);
7385          $this->assertEquals($messageid1, $mua1->messageid);
7386          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
7387          $this->assertEquals($user2->id, $mua2->userid);
7388          $this->assertEquals($messageid1, $mua2->messageid);
7389          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
7390          $this->assertEquals($user3->id, $mua3->userid);
7391          $this->assertEquals($messageid1, $mua3->messageid);
7392          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
7393      }
7394  
7395      /**
7396       * Test delete_message for all users with messaging disabled.
7397       */
7398      public function test_delete_message_for_all_users_messaging_disabled() {
7399          global $CFG;
7400  
7401          $this->resetAfterTest();
7402  
7403          // Create fake data to test it.
7404          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
7405  
7406          // Send message as user1 to group conversation.
7407          $messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
7408  
7409          $this->setUser($user1);
7410  
7411          // Disable messaging.
7412          $CFG->messaging = 0;
7413  
7414          // Ensure an exception is thrown.
7415          $this->expectException('moodle_exception');
7416          core_message_external::delete_message_for_all_users($messageid, $user1->id);
7417      }
7418  
7419      /**
7420       * Test delete_message for all users with no permission.
7421       */
7422      public function test_delete_message_for_all_users_no_permission() {
7423          $this->resetAfterTest();
7424  
7425          // Create fake data to test it.
7426          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
7427  
7428          // Send message as user1 to group conversation.
7429          $messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
7430  
7431          $this->setUser($user2);
7432  
7433          // Try as user2 to delete a message for all users without permission to do it.
7434          $this->expectException('moodle_exception');
7435          $this->expectExceptionMessage('You do not have permission to delete this message for everyone.');
7436          core_message_external::delete_message_for_all_users($messageid, $user2->id);
7437      }
7438  
7439      /**
7440       * Test delete_message for all users in a private conversation.
7441       */
7442      public function test_delete_message_for_all_users_private_conversation() {
7443          global $DB;
7444  
7445          $this->resetAfterTest();
7446  
7447          // Create fake data to test it.
7448          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
7449  
7450          // Send message as user1 to private conversation.
7451          $messageid = testhelper::send_fake_message_to_conversation($user1, $convindividual->id);
7452  
7453          // First, we have to allow user1 (Teacher) can delete messages for all users.
7454          $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
7455          assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, context_system::instance());
7456  
7457          $this->setUser($user1);
7458  
7459          // Try as user1 to delete a private message for all users on individual conversation.
7460          // User1 should not delete message for all users in a private conversations despite being a teacher.
7461          // Because is a teacher in a course and not in a system context.
7462          $this->expectException('moodle_exception');
7463          $this->expectExceptionMessage('You do not have permission to delete this message for everyone.');
7464          core_message_external::delete_message_for_all_users($messageid, $user1->id);
7465      }
7466  
7467      /**
7468       * Test retrieving conversation messages by providing a timefrom higher than last message timecreated. It should return no
7469       * messages but keep the return structure to not break when called from the ws.
7470       */
7471      public function test_get_conversation_messages_timefrom_higher_than_last_timecreated() {
7472          $this->resetAfterTest(true);
7473  
7474          // Create some users.
7475          $user1 = self::getDataGenerator()->create_user();
7476          $user2 = self::getDataGenerator()->create_user();
7477          $user3 = self::getDataGenerator()->create_user();
7478          $user4 = self::getDataGenerator()->create_user();
7479  
7480          // Create group conversation.
7481          $conversation = \core_message\api::create_conversation(
7482              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
7483              [$user1->id, $user2->id, $user3->id, $user4->id]
7484          );
7485  
7486          // The person asking for the messages for another user.
7487          $this->setUser($user1);
7488  
7489          // Send some messages back and forth.
7490          $time = 1;
7491          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
7492          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
7493          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
7494          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
7495  
7496          // Retrieve the messages.
7497          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id, 0, 0, '', $time + 5);
7498  
7499          // We need to execute the return values cleaning process to simulate the web service server.
7500          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(), $result);
7501  
7502          // Check the results are correct.
7503          $this->assertEquals($conversation->id, $result['id']);
7504  
7505          // Confirm the message data is correct.
7506          $messages = $result['messages'];
7507          $this->assertEquals(0, count($messages));
7508  
7509          // Confirm that members key is present.
7510          $this->assertArrayHasKey('members', $result);
7511      }
7512  
7513      /**
7514       * Helper to seed the database with initial state with data.
7515       */
7516      protected function create_delete_message_test_data() {
7517          // Create some users.
7518          $user1 = self::getDataGenerator()->create_user();
7519          $user2 = self::getDataGenerator()->create_user();
7520          $user3 = self::getDataGenerator()->create_user();
7521  
7522          // Create a course and enrol the users.
7523          $course = $this->getDataGenerator()->create_course();
7524          $coursecontext = context_course::instance($course->id);
7525          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'editingteacher');
7526          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
7527          $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student');
7528  
7529          // Create a group and added the users into.
7530          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
7531          groups_add_member($group1->id, $user1->id);
7532          groups_add_member($group1->id, $user2->id);
7533          groups_add_member($group1->id, $user3->id);
7534  
7535          // Create a group conversation linked with the course.
7536          $convgroup = \core_message\api::create_conversation(
7537              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
7538              [$user1->id, $user2->id, $user3->id],
7539              'Group test delete for everyone', \core_message\api::MESSAGE_CONVERSATION_ENABLED,
7540              'core_group',
7541              'groups',
7542              $group1->id,
7543              context_course::instance($course->id)->id
7544          );
7545  
7546          // Create and individual conversation.
7547          $convindividual = \core_message\api::create_conversation(
7548              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
7549              [$user1->id, $user2->id]
7550          );
7551  
7552          return [$user1, $user2, $user3, $convgroup, $convindividual];
7553      }
7554  }