Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core_message;
  18  
  19  use core_external\external_api;
  20  use core_message\tests\helper as testhelper;
  21  use core_message_external;
  22  use externallib_advanced_testcase;
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  global $CFG;
  27  
  28  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  29  require_once($CFG->dirroot . '/message/externallib.php');
  30  
  31  /**
  32   * External message functions unit tests
  33   *
  34   * @package    core_message
  35   * @category   external
  36   * @copyright  2012 Jerome Mouneyrac
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class externallib_test extends externallib_advanced_testcase {
  40  
  41      /**
  42       * Tests set up
  43       */
  44      protected function setUp(): void {
  45          global $CFG;
  46  
  47          require_once($CFG->dirroot . '/message/lib.php');
  48      }
  49  
  50      /**
  51       * Send a fake message.
  52       *
  53       * {@link message_send()} does not support transaction, this function will simulate a message
  54       * sent from a user to another. We should stop using it once {@link message_send()} will support
  55       * transactions. This is not clean at all, this is just used to add rows to the table.
  56       *
  57       * @param \stdClass $userfrom user object of the one sending the message.
  58       * @param \stdClass $userto user object of the one receiving the message.
  59       * @param string $message message to send.
  60       * @param int $notification is the message a notification.
  61       * @param int $time the time the message was sent
  62       */
  63      protected function send_message($userfrom, $userto, $message = 'Hello world!', $notification = 0, $time = 0) {
  64          global $DB;
  65  
  66          if (empty($time)) {
  67              $time = time();
  68          }
  69  
  70          if ($notification) {
  71              $record = new \stdClass();
  72              $record->useridfrom = $userfrom->id;
  73              $record->useridto = $userto->id;
  74              $record->subject = 'No subject';
  75              $record->fullmessage = $message;
  76              $record->smallmessage = $message;
  77              $record->timecreated = $time;
  78  
  79              return $DB->insert_record('notifications', $record);
  80          }
  81  
  82          if (!$conversationid = \core_message\api::get_conversation_between_users([$userfrom->id, $userto->id])) {
  83              $conversation = \core_message\api::create_conversation(
  84                  \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
  85                  [
  86                      $userfrom->id,
  87                      $userto->id
  88                  ]
  89              );
  90              $conversationid = $conversation->id;
  91          }
  92  
  93          // Ok, send the message.
  94          $record = new \stdClass();
  95          $record->useridfrom = $userfrom->id;
  96          $record->conversationid = $conversationid;
  97          $record->subject = 'No subject';
  98          $record->fullmessage = $message;
  99          $record->smallmessage = $message;
 100          $record->timecreated = $time;
 101  
 102          return $DB->insert_record('messages', $record);
 103      }
 104  
 105      /**
 106       * Test send_instant_messages.
 107       */
 108      public function test_send_instant_messages() {
 109          global $DB, $USER;
 110  
 111          $this->resetAfterTest();
 112  
 113          // Transactions used in tests, tell phpunit use alternative reset method.
 114          $this->preventResetByRollback();
 115  
 116          $user1 = self::getDataGenerator()->create_user();
 117          $user2 = self::getDataGenerator()->create_user();
 118  
 119          $this->setUser($user1);
 120  
 121          // Create test message data.
 122          $message1 = array();
 123          $message1['touserid'] = $user2->id;
 124          $message1['text'] = 'the message.';
 125          $message1['clientmsgid'] = 4;
 126          $messages = array($message1);
 127  
 128          $sentmessages = core_message_external::send_instant_messages($messages);
 129          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 130          $this->assertEquals(
 131              get_string('usercantbemessaged', 'message', fullname(\core_user::get_user($message1['touserid']))),
 132              array_pop($sentmessages)['errormessage']
 133          );
 134  
 135          // Add the user1 as a contact.
 136          \core_message\api::add_contact($user1->id, $user2->id);
 137  
 138          // Send message again. Now it should work properly.
 139          $sentmessages = core_message_external::send_instant_messages($messages);
 140          // We need to execute the return values cleaning process to simulate the web service server.
 141          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 142  
 143          $sentmessage = reset($sentmessages);
 144  
 145          $sql = "SELECT m.*, mcm.userid as useridto
 146                   FROM {messages} m
 147             INNER JOIN {message_conversations} mc
 148                     ON m.conversationid = mc.id
 149             INNER JOIN {message_conversation_members} mcm
 150                     ON mcm.conversationid = mc.id
 151                  WHERE mcm.userid != ?
 152                    AND m.id = ?";
 153          $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 154  
 155          // Confirm that the message was inserted correctly.
 156          $this->assertEquals($themessage->useridfrom, $user1->id);
 157          $this->assertEquals($themessage->useridto, $message1['touserid']);
 158          $this->assertEquals($themessage->smallmessage, $message1['text']);
 159          $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
 160      }
 161  
 162      /**
 163       * Test send_instant_messages with a message text longer than permitted.
 164       */
 165      public function test_send_instant_messages_long_text() {
 166          global $CFG;
 167  
 168          $this->resetAfterTest(true);
 169  
 170          // Transactions used in tests, tell phpunit use alternative reset method.
 171          $this->preventResetByRollback();
 172  
 173          $user1 = self::getDataGenerator()->create_user();
 174          $user2 = self::getDataGenerator()->create_user();
 175  
 176          $this->setUser($user1);
 177  
 178          // Create test message data.
 179          $message1 = [
 180              'touserid' => $user2->id,
 181              'text' => str_repeat("M", \core_message\api::MESSAGE_MAX_LENGTH + 100),
 182              'clientmsgid' => 4,
 183          ];
 184          $messages = [$message1];
 185  
 186          // Add the user1 as a contact.
 187          \core_message\api::add_contact($user1->id, $user2->id);
 188  
 189          $sentmessages = core_message_external::send_instant_messages($messages);
 190          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 191          $this->assertEquals(
 192              get_string('errormessagetoolong', 'message'),
 193              array_pop($sentmessages)['errormessage']
 194          );
 195      }
 196  
 197      /**
 198       * Test send_instant_messages to a user who has blocked you.
 199       */
 200      public function test_send_instant_messages_blocked_user() {
 201          global $DB;
 202  
 203          $this->resetAfterTest();
 204  
 205          // Transactions used in tests, tell phpunit use alternative reset method.
 206          $this->preventResetByRollback();
 207  
 208          $user1 = self::getDataGenerator()->create_user();
 209          $user2 = self::getDataGenerator()->create_user();
 210  
 211          $this->setUser($user1);
 212  
 213          \core_message\api::block_user($user2->id, $user1->id);
 214  
 215          // Create test message data.
 216          $message1 = array();
 217          $message1['touserid'] = $user2->id;
 218          $message1['text'] = 'the message.';
 219          $message1['clientmsgid'] = 4;
 220          $messages = array($message1);
 221  
 222          $sentmessages = core_message_external::send_instant_messages($messages);
 223          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 224  
 225          $sentmessage = reset($sentmessages);
 226  
 227          $this->assertEquals(get_string('usercantbemessaged', 'message', fullname($user2)), $sentmessage['errormessage']);
 228  
 229          $this->assertEquals(0, $DB->count_records('messages'));
 230      }
 231  
 232      /**
 233       * Test send_instant_messages when sending a message to a non-contact who has blocked non-contacts.
 234       */
 235      public function test_send_instant_messages_block_non_contacts() {
 236          global $DB;
 237  
 238          $this->resetAfterTest(true);
 239  
 240          // Transactions used in tests, tell phpunit use alternative reset method.
 241          $this->preventResetByRollback();
 242  
 243          $user1 = self::getDataGenerator()->create_user();
 244          $user2 = self::getDataGenerator()->create_user();
 245  
 246          $this->setUser($user1);
 247  
 248          // Set the user preference so user 2 does not accept messages from non-contacts.
 249          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, $user2);
 250  
 251          // Create test message data.
 252          $message1 = array();
 253          $message1['touserid'] = $user2->id;
 254          $message1['text'] = 'the message.';
 255          $message1['clientmsgid'] = 4;
 256          $messages = array($message1);
 257  
 258          $sentmessages = core_message_external::send_instant_messages($messages);
 259          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 260  
 261          $sentmessage = reset($sentmessages);
 262  
 263          $this->assertEquals(get_string('usercantbemessaged', 'message', fullname($user2)), $sentmessage['errormessage']);
 264  
 265          $this->assertEquals(0, $DB->count_records('messages'));
 266      }
 267  
 268      /**
 269       * Test send_instant_messages when sending a message to a contact who has blocked non-contacts.
 270       */
 271      public function test_send_instant_messages_block_non_contacts_but_am_contact() {
 272          global $DB, $USER;
 273  
 274          $this->resetAfterTest(true);
 275  
 276          // Transactions used in tests, tell phpunit use alternative reset method.
 277          $this->preventResetByRollback();
 278  
 279          $user1 = self::getDataGenerator()->create_user();
 280          $user2 = self::getDataGenerator()->create_user();
 281  
 282          $this->setUser($user1);
 283  
 284          // Set the user preference so user 2 does not accept messages from non-contacts.
 285          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, $user2);
 286  
 287          \core_message\api::add_contact($user1->id, $user2->id);
 288  
 289          // Create test message data.
 290          $message1 = array();
 291          $message1['touserid'] = $user2->id;
 292          $message1['text'] = 'the message.';
 293          $message1['clientmsgid'] = 4;
 294          $messages = array($message1);
 295  
 296          $sentmessages = core_message_external::send_instant_messages($messages);
 297          $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 298  
 299          $sentmessage = reset($sentmessages);
 300  
 301          $sql = "SELECT m.*, mcm.userid as useridto
 302                   FROM {messages} m
 303             INNER JOIN {message_conversations} mc
 304                     ON m.conversationid = mc.id
 305             INNER JOIN {message_conversation_members} mcm
 306                     ON mcm.conversationid = mc.id
 307                  WHERE mcm.userid != ?
 308                    AND m.id = ?";
 309          $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 310  
 311          // Confirm that the message was inserted correctly.
 312          $this->assertEquals($themessage->useridfrom, $user1->id);
 313          $this->assertEquals($themessage->useridto, $message1['touserid']);
 314          $this->assertEquals($themessage->smallmessage, $message1['text']);
 315          $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
 316      }
 317  
 318      /**
 319       * Test send_instant_messages with no capabilities
 320       */
 321      public function test_send_instant_messages_no_capability() {
 322          global $DB;
 323  
 324          $this->resetAfterTest(true);
 325  
 326          // Transactions used in tests, tell phpunit use alternative reset method.
 327          $this->preventResetByRollback();
 328  
 329          $user1 = self::getDataGenerator()->create_user();
 330          $user2 = self::getDataGenerator()->create_user();
 331  
 332          $this->setUser($user1);
 333  
 334          // Unset the required capabilities by the external function.
 335          $contextid = \context_system::instance()->id;
 336          $userrole = $DB->get_record('role', array('shortname' => 'user'));
 337          $this->unassignUserCapability('moodle/site:sendmessage', $contextid, $userrole->id);
 338  
 339          // Create test message data.
 340          $message1 = array();
 341          $message1['touserid'] = $user2->id;
 342          $message1['text'] = 'the message.';
 343          $message1['clientmsgid'] = 4;
 344          $messages = array($message1);
 345  
 346          $this->expectException('required_capability_exception');
 347          core_message_external::send_instant_messages($messages);
 348      }
 349  
 350      /**
 351       * Test send_instant_messages when messaging is disabled.
 352       */
 353      public function test_send_instant_messages_messaging_disabled() {
 354          global $CFG;
 355  
 356          $this->resetAfterTest(true);
 357  
 358          // Transactions used in tests, tell phpunit use alternative reset method.
 359          $this->preventResetByRollback();
 360  
 361          $user1 = self::getDataGenerator()->create_user();
 362          $user2 = self::getDataGenerator()->create_user();
 363  
 364          $this->setUser($user1);
 365  
 366          // Disable messaging.
 367          $CFG->messaging = 0;
 368  
 369          // Create test message data.
 370          $message1 = array();
 371          $message1['touserid'] = $user2->id;
 372          $message1['text'] = 'the message.';
 373          $message1['clientmsgid'] = 4;
 374          $messages = array($message1);
 375  
 376          $this->expectException('moodle_exception');
 377          core_message_external::send_instant_messages($messages);
 378      }
 379  
 380      /**
 381       * Test delete_contacts.
 382       */
 383      public function test_delete_contacts() {
 384          $this->resetAfterTest(true);
 385  
 386          $user1 = self::getDataGenerator()->create_user();
 387          $user2 = self::getDataGenerator()->create_user();
 388          $user3 = self::getDataGenerator()->create_user();
 389          $user4 = self::getDataGenerator()->create_user();
 390          $user5 = self::getDataGenerator()->create_user();
 391          $user6 = self::getDataGenerator()->create_user();
 392          $this->setUser($user1);
 393  
 394          \core_message\api::add_contact($user1->id, $user3->id);
 395          \core_message\api::add_contact($user1->id, $user4->id);
 396          \core_message\api::add_contact($user1->id, $user5->id);
 397          \core_message\api::add_contact($user1->id, $user6->id);
 398  
 399          // Removing a non-contact.
 400          $return = core_message_external::delete_contacts(array($user2->id));
 401          $this->assertNull($return);
 402  
 403          // Removing one contact.
 404          $return = core_message_external::delete_contacts(array($user3->id));
 405          $this->assertNull($return);
 406  
 407          // Removing multiple contacts.
 408          $return = core_message_external::delete_contacts(array($user4->id, $user5->id));
 409          $this->assertNull($return);
 410  
 411          // Removing contact from unexisting user.
 412          $return = core_message_external::delete_contacts(array(99999));
 413          $this->assertNull($return);
 414  
 415          // Removing mixed valid and invalid data.
 416          $return = core_message_external::delete_contacts(array($user6->id, 99999));
 417          $this->assertNull($return);
 418  
 419          // Try to delete a contact of another user contact list, should throw an exception.
 420          // All assertions must be added before this point.
 421          $this->expectException('required_capability_exception');
 422          core_message_external::delete_contacts(array($user2->id), $user3->id);
 423      }
 424  
 425      /**
 426       * Test getting contact requests.
 427       */
 428      public function test_get_contact_requests() {
 429          global $PAGE;
 430  
 431          $this->resetAfterTest();
 432  
 433          $user1 = self::getDataGenerator()->create_user();
 434          $user2 = self::getDataGenerator()->create_user();
 435          $user3 = self::getDataGenerator()->create_user();
 436  
 437          $this->setUser($user1);
 438  
 439          // Block one user, their request should not show up.
 440          \core_message\api::block_user($user1->id, $user3->id);
 441  
 442          \core_message\api::create_contact_request($user2->id, $user1->id);
 443          \core_message\api::create_contact_request($user3->id, $user1->id);
 444  
 445          $requests = core_message_external::get_contact_requests($user1->id);
 446          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 447  
 448          $this->assertCount(1, $requests);
 449  
 450          $request = reset($requests);
 451          $userpicture = new \user_picture($user2);
 452          $profileimageurl = $userpicture->get_url($PAGE)->out(false);
 453  
 454          $this->assertEquals($user2->id, $request['id']);
 455          $this->assertEquals(fullname($user2), $request['fullname']);
 456          $this->assertArrayHasKey('profileimageurl', $request);
 457          $this->assertArrayHasKey('profileimageurlsmall', $request);
 458          $this->assertArrayHasKey('isonline', $request);
 459          $this->assertArrayHasKey('showonlinestatus', $request);
 460          $this->assertArrayHasKey('isblocked', $request);
 461          $this->assertArrayHasKey('iscontact', $request);
 462      }
 463  
 464      /**
 465       * Test the get_contact_requests() function when the user has blocked the sender of the request.
 466       */
 467      public function test_get_contact_requests_blocked_sender() {
 468          $this->resetAfterTest();
 469          $user1 = self::getDataGenerator()->create_user();
 470          $user2 = self::getDataGenerator()->create_user();
 471  
 472          // User1 blocks User2.
 473          \core_message\api::block_user($user1->id, $user2->id);
 474  
 475          // User2 tries to add User1 as a contact.
 476          \core_message\api::create_contact_request($user2->id, $user1->id);
 477  
 478          // Verify we don't see the contact request from the blocked user User2 in the requests for User1.
 479          $this->setUser($user1);
 480          $requests = core_message_external::get_contact_requests($user1->id);
 481          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 482  
 483          $this->assertCount(0, $requests);
 484      }
 485  
 486      /**
 487       * Test getting contact requests when there are none.
 488       */
 489      public function test_get_contact_requests_no_requests() {
 490          $this->resetAfterTest();
 491  
 492          $user1 = self::getDataGenerator()->create_user();
 493  
 494          $this->setUser($user1);
 495  
 496          $requests = core_message_external::get_contact_requests($user1->id);
 497          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 498  
 499          $this->assertEmpty($requests);
 500      }
 501  
 502      /**
 503       * Test getting contact requests with limits.
 504       */
 505      public function test_get_contact_requests_with_limits() {
 506          $this->resetAfterTest();
 507  
 508          $user1 = self::getDataGenerator()->create_user();
 509          $user2 = self::getDataGenerator()->create_user();
 510          $user3 = self::getDataGenerator()->create_user();
 511  
 512          $this->setUser($user1);
 513  
 514          \core_message\api::create_contact_request($user2->id, $user1->id);
 515          \core_message\api::create_contact_request($user3->id, $user1->id);
 516  
 517          $requests = core_message_external::get_contact_requests($user1->id, 0, 1);
 518          $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
 519  
 520          $this->assertCount(1, $requests);
 521      }
 522  
 523      /**
 524       * Test getting contact requests with messaging disabled.
 525       */
 526      public function test_get_contact_requests_messaging_disabled() {
 527          global $CFG;
 528  
 529          $this->resetAfterTest();
 530  
 531          // Create some skeleton data just so we can call the WS.
 532          $user1 = self::getDataGenerator()->create_user();
 533  
 534          $this->setUser($user1);
 535  
 536          // Disable messaging.
 537          $CFG->messaging = 0;
 538  
 539          // Ensure an exception is thrown.
 540          $this->expectException('moodle_exception');
 541          core_message_external::get_contact_requests($user1->id);
 542      }
 543  
 544      /**
 545       * Test getting contact requests with no permission.
 546       */
 547      public function test_get_contact_requests_no_permission() {
 548          $this->resetAfterTest();
 549  
 550          // Create some skeleton data just so we can call the WS.
 551          $user1 = self::getDataGenerator()->create_user();
 552          $user2 = self::getDataGenerator()->create_user();
 553          $user3 = self::getDataGenerator()->create_user();
 554  
 555          $this->setUser($user3);
 556  
 557          // Ensure an exception is thrown.
 558          $this->expectException('required_capability_exception');
 559          core_message_external::create_contact_request($user1->id, $user2->id);
 560      }
 561  
 562      /**
 563       * Test getting the number of received contact requests.
 564       */
 565      public function test_get_received_contact_requests_count() {
 566          $this->resetAfterTest();
 567  
 568          $user1 = self::getDataGenerator()->create_user();
 569          $user2 = self::getDataGenerator()->create_user();
 570          $user3 = self::getDataGenerator()->create_user();
 571          $user4 = self::getDataGenerator()->create_user();
 572  
 573          $this->setUser($user1);
 574  
 575          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 576          $contactrequestnumber = external_api::clean_returnvalue(
 577              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 578          $this->assertEquals(0, $contactrequestnumber);
 579  
 580          \core_message\api::create_contact_request($user2->id, $user1->id);
 581  
 582          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 583          $contactrequestnumber = external_api::clean_returnvalue(
 584              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 585          $this->assertEquals(1, $contactrequestnumber);
 586  
 587          \core_message\api::create_contact_request($user3->id, $user1->id);
 588  
 589          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 590          $contactrequestnumber = external_api::clean_returnvalue(
 591              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 592          $this->assertEquals(2, $contactrequestnumber);
 593  
 594          \core_message\api::create_contact_request($user1->id, $user4->id);
 595  
 596          // Web service should ignore sent requests.
 597          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 598          $contactrequestnumber = external_api::clean_returnvalue(
 599              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 600          $this->assertEquals(2, $contactrequestnumber);
 601      }
 602  
 603      /**
 604       * Test the get_received_contact_requests_count() function when the user has blocked the sender of the request.
 605       */
 606      public function test_get_received_contact_requests_count_blocked_sender() {
 607          $this->resetAfterTest();
 608          $user1 = self::getDataGenerator()->create_user();
 609          $user2 = self::getDataGenerator()->create_user();
 610  
 611          // User1 blocks User2.
 612          \core_message\api::block_user($user1->id, $user2->id);
 613  
 614          // User2 tries to add User1 as a contact.
 615          \core_message\api::create_contact_request($user2->id, $user1->id);
 616  
 617          // Verify we don't see the contact request from the blocked user User2 in the count for User1.
 618          $this->setUser($user1);
 619          $contactrequestnumber = core_message_external::get_received_contact_requests_count($user1->id);
 620          $contactrequestnumber = external_api::clean_returnvalue(
 621              core_message_external::get_received_contact_requests_count_returns(), $contactrequestnumber);
 622          $this->assertEquals(0, $contactrequestnumber);
 623      }
 624  
 625      /**
 626       * Test getting the number of received contact requests with no permissions.
 627       */
 628      public function test_get_received_contact_requests_count_no_permission() {
 629          $this->resetAfterTest();
 630  
 631          // Create some skeleton data just so we can call the WS.
 632          $user1 = self::getDataGenerator()->create_user();
 633          $user2 = self::getDataGenerator()->create_user();
 634  
 635          $this->setUser($user2);
 636  
 637          // Ensure an exception is thrown.
 638          $this->expectException('required_capability_exception');
 639          core_message_external::get_received_contact_requests_count($user1->id);
 640      }
 641  
 642      /**
 643       * Test getting the number of received contact requests with messaging disabled.
 644       */
 645      public function test_get_received_contact_requests_count_messaging_disabled() {
 646          global $CFG;
 647  
 648          $this->resetAfterTest();
 649  
 650          // Create some skeleton data just so we can call the WS.
 651          $user1 = self::getDataGenerator()->create_user();
 652  
 653          $this->setUser($user1);
 654  
 655          // Disable messaging.
 656          $CFG->messaging = 0;
 657  
 658          // Ensure an exception is thrown.
 659          $this->expectException('moodle_exception');
 660          core_message_external::get_received_contact_requests_count($user1->id);
 661      }
 662  
 663      /**
 664       * Test creating a contact request.
 665       */
 666      public function test_create_contact_request() {
 667          global $CFG, $DB;
 668  
 669          $this->resetAfterTest();
 670  
 671          $user1 = self::getDataGenerator()->create_user();
 672          $user2 = self::getDataGenerator()->create_user();
 673  
 674          $this->setUser($user1);
 675  
 676          // Allow users to message anyone site-wide.
 677          $CFG->messagingallusers = 1;
 678  
 679          $return = core_message_external::create_contact_request($user1->id, $user2->id);
 680          $return = external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
 681          $this->assertEquals([], $return['warnings']);
 682  
 683          $request = $DB->get_records('message_contact_requests');
 684  
 685          $this->assertCount(1, $request);
 686  
 687          $request = reset($request);
 688  
 689          $this->assertEquals($request->id, $return['request']['id']);
 690          $this->assertEquals($request->userid, $return['request']['userid']);
 691          $this->assertEquals($request->requesteduserid, $return['request']['requesteduserid']);
 692          $this->assertEquals($request->timecreated, $return['request']['timecreated']);
 693      }
 694  
 695      /**
 696       * Test creating a contact request when not allowed.
 697       */
 698      public function test_create_contact_request_not_allowed() {
 699          global $CFG;
 700  
 701          $this->resetAfterTest();
 702  
 703          $user1 = self::getDataGenerator()->create_user();
 704          $user2 = self::getDataGenerator()->create_user();
 705  
 706          $this->setUser($user1);
 707  
 708          $CFG->messagingallusers = 0;
 709  
 710          $return = core_message_external::create_contact_request($user1->id, $user2->id);
 711          $return = external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
 712  
 713          $warning = reset($return['warnings']);
 714  
 715          $this->assertEquals('user', $warning['item']);
 716          $this->assertEquals($user2->id, $warning['itemid']);
 717          $this->assertEquals('cannotcreatecontactrequest', $warning['warningcode']);
 718          $this->assertEquals('You are unable to create a contact request for this user', $warning['message']);
 719      }
 720  
 721      /**
 722       * Test creating a contact request with messaging disabled.
 723       */
 724      public function test_create_contact_request_messaging_disabled() {
 725          global $CFG;
 726  
 727          $this->resetAfterTest();
 728  
 729          // Create some skeleton data just so we can call the WS.
 730          $user1 = self::getDataGenerator()->create_user();
 731          $user2 = self::getDataGenerator()->create_user();
 732  
 733          $this->setUser($user1);
 734  
 735          // Disable messaging.
 736          $CFG->messaging = 0;
 737  
 738          // Ensure an exception is thrown.
 739          $this->expectException('moodle_exception');
 740          core_message_external::create_contact_request($user1->id, $user2->id);
 741      }
 742  
 743      /**
 744       * Test creating a contact request with no permission.
 745       */
 746      public function test_create_contact_request_no_permission() {
 747          $this->resetAfterTest();
 748  
 749          // Create some skeleton data just so we can call the WS.
 750          $user1 = self::getDataGenerator()->create_user();
 751          $user2 = self::getDataGenerator()->create_user();
 752          $user3 = self::getDataGenerator()->create_user();
 753  
 754          $this->setUser($user3);
 755  
 756          // Ensure an exception is thrown.
 757          $this->expectException('required_capability_exception');
 758          core_message_external::create_contact_request($user1->id, $user2->id);
 759      }
 760  
 761      /**
 762       * Test confirming a contact request.
 763       */
 764      public function test_confirm_contact_request() {
 765          global $DB;
 766  
 767          $this->resetAfterTest();
 768  
 769          $user1 = self::getDataGenerator()->create_user();
 770          $user2 = self::getDataGenerator()->create_user();
 771  
 772          $this->setUser($user1);
 773  
 774          \core_message\api::create_contact_request($user1->id, $user2->id);
 775  
 776          $this->setUser($user2);
 777  
 778          $return = core_message_external::confirm_contact_request($user1->id, $user2->id);
 779          $return = external_api::clean_returnvalue(core_message_external::confirm_contact_request_returns(), $return);
 780          $this->assertEquals(array(), $return);
 781  
 782          $this->assertEquals(0, $DB->count_records('message_contact_requests'));
 783  
 784          $contact = $DB->get_records('message_contacts');
 785  
 786          $this->assertCount(1, $contact);
 787  
 788          $contact = reset($contact);
 789  
 790          $this->assertEquals($user1->id, $contact->userid);
 791          $this->assertEquals($user2->id, $contact->contactid);
 792      }
 793  
 794      /**
 795       * Test confirming a contact request with messaging disabled.
 796       */
 797      public function test_confirm_contact_request_messaging_disabled() {
 798          global $CFG;
 799  
 800          $this->resetAfterTest();
 801  
 802          // Create some skeleton data just so we can call the WS.
 803          $user1 = self::getDataGenerator()->create_user();
 804          $user2 = self::getDataGenerator()->create_user();
 805  
 806          $this->setUser($user1);
 807  
 808          // Disable messaging.
 809          $CFG->messaging = 0;
 810  
 811          // Ensure an exception is thrown.
 812          $this->expectException('moodle_exception');
 813          core_message_external::confirm_contact_request($user1->id, $user2->id);
 814      }
 815  
 816      /**
 817       * Test confirming a contact request with no permission.
 818       */
 819      public function test_confirm_contact_request_no_permission() {
 820          $this->resetAfterTest();
 821  
 822          // Create some skeleton data just so we can call the WS.
 823          $user1 = self::getDataGenerator()->create_user();
 824          $user2 = self::getDataGenerator()->create_user();
 825          $user3 = self::getDataGenerator()->create_user();
 826  
 827          $this->setUser($user3);
 828  
 829          // Ensure an exception is thrown.
 830          $this->expectException('required_capability_exception');
 831          core_message_external::confirm_contact_request($user1->id, $user2->id);
 832      }
 833  
 834      /**
 835       * Test declining a contact request.
 836       */
 837      public function test_decline_contact_request() {
 838          global $DB;
 839  
 840          $this->resetAfterTest();
 841  
 842          $user1 = self::getDataGenerator()->create_user();
 843          $user2 = self::getDataGenerator()->create_user();
 844  
 845          $this->setUser($user1);
 846  
 847          \core_message\api::create_contact_request($user1->id, $user2->id);
 848  
 849          $this->setUser($user2);
 850  
 851          $return = core_message_external::decline_contact_request($user1->id, $user2->id);
 852          $return = external_api::clean_returnvalue(core_message_external::decline_contact_request_returns(), $return);
 853          $this->assertEquals(array(), $return);
 854  
 855          $this->assertEquals(0, $DB->count_records('message_contact_requests'));
 856          $this->assertEquals(0, $DB->count_records('message_contacts'));
 857      }
 858  
 859      /**
 860       * Test declining a contact request with messaging disabled.
 861       */
 862      public function test_decline_contact_request_messaging_disabled() {
 863          global $CFG;
 864  
 865          $this->resetAfterTest();
 866  
 867          // Create some skeleton data just so we can call the WS.
 868          $user1 = self::getDataGenerator()->create_user();
 869          $user2 = self::getDataGenerator()->create_user();
 870  
 871          $this->setUser($user1);
 872  
 873          // Disable messaging.
 874          $CFG->messaging = 0;
 875  
 876          // Ensure an exception is thrown.
 877          $this->expectException('moodle_exception');
 878          core_message_external::decline_contact_request($user1->id, $user2->id);
 879      }
 880  
 881      /**
 882       * Test declining a contact request with no permission.
 883       */
 884      public function test_decline_contact_request_no_permission() {
 885          $this->resetAfterTest();
 886  
 887          // Create some skeleton data just so we can call the WS.
 888          $user1 = self::getDataGenerator()->create_user();
 889          $user2 = self::getDataGenerator()->create_user();
 890          $user3 = self::getDataGenerator()->create_user();
 891  
 892          $this->setUser($user3);
 893  
 894          // Ensure an exception is thrown.
 895          $this->expectException('required_capability_exception');
 896          core_message_external::decline_contact_request($user1->id, $user2->id);
 897      }
 898  
 899      /**
 900       * Test muting conversations.
 901       */
 902      public function test_mute_conversations() {
 903          global $DB;
 904  
 905          $this->resetAfterTest(true);
 906  
 907          $user1 = self::getDataGenerator()->create_user();
 908          $user2 = self::getDataGenerator()->create_user();
 909  
 910          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 911              [$user1->id, $user2->id]);
 912  
 913          $this->setUser($user1);
 914  
 915          // Muting a conversation.
 916          $return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
 917          $return = external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
 918          $this->assertEquals(array(), $return);
 919  
 920          // Get list of muted conversations.
 921          $mca = $DB->get_record('message_conversation_actions', []);
 922  
 923          $this->assertEquals($user1->id, $mca->userid);
 924          $this->assertEquals($conversation->id, $mca->conversationid);
 925          $this->assertEquals(\core_message\api::CONVERSATION_ACTION_MUTED, $mca->action);
 926  
 927          // Muting a conversation that is already muted.
 928          $return = core_message_external::mute_conversations($user1->id, [$conversation->id]);
 929          $return = external_api::clean_returnvalue(core_message_external::mute_conversations_returns(), $return);
 930          $this->assertEquals(array(), $return);
 931  
 932          $this->assertEquals(1, $DB->count_records('message_conversation_actions'));
 933      }
 934  
 935      /**
 936       * Test muting a conversation with messaging disabled.
 937       */
 938      public function test_mute_conversations_messaging_disabled() {
 939          global $CFG;
 940  
 941          $this->resetAfterTest();
 942  
 943          // Create some skeleton data just so we can call the WS.
 944          $user1 = self::getDataGenerator()->create_user();
 945          $user2 = self::getDataGenerator()->create_user();
 946  
 947          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 948              [$user1->id, $user2->id]);
 949  
 950          $this->setUser($user1);
 951  
 952          // Disable messaging.
 953          $CFG->messaging = 0;
 954  
 955          // Ensure an exception is thrown.
 956          $this->expectException('moodle_exception');
 957          core_message_external::mute_conversations($user1->id, [$conversation->id]);
 958      }
 959  
 960      /**
 961       * Test muting a conversation with no permission.
 962       */
 963      public function test_mute_conversations_no_permission() {
 964          $this->resetAfterTest();
 965  
 966          // Create some skeleton data just so we can call the WS.
 967          $user1 = self::getDataGenerator()->create_user();
 968          $user2 = self::getDataGenerator()->create_user();
 969          $user3 = self::getDataGenerator()->create_user();
 970  
 971          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 972              [$user1->id, $user2->id]);
 973  
 974          $this->setUser($user3);
 975  
 976          // Ensure an exception is thrown.
 977          $this->expectException('required_capability_exception');
 978          core_message_external::mute_conversations($user1->id, [$conversation->id]);
 979      }
 980  
 981      /**
 982       * Test unmuting conversations.
 983       */
 984      public function test_unmute_conversations() {
 985          global $DB;
 986  
 987          $this->resetAfterTest(true);
 988  
 989          $user1 = self::getDataGenerator()->create_user();
 990          $user2 = self::getDataGenerator()->create_user();
 991  
 992          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
 993              [$user1->id, $user2->id]);
 994  
 995          $this->setUser($user1);
 996  
 997          // Mute the conversation.
 998          \core_message\api::mute_conversation($user1->id, $conversation->id);
 999  
1000          // Unmuting a conversation.
1001          $return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1002          $return = external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
1003          $this->assertEquals(array(), $return);
1004  
1005          $this->assertEquals(0, $DB->count_records('message_conversation_actions'));
1006  
1007          // Unmuting a conversation which is already unmuted.
1008          $return = core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1009          $return = external_api::clean_returnvalue(core_message_external::unmute_conversations_returns(), $return);
1010          $this->assertEquals(array(), $return);
1011  
1012          $this->assertEquals(0, $DB->count_records('message_conversation_actions'));
1013      }
1014  
1015      /**
1016       * Test unmuting a conversation with messaging disabled.
1017       */
1018      public function test_unmute_conversation_messaging_disabled() {
1019          global $CFG;
1020  
1021          $this->resetAfterTest();
1022  
1023          // Create some skeleton data just so we can call the WS.
1024          $user1 = self::getDataGenerator()->create_user();
1025          $user2 = self::getDataGenerator()->create_user();
1026  
1027          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1028              [$user1->id, $user2->id]);
1029  
1030          $this->setUser($user1);
1031  
1032          // Disable messaging.
1033          $CFG->messaging = 0;
1034  
1035          // Ensure an exception is thrown.
1036          $this->expectException('moodle_exception');
1037          core_message_external::unmute_conversations($user1->id, [$user2->id]);
1038      }
1039  
1040      /**
1041       * Test unmuting a conversation with no permission.
1042       */
1043      public function test_unmute_conversation_no_permission() {
1044          $this->resetAfterTest();
1045  
1046          // Create some skeleton data just so we can call the WS.
1047          $user1 = self::getDataGenerator()->create_user();
1048          $user2 = self::getDataGenerator()->create_user();
1049          $user3 = self::getDataGenerator()->create_user();
1050  
1051          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
1052              [$user1->id, $user2->id]);
1053  
1054          $this->setUser($user3);
1055  
1056          // Ensure an exception is thrown.
1057          $this->expectException('required_capability_exception');
1058          core_message_external::unmute_conversations($user1->id, [$conversation->id]);
1059      }
1060  
1061      /**
1062       * Test blocking a user.
1063       */
1064      public function test_block_user() {
1065          global $DB;
1066  
1067          $this->resetAfterTest(true);
1068  
1069          $user1 = self::getDataGenerator()->create_user();
1070          $user2 = self::getDataGenerator()->create_user();
1071  
1072          $this->setUser($user1);
1073  
1074          // Blocking a user.
1075          $return = core_message_external::block_user($user1->id, $user2->id);
1076          $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1077          $this->assertEquals(array(), $return);
1078  
1079          // Get list of blocked users.
1080          $record = $DB->get_record('message_users_blocked', []);
1081  
1082          $this->assertEquals($user1->id, $record->userid);
1083          $this->assertEquals($user2->id, $record->blockeduserid);
1084  
1085          // Blocking a user who is already blocked.
1086          $return = core_message_external::block_user($user1->id, $user2->id);
1087          $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1088          $this->assertEquals(array(), $return);
1089  
1090          $this->assertEquals(1, $DB->count_records('message_users_blocked'));
1091      }
1092  
1093      /**
1094       * Test blocking a user.
1095       */
1096      public function test_block_user_when_ineffective() {
1097          global $DB;
1098  
1099          $this->resetAfterTest(true);
1100  
1101          $user1 = self::getDataGenerator()->create_user();
1102          $user2 = self::getDataGenerator()->create_user();
1103  
1104          $this->setUser($user1);
1105  
1106          $authenticateduser = $DB->get_record('role', array('shortname' => 'user'));
1107          assign_capability('moodle/site:messageanyuser', CAP_ALLOW, $authenticateduser->id, \context_system::instance(), true);
1108  
1109          // Blocking a user.
1110          $return = core_message_external::block_user($user1->id, $user2->id);
1111          $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
1112          $this->assertEquals(array(), $return);
1113  
1114          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1115      }
1116  
1117      /**
1118       * Test blocking a user with messaging disabled.
1119       */
1120      public function test_block_user_messaging_disabled() {
1121          global $CFG;
1122  
1123          $this->resetAfterTest();
1124  
1125          // Create some skeleton data just so we can call the WS.
1126          $user1 = self::getDataGenerator()->create_user();
1127          $user2 = self::getDataGenerator()->create_user();
1128  
1129          $this->setUser($user1);
1130  
1131          // Disable messaging.
1132          $CFG->messaging = 0;
1133  
1134          // Ensure an exception is thrown.
1135          $this->expectException('moodle_exception');
1136          core_message_external::block_user($user1->id, $user2->id);
1137      }
1138  
1139      /**
1140       * Test blocking a user with no permission.
1141       */
1142      public function test_block_user_no_permission() {
1143          $this->resetAfterTest();
1144  
1145          // Create some skeleton data just so we can call the WS.
1146          $user1 = self::getDataGenerator()->create_user();
1147          $user2 = self::getDataGenerator()->create_user();
1148          $user3 = self::getDataGenerator()->create_user();
1149  
1150          $this->setUser($user3);
1151  
1152          // Ensure an exception is thrown.
1153          $this->expectException('required_capability_exception');
1154          core_message_external::block_user($user1->id, $user2->id);
1155      }
1156  
1157      /**
1158       * Test unblocking a user.
1159       */
1160      public function test_unblock_user() {
1161          global $DB;
1162  
1163          $this->resetAfterTest(true);
1164  
1165          $user1 = self::getDataGenerator()->create_user();
1166          $user2 = self::getDataGenerator()->create_user();
1167  
1168          $this->setUser($user1);
1169  
1170          // Block the user.
1171          \core_message\api::block_user($user1->id, $user2->id);
1172  
1173          // Unblocking a user.
1174          $return = core_message_external::unblock_user($user1->id, $user2->id);
1175          $return = external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
1176          $this->assertEquals(array(), $return);
1177  
1178          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1179  
1180          // Unblocking a user who is already unblocked.
1181          $return = core_message_external::unblock_user($user1->id, $user2->id);
1182          $return = external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
1183          $this->assertEquals(array(), $return);
1184  
1185          $this->assertEquals(0, $DB->count_records('message_users_blocked'));
1186      }
1187  
1188      /**
1189       * Test unblocking a user with messaging disabled.
1190       */
1191      public function test_unblock_user_messaging_disabled() {
1192          global $CFG;
1193  
1194          $this->resetAfterTest();
1195  
1196          // Create some skeleton data just so we can call the WS.
1197          $user1 = self::getDataGenerator()->create_user();
1198          $user2 = self::getDataGenerator()->create_user();
1199  
1200          $this->setUser($user1);
1201  
1202          // Disable messaging.
1203          $CFG->messaging = 0;
1204  
1205          // Ensure an exception is thrown.
1206          $this->expectException('moodle_exception');
1207          core_message_external::unblock_user($user1->id, $user2->id);
1208      }
1209  
1210      /**
1211       * Test unblocking a user with no permission.
1212       */
1213      public function test_unblock_user_no_permission() {
1214          $this->resetAfterTest();
1215  
1216          // Create some skeleton data just so we can call the WS.
1217          $user1 = self::getDataGenerator()->create_user();
1218          $user2 = self::getDataGenerator()->create_user();
1219          $user3 = self::getDataGenerator()->create_user();
1220  
1221          $this->setUser($user3);
1222  
1223          // Ensure an exception is thrown.
1224          $this->expectException('required_capability_exception');
1225          core_message_external::unblock_user($user1->id, $user2->id);
1226      }
1227  
1228      /**
1229       * Test search_contacts.
1230       */
1231      public function test_search_contacts() {
1232          global $DB;
1233          $this->resetAfterTest(true);
1234  
1235          $course1 = $this->getDataGenerator()->create_course();
1236          $course2 = $this->getDataGenerator()->create_course();
1237  
1238          $user1 = new \stdClass();
1239          $user1->firstname = 'X';
1240          $user1->lastname = 'X';
1241          $user1 = $this->getDataGenerator()->create_user($user1);
1242          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
1243          $this->getDataGenerator()->enrol_user($user1->id, $course2->id);
1244  
1245          $user2 = new \stdClass();
1246          $user2->firstname = 'Eric';
1247          $user2->lastname = 'Cartman';
1248          $user2 = self::getDataGenerator()->create_user($user2);
1249          $user3 = new \stdClass();
1250          $user3->firstname = 'Stan';
1251          $user3->lastname = 'Marsh';
1252          $user3 = self::getDataGenerator()->create_user($user3);
1253          self::getDataGenerator()->enrol_user($user3->id, $course1->id);
1254          $user4 = new \stdClass();
1255          $user4->firstname = 'Kyle';
1256          $user4->lastname = 'Broflovski';
1257          $user4 = self::getDataGenerator()->create_user($user4);
1258          $user5 = new \stdClass();
1259          $user5->firstname = 'Kenny';
1260          $user5->lastname = 'McCormick';
1261          $user5 = self::getDataGenerator()->create_user($user5);
1262          self::getDataGenerator()->enrol_user($user5->id, $course2->id);
1263  
1264          $this->setUser($user1);
1265  
1266          $results = core_message_external::search_contacts('r');
1267          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1268          $this->assertCount(5, $results); // Users 2 through 5 + admin
1269  
1270          $results = core_message_external::search_contacts('r', true);
1271          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1272          $this->assertCount(2, $results);
1273  
1274          $results = core_message_external::search_contacts('Kyle', false);
1275          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1276          $this->assertCount(1, $results);
1277          $result = reset($results);
1278          $this->assertEquals($user4->id, $result['id']);
1279  
1280          $results = core_message_external::search_contacts('y', false);
1281          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1282          $this->assertCount(2, $results);
1283  
1284          $results = core_message_external::search_contacts('y', true);
1285          $results = external_api::clean_returnvalue(core_message_external::search_contacts_returns(), $results);
1286          $this->assertCount(1, $results);
1287          $result = reset($results);
1288          $this->assertEquals($user5->id, $result['id']);
1289  
1290          // Empty query, will throw an exception.
1291          $this->expectException(\moodle_exception::class);
1292          $results = core_message_external::search_contacts('');
1293      }
1294  
1295      /**
1296       * Test get_messages.
1297       */
1298      public function test_get_messages() {
1299          global $CFG, $DB, $PAGE;
1300          $this->resetAfterTest(true);
1301  
1302          $this->preventResetByRollback();
1303  
1304          $user1 = self::getDataGenerator()->create_user();
1305          $user2 = self::getDataGenerator()->create_user();
1306          $user3 = self::getDataGenerator()->create_user();
1307  
1308          $course = self::getDataGenerator()->create_course();
1309  
1310          // Send a message from one user to another.
1311          $im1 = message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
1312          $im2 = message_post_message($user1, $user3, 'some random text 2', FORMAT_MOODLE);
1313          $im3 = message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
1314          $im4 = message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
1315          $im5 = message_post_message($user3, $user1, 'some random text 5', FORMAT_MOODLE);
1316          $im6 = message_post_message($user1, $user2, 'some random text 6', FORMAT_MOODLE);
1317  
1318          // Mark a message as read by user2.
1319          $message = $DB->get_record('messages', ['id' => $im6]);
1320          \core_message\api::mark_message_as_read($user2->id, $message);
1321  
1322          $this->setUser($user1);
1323          // Get unread conversations from user1 to user2.
1324          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1325          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1326          $this->assertCount(1, $messages['messages']);
1327          $this->assertEquals($im1, $messages['messages'][0]['id']);
1328  
1329          // Get read conversations from user1 to user2.
1330          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', MESSAGE_GET_READ, true, 0, 0);
1331          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1332          $this->assertCount(1, $messages['messages']);
1333          $this->assertEquals($im6, $messages['messages'][0]['id']);
1334  
1335          // Get both read and unread conversations from user1 to user2.
1336          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', MESSAGE_GET_READ_AND_UNREAD,
1337              true, 0, 0);
1338          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1339          $this->assertCount(2, $messages['messages']);
1340  
1341          // Delete an unread message.
1342          \core_message\api::delete_message($user1->id, $im1);
1343  
1344          $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1345          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1346          $this->assertCount(0, $messages['messages']);
1347  
1348          $this->setUser($user2);
1349          // Get unread conversations from any user to user2.
1350          $messages = core_message_external::get_messages($user2->id, 0, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1351          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1352          $this->assertCount(2, $messages['messages']);
1353  
1354          // Conversations from user3 to user2.
1355          $messages = core_message_external::get_messages($user2->id, $user3->id, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1356          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1357          $this->assertCount(1, $messages['messages']);
1358  
1359          // Delete the message.
1360          \core_message\api::delete_message($user2->id, $im4);
1361  
1362          $messages = core_message_external::get_messages($user2->id, $user3->id, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1363          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1364          $this->assertCount(0, $messages['messages']);
1365  
1366          $this->setUser($user3);
1367          // Get unread notifications received by user3.
1368          $messages = core_message_external::get_messages($user3->id, 0, 'notifications', MESSAGE_GET_UNREAD, true, 0, 0);
1369          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1370          $this->assertCount(0, $messages['messages']);
1371  
1372          // Now, create some notifications...
1373          // We are creating fake notifications but based on real ones.
1374  
1375          // This one comes from a disabled plugin's provider and therefore is not sent.
1376          $eventdata = new \core\message\message();
1377          $eventdata->courseid          = $course->id;
1378          $eventdata->notification      = 1;
1379          $eventdata->modulename        = 'moodle';
1380          $eventdata->component         = 'enrol_paypal';
1381          $eventdata->name              = 'paypal_enrolment';
1382          $eventdata->userfrom          = get_admin();
1383          $eventdata->userto            = $user1;
1384          $eventdata->subject           = "Moodle: PayPal payment";
1385          $eventdata->fullmessage       = "Your PayPal payment is pending.";
1386          $eventdata->fullmessageformat = FORMAT_PLAIN;
1387          $eventdata->fullmessagehtml   = '';
1388          $eventdata->smallmessage      = '';
1389          message_send($eventdata);
1390          $this->assertDebuggingCalled('Attempt to send msg from a provider enrol_paypal/paypal_enrolment '.
1391              'that is inactive or not allowed for the user id='.$user1->id);
1392  
1393          // This one omits notification = 1.
1394          $message = new \core\message\message();
1395          $message->courseid          = $course->id;
1396          $message->component         = 'enrol_manual';
1397          $message->name              = 'expiry_notification';
1398          $message->userfrom          = $user2;
1399          $message->userto            = $user1;
1400          $message->subject           = 'Test: This is not a notification but otherwise is valid';
1401          $message->fullmessage       = 'Test: Full message';
1402          $message->fullmessageformat = FORMAT_MARKDOWN;
1403          $message->fullmessagehtml   = markdown_to_html($message->fullmessage);
1404          $message->smallmessage      = $message->subject;
1405          $message->contexturlname    = $course->fullname;
1406          $message->contexturl        = (string)new \moodle_url('/course/view.php', array('id' => $course->id));
1407          message_send($message);
1408  
1409          $message = new \core\message\message();
1410          $message->courseid          = $course->id;
1411          $message->notification      = 1;
1412          $message->component         = 'enrol_manual';
1413          $message->name              = 'expiry_notification';
1414          $message->userfrom          = $user2;
1415          $message->userto            = $user1;
1416          $message->subject           = 'Enrolment expired';
1417          $message->fullmessage       = 'Enrolment expired blah blah blah';
1418          $message->fullmessageformat = FORMAT_MARKDOWN;
1419          $message->fullmessagehtml   = markdown_to_html($message->fullmessage);
1420          $message->smallmessage      = $message->subject;
1421          $message->contexturlname    = $course->fullname;
1422          $message->contexturl        = (string)new \moodle_url('/course/view.php', array('id' => $course->id));
1423          message_send($message);
1424  
1425          $userfrom = \core_user::get_noreply_user();
1426          $userfrom->maildisplay = true;
1427          $eventdata = new \core\message\message();
1428          $eventdata->courseid          = $course->id;
1429          $eventdata->component         = 'moodle';
1430          $eventdata->name              = 'badgecreatornotice';
1431          $eventdata->userfrom          = $userfrom;
1432          $eventdata->userto            = $user1;
1433          $eventdata->notification      = 1;
1434          $eventdata->subject           = 'New badge';
1435          $eventdata->fullmessage       = format_text_email($eventdata->subject, FORMAT_HTML);
1436          $eventdata->fullmessageformat = FORMAT_PLAIN;
1437          $eventdata->fullmessagehtml   = $eventdata->subject;
1438          $eventdata->smallmessage      = $eventdata->subject;
1439          message_send($eventdata);
1440  
1441          // This event contains HTML in the subject field that will be removed by the WS (otherwise it will generate an exception).
1442          $eventdata = new \core\message\message();
1443          $eventdata->courseid         = $course->id;
1444          $eventdata->name             = 'submission';
1445          $eventdata->component        = 'mod_feedback';
1446          $eventdata->userfrom         = $user1;
1447          $eventdata->userto           = $user2;
1448          $eventdata->subject          = 'Feedback submitted <span>with html</span>';
1449          $eventdata->fullmessage      = 'Feedback submitted from an user';
1450          $eventdata->fullmessageformat = FORMAT_PLAIN;
1451          $eventdata->fullmessagehtml  = '<strong>Feedback submitted</strong>';
1452          $eventdata->smallmessage     = '';
1453          $eventdata->customdata         = ['datakey' => 'data'];
1454          message_send($eventdata);
1455  
1456          $this->setUser($user1);
1457          // Get unread notifications from any user to user1.
1458          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', MESSAGE_GET_UNREAD, true, 0, 0);
1459          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1460          $this->assertCount(3, $messages['messages']);
1461  
1462          // Get one unread notifications from any user to user1.
1463          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', MESSAGE_GET_UNREAD, true, 0, 1);
1464          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1465          $this->assertCount(1, $messages['messages']);
1466  
1467          // Get read notifications from any user to user1.
1468          $messages = core_message_external::get_messages($user1->id, 0, 'notifications', MESSAGE_GET_READ, true, 0, 0);
1469          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1470          $this->assertCount(0, $messages['messages']);
1471  
1472          // Get unread both type of messages from any user to user1.
1473          $messages = core_message_external::get_messages($user1->id, 0, 'both', MESSAGE_GET_UNREAD, true, 0, 0);
1474          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1475          $this->assertCount(4, $messages['messages']);
1476  
1477          // Get unread notifications from no-reply-user to user1.
1478          $messages = core_message_external::get_messages($user1->id, $userfrom->id, 'notifications', MESSAGE_GET_UNREAD, true, 0, 0);
1479          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1480          $this->assertCount(1, $messages['messages']);
1481  
1482          // Get notifications send by user1 to any user.
1483          $messages = core_message_external::get_messages(0, $user1->id, 'notifications', MESSAGE_GET_UNREAD, true, 0, 0);
1484          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1485          $this->assertCount(1, $messages['messages']);
1486          // Check we receive custom data as a unserialisable json.
1487          $this->assertObjectHasAttribute('datakey', json_decode($messages['messages'][0]['customdata']));
1488          $this->assertEquals('mod_feedback', $messages['messages'][0]['component']);
1489          $this->assertEquals('submission', $messages['messages'][0]['eventtype']);
1490          $feedbackicon = clean_param($PAGE->get_renderer('core')->image_url('monologo', 'mod_feedback')->out(), PARAM_URL);
1491          $this->assertEquals($feedbackicon, $messages['messages'][0]['iconurl']);
1492  
1493          // Test warnings.
1494          $CFG->messaging = 0;
1495  
1496          $messages = core_message_external::get_messages(0, $user1->id, 'both', MESSAGE_GET_UNREAD, true, 0, 0);
1497          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1498          $this->assertCount(1, $messages['warnings']);
1499  
1500          // Test exceptions.
1501  
1502          // Messaging disabled.
1503          try {
1504              $messages = core_message_external::get_messages(0, $user1->id, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1505              $this->fail('Exception expected due messaging disabled.');
1506          } catch (\moodle_exception $e) {
1507              $this->assertEquals('disabled', $e->errorcode);
1508          }
1509  
1510          $CFG->messaging = 1;
1511  
1512          // Invalid users.
1513          try {
1514              $messages = core_message_external::get_messages(0, 0, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1515              $this->fail('Exception expected due invalid users.');
1516          } catch (\moodle_exception $e) {
1517              $this->assertEquals('accessdenied', $e->errorcode);
1518          }
1519  
1520          // Invalid user ids.
1521          try {
1522              $messages = core_message_external::get_messages(2500, 0, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1523              $this->fail('Exception expected due invalid users.');
1524          } catch (\moodle_exception $e) {
1525              $this->assertEquals('invaliduser', $e->errorcode);
1526          }
1527  
1528          // Invalid users (permissions).
1529          $this->setUser($user2);
1530          try {
1531              $messages = core_message_external::get_messages(0, $user1->id, 'conversations', MESSAGE_GET_UNREAD, true, 0, 0);
1532              $this->fail('Exception expected due invalid user.');
1533          } catch (\moodle_exception $e) {
1534              $this->assertEquals('accessdenied', $e->errorcode);
1535          }
1536  
1537      }
1538  
1539      /**
1540       * Test get_messages where we want all messages from a user, sent to any user.
1541       */
1542      public function test_get_messages_useridto_all() {
1543          $this->resetAfterTest(true);
1544  
1545          $user1 = self::getDataGenerator()->create_user();
1546          $user2 = self::getDataGenerator()->create_user();
1547          $user3 = self::getDataGenerator()->create_user();
1548  
1549          $this->setUser($user1);
1550  
1551          // Send a message from user 1 to two other users.
1552          $this->send_message($user1, $user2, 'some random text 1', 0, 1);
1553          $this->send_message($user1, $user3, 'some random text 2', 0, 2);
1554  
1555          // Get messages sent from user 1.
1556          $messages = core_message_external::get_messages(0, $user1->id, 'conversations', MESSAGE_GET_UNREAD, false, 0, 0);
1557          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1558  
1559          // Confirm the data is correct.
1560          $messages = $messages['messages'];
1561          $this->assertCount(2, $messages);
1562  
1563          $message1 = array_shift($messages);
1564          $message2 = array_shift($messages);
1565  
1566          $this->assertEquals($user1->id, $message1['useridfrom']);
1567          $this->assertEquals($user2->id, $message1['useridto']);
1568  
1569          $this->assertEquals($user1->id, $message2['useridfrom']);
1570          $this->assertEquals($user3->id, $message2['useridto']);
1571      }
1572  
1573      /**
1574       * Test get_messages where we want all messages to a user, sent by any user.
1575       */
1576      public function test_get_messages_useridfrom_all() {
1577          $this->resetAfterTest();
1578  
1579          $user1 = self::getDataGenerator()->create_user();
1580          $user2 = self::getDataGenerator()->create_user();
1581          $user3 = self::getDataGenerator()->create_user();
1582  
1583          $this->setUser($user1);
1584  
1585          // Send a message to user 1 from two other users.
1586          $this->send_message($user2, $user1, 'some random text 1', 0, 1);
1587          $this->send_message($user3, $user1, 'some random text 2', 0, 2);
1588  
1589          // Get messages sent to user 1.
1590          $messages = core_message_external::get_messages($user1->id, 0, 'conversations', MESSAGE_GET_UNREAD, false, 0, 0);
1591          $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
1592  
1593          // Confirm the data is correct.
1594          $messages = $messages['messages'];
1595          $this->assertCount(2, $messages);
1596  
1597          $message1 = array_shift($messages);
1598          $message2 = array_shift($messages);
1599  
1600          $this->assertEquals($user2->id, $message1['useridfrom']);
1601          $this->assertEquals($user1->id, $message1['useridto']);
1602  
1603          $this->assertEquals($user3->id, $message2['useridfrom']);
1604          $this->assertEquals($user1->id, $message2['useridto']);
1605      }
1606  
1607      /**
1608       * Test get_blocked_users.
1609       */
1610      public function test_get_blocked_users() {
1611          $this->resetAfterTest(true);
1612  
1613          $user1 = self::getDataGenerator()->create_user();
1614          $userstranger = self::getDataGenerator()->create_user();
1615          $useroffline1 = self::getDataGenerator()->create_user();
1616          $useroffline2 = self::getDataGenerator()->create_user();
1617          $userblocked = self::getDataGenerator()->create_user();
1618  
1619          // Login as user1.
1620          $this->setUser($user1);
1621  
1622          \core_message\api::add_contact($user1->id, $useroffline1->id);
1623          \core_message\api::add_contact($user1->id, $useroffline2->id);
1624  
1625          // The userstranger sends a couple of messages to user1.
1626          $this->send_message($userstranger, $user1, 'Hello there!');
1627          $this->send_message($userstranger, $user1, 'How you goin?');
1628  
1629          // The userblocked sends a message to user1.
1630          // Note that this user is not blocked at this point.
1631          $this->send_message($userblocked, $user1, 'Here, have some spam.');
1632  
1633          // Retrieve the list of blocked users.
1634          $this->setUser($user1);
1635          $blockedusers = core_message_external::get_blocked_users($user1->id);
1636          $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1637          $this->assertCount(0, $blockedusers['users']);
1638  
1639          // Block the $userblocked and retrieve again the list.
1640          \core_message\api::block_user($user1->id, $userblocked->id);
1641          $blockedusers = core_message_external::get_blocked_users($user1->id);
1642          $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1643          $this->assertCount(1, $blockedusers['users']);
1644  
1645          // Remove the $userblocked and check that the list now is empty.
1646          delete_user($userblocked);
1647          $blockedusers = core_message_external::get_blocked_users($user1->id);
1648          $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
1649          $this->assertCount(0, $blockedusers['users']);
1650      }
1651  
1652      /**
1653       * Test mark_message_read.
1654       */
1655      public function test_mark_message_read() {
1656          $this->resetAfterTest(true);
1657  
1658          $user1 = self::getDataGenerator()->create_user();
1659          $user2 = self::getDataGenerator()->create_user();
1660          $user3 = self::getDataGenerator()->create_user();
1661  
1662          // Login as user1.
1663          $this->setUser($user1);
1664          \core_message\api::add_contact($user1->id, $user2->id);
1665          \core_message\api::add_contact($user1->id, $user3->id);
1666  
1667          // The user2 sends a couple of messages to user1.
1668          $this->send_message($user2, $user1, 'Hello there!');
1669          $this->send_message($user2, $user1, 'How you goin?');
1670          $this->send_message($user3, $user1, 'How you goin?');
1671          $this->send_message($user3, $user2, 'How you goin?');
1672  
1673          // Retrieve all messages sent by user2 (they are currently unread).
1674          $lastmessages = message_get_messages($user1->id, $user2->id, 0, MESSAGE_GET_UNREAD);
1675  
1676          $messageids = array();
1677          foreach ($lastmessages as $m) {
1678              $messageid = core_message_external::mark_message_read($m->id, time());
1679              $messageids[] = external_api::clean_returnvalue(core_message_external::mark_message_read_returns(), $messageid);
1680          }
1681  
1682          // Retrieve all messages sent (they are currently read).
1683          $lastmessages = message_get_messages($user1->id, $user2->id, 0, MESSAGE_GET_READ);
1684          $this->assertCount(2, $lastmessages);
1685          $this->assertArrayHasKey($messageids[0]['messageid'], $lastmessages);
1686          $this->assertArrayHasKey($messageids[1]['messageid'], $lastmessages);
1687  
1688          // Retrieve all messages sent by any user (that are currently unread).
1689          $lastmessages = message_get_messages($user1->id, 0, 0, MESSAGE_GET_UNREAD);
1690          $this->assertCount(1, $lastmessages);
1691  
1692          // Invalid message ids.
1693          try {
1694              $messageid = core_message_external::mark_message_read(1337, time());
1695              $this->fail('Exception expected due invalid messageid.');
1696          } catch (\dml_missing_record_exception $e) {
1697              $this->assertEquals('invalidrecordunknown', $e->errorcode);
1698          }
1699  
1700          // A message to a different user.
1701          $lastmessages = message_get_messages($user2->id, $user3->id, 0, MESSAGE_GET_UNREAD);
1702          $messageid = array_pop($lastmessages)->id;
1703          try {
1704              $messageid = core_message_external::mark_message_read($messageid, time());
1705              $this->fail('Exception expected due invalid messageid.');
1706          } catch (\invalid_parameter_exception $e) {
1707              $this->assertEquals('invalidparameter', $e->errorcode);
1708          }
1709      }
1710  
1711      /**
1712       * Test mark_notification_read.
1713       */
1714      public function test_mark_notification_read() {
1715          $this->resetAfterTest(true);
1716  
1717          $user1 = self::getDataGenerator()->create_user();
1718          $user2 = self::getDataGenerator()->create_user();
1719          $user3 = self::getDataGenerator()->create_user();
1720  
1721          // Login as user1.
1722          $this->setUser($user1);
1723          \core_message\api::add_contact($user1->id, $user2->id);
1724          \core_message\api::add_contact($user1->id, $user3->id);
1725  
1726          // The user2 sends a couple of notifications to user1.
1727          $this->send_message($user2, $user1, 'Hello there!', 1);
1728          $this->send_message($user2, $user1, 'How you goin?', 1);
1729          $this->send_message($user3, $user1, 'How you goin?', 1);
1730          $this->send_message($user3, $user2, 'How you goin?', 1);
1731  
1732          // Retrieve all notifications sent by user2 (they are currently unread).
1733          $lastnotifications = message_get_messages($user1->id, $user2->id, 1, MESSAGE_GET_UNREAD);
1734  
1735          $notificationids = array();
1736          foreach ($lastnotifications as $n) {
1737              $notificationid = core_message_external::mark_notification_read($n->id, time());
1738              $notificationids[] = external_api::clean_returnvalue(core_message_external::mark_notification_read_returns(),
1739                  $notificationid);
1740          }
1741  
1742          // Retrieve all notifications sent (they are currently read).
1743          $lastnotifications = message_get_messages($user1->id, $user2->id, 1, MESSAGE_GET_READ);
1744          $this->assertCount(2, $lastnotifications);
1745          $this->assertArrayHasKey($notificationids[1]['notificationid'], $lastnotifications);
1746          $this->assertArrayHasKey($notificationids[0]['notificationid'], $lastnotifications);
1747  
1748          // Retrieve all notifications sent by any user (that are currently unread).
1749          $lastnotifications = message_get_messages($user1->id, 0, 1, MESSAGE_GET_UNREAD);
1750          $this->assertCount(1, $lastnotifications);
1751  
1752          // Invalid notification ids.
1753          try {
1754              $notificationid = core_message_external::mark_notification_read(1337, time());
1755              $this->fail('Exception expected due invalid notificationid.');
1756          } catch (\dml_missing_record_exception $e) {
1757              $this->assertEquals('invalidrecord', $e->errorcode);
1758          }
1759  
1760          // A notification to a different user.
1761          $lastnotifications = message_get_messages($user2->id, $user3->id, 1, MESSAGE_GET_UNREAD);
1762          $notificationid = array_pop($lastnotifications)->id;
1763          try {
1764              $notificationid = core_message_external::mark_notification_read($notificationid, time());
1765              $this->fail('Exception expected due invalid notificationid.');
1766          } catch (\invalid_parameter_exception $e) {
1767              $this->assertEquals('invalidparameter', $e->errorcode);
1768          }
1769      }
1770  
1771      /**
1772       * Test delete_message.
1773       */
1774      public function test_delete_message() {
1775          global $DB;
1776          $this->resetAfterTest(true);
1777  
1778          $user1 = self::getDataGenerator()->create_user();
1779          $user2 = self::getDataGenerator()->create_user();
1780          $user3 = self::getDataGenerator()->create_user();
1781          $user4 = self::getDataGenerator()->create_user();
1782  
1783          // Login as user1.
1784          $this->setUser($user1);
1785          \core_message\api::add_contact($user1->id, $user2->id);
1786          \core_message\api::add_contact($user1->id, $user3->id);
1787  
1788          // User user1 does not interchange messages with user3.
1789          $m1to2 = message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
1790          $m2to3 = message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
1791          $m3to2 = message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
1792          $m3to4 = message_post_message($user3, $user4, 'some random text 4', FORMAT_MOODLE);
1793  
1794          // Retrieve all messages sent by user2 (they are currently unread).
1795          $lastmessages = message_get_messages($user1->id, $user2->id, 0, MESSAGE_GET_UNREAD);
1796  
1797          // Delete a message not read, as a user from.
1798          $result = core_message_external::delete_message($m1to2, $user1->id, false);
1799          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1800          $this->assertTrue($result['status']);
1801          $this->assertCount(0, $result['warnings']);
1802          $mua = $DB->get_record('message_user_actions', array('messageid' => $m1to2, 'userid' => $user1->id));
1803          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua->action);
1804  
1805          // Try to delete the same message again.
1806          $result = core_message_external::delete_message($m1to2, $user1->id, false);
1807          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1808          $this->assertFalse($result['status']);
1809  
1810          // Try to delete a message that does not belong to me.
1811          try {
1812              $messageid = core_message_external::delete_message($m2to3, $user3->id, false);
1813              $this->fail('Exception expected due invalid messageid.');
1814          } catch (\moodle_exception $e) {
1815              $this->assertEquals('You do not have permission to delete this message', $e->errorcode);
1816          }
1817  
1818          $this->setUser($user3);
1819          // Delete a message not read, as a user to.
1820          $result = core_message_external::delete_message($m2to3, $user3->id, false);
1821          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1822          $this->assertTrue($result['status']);
1823          $this->assertCount(0, $result['warnings']);
1824          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m2to3, 'userid' => $user3->id,
1825              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
1826  
1827          // Delete a message read.
1828          $message = $DB->get_record('messages', ['id' => $m3to2]);
1829          \core_message\api::mark_message_as_read($user3->id, $message, time());
1830          $result = core_message_external::delete_message($m3to2, $user3->id);
1831          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1832          $this->assertTrue($result['status']);
1833          $this->assertCount(0, $result['warnings']);
1834          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m3to2, 'userid' => $user3->id,
1835              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
1836  
1837          // Invalid message ids.
1838          try {
1839              $result = core_message_external::delete_message(-1, $user1->id);
1840              $this->fail('Exception expected due invalid messageid.');
1841          } catch (\dml_missing_record_exception $e) {
1842              $this->assertEquals('invalidrecord', $e->errorcode);
1843          }
1844  
1845          // Invalid user.
1846          try {
1847              $result = core_message_external::delete_message($m1to2, -1, false);
1848              $this->fail('Exception expected due invalid user.');
1849          } catch (\moodle_exception $e) {
1850              $this->assertEquals('invaliduser', $e->errorcode);
1851          }
1852  
1853          // Not active user.
1854          delete_user($user2);
1855          try {
1856              $result = core_message_external::delete_message($m1to2, $user2->id, false);
1857              $this->fail('Exception expected due invalid user.');
1858          } catch (\moodle_exception $e) {
1859              $this->assertEquals('userdeleted', $e->errorcode);
1860          }
1861  
1862          // Now, as an admin, try to delete any message.
1863          $this->setAdminUser();
1864          $result = core_message_external::delete_message($m3to4, $user4->id, false);
1865          $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
1866          $this->assertTrue($result['status']);
1867          $this->assertCount(0, $result['warnings']);
1868          $this->assertTrue($DB->record_exists('message_user_actions', array('messageid' => $m3to4, 'userid' => $user4->id,
1869              'action' => \core_message\api::MESSAGE_ACTION_DELETED)));
1870  
1871      }
1872  
1873      public function test_mark_all_notifications_as_read_invalid_user_exception() {
1874          $this->resetAfterTest(true);
1875  
1876          $this->expectException('moodle_exception');
1877          core_message_external::mark_all_notifications_as_read(-2132131, 0);
1878      }
1879  
1880      public function test_mark_all_notifications_as_read_access_denied_exception() {
1881          $this->resetAfterTest(true);
1882  
1883          $sender = $this->getDataGenerator()->create_user();
1884          $user = $this->getDataGenerator()->create_user();
1885  
1886          $this->setUser($user);
1887          $this->expectException('moodle_exception');
1888          core_message_external::mark_all_notifications_as_read($sender->id, 0);
1889      }
1890  
1891      public function test_mark_all_notifications_as_read_missing_from_user_exception() {
1892          $this->resetAfterTest(true);
1893  
1894          $sender = $this->getDataGenerator()->create_user();
1895  
1896          $this->setUser($sender);
1897          $this->expectException('moodle_exception');
1898          core_message_external::mark_all_notifications_as_read($sender->id, 99999);
1899      }
1900  
1901      public function test_mark_all_notifications_as_read() {
1902          global $DB;
1903  
1904          $this->resetAfterTest(true);
1905  
1906          $sender1 = $this->getDataGenerator()->create_user();
1907          $sender2 = $this->getDataGenerator()->create_user();
1908          $sender3 = $this->getDataGenerator()->create_user();
1909          $recipient = $this->getDataGenerator()->create_user();
1910  
1911          $this->setUser($recipient);
1912  
1913          $this->send_message($sender1, $recipient, 'Notification', 1);
1914          $this->send_message($sender1, $recipient, 'Notification', 1);
1915          $this->send_message($sender2, $recipient, 'Notification', 1);
1916          $this->send_message($sender2, $recipient, 'Notification', 1);
1917          $this->send_message($sender3, $recipient, 'Notification', 1);
1918          $this->send_message($sender3, $recipient, 'Notification', 1);
1919  
1920          core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id);
1921          $readnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', [$recipient->id]);
1922          $unreadnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NULL', [$recipient->id]);
1923  
1924          $this->assertCount(2, $readnotifications);
1925          $this->assertCount(4, $unreadnotifications);
1926  
1927          core_message_external::mark_all_notifications_as_read($recipient->id, 0);
1928          $readnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', [$recipient->id]);
1929          $unreadnotifications = $DB->get_records_select('notifications', 'useridto = ? AND timeread IS NULL', [$recipient->id]);
1930  
1931          $this->assertCount(6, $readnotifications);
1932          $this->assertCount(0, $unreadnotifications);
1933      }
1934  
1935      public function test_mark_all_notifications_as_read_time_created_to() {
1936          global $DB;
1937  
1938          $this->resetAfterTest(true);
1939  
1940          $sender1 = $this->getDataGenerator()->create_user();
1941          $sender2 = $this->getDataGenerator()->create_user();
1942  
1943          $recipient = $this->getDataGenerator()->create_user();
1944          $this->setUser($recipient);
1945  
1946          // Record messages as sent on one second intervals.
1947          $time = time();
1948  
1949          $this->send_message($sender1, $recipient, 'Message 1', 1, $time);
1950          $this->send_message($sender2, $recipient, 'Message 2', 1, $time + 1);
1951          $this->send_message($sender1, $recipient, 'Message 3', 1, $time + 2);
1952          $this->send_message($sender2, $recipient, 'Message 4', 1, $time + 3);
1953  
1954          // Mark notifications sent from sender1 up until the second message; should only mark the first notification as read.
1955          core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id, $time + 1);
1956  
1957          $params = [$recipient->id];
1958  
1959          $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
1960          $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
1961  
1962          // Mark all notifications as read from any sender up to the time the third message was sent.
1963          core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 2);
1964  
1965          $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
1966          $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
1967  
1968          // Mark all notifications as read from any sender with a time after all messages were sent.
1969          core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 10);
1970  
1971          $this->assertEquals(4, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
1972          $this->assertEquals(0, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
1973      }
1974  
1975      /**
1976       * Test get_user_notification_preferences
1977       */
1978      public function test_get_user_notification_preferences() {
1979          $this->resetAfterTest(true);
1980  
1981          $user = self::getDataGenerator()->create_user();
1982          $this->setUser($user);
1983  
1984          // Set a couple of preferences to test.
1985          set_user_preference('message_provider_mod_assign_assign_notification_enabled', 'popup', $user);
1986  
1987          $prefs = core_message_external::get_user_notification_preferences();
1988          $prefs = external_api::clean_returnvalue(core_message_external::get_user_notification_preferences_returns(), $prefs);
1989          // Check processors.
1990          $this->assertGreaterThanOrEqual(2, count($prefs['preferences']['processors']));
1991          $this->assertEquals($user->id, $prefs['preferences']['userid']);
1992  
1993          // Check components.
1994          $this->assertGreaterThanOrEqual(8, count($prefs['preferences']['components']));
1995  
1996          // Check some preferences that we previously set.
1997          $found = 0;
1998          foreach ($prefs['preferences']['components'] as $component) {
1999              foreach ($component['notifications'] as $prefdata) {
2000                  if ($prefdata['preferencekey'] != 'message_provider_mod_assign_assign_notification') {
2001                      continue;
2002                  }
2003                  foreach ($prefdata['processors'] as $processor) {
2004                      if ($processor['name'] == 'popup') {
2005                          $this->assertTrue($processor['enabled']);
2006                          $found = 1;
2007                      }
2008                  }
2009              }
2010          }
2011          $this->assertEquals(1, $found);
2012      }
2013  
2014      /**
2015       * Test get_user_notification_preferences permissions
2016       */
2017      public function test_get_user_notification_preferences_permissions() {
2018          $this->resetAfterTest(true);
2019  
2020          $user = self::getDataGenerator()->create_user();
2021          $otheruser = self::getDataGenerator()->create_user();
2022          $this->setUser($user);
2023  
2024          $this->expectException('moodle_exception');
2025          $prefs = core_message_external::get_user_notification_preferences($otheruser->id);
2026      }
2027  
2028      /**
2029       * Tests searching for users when site-wide messaging is disabled.
2030       *
2031       * This test verifies that any contacts are returned, as well as any non-contacts whose profile we can view.
2032       * If checks this by placing some users in the same course, where default caps would permit a user to view another user's
2033       * profile.
2034       */
2035      public function test_message_search_users_messagingallusers_disabled() {
2036          global $DB;
2037          $this->resetAfterTest();
2038  
2039          // Create some users.
2040          $users = [];
2041          foreach (range(1, 8) as $i) {
2042              $user = new \stdClass();
2043              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2044              $user->lastname = $i;
2045              $user = $this->getDataGenerator()->create_user($user);
2046              $users[$i] = $user;
2047          }
2048  
2049          // Enrol a few users in the same course, but leave them as non-contacts.
2050          $course1 = $this->getDataGenerator()->create_course();
2051          $course2 = $this->getDataGenerator()->create_course();
2052  
2053          $this->setAdminUser();
2054          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2055          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2056          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2057  
2058          // Add some other users as contacts.
2059          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2060          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2061          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2062  
2063          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
2064          $this->getDataGenerator()->enrol_user($users[8]->id, $course2->id, 'editingteacher');
2065          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2066          set_config('coursecontact', $teacherrole->id);
2067  
2068          // Create individual conversations between some users, one contact and one non-contact.
2069          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2070              [$users[1]->id, $users[2]->id]);
2071          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2072              [$users[6]->id, $users[1]->id]);
2073  
2074          // Create a group conversation between 4 users, including a contact and a non-contact.
2075          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2076              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id], 'Project chat');
2077  
2078          // Set as the user performing the search.
2079          $this->setUser($users[1]);
2080  
2081          // Perform a search with $CFG->messagingallusers disabled.
2082          set_config('messagingallusers', 0);
2083          $result = core_message_external::message_search_users($users[1]->id, 'search');
2084          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2085  
2086          // Confirm that we returns contacts and non-contacts.
2087          $this->assertArrayHasKey('contacts', $result);
2088          $this->assertArrayHasKey('noncontacts', $result);
2089          $contacts = $result['contacts'];
2090          $noncontacts = $result['noncontacts'];
2091  
2092          // Check that we retrieved the correct contacts.
2093          $this->assertCount(2, $contacts);
2094          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2095          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2096  
2097          // Verify the correct conversations were returned for the contacts.
2098          $this->assertCount(2, $contacts[0]['conversations']);
2099          // We can't rely on the ordering of conversations within the results, so sort by id first.
2100          usort($contacts[0]['conversations'], function($a, $b) {
2101              return $b['id'] <=> $a['id'];
2102          });
2103          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]['conversations'][0]['type']);
2104          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]['conversations'][1]['type']);
2105  
2106          $this->assertCount(0, $contacts[1]['conversations']);
2107  
2108          // Check that we retrieved the correct non-contacts.
2109          // When site wide messaging is disabled, we expect to see only those users who we share a course with and whose profiles
2110          // are visible in that course. This excludes users like course contacts.
2111          $this->assertCount(3, $noncontacts);
2112          // Self-conversation first.
2113          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2114          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2115          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2116  
2117          // Verify the correct conversations were returned for the non-contacts.
2118          $this->assertCount(1, $noncontacts[1]['conversations']);
2119          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $noncontacts[1]['conversations'][0]['type']);
2120  
2121          $this->assertCount(1, $noncontacts[2]['conversations']);
2122          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]['conversations'][0]['type']);
2123      }
2124  
2125      /**
2126       * Tests searching for users when site-wide messaging is enabled.
2127       *
2128       * This test verifies that any contacts are returned, as well as any non-contacts, regardless of whether the searching user
2129       * can view their respective profile.
2130       */
2131      public function test_message_search_users_messagingallusers_enabled() {
2132          global $DB;
2133          $this->resetAfterTest();
2134  
2135          // Create some users.
2136          $users = [];
2137          foreach (range(1, 9) as $i) {
2138              $user = new \stdClass();
2139              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2140              $user->lastname = $i;
2141              $user = $this->getDataGenerator()->create_user($user);
2142              $users[$i] = $user;
2143          }
2144  
2145          // Enrol a few users in the same course, but leave them as non-contacts.
2146          $course1 = $this->getDataGenerator()->create_course();
2147          $course2 = $this->getDataGenerator()->create_course();
2148  
2149          $this->setAdminUser();
2150          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2151          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2152          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2153  
2154          // Add some other users as contacts.
2155          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2156          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2157          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2158  
2159          // Enrol a user as a teacher in the course, and make the teacher role a course contact role.
2160          $this->getDataGenerator()->enrol_user($users[9]->id, $course2->id, 'editingteacher');
2161          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
2162          set_config('coursecontact', $teacherrole->id);
2163  
2164          // Create individual conversations between some users, one contact and one non-contact.
2165          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2166              [$users[1]->id, $users[2]->id]);
2167          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
2168              [$users[6]->id, $users[1]->id]);
2169  
2170          // Create a group conversation between 5 users, including a contact and a non-contact, and a user NOT in a shared course.
2171          \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2172              [$users[1]->id, $users[2]->id, $users[4]->id, $users[7]->id, $users[8]->id], 'Project chat');
2173  
2174          // Set as the user performing the search.
2175          $this->setUser($users[1]);
2176  
2177          // Perform a search with $CFG->messagingallusers enabled.
2178          set_config('messagingallusers', 1);
2179          $result = core_message_external::message_search_users($users[1]->id, 'search');
2180          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2181  
2182          // Confirm that we returns contacts and non-contacts.
2183          $this->assertArrayHasKey('contacts', $result);
2184          $this->assertArrayHasKey('noncontacts', $result);
2185          $contacts = $result['contacts'];
2186          $noncontacts = $result['noncontacts'];
2187  
2188          // Check that we retrieved the correct contacts.
2189          $this->assertCount(2, $contacts);
2190          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2191          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2192  
2193          // Verify the correct conversations were returned for the contacts.
2194          $this->assertCount(2, $contacts[0]['conversations']);
2195          // We can't rely on the ordering of conversations within the results, so sort by id first.
2196          usort($contacts[0]['conversations'], function($a, $b) {
2197              return $b['id'] <=> $a['id'];
2198          });
2199          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $contacts[0]['conversations'][0]['type']);
2200          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $contacts[0]['conversations'][1]['type']);
2201  
2202          $this->assertCount(0, $contacts[1]['conversations']);
2203  
2204          // Check that we retrieved the correct non-contacts.
2205          // If site wide messaging is enabled, we expect to be able to search for any users whose profiles we can view.
2206          // In this case, as a student, that's the course contact for course2 and those noncontacts sharing a course with user1.
2207          $this->assertCount(4, $noncontacts);
2208          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2209          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2210          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2211          $this->assertEquals($users[9]->id, $noncontacts[3]['id']);
2212  
2213          // Verify the correct conversations were returned for the non-contacts.
2214          $this->assertCount(1, $noncontacts[1]['conversations']);
2215          $this->assertCount(1, $noncontacts[2]['conversations']);
2216          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $noncontacts[1]['conversations'][0]['type']);
2217          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, $noncontacts[2]['conversations'][0]['type']);
2218          $this->assertCount(0, $noncontacts[3]['conversations']);
2219      }
2220  
2221      /**
2222       * Verify searching for users find themselves when they have self-conversations.
2223       */
2224      public function test_message_search_users_self_conversations() {
2225          $this->resetAfterTest();
2226  
2227          // Create some users.
2228          $user1 = new \stdClass();
2229          $user1->firstname = 'User';
2230          $user1->lastname = 'One';
2231          $user1 = $this->getDataGenerator()->create_user($user1);
2232          $user2 = new \stdClass();
2233          $user2->firstname = 'User';
2234          $user2->lastname = 'Two';
2235          $user2 = $this->getDataGenerator()->create_user($user2);
2236  
2237          // Get self-conversation for user1.
2238          $sc1 = \core_message\api::get_self_conversation($user1->id);
2239          testhelper::send_fake_message_to_conversation($user1, $sc1->id, 'Hi myself!');
2240  
2241          // Perform a search as user1.
2242          $this->setUser($user1);
2243          $result = core_message_external::message_search_users($user1->id, 'One');
2244          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2245  
2246          // Check results are empty.
2247          $this->assertCount(0, $result['contacts']);
2248          $this->assertCount(1, $result['noncontacts']);
2249      }
2250  
2251      /**
2252       * Verify searching for users works even if no matching users from either contacts, or non-contacts can be found.
2253       */
2254      public function test_message_search_users_with_empty_result() {
2255          $this->resetAfterTest();
2256  
2257          // Create some users, but make sure neither will match the search term.
2258          $user1 = new \stdClass();
2259          $user1->firstname = 'User';
2260          $user1->lastname = 'One';
2261          $user1 = $this->getDataGenerator()->create_user($user1);
2262          $user2 = new \stdClass();
2263          $user2->firstname = 'User';
2264          $user2->lastname = 'Two';
2265          $user2 = $this->getDataGenerator()->create_user($user2);
2266  
2267          // Perform a search as user1.
2268          $this->setUser($user1);
2269          $result = core_message_external::message_search_users($user1->id, 'search');
2270          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2271  
2272          // Check results are empty.
2273          $this->assertCount(0, $result['contacts']);
2274          $this->assertCount(0, $result['noncontacts']);
2275      }
2276  
2277      /**
2278       * Test verifying that limits and offsets work for both the contacts and non-contacts return data.
2279       */
2280      public function test_message_search_users_limit_offset() {
2281          $this->resetAfterTest();
2282  
2283          // Create 20 users.
2284          $users = [];
2285          foreach (range(1, 20) as $i) {
2286              $user = new \stdClass();
2287              $user->firstname = "User search";
2288              $user->lastname = $i;
2289              $user = $this->getDataGenerator()->create_user($user);
2290              $users[$i] = $user;
2291          }
2292  
2293          // Enrol the first 8 users in the same course, but leave them as non-contacts.
2294          $this->setAdminUser();
2295          $course1 = $this->getDataGenerator()->create_course();
2296          foreach (range(1, 8) as $i) {
2297              $this->getDataGenerator()->enrol_user($users[$i]->id, $course1->id);
2298          }
2299  
2300          // Add 5 users, starting at the 11th user, as contacts for user1.
2301          foreach (range(11, 15) as $i) {
2302              \core_message\api::add_contact($users[1]->id, $users[$i]->id);
2303          }
2304  
2305          // Set as the user performing the search.
2306          $this->setUser($users[1]);
2307  
2308          // Search using a limit of 3.
2309          // This tests the case where we have more results than the limit for both contacts and non-contacts.
2310          $result = core_message_external::message_search_users($users[1]->id, 'search', 0, 3);
2311          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2312          $contacts = $result['contacts'];
2313          $noncontacts = $result['noncontacts'];
2314  
2315          // Check that we retrieved the correct contacts.
2316          $this->assertCount(3, $contacts);
2317          $this->assertEquals($users[11]->id, $contacts[0]['id']);
2318          $this->assertEquals($users[12]->id, $contacts[1]['id']);
2319          $this->assertEquals($users[13]->id, $contacts[2]['id']);
2320  
2321          // Check that we retrieved the correct non-contacts.
2322          // Consider first conversation is self-conversation.
2323          $this->assertCount(3, $noncontacts);
2324          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2325          $this->assertEquals($users[2]->id, $noncontacts[1]['id']);
2326          $this->assertEquals($users[3]->id, $noncontacts[2]['id']);
2327  
2328          // Now, offset to get the next batch of results.
2329          // We expect to see 2 contacts, and 3 non-contacts.
2330          $result = core_message_external::message_search_users($users[1]->id, 'search', 3, 3);
2331          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2332          $contacts = $result['contacts'];
2333          $noncontacts = $result['noncontacts'];
2334          $this->assertCount(2, $contacts);
2335          $this->assertEquals($users[14]->id, $contacts[0]['id']);
2336          $this->assertEquals($users[15]->id, $contacts[1]['id']);
2337  
2338          $this->assertCount(3, $noncontacts);
2339          $this->assertEquals($users[4]->id, $noncontacts[0]['id']);
2340          $this->assertEquals($users[5]->id, $noncontacts[1]['id']);
2341          $this->assertEquals($users[6]->id, $noncontacts[2]['id']);
2342  
2343          // Now, offset to get the next batch of results.
2344          // We expect to see 0 contacts, and 2 non-contacts.
2345          $result = core_message_external::message_search_users($users[1]->id, 'search', 6, 3);
2346          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2347          $contacts = $result['contacts'];
2348          $noncontacts = $result['noncontacts'];
2349          $this->assertCount(0, $contacts);
2350  
2351          $this->assertCount(2, $noncontacts);
2352          $this->assertEquals($users[7]->id, $noncontacts[0]['id']);
2353          $this->assertEquals($users[8]->id, $noncontacts[1]['id']);
2354      }
2355  
2356      /**
2357       * Tests searching users as another user having the 'moodle/user:viewdetails' capability.
2358       */
2359      public function test_message_search_users_with_cap() {
2360          $this->resetAfterTest();
2361          global $DB;
2362  
2363          // Create some users.
2364          $users = [];
2365          foreach (range(1, 8) as $i) {
2366              $user = new \stdClass();
2367              $user->firstname = ($i == 4) ? 'User' : 'User search'; // Ensure the fourth user won't match the search term.
2368              $user->lastname = $i;
2369              $user = $this->getDataGenerator()->create_user($user);
2370              $users[$i] = $user;
2371          }
2372  
2373          // Enrol a few users in the same course, but leave them as non-contacts.
2374          $course1 = $this->getDataGenerator()->create_course();
2375          $this->setAdminUser();
2376          $this->getDataGenerator()->enrol_user($users[1]->id, $course1->id);
2377          $this->getDataGenerator()->enrol_user($users[6]->id, $course1->id);
2378          $this->getDataGenerator()->enrol_user($users[7]->id, $course1->id);
2379  
2380          // Add some other users as contacts.
2381          \core_message\api::add_contact($users[1]->id, $users[2]->id);
2382          \core_message\api::add_contact($users[3]->id, $users[1]->id);
2383          \core_message\api::add_contact($users[1]->id, $users[4]->id);
2384  
2385          // Set as the user performing the search.
2386          $this->setUser($users[1]);
2387  
2388          // Grant the authenticated user role the capability 'user:viewdetails' at site context.
2389          $authenticatedrole = $DB->get_record('role', ['shortname' => 'user'], '*', MUST_EXIST);
2390          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $authenticatedrole->id, \context_system::instance());
2391  
2392          // Perform a search with $CFG->messagingallusers disabled.
2393          set_config('messagingallusers', 0);
2394          $result = core_message_external::message_search_users($users[1]->id, 'search');
2395          $result = external_api::clean_returnvalue(core_message_external::message_search_users_returns(), $result);
2396          $contacts = $result['contacts'];
2397          $noncontacts = $result['noncontacts'];
2398  
2399          // Check that we retrieved the correct contacts.
2400          $this->assertCount(2, $contacts);
2401          $this->assertEquals($users[2]->id, $contacts[0]['id']);
2402          $this->assertEquals($users[3]->id, $contacts[1]['id']);
2403  
2404          // Check that we retrieved the correct non-contacts.
2405          // Site-wide messaging is disabled, so we expect to be able to search for any users whose profile we can view.
2406          // Consider first conversations is self-conversation.
2407          $this->assertCount(3, $noncontacts);
2408          $this->assertEquals($users[1]->id, $noncontacts[0]['id']);
2409          $this->assertEquals($users[6]->id, $noncontacts[1]['id']);
2410          $this->assertEquals($users[7]->id, $noncontacts[2]['id']);
2411      }
2412  
2413      /**
2414       * Tests searching users as another user without the 'moodle/user:viewdetails' capability.
2415       */
2416      public function test_message_search_users_without_cap() {
2417          $this->resetAfterTest();
2418  
2419          // Create some users.
2420          $user1 = $this->getDataGenerator()->create_user();
2421          $user2 = $this->getDataGenerator()->create_user();
2422  
2423          // The person doing the search for another user.
2424          $this->setUser($user1);
2425  
2426          // Ensure an exception is thrown.
2427          $this->expectException('moodle_exception');
2428          core_message_external::message_search_users($user2->id, 'User');
2429          $this->assertDebuggingCalled();
2430      }
2431  
2432      /**
2433       * Tests searching users with messaging disabled.
2434       */
2435      public function test_message_search_users_messaging_disabled() {
2436          $this->resetAfterTest();
2437  
2438          // Create some skeleton data just so we can call the WS.
2439          $user = $this->getDataGenerator()->create_user();
2440  
2441          // The person doing the search.
2442          $this->setUser($user);
2443  
2444          // Disable messaging.
2445          set_config('messaging', 0);
2446  
2447          // Ensure an exception is thrown.
2448          $this->expectException('moodle_exception');
2449          core_message_external::message_search_users($user->id, 'User');
2450      }
2451  
2452      /**
2453       * Tests searching messages.
2454       */
2455      public function test_messagearea_search_messages() {
2456          $this->resetAfterTest(true);
2457  
2458          // Create some users.
2459          $user1 = self::getDataGenerator()->create_user();
2460          $user2 = self::getDataGenerator()->create_user();
2461  
2462          // The person doing the search.
2463          $this->setUser($user1);
2464  
2465          // Send some messages back and forth.
2466          $time = time();
2467          $this->send_message($user1, $user2, 'Yo!', 0, $time);
2468          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
2469          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
2470          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
2471          $convid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
2472  
2473          // Perform a search.
2474          $result = core_message_external::data_for_messagearea_search_messages($user1->id, 'o');
2475  
2476          // We need to execute the return values cleaning process to simulate the web service server.
2477          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_messages_returns(), $result);
2478  
2479          // Confirm the data is correct.
2480          $messages = $result['contacts'];
2481          $this->assertCount(2, $messages);
2482  
2483          $message1 = $messages[0];
2484          $message2 = $messages[1];
2485  
2486          $this->assertEquals($user2->id, $message1['userid']);
2487          $this->assertEquals(fullname($user2), $message1['fullname']);
2488          $this->assertTrue($message1['ismessaging']);
2489          $this->assertFalse($message1['sentfromcurrentuser']);
2490          $this->assertEquals('Word.', $message1['lastmessage']);
2491          $this->assertNotEmpty($message1['messageid']);
2492          $this->assertNull($message1['isonline']);
2493          $this->assertFalse($message1['isread']);
2494          $this->assertFalse($message1['isblocked']);
2495          $this->assertNull($message1['unreadcount']);
2496          $this->assertEquals($convid, $message1['conversationid']);
2497  
2498          $this->assertEquals($user2->id, $message2['userid']);
2499          $this->assertEquals(fullname($user2), $message2['fullname']);
2500          $this->assertTrue($message2['ismessaging']);
2501          $this->assertTrue($message2['sentfromcurrentuser']);
2502          $this->assertEquals('Yo!', $message2['lastmessage']);
2503          $this->assertNotEmpty($message2['messageid']);
2504          $this->assertNull($message2['isonline']);
2505          $this->assertTrue($message2['isread']);
2506          $this->assertFalse($message2['isblocked']);
2507          $this->assertNull($message2['unreadcount']);
2508          $this->assertEquals($convid, $message2['conversationid']);
2509      }
2510  
2511      /**
2512       * Tests searching messages as another user.
2513       */
2514      public function test_messagearea_search_messages_as_other_user() {
2515          $this->resetAfterTest(true);
2516  
2517          // The person doing the search.
2518          $this->setAdminUser();
2519  
2520          // Create some users.
2521          $user1 = self::getDataGenerator()->create_user();
2522          $user2 = self::getDataGenerator()->create_user();
2523  
2524          // Send some messages back and forth.
2525          $time = time();
2526          $this->send_message($user1, $user2, 'Yo!', 0, $time);
2527          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
2528          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
2529          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
2530  
2531          // Perform a search.
2532          $result = core_message_external::data_for_messagearea_search_messages($user1->id, 'o');
2533  
2534          // We need to execute the return values cleaning process to simulate the web service server.
2535          $result = external_api::clean_returnvalue(core_message_external::data_for_messagearea_search_messages_returns(),
2536              $result);
2537  
2538          // Confirm the data is correct.
2539          $messages = $result['contacts'];
2540          $this->assertCount(2, $messages);
2541  
2542          $message1 = $messages[0];
2543          $message2 = $messages[1];
2544  
2545          $this->assertEquals($user2->id, $message1['userid']);
2546          $this->assertEquals(fullname($user2), $message1['fullname']);
2547          $this->assertTrue($message1['ismessaging']);
2548          $this->assertFalse($message1['sentfromcurrentuser']);
2549          $this->assertEquals('Word.', $message1['lastmessage']);
2550          $this->assertNotEmpty($message1['messageid']);
2551          $this->assertFalse($message1['isonline']);
2552          $this->assertFalse($message1['isread']);
2553          $this->assertFalse($message1['isblocked']);
2554          $this->assertNull($message1['unreadcount']);
2555  
2556          $this->assertEquals($user2->id, $message2['userid']);
2557          $this->assertEquals(fullname($user2), $message2['fullname']);
2558          $this->assertTrue($message2['ismessaging']);
2559          $this->assertTrue($message2['sentfromcurrentuser']);
2560          $this->assertEquals('Yo!', $message2['lastmessage']);
2561          $this->assertNotEmpty($message2['messageid']);
2562          $this->assertFalse($message2['isonline']);
2563          $this->assertTrue($message2['isread']);
2564          $this->assertFalse($message2['isblocked']);
2565          $this->assertNull($message2['unreadcount']);
2566      }
2567  
2568      /**
2569       * Tests searching messages as another user without the proper capabilities.
2570       */
2571      public function test_messagearea_search_messages_as_other_user_without_cap() {
2572          $this->resetAfterTest(true);
2573  
2574          // Create some users.
2575          $user1 = self::getDataGenerator()->create_user();
2576          $user2 = self::getDataGenerator()->create_user();
2577  
2578          // The person doing the search for another user.
2579          $this->setUser($user1);
2580  
2581          // Ensure an exception is thrown.
2582          $this->expectException('moodle_exception');
2583          core_message_external::data_for_messagearea_search_messages($user2->id, 'Search');
2584      }
2585  
2586      /**
2587       * Tests searching messages with messaging disabled
2588       */
2589      public function test_messagearea_search_messages_messaging_disabled() {
2590          global $CFG;
2591  
2592          $this->resetAfterTest(true);
2593  
2594          // Create some skeleton data just so we can call the WS.
2595          $user = self::getDataGenerator()->create_user();
2596  
2597          // The person doing the search .
2598          $this->setUser($user);
2599  
2600          // Disable messaging.
2601          $CFG->messaging = 0;
2602  
2603          // Ensure an exception is thrown.
2604          $this->expectException('moodle_exception');
2605          core_message_external::data_for_messagearea_search_messages($user->id, 'Search');
2606      }
2607  
2608      /**
2609       * Tests retrieving contacts.
2610       */
2611      public function test_get_user_contacts() {
2612          $this->resetAfterTest(true);
2613  
2614          // Create some users.
2615          $user1 = self::getDataGenerator()->create_user();
2616  
2617          // Set as the user.
2618          $this->setUser($user1);
2619  
2620          $user2 = new \stdClass();
2621          $user2->firstname = 'User';
2622          $user2->lastname = 'A';
2623          $user2 = self::getDataGenerator()->create_user($user2);
2624  
2625          $user3 = new \stdClass();
2626          $user3->firstname = 'User';
2627          $user3->lastname = 'B';
2628          $user3 = self::getDataGenerator()->create_user($user3);
2629  
2630          $user4 = new \stdClass();
2631          $user4->firstname = 'User';
2632          $user4->lastname = 'C';
2633          $user4 = self::getDataGenerator()->create_user($user4);
2634  
2635          $user5 = new \stdClass();
2636          $user5->firstname = 'User';
2637          $user5->lastname = 'D';
2638          $user5 = self::getDataGenerator()->create_user($user5);
2639  
2640          // Add some users as contacts.
2641          \core_message\api::add_contact($user1->id, $user2->id);
2642          \core_message\api::add_contact($user1->id, $user3->id);
2643          \core_message\api::add_contact($user1->id, $user4->id);
2644  
2645          // Retrieve the contacts.
2646          $result = core_message_external::get_user_contacts($user1->id);
2647  
2648          // We need to execute the return values cleaning process to simulate the web service server.
2649          $result = external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(),
2650              $result);
2651  
2652          // Confirm the data is correct.
2653          $contacts = $result;
2654          usort($contacts, [static::class, 'sort_contacts_id']);
2655          $this->assertCount(3, $contacts);
2656  
2657          $contact1 = array_shift($contacts);
2658          $contact2 = array_shift($contacts);
2659          $contact3 = array_shift($contacts);
2660  
2661          $this->assertEquals($user2->id, $contact1['id']);
2662          $this->assertEquals(fullname($user2), $contact1['fullname']);
2663          $this->assertTrue($contact1['iscontact']);
2664  
2665          $this->assertEquals($user3->id, $contact2['id']);
2666          $this->assertEquals(fullname($user3), $contact2['fullname']);
2667          $this->assertTrue($contact2['iscontact']);
2668  
2669          $this->assertEquals($user4->id, $contact3['id']);
2670          $this->assertEquals(fullname($user4), $contact3['fullname']);
2671          $this->assertTrue($contact3['iscontact']);
2672      }
2673  
2674      /**
2675       * Tests retrieving contacts as another user.
2676       */
2677      public function test_get_user_contacts_as_other_user() {
2678          $this->resetAfterTest(true);
2679  
2680          $this->setAdminUser();
2681  
2682          // Create some users.
2683          $user1 = self::getDataGenerator()->create_user();
2684  
2685          $user2 = new \stdClass();
2686          $user2->firstname = 'User';
2687          $user2->lastname = 'A';
2688          $user2 = self::getDataGenerator()->create_user($user2);
2689  
2690          $user3 = new \stdClass();
2691          $user3->firstname = 'User';
2692          $user3->lastname = 'B';
2693          $user3 = self::getDataGenerator()->create_user($user3);
2694  
2695          $user4 = new \stdClass();
2696          $user4->firstname = 'User';
2697          $user4->lastname = 'C';
2698          $user4 = self::getDataGenerator()->create_user($user4);
2699  
2700          $user5 = new \stdClass();
2701          $user5->firstname = 'User';
2702          $user5->lastname = 'D';
2703          $user5 = self::getDataGenerator()->create_user($user5);
2704  
2705          // Add some users as contacts.
2706          \core_message\api::add_contact($user1->id, $user2->id);
2707          \core_message\api::add_contact($user1->id, $user3->id);
2708          \core_message\api::add_contact($user1->id, $user4->id);
2709  
2710          // Retrieve the contacts.
2711          $result = core_message_external::get_user_contacts($user1->id);
2712  
2713          // We need to execute the return values cleaning process to simulate the web service server.
2714          $result = external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(),
2715              $result);
2716  
2717          // Confirm the data is correct.
2718          $contacts = $result;
2719          usort($contacts, [static::class, 'sort_contacts_id']);
2720          $this->assertCount(3, $contacts);
2721  
2722          $contact1 = array_shift($contacts);
2723          $contact2 = array_shift($contacts);
2724          $contact3 = array_shift($contacts);
2725  
2726          $this->assertEquals($user2->id, $contact1['id']);
2727          $this->assertEquals(fullname($user2), $contact1['fullname']);
2728          $this->assertTrue($contact1['iscontact']);
2729  
2730          $this->assertEquals($user3->id, $contact2['id']);
2731          $this->assertEquals(fullname($user3), $contact2['fullname']);
2732          $this->assertTrue($contact2['iscontact']);
2733  
2734          $this->assertEquals($user4->id, $contact3['id']);
2735          $this->assertEquals(fullname($user4), $contact3['fullname']);
2736          $this->assertTrue($contact3['iscontact']);
2737      }
2738  
2739      /**
2740       * Tests retrieving contacts as another user without the proper capabilities.
2741       */
2742      public function test_get_user_contacts_as_other_user_without_cap() {
2743          $this->resetAfterTest(true);
2744  
2745          // Create some users.
2746          $user1 = self::getDataGenerator()->create_user();
2747          $user2 = self::getDataGenerator()->create_user();
2748  
2749          // The person retrieving the contacts for another user.
2750          $this->setUser($user1);
2751  
2752          // Perform the WS call and ensure an exception is thrown.
2753          $this->expectException('moodle_exception');
2754          core_message_external::get_user_contacts($user2->id);
2755      }
2756  
2757      /**
2758       * Tests retrieving contacts with messaging disabled.
2759       */
2760      public function test_get_user_contacts_messaging_disabled() {
2761          global $CFG;
2762  
2763          $this->resetAfterTest(true);
2764  
2765          // Create some skeleton data just so we can call the WS.
2766          $user = self::getDataGenerator()->create_user();
2767  
2768          // The person retrieving the contacts.
2769          $this->setUser($user);
2770  
2771          // Disable messaging.
2772          $CFG->messaging = 0;
2773  
2774          // Perform the WS call and ensure we are shown that it is disabled.
2775          $this->expectException('moodle_exception');
2776          core_message_external::get_user_contacts($user->id);
2777      }
2778  
2779      /**
2780       * Test getting contacts when there are no results.
2781       */
2782      public function test_get_user_contacts_no_results() {
2783          $this->resetAfterTest();
2784  
2785          $user1 = self::getDataGenerator()->create_user();
2786  
2787          $this->setUser($user1);
2788  
2789          $requests = core_message_external::get_user_contacts($user1->id);
2790          $requests = external_api::clean_returnvalue(core_message_external::get_user_contacts_returns(), $requests);
2791  
2792          $this->assertEmpty($requests);
2793      }
2794  
2795      /**
2796       * Tests get_conversation_messages for retrieving messages.
2797       */
2798      public function test_get_conversation_messages() {
2799          $this->resetAfterTest(true);
2800  
2801          // Create some users.
2802          $user1 = self::getDataGenerator()->create_user();
2803          $user2 = self::getDataGenerator()->create_user();
2804          $user3 = self::getDataGenerator()->create_user();
2805          $user4 = self::getDataGenerator()->create_user();
2806          $user5 = self::getDataGenerator()->create_user();
2807  
2808          // Create group conversation.
2809          $conversation = \core_message\api::create_conversation(
2810              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2811              [$user1->id, $user2->id, $user3->id, $user4->id]
2812          );
2813  
2814          // The person asking for the messages.
2815          $this->setUser($user1);
2816  
2817          // Send some messages back and forth.
2818          $time = time();
2819          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time);
2820          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Sup mang?', $time + 1);
2821          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Writing PHPUnit tests!', $time + 2);
2822          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 3);
2823  
2824          // Retrieve the messages.
2825          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id);
2826  
2827          // We need to execute the return values cleaning process to simulate the web service server.
2828          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
2829              $result);
2830  
2831          // Check the results are correct.
2832          $this->assertEquals($conversation->id, $result['id']);
2833  
2834          // Confirm the members data is correct.
2835          $members = $result['members'];
2836          $this->assertCount(3, $members);
2837          $membersid = [$members[0]['id'], $members[1]['id'], $members[2]['id']];
2838          $this->assertContainsEquals($user1->id, $membersid);
2839          $this->assertContainsEquals($user2->id, $membersid);
2840          $this->assertContainsEquals($user3->id, $membersid);
2841  
2842          $membersfullnames = [$members[0]['fullname'], $members[1]['fullname'], $members[2]['fullname']];
2843          $this->assertContainsEquals(fullname($user1), $membersfullnames);
2844          $this->assertContainsEquals(fullname($user2), $membersfullnames);
2845          $this->assertContainsEquals(fullname($user3), $membersfullnames);
2846  
2847          // Confirm the messages data is correct.
2848          $messages = $result['messages'];
2849          $this->assertCount(4, $messages);
2850  
2851          $message1 = $messages[0];
2852          $message2 = $messages[1];
2853          $message3 = $messages[2];
2854          $message4 = $messages[3];
2855  
2856          $this->assertEquals($user1->id, $message1['useridfrom']);
2857          $this->assertStringContainsString('Yo!', $message1['text']);
2858  
2859          $this->assertEquals($user3->id, $message2['useridfrom']);
2860          $this->assertStringContainsString('Sup mang?', $message2['text']);
2861  
2862          $this->assertEquals($user2->id, $message3['useridfrom']);
2863          $this->assertStringContainsString('Writing PHPUnit tests!', $message3['text']);
2864  
2865          $this->assertEquals($user1->id, $message4['useridfrom']);
2866          $this->assertStringContainsString('Word.', $message4['text']);
2867      }
2868  
2869      /**
2870       * Tests get_conversation_messages for retrieving messages using timefrom parameter.
2871       */
2872      public function test_get_conversation_messages_timefrom() {
2873          $this->resetAfterTest(true);
2874  
2875          // Create some users.
2876          $user1 = self::getDataGenerator()->create_user();
2877          $user2 = self::getDataGenerator()->create_user();
2878          $user3 = self::getDataGenerator()->create_user();
2879          $user4 = self::getDataGenerator()->create_user();
2880  
2881          // Create group conversation.
2882          $conversation = \core_message\api::create_conversation(
2883              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2884              [$user1->id, $user2->id, $user3->id]
2885          );
2886  
2887          // The person asking for the messages.
2888          $this->setUser($user1);
2889  
2890          // Send some messages back and forth.
2891          $time = time();
2892          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time - 4);
2893          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time - 3);
2894          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 3', $time - 2);
2895          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 4', $time - 1);
2896  
2897          // Retrieve the messages from $time - 3, which should be the 3 most recent messages.
2898          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id, 0, 0, false, $time - 3);
2899  
2900          // We need to execute the return values cleaning process to simulate the web service server.
2901          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
2902              $result);
2903  
2904          // Check the results are correct.
2905          $this->assertEquals($conversation->id, $result['id']);
2906  
2907          // Confirm the messages data is correct.
2908          $messages = $result['messages'];
2909          $this->assertCount(3, $messages);
2910  
2911          $message1 = $messages[0];
2912          $message2 = $messages[1];
2913          $message3 = $messages[2];
2914  
2915          $this->assertStringContainsString('Message 2', $message1['text']);
2916          $this->assertStringContainsString('Message 3', $message2['text']);
2917          $this->assertStringContainsString('Message 4', $message3['text']);
2918  
2919          // Confirm the members data is correct.
2920          $members = $result['members'];
2921          $this->assertCount(1, $members);
2922          $this->assertEquals($user2->id, $members[0]['id']);
2923      }
2924  
2925      /**
2926       * Tests get_conversation_messages for retrieving messages as another user.
2927       */
2928      public function test_get_conversation_messages_as_other_user() {
2929          $this->resetAfterTest(true);
2930  
2931          // Set as admin.
2932          $this->setAdminUser();
2933  
2934          // Create some users.
2935          $user1 = self::getDataGenerator()->create_user();
2936          $user2 = self::getDataGenerator()->create_user();
2937          $user3 = self::getDataGenerator()->create_user();
2938          $user4 = self::getDataGenerator()->create_user();
2939  
2940          // Create group conversation.
2941          $conversation = \core_message\api::create_conversation(
2942              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
2943              [$user1->id, $user2->id, $user3->id, $user4->id]
2944          );
2945  
2946          // Send some messages back and forth.
2947          $time = time();
2948          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Yo!', $time);
2949          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Sup mang?', $time + 1);
2950          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Writing PHPUnit tests!', $time + 2);
2951          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Word.', $time + 3);
2952  
2953          // Retrieve the messages.
2954          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id);
2955  
2956          // We need to execute the return values cleaning process to simulate the web service server.
2957          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(),
2958              $result);
2959  
2960          // Check the results are correct.
2961          $this->assertEquals($conversation->id, $result['id']);
2962  
2963          // Confirm the members data is correct.
2964          $members = $result['members'];
2965          $this->assertCount(3, $members);
2966          $membersid = [$members[0]['id'], $members[1]['id'], $members[2]['id']];
2967          $this->assertContainsEquals($user1->id, $membersid);
2968          $this->assertContainsEquals($user2->id, $membersid);
2969          $this->assertContainsEquals($user3->id, $membersid);
2970  
2971          // Confirm the message data is correct.
2972          $messages = $result['messages'];
2973          $this->assertCount(4, $messages);
2974  
2975          $message1 = $messages[0];
2976          $message2 = $messages[1];
2977          $message3 = $messages[2];
2978          $message4 = $messages[3];
2979  
2980          $this->assertEquals($user1->id, $message1['useridfrom']);
2981          $this->assertStringContainsString('Yo!', $message1['text']);
2982  
2983          $this->assertEquals($user3->id, $message2['useridfrom']);
2984          $this->assertStringContainsString('Sup mang?', $message2['text']);
2985  
2986          $this->assertEquals($user2->id, $message3['useridfrom']);
2987          $this->assertStringContainsString('Writing PHPUnit tests!', $message3['text']);
2988  
2989          $this->assertEquals($user1->id, $message4['useridfrom']);
2990          $this->assertStringContainsString('Word.', $message4['text']);
2991      }
2992  
2993      /**
2994       * Tests get_conversation_messages for retrieving messages as another user without the proper capabilities.
2995       */
2996      public function test_get_conversation_messages_as_other_user_without_cap() {
2997          $this->resetAfterTest(true);
2998  
2999          // Create some users.
3000          $user1 = self::getDataGenerator()->create_user();
3001          $user2 = self::getDataGenerator()->create_user();
3002          $user3 = self::getDataGenerator()->create_user();
3003          $user4 = self::getDataGenerator()->create_user();
3004  
3005          // Create group conversation.
3006          $conversation = \core_message\api::create_conversation(
3007              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3008              [$user1->id, $user2->id, $user3->id, $user4->id]
3009          );
3010  
3011          // The person asking for the messages for another user.
3012          $this->setUser($user1);
3013  
3014          // Ensure an exception is thrown.
3015          $this->expectException('moodle_exception');
3016          core_message_external::get_conversation_messages($user2->id, $conversation->id);
3017      }
3018  
3019      /**
3020       * Tests get_conversation_messages for retrieving messages as another user not in the conversation.
3021       */
3022      public function test_get_conversation_messages_as_user_not_in_conversation() {
3023          $this->resetAfterTest(true);
3024  
3025          // Create some users.
3026          $user1 = self::getDataGenerator()->create_user();
3027          $user2 = self::getDataGenerator()->create_user();
3028          $user3 = self::getDataGenerator()->create_user(); // Not in group.
3029  
3030          // Create group conversation.
3031          $conversation = \core_message\api::create_conversation(
3032              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3033              [$user1->id, $user2->id]
3034          );
3035  
3036          // The person asking for the messages for a conversation he does not belong to.
3037          $this->setUser($user3);
3038  
3039          // Ensure an exception is thrown.
3040          $this->expectExceptionMessage('User is not part of conversation.');
3041          core_message_external::get_conversation_messages($user3->id, $conversation->id);
3042      }
3043  
3044      /**
3045       * Tests get_conversation_messages for retrieving messages with messaging disabled.
3046       */
3047      public function test_get_conversation_messages_messaging_disabled() {
3048          $this->resetAfterTest(true);
3049  
3050          // Create some skeleton data just so we can call the WS.
3051          $user1 = self::getDataGenerator()->create_user();
3052          $user2 = self::getDataGenerator()->create_user();
3053          $user3 = self::getDataGenerator()->create_user();
3054          $user4 = self::getDataGenerator()->create_user();
3055  
3056          // Create group conversation.
3057          $conversation = \core_message\api::create_conversation(
3058              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3059              [$user1->id, $user2->id, $user3->id, $user4->id]
3060          );
3061  
3062          // The person asking for the messages for another user.
3063          $this->setUser($user1);
3064  
3065          // Disable messaging.
3066          set_config('messaging', 0);
3067  
3068          // Ensure an exception is thrown.
3069          $this->expectException('moodle_exception');
3070          core_message_external::get_conversation_messages($user1->id, $conversation->id);
3071      }
3072  
3073      /**
3074       * Test marking all conversation messages as read with an invalid user.
3075       */
3076      public function test_mark_all_conversation_messages_as_read_invalid_user_exception() {
3077          $this->resetAfterTest(true);
3078  
3079          $user1 = self::getDataGenerator()->create_user();
3080          $user2 = self::getDataGenerator()->create_user();
3081  
3082          // Send some messages back and forth.
3083          $time = time();
3084          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3085          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3086          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3087          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3088  
3089          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3090  
3091          $this->expectException('moodle_exception');
3092          core_message_external::mark_all_conversation_messages_as_read(-2132131, $conversationid);
3093      }
3094  
3095      /**
3096       * Test marking all conversation messages as read without proper access.
3097       */
3098      public function test_mark_all_conversation_messages_as_read_access_denied_exception() {
3099          $this->resetAfterTest(true);
3100  
3101          $user1 = self::getDataGenerator()->create_user();
3102          $user2 = self::getDataGenerator()->create_user();
3103          $user3 = self::getDataGenerator()->create_user();
3104  
3105          // Send some messages back and forth.
3106          $time = time();
3107          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3108          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3109          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3110          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3111  
3112          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3113  
3114          // User 3 is not in the conversation.
3115          $this->expectException('moodle_exception');
3116          core_message_external::mark_all_conversation_messages_as_read($user3->id, $conversationid);
3117      }
3118  
3119      /**
3120       * Test marking all conversation messages as read for another user.
3121       */
3122      public function test_mark_all_conversation_messages_as_read_wrong_user() {
3123          $this->resetAfterTest(true);
3124  
3125          $user1 = self::getDataGenerator()->create_user();
3126          $user2 = self::getDataGenerator()->create_user();
3127  
3128          // Send some messages back and forth.
3129          $time = time();
3130          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3131          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3132          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3133          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3134  
3135          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3136  
3137          // Can't mark the messages as read for user 2.
3138          $this->setUser($user1);
3139          $this->expectException('moodle_exception');
3140          core_message_external::mark_all_conversation_messages_as_read($user2->id, $conversationid);
3141      }
3142  
3143      /**
3144       * Test marking all conversation messages as admin.
3145       */
3146      public function test_mark_all_conversation_messages_as_admin() {
3147          global $DB;
3148  
3149          $this->resetAfterTest(true);
3150  
3151          $user1 = self::getDataGenerator()->create_user();
3152          $user2 = self::getDataGenerator()->create_user();
3153  
3154          // Send some messages back and forth.
3155          $time = time();
3156          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3157          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3158          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3159          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3160  
3161          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3162  
3163          // Admin can do anything.
3164          $this->setAdminUser();
3165          core_message_external::mark_all_conversation_messages_as_read($user2->id, $conversationid);
3166          $this->assertEquals(2, $DB->count_records('message_user_actions'));
3167      }
3168  
3169      /**
3170       * Test marking all conversation messages.
3171       */
3172      public function test_mark_all_conversation_messages_as_read() {
3173          global $DB;
3174  
3175          $this->resetAfterTest(true);
3176  
3177          $user1 = self::getDataGenerator()->create_user();
3178          $user2 = self::getDataGenerator()->create_user();
3179  
3180          // Send some messages back and forth.
3181          $time = time();
3182          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3183          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3184          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3185          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3186  
3187          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3188  
3189          // We are the user we want to mark the messages for and we are in the conversation, all good.
3190          $this->setUser($user1);
3191          core_message_external::mark_all_conversation_messages_as_read($user1->id, $conversationid);
3192          $this->assertEquals(2, $DB->count_records('message_user_actions'));
3193      }
3194  
3195      /**
3196       * Test getting unread conversation count.
3197       */
3198      public function test_get_unread_conversations_count() {
3199          $this->resetAfterTest(true);
3200  
3201          // Create some users.
3202          $user1 = self::getDataGenerator()->create_user();
3203          $user2 = self::getDataGenerator()->create_user();
3204          $user3 = self::getDataGenerator()->create_user();
3205          $user4 = self::getDataGenerator()->create_user();
3206  
3207          // The person wanting the conversation count.
3208          $this->setUser($user1);
3209  
3210          // Send some messages back and forth, have some different conversations with different users.
3211          $this->send_message($user1, $user2, 'Yo!');
3212          $this->send_message($user2, $user1, 'Sup mang?');
3213          $this->send_message($user1, $user2, 'Writing PHPUnit tests!');
3214          $this->send_message($user2, $user1, 'Word.');
3215  
3216          $this->send_message($user1, $user3, 'Booyah');
3217          $this->send_message($user3, $user1, 'Whaaat?');
3218          $this->send_message($user1, $user3, 'Nothing.');
3219          $this->send_message($user3, $user1, 'Cool.');
3220  
3221          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
3222          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
3223          $this->send_message($user1, $user4, 'Dope.');
3224  
3225          // Get the unread conversation count.
3226          $result = core_message_external::get_unread_conversations_count($user1->id);
3227  
3228          // We need to execute the return values cleaning process to simulate the web service server.
3229          $result = external_api::clean_returnvalue(core_message_external::get_unread_conversations_count_returns(),
3230              $result);
3231  
3232          $this->assertEquals(3, $result);
3233      }
3234  
3235      /**
3236       * Test getting unread conversation count as other user.
3237       */
3238      public function test_get_unread_conversations_count_as_other_user() {
3239          $this->resetAfterTest(true);
3240  
3241          // The person wanting the conversation count.
3242          $this->setAdminUser();
3243  
3244          // Create some users.
3245          $user1 = self::getDataGenerator()->create_user();
3246          $user2 = self::getDataGenerator()->create_user();
3247          $user3 = self::getDataGenerator()->create_user();
3248          $user4 = self::getDataGenerator()->create_user();
3249  
3250          // Send some messages back and forth, have some different conversations with different users.
3251          $this->send_message($user1, $user2, 'Yo!');
3252          $this->send_message($user2, $user1, 'Sup mang?');
3253          $this->send_message($user1, $user2, 'Writing PHPUnit tests!');
3254          $this->send_message($user2, $user1, 'Word.');
3255  
3256          $this->send_message($user1, $user3, 'Booyah');
3257          $this->send_message($user3, $user1, 'Whaaat?');
3258          $this->send_message($user1, $user3, 'Nothing.');
3259          $this->send_message($user3, $user1, 'Cool.');
3260  
3261          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
3262          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
3263          $this->send_message($user1, $user4, 'Dope.');
3264  
3265          // Get the unread conversation count.
3266          $result = core_message_external::get_unread_conversations_count($user1->id);
3267  
3268          // We need to execute the return values cleaning process to simulate the web service server.
3269          $result = external_api::clean_returnvalue(core_message_external::get_unread_conversations_count_returns(),
3270              $result);
3271  
3272          $this->assertEquals(3, $result);
3273      }
3274  
3275      /**
3276       * Test getting unread conversation count as other user without proper capability.
3277       */
3278      public function test_get_unread_conversations_count_as_other_user_without_cap() {
3279          $this->resetAfterTest(true);
3280  
3281          // Create some users.
3282          $user1 = self::getDataGenerator()->create_user();
3283          $user2 = self::getDataGenerator()->create_user();
3284  
3285          // The person wanting the conversation count.
3286          $this->setUser($user1);
3287  
3288          // Ensure an exception is thrown.
3289          $this->expectException('moodle_exception');
3290          core_message_external::get_unread_conversations_count($user2->id);
3291      }
3292  
3293      /**
3294       * Test deleting conversations.
3295       */
3296      public function test_delete_conversations_by_id() {
3297          global $DB;
3298  
3299          $this->resetAfterTest(true);
3300  
3301          // Create some users.
3302          $user1 = self::getDataGenerator()->create_user();
3303          $user2 = self::getDataGenerator()->create_user();
3304  
3305          // The person wanting to delete the conversation.
3306          $this->setUser($user1);
3307  
3308          // Send some messages back and forth.
3309          $time = time();
3310          $m1id = $this->send_message($user1, $user2, 'Yo!', 0, $time);
3311          $m2id = $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3312          $m3id = $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3313          $m4id = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3314  
3315          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3316  
3317          // Delete the conversation.
3318          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
3319  
3320          $muas = $DB->get_records('message_user_actions', array(), 'timecreated ASC');
3321          $this->assertCount(4, $muas);
3322          // Sort by id.
3323          ksort($muas);
3324  
3325          $mua1 = array_shift($muas);
3326          $mua2 = array_shift($muas);
3327          $mua3 = array_shift($muas);
3328          $mua4 = array_shift($muas);
3329  
3330          $this->assertEquals($user1->id, $mua1->userid);
3331          $this->assertEquals($m1id, $mua1->messageid);
3332          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
3333  
3334          $this->assertEquals($user1->id, $mua2->userid);
3335          $this->assertEquals($m2id, $mua2->messageid);
3336          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
3337  
3338          $this->assertEquals($user1->id, $mua3->userid);
3339          $this->assertEquals($m3id, $mua3->messageid);
3340          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
3341  
3342          $this->assertEquals($user1->id, $mua4->userid);
3343          $this->assertEquals($m4id, $mua4->messageid);
3344          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua4->action);
3345      }
3346  
3347      /**
3348       * Test deleting conversations as other user.
3349       */
3350      public function test_delete_conversations_by_id_as_other_user() {
3351          global $DB;
3352  
3353          $this->resetAfterTest(true);
3354  
3355          $this->setAdminUser();
3356  
3357          // Create some users.
3358          $user1 = self::getDataGenerator()->create_user();
3359          $user2 = self::getDataGenerator()->create_user();
3360  
3361          // Send some messages back and forth.
3362          $time = time();
3363          $m1id = $this->send_message($user1, $user2, 'Yo!', 0, $time);
3364          $m2id = $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3365          $m3id = $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3366          $m4id = $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3367  
3368          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3369  
3370          // Delete the conversation.
3371          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
3372  
3373          $muas = $DB->get_records('message_user_actions', array(), 'timecreated ASC');
3374          $this->assertCount(4, $muas);
3375          // Sort by id.
3376          ksort($muas);
3377  
3378          $mua1 = array_shift($muas);
3379          $mua2 = array_shift($muas);
3380          $mua3 = array_shift($muas);
3381          $mua4 = array_shift($muas);
3382  
3383          $this->assertEquals($user1->id, $mua1->userid);
3384          $this->assertEquals($m1id, $mua1->messageid);
3385          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
3386  
3387          $this->assertEquals($user1->id, $mua2->userid);
3388          $this->assertEquals($m2id, $mua2->messageid);
3389          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
3390  
3391          $this->assertEquals($user1->id, $mua3->userid);
3392          $this->assertEquals($m3id, $mua3->messageid);
3393          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
3394  
3395          $this->assertEquals($user1->id, $mua4->userid);
3396          $this->assertEquals($m4id, $mua4->messageid);
3397          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua4->action);
3398      }
3399  
3400      /**
3401       * Test deleting conversations as other user without proper capability.
3402       */
3403      public function test_delete_conversations_by_id_as_other_user_without_cap() {
3404          $this->resetAfterTest(true);
3405  
3406          // Create some users.
3407          $user1 = self::getDataGenerator()->create_user();
3408          $user2 = self::getDataGenerator()->create_user();
3409          $user3 = self::getDataGenerator()->create_user();
3410  
3411          // Send some messages back and forth.
3412          $time = time();
3413          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3414          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3415          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3416          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3417  
3418          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3419  
3420          // The person wanting to delete the conversation.
3421          $this->setUser($user3);
3422  
3423          // Ensure an exception is thrown.
3424          $this->expectException('moodle_exception');
3425          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
3426      }
3427  
3428      /**
3429       * Test deleting conversations with messaging disabled.
3430       */
3431      public function test_delete_conversations_by_id_messaging_disabled() {
3432          global $CFG;
3433  
3434          $this->resetAfterTest(true);
3435  
3436          // Create some users.
3437          $user1 = self::getDataGenerator()->create_user();
3438          $user2 = self::getDataGenerator()->create_user();
3439  
3440          // Send some messages back and forth.
3441          $time = time();
3442          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3443          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3444          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3445          $this->send_message($user2, $user1, 'Word.', 0, $time + 3);
3446  
3447          $conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3448  
3449          // The person wanting to delete the conversation.
3450          $this->setUser($user1);
3451  
3452          // Disable messaging.
3453          $CFG->messaging = 0;
3454  
3455          // Ensure an exception is thrown.
3456          $this->expectException('moodle_exception');
3457          core_message_external::delete_conversations_by_id($user1->id, [$conversationid]);
3458      }
3459  
3460      /**
3461       * Test get message processor.
3462       */
3463      public function test_get_message_processor() {
3464          $this->resetAfterTest(true);
3465  
3466          // Create a user.
3467          $user1 = self::getDataGenerator()->create_user();
3468  
3469          // Set you as the user.
3470          $this->setUser($user1);
3471  
3472          // Get the message processors.
3473          $result = core_message_external::get_message_processor($user1->id, 'popup');
3474  
3475          // We need to execute the return values cleaning process to simulate the web service server.
3476          $result = external_api::clean_returnvalue(core_message_external::get_message_processor_returns(), $result);
3477  
3478          $this->assertNotEmpty($result['systemconfigured']);
3479          $this->assertNotEmpty($result['userconfigured']);
3480      }
3481  
3482      /**
3483       * Test get_user_notification_preferences
3484       */
3485      public function test_get_user_message_preferences() {
3486          $this->resetAfterTest(true);
3487  
3488          $user = self::getDataGenerator()->create_user();
3489          $this->setUser($user);
3490  
3491          // Enable site-wide messagging privacy setting. The user will be able to receive messages from everybody.
3492          set_config('messagingallusers', true);
3493  
3494          // Set a couple of preferences to test.
3495          set_user_preference('message_provider_moodle_instantmessage_enabled', 'email', $user);
3496          set_user_preference('message_blocknoncontacts', \core_message\api::MESSAGE_PRIVACY_SITE, $user);
3497  
3498          $prefs = core_message_external::get_user_message_preferences();
3499          $prefs = external_api::clean_returnvalue(core_message_external::get_user_message_preferences_returns(), $prefs);
3500          $this->assertEquals($user->id, $prefs['preferences']['userid']);
3501  
3502          // Check components.
3503          $this->assertCount(1, $prefs['preferences']['components']);
3504          $this->assertEquals(\core_message\api::MESSAGE_PRIVACY_SITE, $prefs['blocknoncontacts']);
3505  
3506          // Check some preferences that we previously set.
3507          $found = false;
3508          foreach ($prefs['preferences']['components'] as $component) {
3509              foreach ($component['notifications'] as $prefdata) {
3510                  if ($prefdata['preferencekey'] != 'message_provider_moodle_instantmessage') {
3511                      continue;
3512                  }
3513                  foreach ($prefdata['processors'] as $processor) {
3514                      if ($processor['name'] == 'email') {
3515                          $this->assertTrue($processor['enabled']);
3516                          $found = true;
3517                      }
3518                  }
3519              }
3520          }
3521          $this->assertTrue($found);
3522      }
3523  
3524      /**
3525       * Test get_user_message_preferences permissions
3526       */
3527      public function test_get_user_message_preferences_permissions() {
3528          $this->resetAfterTest(true);
3529  
3530          $user = self::getDataGenerator()->create_user();
3531          $otheruser = self::getDataGenerator()->create_user();
3532          $this->setUser($user);
3533  
3534          $this->expectException('moodle_exception');
3535          $prefs = core_message_external::get_user_message_preferences($otheruser->id);
3536      }
3537  
3538      /**
3539       * Comparison function for sorting contacts.
3540       *
3541       * @param array $a
3542       * @param array $b
3543       * @return bool
3544       */
3545      protected static function sort_contacts($a, $b) {
3546          return $a['userid'] <=> $b['userid'];
3547      }
3548  
3549      /**
3550       * Comparison function for sorting contacts.
3551       *
3552       * @param array $a
3553       * @param array $b
3554       * @return bool
3555       */
3556      protected static function sort_contacts_id($a, $b) {
3557          return $a['id'] <=> $b['id'];
3558      }
3559  
3560      /**
3561       * Test verifying that conversations can be marked as favourite conversations.
3562       */
3563      public function test_set_favourite_conversations_basic() {
3564          $this->resetAfterTest();
3565  
3566          $user1 = self::getDataGenerator()->create_user();
3567          $user2 = self::getDataGenerator()->create_user();
3568          $user3 = self::getDataGenerator()->create_user();
3569          $user4 = self::getDataGenerator()->create_user();
3570  
3571          $this->setUser($user1);
3572  
3573          // Now, create some conversations.
3574          $time = time();
3575          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3576          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3577          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3578  
3579          $this->send_message($user1, $user3, 'Booyah');
3580          $this->send_message($user3, $user1, 'Whaaat?');
3581          $this->send_message($user1, $user3, 'Nothing.');
3582  
3583          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
3584          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
3585  
3586          // Favourite 2 conversations as user 1.
3587          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3588          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
3589          $result = core_message_external::set_favourite_conversations($user1->id, [$conversation1, $conversation2]);
3590  
3591          // We need to execute the return values cleaning process to simulate the web service server.
3592          $result = external_api::clean_returnvalue(core_message_external::set_favourite_conversations_returns(), $result);
3593          $this->assertCount(0, $result);
3594      }
3595  
3596      /**
3597       * Test confirming that a user can't favourite a conversation on behalf of another user.
3598       */
3599      public function test_set_favourite_conversations_another_users_conversation() {
3600          $this->resetAfterTest();
3601  
3602          $user1 = self::getDataGenerator()->create_user();
3603          $user2 = self::getDataGenerator()->create_user();
3604          $user3 = self::getDataGenerator()->create_user();
3605  
3606          $this->setUser($user3);
3607  
3608          // Now, create some conversations.
3609          $time = time();
3610          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3611          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3612          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3613  
3614          $this->send_message($user1, $user3, 'Booyah');
3615          $this->send_message($user3, $user1, 'Whaaat?');
3616          $this->send_message($user1, $user3, 'Nothing.');
3617  
3618          // Try to favourite conversation 1 for user 2, as user3.
3619          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3620          $this->expectException(\moodle_exception::class);
3621          $result = core_message_external::set_favourite_conversations($user2->id, [$conversation1]);
3622      }
3623  
3624      /**
3625       * 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.
3626       */
3627      public function test_set_favourite_conversations_non_member() {
3628          $this->resetAfterTest();
3629  
3630          $user1 = self::getDataGenerator()->create_user();
3631          $user2 = self::getDataGenerator()->create_user();
3632          $user3 = self::getDataGenerator()->create_user();
3633  
3634          $this->setUser($user3);
3635  
3636          // Now, create some conversations.
3637          $time = time();
3638          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3639          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3640          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3641  
3642          $this->send_message($user1, $user3, 'Booyah');
3643          $this->send_message($user3, $user1, 'Whaaat?');
3644          $this->send_message($user1, $user3, 'Nothing.');
3645  
3646          // Try to favourite conversation 1 as user 3.
3647          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3648          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
3649          $this->expectException(\moodle_exception::class);
3650          $result = core_message_external::set_favourite_conversations($user3->id, [$conversation1]);
3651      }
3652  
3653      /**
3654       * Test confirming that a user can't favourite a non-existent conversation.
3655       */
3656      public function test_set_favourite_conversations_non_existent_conversation() {
3657          $this->resetAfterTest();
3658  
3659          $user1 = self::getDataGenerator()->create_user();
3660          $this->setUser($user1);
3661  
3662          // Try to favourite a non-existent conversation.
3663          $this->expectException(\moodle_exception::class);
3664          $result = core_message_external::set_favourite_conversations($user1->id, [0]);
3665      }
3666  
3667      /**
3668       * Test confirming that a user can unset a favourite conversation, or list of favourite conversations.
3669       */
3670      public function test_unset_favourite_conversations_basic() {
3671          $this->resetAfterTest();
3672  
3673          $user1 = self::getDataGenerator()->create_user();
3674          $user2 = self::getDataGenerator()->create_user();
3675          $user3 = self::getDataGenerator()->create_user();
3676          $user4 = self::getDataGenerator()->create_user();
3677  
3678          $this->setUser($user1);
3679  
3680          // Now, create some conversations.
3681          $time = time();
3682          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3683          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3684          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3685  
3686          $this->send_message($user1, $user3, 'Booyah');
3687          $this->send_message($user3, $user1, 'Whaaat?');
3688          $this->send_message($user1, $user3, 'Nothing.');
3689  
3690          $this->send_message($user1, $user4, 'Hey mate, you see the new messaging UI in Moodle?');
3691          $this->send_message($user4, $user1, 'Yah brah, it\'s pretty rad.');
3692  
3693          // Favourite 2 conversations as user 1.
3694          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3695          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
3696          \core_message\api::set_favourite_conversation($conversation1, $user1->id);
3697          \core_message\api::set_favourite_conversation($conversation2, $user1->id);
3698          // Consider first conversations is self-conversation.
3699          $this->assertCount(3, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
3700  
3701          // Unset favourite self-conversation.
3702          $selfconversation = \core_message\api::get_self_conversation($user1->id);
3703          $result = core_message_external::unset_favourite_conversations($user1->id, [$selfconversation->id]);
3704          $this->assertCount(2, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
3705  
3706          // Now, using the web service, unset the favourite conversations.
3707          $result = core_message_external::unset_favourite_conversations($user1->id, [$conversation1, $conversation2]);
3708  
3709          // We need to execute the return values cleaning process to simulate the web service server.
3710          $result = external_api::clean_returnvalue(core_message_external::unset_favourite_conversations_returns(), $result);
3711          $this->assertCount(0, $result);
3712      }
3713  
3714      /**
3715       * Test confirming that a user can't unfavourite a conversation for another user.
3716       */
3717      public function test_unset_favourite_conversations_another_users_conversation() {
3718          $this->resetAfterTest();
3719  
3720          $user1 = self::getDataGenerator()->create_user();
3721          $user2 = self::getDataGenerator()->create_user();
3722          $user3 = self::getDataGenerator()->create_user();
3723  
3724          $this->setUser($user3);
3725  
3726          // Now, create some conversations.
3727          $time = time();
3728          $this->send_message($user1, $user2, 'Yo!', 0, $time);
3729          $this->send_message($user2, $user1, 'Sup mang?', 0, $time + 1);
3730          $this->send_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
3731  
3732          $this->send_message($user1, $user3, 'Booyah');
3733          $this->send_message($user3, $user1, 'Whaaat?');
3734          $this->send_message($user1, $user3, 'Nothing.');
3735  
3736          // Favourite conversation 1 for user1. The current user ($USER) isn't checked for this action.
3737          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3738          \core_message\api::set_favourite_conversation($conversation1, $user1->id);
3739          // Consider first conversations is self-conversation.
3740          $this->assertCount(2, \core_message\api::get_conversations($user1->id, 0, 20, null, true));
3741  
3742          // Try to unfavourite conversation 1 for user 2, as user3.
3743          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
3744          $this->expectException(\moodle_exception::class);
3745          $result = core_message_external::unset_favourite_conversations($user2->id, [$conversation1]);
3746      }
3747  
3748      /**
3749       * Test confirming that a user can't unfavourite a non-existent conversation.
3750       */
3751      public function test_unset_favourite_conversations_non_existent_conversation() {
3752          $this->resetAfterTest();
3753  
3754          $user1 = self::getDataGenerator()->create_user();
3755          $this->setUser($user1);
3756  
3757          // Try to unfavourite a non-existent conversation.
3758          $this->expectException(\moodle_exception::class);
3759          $result = core_message_external::unset_favourite_conversations($user1->id, [0]);
3760      }
3761  
3762      /**
3763       * Helper to seed the database with initial state.
3764       */
3765      protected function create_conversation_test_data() {
3766          // Create some users.
3767          $user1 = self::getDataGenerator()->create_user();
3768          $user2 = self::getDataGenerator()->create_user();
3769          $user3 = self::getDataGenerator()->create_user();
3770          $user4 = self::getDataGenerator()->create_user();
3771  
3772          $time = 1;
3773  
3774          // Create some conversations. We want:
3775          // 1) At least one of each type (group, individual) of which user1 IS a member and DID send the most recent message.
3776          // 2) At least one of each type (group, individual) of which user1 IS a member and DID NOT send the most recent message.
3777          // 3) At least one of each type (group, individual) of which user1 IS NOT a member.
3778          // 4) At least two group conversation having 0 messages, of which user1 IS a member (To confirm conversationid ordering).
3779          // 5) At least one group conversation having 0 messages, of which user1 IS NOT a member.
3780  
3781          // Individual conversation, user1 is a member, last message from other user.
3782          $ic1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
3783              [$user1->id, $user2->id]);
3784          testhelper::send_fake_message_to_conversation($user1, $ic1->id, 'Message 1', $time);
3785          testhelper::send_fake_message_to_conversation($user2, $ic1->id, 'Message 2', $time + 1);
3786  
3787          // Individual conversation, user1 is a member, last message from user1.
3788          $ic2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
3789              [$user1->id, $user3->id]);
3790          testhelper::send_fake_message_to_conversation($user3, $ic2->id, 'Message 3', $time + 2);
3791          testhelper::send_fake_message_to_conversation($user1, $ic2->id, 'Message 4', $time + 3);
3792  
3793          // Individual conversation, user1 is not a member.
3794          $ic3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
3795              [$user2->id, $user3->id]);
3796          testhelper::send_fake_message_to_conversation($user2, $ic3->id, 'Message 5', $time + 4);
3797          testhelper::send_fake_message_to_conversation($user3, $ic3->id, 'Message 6', $time + 5);
3798  
3799          // Group conversation, user1 is not a member.
3800          $gc1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3801              [$user2->id, $user3->id, $user4->id], 'Project discussions');
3802          testhelper::send_fake_message_to_conversation($user2, $gc1->id, 'Message 7', $time + 6);
3803          testhelper::send_fake_message_to_conversation($user4, $gc1->id, 'Message 8', $time + 7);
3804  
3805          // Group conversation, user1 is a member, last message from another user.
3806          $gc2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3807              [$user1->id, $user3->id, $user4->id], 'Group chat');
3808          testhelper::send_fake_message_to_conversation($user1, $gc2->id, 'Message 9', $time + 8);
3809          testhelper::send_fake_message_to_conversation($user3, $gc2->id, 'Message 10', $time + 9);
3810          testhelper::send_fake_message_to_conversation($user4, $gc2->id, 'Message 11', $time + 10);
3811  
3812          // Group conversation, user1 is a member, last message from user1.
3813          $gc3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3814              [$user1->id, $user2->id, $user3->id, $user4->id], 'Group chat again!');
3815          testhelper::send_fake_message_to_conversation($user4, $gc3->id, 'Message 12', $time + 11);
3816          testhelper::send_fake_message_to_conversation($user3, $gc3->id, 'Message 13', $time + 12);
3817          testhelper::send_fake_message_to_conversation($user1, $gc3->id, 'Message 14', $time + 13);
3818  
3819          // Empty group conversations (x2), user1 is a member.
3820          $gc4 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3821              [$user1->id, $user2->id, $user3->id], 'Empty group');
3822          $gc5 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3823              [$user1->id, $user2->id, $user4->id], 'Another empty group');
3824  
3825          // Empty group conversation, user1 is NOT a member.
3826          $gc6 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
3827              [$user2->id, $user3->id, $user4->id], 'Empty group 3');
3828  
3829          return [$user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, $gc1, $gc2, $gc3, $gc4, $gc5, $gc6];
3830      }
3831  
3832      /**
3833       * Test confirming the basic use of get_conversations, with no limits, nor type or favourite restrictions.
3834       */
3835      public function test_get_conversations_no_restrictions() {
3836          $this->resetAfterTest(true);
3837  
3838          // Get a bunch of conversations, some group, some individual and in different states.
3839          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
3840              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
3841  
3842          // The user making the request.
3843          $this->setUser($user1);
3844  
3845          // Get all conversations for user1.
3846          $result = core_message_external::get_conversations($user1->id);
3847          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
3848          $conversations = $result['conversations'];
3849  
3850          $selfconversation = \core_message\api::get_self_conversation($user1->id);
3851  
3852          // Verify there are 7 conversations: 2 individual, 2 group with message, 2 group without messages
3853          // and a self-conversation.
3854          // The conversations with the most recent messages should be listed first, followed by the most newly created
3855          // conversations without messages.
3856          $this->assertCount(7, $conversations);
3857          $this->assertEquals($gc3->id, $conversations[0]['id']);
3858          $this->assertEquals($gc2->id, $conversations[1]['id']);
3859          $this->assertEquals($ic2->id, $conversations[2]['id']);
3860          $this->assertEquals($ic1->id, $conversations[3]['id']);
3861          $this->assertEquals($gc5->id, $conversations[4]['id']);
3862          $this->assertEquals($gc4->id, $conversations[5]['id']);
3863          $this->assertEquals($selfconversation->id, $conversations[6]['id']);
3864  
3865          foreach ($conversations as $conv) {
3866              $this->assertArrayHasKey('id', $conv);
3867              $this->assertArrayHasKey('name', $conv);
3868              $this->assertArrayHasKey('subname', $conv);
3869              $this->assertArrayHasKey('imageurl', $conv);
3870              $this->assertArrayHasKey('type', $conv);
3871              $this->assertArrayHasKey('membercount', $conv);
3872              $this->assertArrayHasKey('isfavourite', $conv);
3873              $this->assertArrayHasKey('isread', $conv);
3874              $this->assertArrayHasKey('unreadcount', $conv);
3875              $this->assertArrayHasKey('members', $conv);
3876              foreach ($conv['members'] as $member) {
3877                  $this->assertArrayHasKey('id', $member);
3878                  $this->assertArrayHasKey('fullname', $member);
3879                  $this->assertArrayHasKey('profileimageurl', $member);
3880                  $this->assertArrayHasKey('profileimageurlsmall', $member);
3881                  $this->assertArrayHasKey('isonline', $member);
3882                  $this->assertArrayHasKey('showonlinestatus', $member);
3883                  $this->assertArrayHasKey('isblocked', $member);
3884                  $this->assertArrayHasKey('iscontact', $member);
3885                  $this->assertArrayHasKey('isdeleted', $member);
3886                  $this->assertArrayHasKey('canmessage', $member);
3887                  $this->assertArrayHasKey('requirescontact', $member);
3888                  $this->assertArrayHasKey('contactrequests', $member);
3889              }
3890              $this->assertArrayHasKey('messages', $conv);
3891              foreach ($conv['messages'] as $message) {
3892                  $this->assertArrayHasKey('id', $message);
3893                  $this->assertArrayHasKey('useridfrom', $message);
3894                  $this->assertArrayHasKey('text', $message);
3895                  $this->assertArrayHasKey('timecreated', $message);
3896              }
3897          }
3898      }
3899  
3900      /**
3901       * Test verifying that html format messages are supported, and that message_format_message_text() is being called appropriately.
3902       */
3903      public function test_get_conversations_message_format() {
3904          $this->resetAfterTest();
3905  
3906          global $DB;
3907          // Create some users.
3908          $user1 = self::getDataGenerator()->create_user();
3909          $user2 = self::getDataGenerator()->create_user();
3910  
3911          // Create conversation.
3912          $conversation = \core_message\api::create_conversation(
3913              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
3914              [$user1->id, $user2->id]
3915          );
3916  
3917          // Send some messages back and forth.
3918          $time = 1;
3919          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Sup mang?', $time + 1);
3920          $mid = testhelper::send_fake_message_to_conversation($user1, $conversation->id, '<a href="#">A link</a>', $time + 2);
3921          $message = $DB->get_record('messages', ['id' => $mid]);
3922  
3923          // The user in scope.
3924          $this->setUser($user1);
3925  
3926          // Verify the format of the html message.
3927          $expectedmessagetext = message_format_message_text($message);
3928          $result = core_message_external::get_conversations($user1->id);
3929          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
3930          $conversations = $result['conversations'];
3931          $messages = $conversations[0]['messages'];
3932          $this->assertEquals($expectedmessagetext, $messages[0]['text']);
3933      }
3934  
3935      /**
3936       * Tests retrieving conversations with a limit and offset to ensure pagination works correctly.
3937       */
3938      public function test_get_conversations_limit_offset() {
3939          $this->resetAfterTest(true);
3940  
3941          // Get a bunch of conversations, some group, some individual and in different states.
3942          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
3943              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
3944  
3945          // The user making the request.
3946          $this->setUser($user1);
3947  
3948          // Get all conversations for user1.
3949          $result = core_message_external::get_conversations($user1->id, 0, 1);
3950          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
3951          $conversations = $result['conversations'];
3952  
3953          // Verify the first conversation.
3954          $this->assertCount(1, $conversations);
3955          $conversation = array_shift($conversations);
3956          $this->assertEquals($gc3->id, $conversation['id']);
3957  
3958          // Verify the next conversation.
3959          $result = core_message_external::get_conversations($user1->id, 1, 1);
3960          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
3961          $conversations = $result['conversations'];
3962          $this->assertCount(1, $conversations);
3963          $this->assertEquals($gc2->id, $conversations[0]['id']);
3964  
3965          // Verify the next conversation.
3966          $result = core_message_external::get_conversations($user1->id, 2, 1);
3967          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
3968          $conversations = $result['conversations'];
3969          $this->assertCount(1, $conversations);
3970          $this->assertEquals($ic2->id, $conversations[0]['id']);
3971  
3972          // Skip one and get both empty conversations.
3973          $result = core_message_external::get_conversations($user1->id, 4, 2);
3974          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
3975          $conversations = $result['conversations'];
3976          $this->assertCount(2, $conversations);
3977          $this->assertEquals($gc5->id, $conversations[0]['id']);
3978          $this->assertEmpty($conversations[0]['messages']);
3979          $this->assertEquals($gc4->id, $conversations[1]['id']);
3980          $this->assertEmpty($conversations[1]['messages']);
3981  
3982          // Ask for an offset that doesn't exist and verify no conversations are returned.
3983          $conversations = \core_message\api::get_conversations($user1->id, 10, 1);
3984          $this->assertCount(0, $conversations);
3985      }
3986  
3987      /**
3988       * Test verifying the type filtering behaviour of the get_conversations external method.
3989       */
3990      public function test_get_conversations_type_filter() {
3991          $this->resetAfterTest(true);
3992  
3993          // Get a bunch of conversations, some group, some individual and in different states.
3994          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
3995              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
3996  
3997          // The user making the request.
3998          $this->setUser($user1);
3999  
4000          // Verify we can ask for only individual conversations.
4001          $result = core_message_external::get_conversations($user1->id, 0, 20,
4002              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL);
4003          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4004          $conversations = $result['conversations'];
4005          $this->assertCount(2, $conversations);
4006  
4007          // Verify we can ask for only group conversations.
4008          $result = core_message_external::get_conversations($user1->id, 0, 20,
4009              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP);
4010          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4011          $conversations = $result['conversations'];
4012          $this->assertCount(4, $conversations);
4013  
4014          // Verify an exception is thrown if an unrecognized type is specified.
4015          $this->expectException(\moodle_exception::class);
4016          core_message_external::get_conversations($user1->id, 0, 20, 0);
4017      }
4018  
4019      /**
4020       * Tests retrieving conversations when a 'self' conversation exists.
4021       */
4022      public function test_get_conversations_self_conversations() {
4023          global $DB;
4024          $this->resetAfterTest();
4025  
4026          // Create a conversation between one user and themself.
4027          $user1 = self::getDataGenerator()->create_user();
4028          $conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
4029              [$user1->id]);
4030          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Test message to self!');
4031  
4032          // Verify we are in a 'self' conversation state.
4033          $members = $DB->get_records('message_conversation_members', ['conversationid' => $conversation->id]);
4034          $this->assertCount(1, $members);
4035          $member = array_pop($members);
4036          $this->assertEquals($user1->id, $member->userid);
4037  
4038          // Verify this conversation is returned by the method.
4039          $this->setUser($user1);
4040          $result = core_message_external::get_conversations($user1->id, 0, 20);
4041          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4042          $conversations = $result['conversations'];
4043          $this->assertCount(1, $conversations);
4044      }
4045  
4046      /**
4047       * Tests retrieving conversations when a conversation contains a deleted user.
4048       */
4049      public function test_get_conversations_deleted_user() {
4050          $this->resetAfterTest(true);
4051  
4052          // Get a bunch of conversations, some group, some individual and in different states.
4053          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4054              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4055  
4056          // The user making the request.
4057          $this->setUser($user1);
4058  
4059          $selfconversation = \core_message\api::get_self_conversation($user1->id);
4060  
4061          // Delete the second user and retrieve the conversations.
4062          // We should have 6 still, as conversations with soft-deleted users are still returned.
4063          // Group conversations are also present, albeit with less members.
4064          delete_user($user2);
4065          $result = core_message_external::get_conversations($user1->id);
4066          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4067          $conversations = $result['conversations'];
4068          $this->assertCount(7, $conversations);
4069          $this->assertEquals($gc3->id, $conversations[0]['id']);
4070          $this->assertcount(1, $conversations[0]['members']);
4071          $this->assertEquals($gc2->id, $conversations[1]['id']);
4072          $this->assertcount(1, $conversations[1]['members']);
4073          $this->assertEquals($ic2->id, $conversations[2]['id']);
4074          $this->assertEquals($ic1->id, $conversations[3]['id']);
4075          $this->assertEquals($gc5->id, $conversations[4]['id']);
4076          $this->assertEquals($gc4->id, $conversations[5]['id']);
4077          $this->assertEquals($selfconversation->id, $conversations[6]['id']);
4078  
4079          // Delete a user from a group conversation where that user had sent the most recent message.
4080          // This user will still be present in the members array, as will the message in the messages array.
4081          delete_user($user4);
4082          $result = core_message_external::get_conversations($user1->id);
4083          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4084          $conversations = $result['conversations'];
4085          $this->assertCount(7, $conversations);
4086          $this->assertEquals($gc2->id, $conversations[1]['id']);
4087          $this->assertcount(1, $conversations[1]['members']);
4088          $this->assertEquals($user4->id, $conversations[1]['members'][0]['id']);
4089          $this->assertcount(1, $conversations[1]['messages']);
4090          $this->assertEquals($user4->id, $conversations[1]['messages'][0]['useridfrom']);
4091  
4092          // Delete the third user and retrieve the conversations.
4093          // We should have 7 still (including self-conversation), as conversations with soft-deleted users are still returned.
4094          // Group conversations are also present, albeit with less members.
4095          delete_user($user3);
4096          $result = core_message_external::get_conversations($user1->id);
4097          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4098          $conversations = $result['conversations'];
4099          $this->assertCount(7, $conversations);
4100          $this->assertEquals($gc3->id, $conversations[0]['id']);
4101          $this->assertcount(1, $conversations[0]['members']);
4102          $this->assertEquals($gc2->id, $conversations[1]['id']);
4103          $this->assertcount(1, $conversations[1]['members']);
4104          $this->assertEquals($ic2->id, $conversations[2]['id']);
4105          $this->assertEquals($ic1->id, $conversations[3]['id']);
4106          $this->assertEquals($gc5->id, $conversations[4]['id']);
4107          $this->assertEquals($gc4->id, $conversations[5]['id']);
4108          $this->assertEquals($selfconversation->id, $conversations[6]['id']);
4109      }
4110  
4111      /**
4112       * Tests retrieving conversations when a conversation contains a deleted from the database user.
4113       */
4114      public function test_get_conversations_deleted_user_from_database() {
4115          global $DB;
4116  
4117          $this->resetAfterTest();
4118  
4119          $user1 = self::getDataGenerator()->create_user();
4120          $user2 = self::getDataGenerator()->create_user();
4121          $user3 = self::getDataGenerator()->create_user();
4122  
4123          $conversation1 = \core_message\api::create_conversation(
4124              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4125              [
4126                  $user1->id,
4127                  $user2->id
4128              ],
4129              'Individual conversation 1'
4130          );
4131  
4132          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'A');
4133          testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'B');
4134          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'C');
4135  
4136          $conversation2 = \core_message\api::create_conversation(
4137              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4138              [
4139                  $user1->id,
4140                  $user3->id
4141              ],
4142              'Individual conversation 2'
4143          );
4144  
4145          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'A');
4146          testhelper::send_fake_message_to_conversation($user3, $conversation2->id, 'B');
4147          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'C');
4148  
4149          $this->setUser($user1);
4150  
4151          // Delete the second user (from DB as well as this could happen in the past).
4152          delete_user($user2);
4153          $DB->delete_records('user', ['id' => $user2->id]);
4154          $result = core_message_external::get_conversations($user1->id, 0, 20, 1, false);
4155          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4156  
4157          $conversation = $result['conversations'];
4158  
4159          $this->assertCount(1, $conversation);
4160  
4161          $conversation = reset($conversation);
4162  
4163          $this->assertEquals($conversation2->id, $conversation['id']);
4164      }
4165  
4166      /**
4167       * Test verifying the behaviour of get_conversations() when fetching favourite conversations.
4168       */
4169      public function test_get_conversations_favourite_conversations() {
4170          $this->resetAfterTest(true);
4171  
4172          // Get a bunch of conversations, some group, some individual and in different states.
4173          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4174              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4175  
4176          // The user making the request.
4177          $this->setUser($user1);
4178  
4179          // Unset favourite self-conversation.
4180          $selfconversation = \core_message\api::get_self_conversation($user1->id);
4181          \core_message\api::unset_favourite_conversation($selfconversation->id, $user1->id);
4182  
4183          // Try to get ONLY favourite conversations, when no favourites exist.
4184          $result = core_message_external::get_conversations($user1->id, 0, 20, null, true);
4185          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4186          $conversations = $result['conversations'];
4187          $this->assertEquals([], $conversations);
4188  
4189          // Try to get NO favourite conversations, when no favourites exist.
4190          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
4191          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4192          $conversations = $result['conversations'];
4193          // Consider first conversations is self-conversation.
4194          $this->assertCount(7, $conversations);
4195  
4196          // Mark a few conversations as favourites.
4197          \core_message\api::set_favourite_conversation($ic1->id, $user1->id);
4198          \core_message\api::set_favourite_conversation($gc2->id, $user1->id);
4199          \core_message\api::set_favourite_conversation($gc5->id, $user1->id);
4200  
4201          // Get the conversations, first with no restrictions, confirming the favourite status of the conversations.
4202          $result = core_message_external::get_conversations($user1->id);
4203          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4204          $conversations = $result['conversations'];
4205          $this->assertCount(7, $conversations);
4206          foreach ($conversations as $conv) {
4207              if (in_array($conv['id'], [$ic1->id, $gc2->id, $gc5->id])) {
4208                  $this->assertTrue($conv['isfavourite']);
4209              }
4210          }
4211  
4212          // Now, get ONLY favourite conversations.
4213          $result = core_message_external::get_conversations($user1->id, 0, 20, null, true);
4214          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4215          $conversations = $result['conversations'];
4216          $this->assertCount(3, $conversations);
4217          foreach ($conversations as $conv) {
4218              $this->assertTrue($conv['isfavourite']);
4219          }
4220  
4221          // Now, try ONLY favourites of type 'group'.
4222          $conversations = \core_message\api::get_conversations($user1->id, 0, 20,
4223              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, true);
4224          $this->assertCount(2, $conversations);
4225          foreach ($conversations as $conv) {
4226              $this->assertTrue($conv->isfavourite);
4227          }
4228  
4229          // And NO favourite conversations.
4230          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
4231          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4232          $conversations = $result['conversations'];
4233          $this->assertCount(4, $conversations);
4234          foreach ($conversations as $conv) {
4235              $this->assertFalse($conv['isfavourite']);
4236          }
4237      }
4238  
4239      /**
4240       * Test verifying that group linked conversations are returned and contain a subname matching the course name.
4241       */
4242      public function test_get_conversations_group_linked() {
4243          $this->resetAfterTest();
4244          global $CFG, $DB;
4245  
4246          // Create some users.
4247          $user1 = self::getDataGenerator()->create_user();
4248          $user2 = self::getDataGenerator()->create_user();
4249          $user3 = self::getDataGenerator()->create_user();
4250  
4251          $course1 = $this->getDataGenerator()->create_course();
4252  
4253          // Create a group with a linked conversation.
4254          $this->setAdminUser();
4255          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4256          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4257          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
4258          $group1 = $this->getDataGenerator()->create_group([
4259              'courseid' => $course1->id,
4260              'enablemessaging' => 1,
4261              'picturepath' => $CFG->dirroot . '/lib/tests/fixtures/gd-logo.png'
4262          ]);
4263  
4264          // Add users to group1.
4265          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
4266          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
4267  
4268          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
4269          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4270          $conversations = $result['conversations'];
4271  
4272          $this->assertEquals(2, $conversations[0]['membercount']);
4273          $this->assertEquals($course1->shortname, $conversations[0]['subname']);
4274          $groupimageurl = get_group_picture_url($group1, $group1->courseid, true);
4275          $this->assertEquals($groupimageurl, $conversations[0]['imageurl']);
4276  
4277          // Now, disable the conversation linked to the group and verify it's no longer returned.
4278          $DB->set_field('message_conversations', 'enabled', 0, ['id' => $conversations[0]['id']]);
4279          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
4280          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4281          $conversations = $result['conversations'];
4282          $this->assertCount(0, $conversations);
4283      }
4284  
4285      /**
4286       * Test that group conversations containing MathJax don't break the WebService.
4287       */
4288      public function test_get_conversations_group_with_mathjax() {
4289          $this->resetAfterTest(true);
4290          $this->setAdminUser();
4291  
4292          // Enable MathJax filter in content and headings.
4293          $this->configure_filters([
4294              ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true],
4295          ]);
4296  
4297          // Create some users, a course and a group with a linked conversation.
4298          $user1 = self::getDataGenerator()->create_user();
4299          $user2 = self::getDataGenerator()->create_user();
4300  
4301          $coursename = 'Course $$(a+b)=2$$';
4302          $groupname = 'Group $$(a+b)=2$$';
4303          $course1 = $this->getDataGenerator()->create_course(['shortname' => $coursename]);
4304  
4305          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4306          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4307          $group1 = $this->getDataGenerator()->create_group([
4308              'name' => $groupname,
4309              'courseid' => $course1->id,
4310              'enablemessaging' => 1,
4311          ]);
4312  
4313          // Add users to group1.
4314          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
4315          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
4316  
4317          // Call the WebService.
4318          $result = core_message_external::get_conversations($user1->id, 0, 20, null, false);
4319          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4320          $conversations = $result['conversations'];
4321  
4322          // Format original data.
4323          $coursecontext = \context_course::instance($course1->id);
4324          $coursename = \core_external\util::format_string($coursename, $coursecontext->id);
4325          $groupname = \core_external\util::format_string($groupname, $coursecontext->id);
4326  
4327          $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $conversations[0]['name']);
4328          $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $conversations[0]['subname']);
4329          $this->assertEquals($groupname, $conversations[0]['name']);
4330          $this->assertEquals($coursename, $conversations[0]['subname']);
4331      }
4332  
4333      /**
4334       * Test verifying get_conversations when there are users in a group and/or individual conversation. The reason this
4335       * test is performed is because we do not need as much data for group conversations (saving DB calls), so we want
4336       * to confirm this happens.
4337       */
4338      public function test_get_conversations_user_in_group_and_individual_chat() {
4339          $this->resetAfterTest();
4340  
4341          $user1 = self::getDataGenerator()->create_user();
4342          $user2 = self::getDataGenerator()->create_user();
4343          $user3 = self::getDataGenerator()->create_user();
4344  
4345          $conversation = \core_message\api::create_conversation(
4346              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4347              [
4348                  $user1->id,
4349                  $user2->id
4350              ],
4351              'Individual conversation'
4352          );
4353  
4354          testhelper::send_fake_message_to_conversation($user1, $conversation->id);
4355  
4356          $conversation = \core_message\api::create_conversation(
4357              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4358              [
4359                  $user1->id,
4360                  $user2->id,
4361              ],
4362              'Group conversation'
4363          );
4364  
4365          testhelper::send_fake_message_to_conversation($user1, $conversation->id);
4366  
4367          \core_message\api::create_contact_request($user1->id, $user2->id);
4368          \core_message\api::create_contact_request($user1->id, $user3->id);
4369  
4370          $this->setUser($user2);
4371          $result = core_message_external::get_conversations($user2->id);
4372          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4373          $conversations = $result['conversations'];
4374  
4375          $groupconversation = array_shift($conversations);
4376          $individualconversation = array_shift($conversations);
4377  
4378          $this->assertEquals('Group conversation', $groupconversation['name']);
4379          $this->assertEquals('Individual conversation', $individualconversation['name']);
4380  
4381          $this->assertCount(1, $groupconversation['members']);
4382          $this->assertCount(1, $individualconversation['members']);
4383  
4384          $groupmember = reset($groupconversation['members']);
4385          $this->assertNull($groupmember['requirescontact']);
4386          $this->assertNull($groupmember['canmessage']);
4387          $this->assertEmpty($groupmember['contactrequests']);
4388  
4389          $individualmember = reset($individualconversation['members']);
4390          $this->assertNotNull($individualmember['requirescontact']);
4391          $this->assertNotNull($individualmember['canmessage']);
4392          $this->assertNotEmpty($individualmember['contactrequests']);
4393      }
4394  
4395      /**
4396       * Test verifying get_conversations identifies if a conversation is muted or not.
4397       */
4398      public function test_get_conversations_some_muted() {
4399          $this->resetAfterTest();
4400  
4401          // Create some users.
4402          $user1 = self::getDataGenerator()->create_user();
4403          $user2 = self::getDataGenerator()->create_user();
4404          $user3 = self::getDataGenerator()->create_user();
4405  
4406          $conversation1 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4407              [$user1->id, $user2->id]);
4408          testhelper::send_fake_message_to_conversation($user1, $conversation1->id, 'Message 1');
4409          testhelper::send_fake_message_to_conversation($user2, $conversation1->id, 'Message 2');
4410          \core_message\api::mute_conversation($user1->id, $conversation1->id);
4411  
4412          $conversation2 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4413              [$user1->id, $user3->id]);
4414          testhelper::send_fake_message_to_conversation($user1, $conversation2->id, 'Message 1');
4415          testhelper::send_fake_message_to_conversation($user2, $conversation2->id, 'Message 2');
4416  
4417          $conversation3 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4418              [$user1->id, $user2->id]);
4419          \core_message\api::mute_conversation($user1->id, $conversation3->id);
4420  
4421          $conversation4 = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4422              [$user1->id, $user3->id]);
4423  
4424          $this->setUser($user1);
4425          $result = core_message_external::get_conversations($user1->id);
4426          $result = external_api::clean_returnvalue(core_message_external::get_conversations_returns(), $result);
4427          $conversations = $result['conversations'];
4428  
4429          usort($conversations, function($first, $second){
4430              return $first['id'] <=> $second['id'];
4431          });
4432  
4433          $selfconversation = array_shift($conversations);
4434          $conv1 = array_shift($conversations);
4435          $conv2 = array_shift($conversations);
4436          $conv3 = array_shift($conversations);
4437          $conv4 = array_shift($conversations);
4438  
4439          $this->assertTrue($conv1['ismuted']);
4440          $this->assertFalse($conv2['ismuted']);
4441          $this->assertTrue($conv3['ismuted']);
4442          $this->assertFalse($conv4['ismuted']);
4443      }
4444  
4445      /**
4446       * Test returning members in a conversation with no contact requests.
4447       */
4448      public function test_get_conversation_members_messaging_disabled() {
4449          global $CFG;
4450  
4451          $this->resetAfterTest();
4452  
4453          $CFG->messaging = 0;
4454  
4455          $this->expectException('moodle_exception');
4456          core_message_external::get_conversation_members(1, 2);
4457      }
4458  
4459      /**
4460       * Test returning members in a conversation with no contact requests.
4461       */
4462      public function test_get_conversation_members_wrong_user() {
4463          $this->resetAfterTest();
4464  
4465          $user1 = self::getDataGenerator()->create_user();
4466          $user2 = self::getDataGenerator()->create_user();
4467  
4468          $this->setUser($user2);
4469  
4470          $this->expectException('moodle_exception');
4471          core_message_external::get_conversation_members($user1->id, 2);
4472      }
4473  
4474      /**
4475       * Test returning members in a conversation with no contact requests.
4476       */
4477      public function test_get_conversation_members() {
4478          $this->resetAfterTest();
4479  
4480          $lastaccess = new \stdClass();
4481          $lastaccess->lastaccess = time();
4482  
4483          $user1 = self::getDataGenerator()->create_user($lastaccess);
4484          $user2 = self::getDataGenerator()->create_user();
4485          $user3 = self::getDataGenerator()->create_user();
4486  
4487          // This user will not be in the conversation, but a contact request will exist for them.
4488          $user4 = self::getDataGenerator()->create_user();
4489  
4490          // Add some contact requests.
4491          \core_message\api::create_contact_request($user1->id, $user3->id);
4492          \core_message\api::create_contact_request($user1->id, $user4->id);
4493          \core_message\api::create_contact_request($user2->id, $user3->id);
4494  
4495          // User 1 and 2 are already contacts.
4496          \core_message\api::add_contact($user1->id, $user2->id);
4497  
4498          // User 1 has blocked user 3.
4499          \core_message\api::block_user($user1->id, $user3->id);
4500  
4501          $conversation = \core_message\api::create_conversation(
4502              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4503              [
4504                  $user1->id,
4505                  $user2->id,
4506                  $user3->id
4507              ]
4508          );
4509          $conversationid = $conversation->id;
4510  
4511          $this->setAdminUser();
4512  
4513          $members = core_message_external::get_conversation_members($user1->id, $conversationid, false);
4514          external_api::clean_returnvalue(core_message_external::get_conversation_members_returns(), $members);
4515  
4516          // Sort them by id.
4517          ksort($members);
4518          $this->assertCount(3, $members);
4519          $member1 = array_shift($members);
4520          $member2 = array_shift($members);
4521          $member3 = array_shift($members);
4522  
4523          // Confirm the standard fields are OK.
4524          $this->assertEquals($user1->id, $member1->id);
4525          $this->assertEquals(fullname($user1), $member1->fullname);
4526          $this->assertEquals(true, $member1->isonline);
4527          $this->assertEquals(true, $member1->showonlinestatus);
4528          $this->assertEquals(false, $member1->iscontact);
4529          $this->assertEquals(false, $member1->isblocked);
4530          $this->assertObjectHasAttribute('contactrequests', $member1);
4531          $this->assertEmpty($member1->contactrequests);
4532  
4533          $this->assertEquals($user2->id, $member2->id);
4534          $this->assertEquals(fullname($user2), $member2->fullname);
4535          $this->assertEquals(false, $member2->isonline);
4536          $this->assertEquals(true, $member2->showonlinestatus);
4537          $this->assertEquals(true, $member2->iscontact);
4538          $this->assertEquals(false, $member2->isblocked);
4539          $this->assertObjectHasAttribute('contactrequests', $member2);
4540          $this->assertEmpty($member2->contactrequests);
4541  
4542          $this->assertEquals($user3->id, $member3->id);
4543          $this->assertEquals(fullname($user3), $member3->fullname);
4544          $this->assertEquals(false, $member3->isonline);
4545          $this->assertEquals(true, $member3->showonlinestatus);
4546          $this->assertEquals(false, $member3->iscontact);
4547          $this->assertEquals(true, $member3->isblocked);
4548          $this->assertObjectHasAttribute('contactrequests', $member3);
4549          $this->assertEmpty($member3->contactrequests);
4550      }
4551  
4552      /**
4553       * Test returning members in a conversation with contact requests.
4554       */
4555      public function test_get_conversation_members_with_contact_requests() {
4556          $this->resetAfterTest();
4557  
4558          $lastaccess = new \stdClass();
4559          $lastaccess->lastaccess = time();
4560  
4561          $user1 = self::getDataGenerator()->create_user($lastaccess);
4562          $user2 = self::getDataGenerator()->create_user();
4563          $user3 = self::getDataGenerator()->create_user();
4564  
4565          // This user will not be in the conversation, but a contact request will exist for them.
4566          $user4 = self::getDataGenerator()->create_user();
4567  
4568          // Add some contact requests.
4569          \core_message\api::create_contact_request($user1->id, $user2->id);
4570          \core_message\api::create_contact_request($user1->id, $user3->id);
4571          \core_message\api::create_contact_request($user1->id, $user4->id);
4572          \core_message\api::create_contact_request($user2->id, $user3->id);
4573  
4574          // User 1 and 2 are already contacts.
4575          \core_message\api::add_contact($user1->id, $user2->id);
4576          // User 1 has blocked user 3.
4577          \core_message\api::block_user($user1->id, $user3->id);
4578  
4579          $conversation = \core_message\api::create_conversation(
4580              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4581              [
4582                  $user1->id,
4583                  $user2->id,
4584                  $user3->id
4585              ]
4586          );
4587          $conversationid = $conversation->id;
4588  
4589          $this->setAdminUser();
4590  
4591          $members = core_message_external::get_conversation_members($user1->id, $conversationid, true);
4592          external_api::clean_returnvalue(core_message_external::get_conversation_members_returns(), $members);
4593  
4594          // Sort them by id.
4595          ksort($members);
4596          $this->assertCount(3, $members);
4597          $member1 = array_shift($members);
4598          $member2 = array_shift($members);
4599          $member3 = array_shift($members);
4600  
4601          // Confirm the standard fields are OK.
4602          $this->assertEquals($user1->id, $member1->id);
4603          $this->assertEquals(fullname($user1), $member1->fullname);
4604          $this->assertEquals(true, $member1->isonline);
4605          $this->assertEquals(true, $member1->showonlinestatus);
4606          $this->assertEquals(false, $member1->iscontact);
4607          $this->assertEquals(false, $member1->isblocked);
4608          $this->assertCount(2, $member1->contactrequests);
4609  
4610          $this->assertEquals($user2->id, $member2->id);
4611          $this->assertEquals(fullname($user2), $member2->fullname);
4612          $this->assertEquals(false, $member2->isonline);
4613          $this->assertEquals(true, $member2->showonlinestatus);
4614          $this->assertEquals(true, $member2->iscontact);
4615          $this->assertEquals(false, $member2->isblocked);
4616          $this->assertCount(1, $member2->contactrequests);
4617  
4618          $this->assertEquals($user3->id, $member3->id);
4619          $this->assertEquals(fullname($user3), $member3->fullname);
4620          $this->assertEquals(false, $member3->isonline);
4621          $this->assertEquals(true, $member3->showonlinestatus);
4622          $this->assertEquals(false, $member3->iscontact);
4623          $this->assertEquals(true, $member3->isblocked);
4624          $this->assertCount(1, $member3->contactrequests);
4625  
4626          // Confirm the contact requests are OK.
4627          $request1 = array_shift($member1->contactrequests);
4628          $request2 = array_shift($member1->contactrequests);
4629  
4630          $this->assertEquals($user1->id, $request1->userid);
4631          $this->assertEquals($user2->id, $request1->requesteduserid);
4632  
4633          $this->assertEquals($user1->id, $request2->userid);
4634          $this->assertEquals($user3->id, $request2->requesteduserid);
4635  
4636          $request1 = array_shift($member2->contactrequests);
4637  
4638          $this->assertEquals($user1->id, $request1->userid);
4639          $this->assertEquals($user2->id, $request1->requesteduserid);
4640  
4641          $request1 = array_shift($member3->contactrequests);
4642  
4643          $this->assertEquals($user1->id, $request1->userid);
4644          $this->assertEquals($user3->id, $request1->requesteduserid);
4645      }
4646  
4647      /**
4648       * Test returning members in a conversation when you are not a member.
4649       */
4650      public function test_get_conversation_members_not_a_member() {
4651          $this->resetAfterTest();
4652  
4653          $user1 = self::getDataGenerator()->create_user();
4654          $user2 = self::getDataGenerator()->create_user();
4655  
4656          // This user will not be in the conversation.
4657          $user3 = self::getDataGenerator()->create_user();
4658  
4659          $conversation = \core_message\api::create_conversation(
4660              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
4661              [
4662                  $user1->id,
4663                  $user2->id,
4664              ]
4665          );
4666          $conversationid = $conversation->id;
4667  
4668          $this->setUser($user3);
4669  
4670          $this->expectException('moodle_exception');
4671          core_message_external::get_conversation_members($user3->id, $conversationid);
4672      }
4673  
4674      /**
4675       * Test verifying multiple messages can be sent to an individual conversation.
4676       */
4677      public function test_send_messages_to_conversation_individual() {
4678          $this->resetAfterTest(true);
4679  
4680          // Get a bunch of conversations, some group, some individual and in different states.
4681          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4682              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4683  
4684          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
4685          $course1 = $this->getDataGenerator()->create_course();
4686          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4687          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4688          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
4689          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
4690  
4691          // The user making the request.
4692          $this->setUser($user1);
4693  
4694          // Try to send a message as user1 to a conversation user1 is a a part of.
4695          $messages = [
4696              [
4697                  'text' => 'a message from user 1',
4698                  'textformat' => FORMAT_MOODLE
4699              ],
4700              [
4701                  'text' => 'another message from user 1',
4702                  'textformat' => FORMAT_MOODLE
4703              ],
4704          ];
4705          // Redirect messages.
4706          // This marks messages as read, but we can still observe and verify the number of conversation recipients,
4707          // based on the message_viewed events generated as part of marking the message as read for each user.
4708          $this->preventResetByRollback();
4709          $sink = $this->redirectMessages();
4710          $writtenmessages = core_message_external::send_messages_to_conversation($ic1->id, $messages);
4711  
4712          external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages);
4713  
4714          $this->assertCount(2, $writtenmessages);
4715          $this->assertObjectHasAttribute('id', $writtenmessages[0]);
4716          $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom);
4717          $this->assertEquals('<p>a message from user 1</p>', $writtenmessages[0]->text);
4718          $this->assertNotEmpty($writtenmessages[0]->timecreated);
4719  
4720          $this->assertObjectHasAttribute('id', $writtenmessages[1]);
4721          $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom);
4722          $this->assertEquals('<p>another message from user 1</p>', $writtenmessages[1]->text);
4723          $this->assertNotEmpty($writtenmessages[1]->timecreated);
4724      }
4725  
4726      /**
4727       * Test verifying multiple messages can be sent to an group conversation.
4728       */
4729      public function test_send_messages_to_conversation_group() {
4730          $this->resetAfterTest(true);
4731  
4732          // Get a bunch of conversations, some group, some individual and in different states.
4733          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4734              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4735  
4736          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
4737          $course1 = $this->getDataGenerator()->create_course();
4738          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4739          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4740          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
4741          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
4742  
4743          // The user making the request.
4744          $this->setUser($user1);
4745  
4746          // Try to send a message as user1 to a conversation user1 is a a part of.
4747          $messages = [
4748              [
4749                  'text' => 'a message from user 1 to group conv',
4750                  'textformat' => FORMAT_MOODLE
4751              ],
4752              [
4753                  'text' => 'another message from user 1 to group conv',
4754                  'textformat' => FORMAT_MOODLE
4755              ],
4756          ];
4757          // Redirect messages.
4758          // This marks messages as read, but we can still observe and verify the number of conversation recipients,
4759          // based on the message_viewed events generated as part of marking the message as read for each user.
4760          $this->preventResetByRollback();
4761          $sink = $this->redirectMessages();
4762          $writtenmessages = core_message_external::send_messages_to_conversation($gc2->id, $messages);
4763  
4764          external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages);
4765  
4766          $this->assertCount(2, $writtenmessages);
4767          $this->assertObjectHasAttribute('id', $writtenmessages[0]);
4768          $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom);
4769          $this->assertEquals('<p>a message from user 1 to group conv</p>', $writtenmessages[0]->text);
4770          $this->assertNotEmpty($writtenmessages[0]->timecreated);
4771  
4772          $this->assertObjectHasAttribute('id', $writtenmessages[1]);
4773          $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom);
4774          $this->assertEquals('<p>another message from user 1 to group conv</p>', $writtenmessages[1]->text);
4775          $this->assertNotEmpty($writtenmessages[1]->timecreated);
4776      }
4777  
4778      /**
4779       * Test verifying multiple messages can not be sent to a non existent conversation.
4780       */
4781      public function test_send_messages_to_conversation_non_existent_conversation() {
4782          $this->resetAfterTest(true);
4783  
4784          // Get a bunch of conversations, some group, some individual and in different states.
4785          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4786              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4787  
4788          // The user making the request.
4789          $this->setUser($user1);
4790  
4791          // Try to send a message as user1 to a conversation user1 is a a part of.
4792          $messages = [
4793              [
4794                  'text' => 'a message from user 1',
4795                  'textformat' => FORMAT_MOODLE
4796              ],
4797              [
4798                  'text' => 'another message from user 1',
4799                  'textformat' => FORMAT_MOODLE
4800              ],
4801          ];
4802          $this->expectException(\moodle_exception::class);
4803          $writtenmessages = core_message_external::send_messages_to_conversation(0, $messages);
4804      }
4805  
4806      /**
4807       * Test verifying multiple messages can not be sent to a conversation by a non-member.
4808       */
4809      public function test_send_messages_to_conversation_non_member() {
4810          $this->resetAfterTest(true);
4811  
4812          // Get a bunch of conversations, some group, some individual and in different states.
4813          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4814              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4815  
4816          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
4817          $course1 = $this->getDataGenerator()->create_course();
4818          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4819          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4820          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
4821          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
4822  
4823          // The user making the request. This user is not a member of group conversation 1 (gc1).
4824          $this->setUser($user1);
4825  
4826          // Try to send a message as user1 to a conversation user1 is a a part of.
4827          $messages = [
4828              [
4829                  'text' => 'a message from user 1 to group conv',
4830                  'textformat' => FORMAT_MOODLE
4831              ],
4832              [
4833                  'text' => 'another message from user 1 to group conv',
4834                  'textformat' => FORMAT_MOODLE
4835              ],
4836          ];
4837          $this->expectException(\moodle_exception::class);
4838          $writtenmessages = core_message_external::send_messages_to_conversation($gc1->id, $messages);
4839      }
4840  
4841      /**
4842       * Test verifying a to long message can not be sent to a conversation.
4843       */
4844      public function test_send_messages_to_conversation_long_text() {
4845          $this->resetAfterTest(true);
4846  
4847          // Get a bunch of conversations, some group, some individual and in different states.
4848          list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3,
4849              $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data();
4850  
4851          // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used.
4852          $course1 = $this->getDataGenerator()->create_course();
4853          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4854          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4855          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
4856          $this->getDataGenerator()->enrol_user($user4->id, $course1->id);
4857  
4858          // The user making the request.
4859          $this->setUser($user1);
4860  
4861          // Try to send a message as user1 to a conversation user1 is a a part of.
4862          $messages = [
4863              [
4864                  'text' => str_repeat("M", \core_message\api::MESSAGE_MAX_LENGTH + 100),
4865                  'textformat' => FORMAT_MOODLE
4866              ],
4867          ];
4868  
4869          $this->expectException(\moodle_exception::class);
4870          $writtenmessages = core_message_external::send_messages_to_conversation($gc2->id, $messages);
4871      }
4872  
4873      /**
4874       * Test getting a conversation that doesn't exist.
4875       */
4876      public function test_get_conversation_no_conversation() {
4877          $this->resetAfterTest();
4878  
4879          $user1 = self::getDataGenerator()->create_user();
4880          $user2 = self::getDataGenerator()->create_user();
4881  
4882          $name = 'lol conversation';
4883          $conversation = \core_message\api::create_conversation(
4884              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4885              [
4886                  $user1->id,
4887                  $user2->id,
4888              ],
4889              $name
4890          );
4891          $conversationid = $conversation->id;
4892  
4893          $this->setUser($user1);
4894  
4895          $this->expectException('moodle_exception');
4896          $conv = core_message_external::get_conversation($user1->id, $conversationid + 1);
4897          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
4898      }
4899  
4900      /**
4901       * Test verifying that the correct favourite information is returned for a non-linked converastion at user context.
4902       */
4903      public function test_get_conversation_favourited() {
4904          $this->resetAfterTest();
4905  
4906          $user1 = self::getDataGenerator()->create_user();
4907          $user2 = self::getDataGenerator()->create_user();
4908  
4909          // Create a conversation between the 2 users.
4910          $conversation = \core_message\api::create_conversation(
4911              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4912              [
4913                  $user1->id,
4914                  $user2->id,
4915              ],
4916              'An individual conversation'
4917          );
4918  
4919          // Favourite the conversation as user 1 only.
4920          \core_message\api::set_favourite_conversation($conversation->id, $user1->id);
4921  
4922          // Get the conversation for user1 and confirm it's favourited.
4923          $this->setUser($user1);
4924          $conv = core_message_external::get_conversation($user1->id, $conversation->id);
4925          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
4926          $this->assertTrue($conv['isfavourite']);
4927  
4928          // Get the conversation for user2 and confirm it's NOT favourited.
4929          $this->setUser($user2);
4930          $conv = core_message_external::get_conversation($user2->id, $conversation->id);
4931          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
4932          $this->assertFalse($conv['isfavourite']);
4933      }
4934  
4935      /**
4936       * Test verifying that the correct favourite information is returned for a group-linked conversation at course context.
4937       */
4938      public function test_get_conversation_favourited_group_linked() {
4939          $this->resetAfterTest();
4940          global $DB;
4941  
4942          $user1 = self::getDataGenerator()->create_user();
4943          $user2 = self::getDataGenerator()->create_user();
4944          $user3 = self::getDataGenerator()->create_user();
4945  
4946          $course1 = $this->getDataGenerator()->create_course();
4947          $course1context = \context_course::instance($course1->id);
4948  
4949          // Create a group with a linked conversation and a valid image.
4950          $this->setAdminUser();
4951          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
4952          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
4953          $this->getDataGenerator()->enrol_user($user3->id, $course1->id);
4954          $group1 = $this->getDataGenerator()->create_group([
4955              'courseid' => $course1->id,
4956              'enablemessaging' => 1
4957          ]);
4958  
4959          // Add users to group1.
4960          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
4961          $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
4962  
4963          // Verify that the conversation is a group linked conversation in the course context.
4964          $conversationrecord = $DB->get_record('message_conversations', ['component' => 'core_group', 'itemtype' => 'groups']);
4965          $this->assertEquals($course1context->id, $conversationrecord->contextid);
4966  
4967          // Favourite the conversation as user 1 only.
4968          \core_message\api::set_favourite_conversation($conversationrecord->id, $user1->id);
4969  
4970          // Get the conversation for user1 and confirm it's favourited.
4971          $this->setUser($user1);
4972          $conv = core_message_external::get_conversation($user1->id, $conversationrecord->id);
4973          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
4974          $this->assertTrue($conv['isfavourite']);
4975  
4976          // Get the conversation for user2 and confirm it's NOT favourited.
4977          $this->setUser($user2);
4978          $conv = core_message_external::get_conversation($user2->id, $conversationrecord->id);
4979          $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
4980          $this->assertFalse($conv['isfavourite']);
4981      }
4982  
4983      /**
4984       * Test getting a conversation with no messages.
4985       */
4986      public function test_get_conversation_no_messages() {
4987          $this->resetAfterTest();
4988  
4989          $user1 = self::getDataGenerator()->create_user();
4990          $user2 = self::getDataGenerator()->create_user();
4991  
4992          $name = 'lol conversation';
4993          $conversation = \core_message\api::create_conversation(
4994              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
4995              [
4996                  $user1->id,
4997                  $user2->id,
4998              ],
4999              $name
5000          );
5001          $conversationid = $conversation->id;
5002  
5003          $this->setUser($user1);
5004  
5005          $conv = core_message_external::get_conversation($user1->id, $conversationid);
5006          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
5007  
5008          $conv = (array) $conv;
5009          $this->assertEquals($conversationid, $conv['id']);
5010          $this->assertEquals($name, $conv['name']);
5011          $this->assertArrayHasKey('subname', $conv);
5012          $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conv['type']);
5013          $this->assertEquals(2, $conv['membercount']);
5014          $this->assertEquals(false, $conv['isfavourite']);
5015          $this->assertEquals(true, $conv['isread']);
5016          $this->assertEquals(0, $conv['unreadcount']);
5017          $this->assertCount(1, $conv['members']);
5018          foreach ($conv['members'] as $member) {
5019              $member = (array) $member;
5020              $this->assertArrayHasKey('id', $member);
5021              $this->assertArrayHasKey('fullname', $member);
5022              $this->assertArrayHasKey('profileimageurl', $member);
5023              $this->assertArrayHasKey('profileimageurlsmall', $member);
5024              $this->assertArrayHasKey('isonline', $member);
5025              $this->assertArrayHasKey('showonlinestatus', $member);
5026              $this->assertArrayHasKey('isblocked', $member);
5027              $this->assertArrayHasKey('iscontact', $member);
5028          }
5029          $this->assertEmpty($conv['messages']);
5030      }
5031  
5032      /**
5033       * Test getting a conversation with messages.
5034       */
5035      public function test_get_conversation_with_messages() {
5036          $this->resetAfterTest();
5037  
5038          $user1 = self::getDataGenerator()->create_user();
5039          $user2 = self::getDataGenerator()->create_user();
5040          $user3 = self::getDataGenerator()->create_user();
5041  
5042          // Some random conversation.
5043          $otherconversation = \core_message\api::create_conversation(
5044              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5045              [
5046                  $user1->id,
5047                  $user3->id,
5048              ]
5049          );
5050  
5051          $conversation = \core_message\api::create_conversation(
5052              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5053              [
5054                  $user1->id,
5055                  $user2->id,
5056              ]
5057          );
5058          $conversationid = $conversation->id;
5059  
5060          $time = time();
5061          $message1id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'A', $time - 10);
5062          $message2id = testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'B', $time - 5);
5063          $message3id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'C', $time);
5064  
5065          // Add some messages to the other convo to make sure they aren't included.
5066          testhelper::send_fake_message_to_conversation($user1, $otherconversation->id, 'foo');
5067  
5068          $this->setUser($user1);
5069  
5070          // Test newest first.
5071          $conv = core_message_external::get_conversation(
5072              $user1->id,
5073              $conversationid,
5074              false,
5075              false,
5076              0,
5077              0,
5078              0,
5079              0,
5080              true
5081          );
5082          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
5083  
5084          $conv = (array) $conv;
5085          $this->assertEquals(false, $conv['isread']);
5086          $this->assertEquals(1, $conv['unreadcount']);
5087          $this->assertCount(3, $conv['messages']);
5088          $this->assertEquals($message3id, $conv['messages'][0]->id);
5089          $this->assertEquals($user1->id, $conv['messages'][0]->useridfrom);
5090          $this->assertEquals($message2id, $conv['messages'][1]->id);
5091          $this->assertEquals($user2->id, $conv['messages'][1]->useridfrom);
5092          $this->assertEquals($message1id, $conv['messages'][2]->id);
5093          $this->assertEquals($user1->id, $conv['messages'][2]->useridfrom);
5094  
5095          // Test newest last.
5096          $conv = core_message_external::get_conversation(
5097              $user1->id,
5098              $conversationid,
5099              false,
5100              false,
5101              0,
5102              0,
5103              0,
5104              0,
5105              false
5106          );
5107          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
5108  
5109          $conv = (array) $conv;
5110          $this->assertCount(3, $conv['messages']);
5111          $this->assertEquals($message3id, $conv['messages'][2]->id);
5112          $this->assertEquals($user1->id, $conv['messages'][2]->useridfrom);
5113          $this->assertEquals($message2id, $conv['messages'][1]->id);
5114          $this->assertEquals($user2->id, $conv['messages'][1]->useridfrom);
5115          $this->assertEquals($message1id, $conv['messages'][0]->id);
5116          $this->assertEquals($user1->id, $conv['messages'][0]->useridfrom);
5117  
5118          // Test message offest and limit.
5119          $conv = core_message_external::get_conversation(
5120              $user1->id,
5121              $conversationid,
5122              false,
5123              false,
5124              0,
5125              0,
5126              1,
5127              1,
5128              true
5129          );
5130          external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv);
5131  
5132          $conv = (array) $conv;
5133          $this->assertCount(1, $conv['messages']);
5134          $this->assertEquals($message2id, $conv['messages'][0]->id);
5135          $this->assertEquals($user2->id, $conv['messages'][0]->useridfrom);
5136      }
5137  
5138      /**
5139       * Data provider for test_get_conversation_counts().
5140       */
5141      public function get_conversation_counts_test_cases() {
5142          $typeindividual = \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL;
5143          $typegroup = \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP;
5144          $typeself = \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF;
5145          list($user1, $user2, $user3, $user4, $user5, $user6, $user7, $user8) = [0, 1, 2, 3, 4, 5, 6, 7];
5146          $conversations = [
5147              [
5148                  'type' => $typeindividual,
5149                  'users' => [$user1, $user2],
5150                  'messages' => [$user1, $user2, $user2],
5151                  'favourites' => [$user1],
5152                  'enabled' => null // Individual conversations cannot be disabled.
5153              ],
5154              [
5155                  'type' => $typeindividual,
5156                  'users' => [$user1, $user3],
5157                  'messages' => [$user1, $user3, $user1],
5158                  'favourites' => [],
5159                  'enabled' => null // Individual conversations cannot be disabled.
5160              ],
5161              [
5162                  'type' => $typegroup,
5163                  'users' => [$user1, $user2, $user3, $user4],
5164                  'messages' => [$user1, $user2, $user3, $user4],
5165                  'favourites' => [],
5166                  'enabled' => true
5167              ],
5168              [
5169                  'type' => $typegroup,
5170                  'users' => [$user2, $user3, $user4],
5171                  'messages' => [$user2, $user3, $user4],
5172                  'favourites' => [],
5173                  'enabled' => true
5174              ],
5175              [
5176                  'type' => $typegroup,
5177                  'users' => [$user6, $user7],
5178                  'messages' => [$user6, $user7, $user7],
5179                  'favourites' => [$user6],
5180                  'enabled' => false
5181              ],
5182              [
5183                  'type' => $typeself,
5184                  'users' => [$user8],
5185                  'messages' => [$user8],
5186                  'favourites' => [],
5187                  'enabled' => null // Individual conversations cannot be disabled.
5188              ],
5189          ];
5190  
5191          return [
5192              'No conversations' => [
5193                  'conversationConfigs' => $conversations,
5194                  'deletemessagesuser' => null,
5195                  'deletemessages' => [],
5196                  'arguments' => [$user5],
5197                  'expectedcounts' => ['favourites' => 1, 'types' => [
5198                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5199                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5200                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5201                  ]],
5202                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5203                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5204                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5205                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5206                  ]],
5207                  'deletedusers' => []
5208              ],
5209              'No individual conversations, 2 group conversations' => [
5210                  'conversationConfigs' => $conversations,
5211                  'deletemessagesuser' => null,
5212                  'deletemessages' => [],
5213                  'arguments' => [$user4],
5214                  'expectedcounts' => ['favourites' => 1, 'types' => [
5215                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5216                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5217                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5218                  ]],
5219                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5220                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5221                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5222                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5223                  ]],
5224                  'deletedusers' => []
5225              ],
5226              '2 individual conversations (one favourited), 1 group conversation' => [
5227                  'conversationConfigs' => $conversations,
5228                  'deletemessagesuser' => null,
5229                  'deletemessages' => [],
5230                  'arguments' => [$user1],
5231                  'expectedcounts' => ['favourites' => 2, 'types' => [
5232                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5233                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5234                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5235                  ]],
5236                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5237                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5238                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5239                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5240                  ]],
5241                  'deletedusers' => []
5242              ],
5243              '1 individual conversation, 2 group conversations' => [
5244                  'conversationConfigs' => $conversations,
5245                  'deletemessagesuser' => null,
5246                  'deletemessages' => [],
5247                  'arguments' => [$user2],
5248                  'expectedcounts' => ['favourites' => 1, 'types' => [
5249                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5250                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5251                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5252                  ]],
5253                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5254                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5255                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5256                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5257                  ]],
5258                  'deletedusers' => []
5259              ],
5260              '2 group conversations only' => [
5261                  'conversationConfigs' => $conversations,
5262                  'deletemessagesuser' => null,
5263                  'deletemessages' => [],
5264                  'arguments' => [$user4],
5265                  'expectedcounts' => ['favourites' => 1, 'types' => [
5266                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5267                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5268                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5269                  ]],
5270                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5271                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5272                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5273                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5274                  ]],
5275                  'deletedusers' => []
5276              ],
5277              'All conversation types, delete a message from individual favourited, messages remaining' => [
5278                  'conversationConfigs' => $conversations,
5279                  'deletemessagesuser' => $user1,
5280                  'deletemessages' => [0],
5281                  'arguments' => [$user1],
5282                  'expectedcounts' => ['favourites' => 2, 'types' => [
5283                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5284                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5285                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5286                  ]],
5287                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5288                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5289                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5290                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5291                  ]],
5292                  'deletedusers' => []
5293              ],
5294              'All conversation types, delete a message from individual non-favourited, messages remaining' => [
5295                  'conversationConfigs' => $conversations,
5296                  'deletemessagesuser' => $user1,
5297                  'deletemessages' => [3],
5298                  'arguments' => [$user1],
5299                  'expectedcounts' => ['favourites' => 2, 'types' => [
5300                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5301                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5302                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5303                  ]],
5304                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5305                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5306                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5307                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5308                  ]],
5309                  'deletedusers' => []
5310              ],
5311              'All conversation types, delete all messages from individual favourited, no messages remaining' => [
5312                  'conversationConfigs' => $conversations,
5313                  'deletemessagesuser' => $user1,
5314                  'deletemessages' => [0, 1, 2],
5315                  'arguments' => [$user1],
5316                  'expectedcounts' => ['favourites' => 1, 'types' => [
5317                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5318                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5319                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5320                  ]],
5321                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5322                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5323                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5324                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5325                  ]],
5326                  'deletedusers' => []
5327              ],
5328              'All conversation types, delete all messages from individual non-favourited, no messages remaining' => [
5329                  'conversationConfigs' => $conversations,
5330                  'deletemessagesuser' => $user1,
5331                  'deletemessages' => [3, 4, 5],
5332                  'arguments' => [$user1],
5333                  'expectedcounts' => ['favourites' => 2, 'types' => [
5334                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5335                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5336                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5337                  ]],
5338                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5339                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5340                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5341                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5342                  ]],
5343                  'deletedusers' => []
5344              ],
5345              'All conversation types, delete all messages from individual favourited, no messages remaining, different user' => [
5346                  'conversationConfigs' => $conversations,
5347                  'deletemessagesuser' => $user1,
5348                  'deletemessages' => [0, 1, 2],
5349                  'arguments' => [$user2],
5350                  'expectedcounts' => ['favourites' => 1, 'types' => [
5351                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5352                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5353                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5354                  ]],
5355                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5356                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5357                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5358                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5359                  ]],
5360                  'deletedusers' => []
5361              ],
5362              'All conversation types, delete all messages from individual non-favourited, no messages remaining, different user' => [
5363                  'conversationConfigs' => $conversations,
5364                  'deletemessagesuser' => $user1,
5365                  'deletemessages' => [3, 4, 5],
5366                  'arguments' => [$user3],
5367                  'expectedcounts' => ['favourites' => 1, 'types' => [
5368                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5369                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5370                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5371                  ]],
5372                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5373                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5374                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2,
5375                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5376                  ]],
5377                  'deletedusers' => []
5378              ],
5379              'All conversation types, delete some messages from group non-favourited, messages remaining,' => [
5380                  'conversationConfigs' => $conversations,
5381                  'deletemessagesuser' => $user1,
5382                  'deletemessages' => [6, 7],
5383                  'arguments' => [$user1],
5384                  'expectedcounts' => ['favourites' => 2, 'types' => [
5385                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5386                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5387                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5388                  ]],
5389                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5390                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5391                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5392                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5393                  ]],
5394                  'deletedusers' => []
5395              ],
5396              'All conversation types, delete all messages from group non-favourited, no messages remaining,' => [
5397                  'conversationConfigs' => $conversations,
5398                  'deletemessagesuser' => $user1,
5399                  'deletemessages' => [6, 7, 8, 9],
5400                  'arguments' => [$user1],
5401                  'expectedcounts' => ['favourites' => 2, 'types' => [
5402                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5403                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5404                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5405                  ]],
5406                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5407                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5408                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5409                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5410                  ]],
5411                  'deletedusers' => []
5412              ],
5413              'All conversation types, another user soft deleted' => [
5414                  'conversationConfigs' => $conversations,
5415                  'deletemessagesuser' => null,
5416                  'deletemessages' => [],
5417                  'arguments' => [$user1],
5418                  'expectedcounts' => ['favourites' => 2, 'types' => [
5419                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5420                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5421                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5422                  ]],
5423                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5424                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5425                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5426                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5427                  ]],
5428                  'deletedusers' => [$user2]
5429              ],
5430              'All conversation types, all group users soft deleted' => [
5431                  'conversationConfigs' => $conversations,
5432                  'deletemessagesuser' => null,
5433                  'deletemessages' => [],
5434                  'arguments' => [$user1],
5435                  'expectedcounts' => ['favourites' => 2, 'types' => [
5436                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5437                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5438                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5439                  ]],
5440                  'expectedunreadcounts' => ['favourites' => 1, 'types' => [
5441                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1,
5442                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1,
5443                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5444                  ]],
5445                  'deletedusers' => [$user2, $user3, $user4]
5446              ],
5447              'Group conversation which is disabled, favourited' => [
5448                  'conversationConfigs' => $conversations,
5449                  'deletemessagesuser' => null,
5450                  'deletemessages' => [],
5451                  'arguments' => [$user6],
5452                  'expectedcounts' => ['favourites' => 1, 'types' => [
5453                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5454                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5455                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5456                  ]],
5457                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5458                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5459                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5460                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5461                  ]],
5462                  'deletedusers' => []
5463              ],
5464              'Group conversation which is disabled, non-favourited' => [
5465                  'conversationConfigs' => $conversations,
5466                  'deletemessagesuser' => null,
5467                  'deletemessages' => [],
5468                  'arguments' => [$user7],
5469                  'expectedcounts' => ['favourites' => 1, 'types' => [
5470                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5471                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5472                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5473                  ]],
5474                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5475                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5476                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5477                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5478                  ]],
5479                  'deletedusers' => []
5480              ],
5481              'Conversation with self' => [
5482                  'conversationConfigs' => $conversations,
5483                  'deletemessagesuser' => null,
5484                  'deletemessages' => [],
5485                  'arguments' => [$user8],
5486                  'expectedcounts' => ['favourites' => 0, 'types' => [
5487                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5488                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5489                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 1
5490                  ]],
5491                  'expectedunreadcounts' => ['favourites' => 0, 'types' => [
5492                      \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0,
5493                      \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0,
5494                      \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0
5495                  ]],
5496                  'deletedusers' => []
5497              ],
5498          ];
5499      }
5500  
5501      /**
5502       * Test the get_conversation_counts() function.
5503       *
5504       * @dataProvider get_conversation_counts_test_cases()
5505       * @param array $conversationconfigs Conversations to create
5506       * @param int $deletemessagesuser The user who is deleting the messages
5507       * @param array $deletemessages The list of messages to delete (by index)
5508       * @param array $arguments Arguments for the count conversations function
5509       * @param array $expectedcounts the expected conversation counts
5510       * @param array $expectedunreadcounts the expected unread conversation counts
5511       * @param array $deletedusers the array of users to soft delete.
5512       */
5513      public function test_get_conversation_counts(
5514          $conversationconfigs,
5515          $deletemessagesuser,
5516          $deletemessages,
5517          $arguments,
5518          $expectedcounts,
5519          $expectedunreadcounts,
5520          $deletedusers
5521      ) {
5522          $this->resetAfterTest();
5523          $generator = $this->getDataGenerator();
5524          $users = [
5525              $generator->create_user(),
5526              $generator->create_user(),
5527              $generator->create_user(),
5528              $generator->create_user(),
5529              $generator->create_user(),
5530              $generator->create_user(),
5531              $generator->create_user(),
5532              $generator->create_user()
5533          ];
5534  
5535          $deleteuser = !is_null($deletemessagesuser) ? $users[$deletemessagesuser] : null;
5536          $this->setUser($users[$arguments[0]]);
5537          $arguments[0] = $users[$arguments[0]]->id;
5538          $systemcontext = \context_system::instance();
5539          $conversations = [];
5540          $messageids = [];
5541  
5542          foreach ($conversationconfigs as $config) {
5543              $conversation = \core_message\api::create_conversation(
5544                  $config['type'],
5545                  array_map(function($userindex) use ($users) {
5546                      return $users[$userindex]->id;
5547                  }, $config['users']),
5548                  null,
5549                  ($config['enabled'] ?? true)
5550              );
5551  
5552              foreach ($config['messages'] as $userfromindex) {
5553                  $userfrom = $users[$userfromindex];
5554                  $messageids[] = testhelper::send_fake_message_to_conversation($userfrom, $conversation->id);
5555              }
5556  
5557              // Remove the self conversations created by the generator,
5558              // so we can choose to set that ourself and honour the original intention of the test.
5559              $userids = array_map(function($userindex) use ($users) {
5560                  return $users[$userindex]->id;
5561              }, $config['users']);
5562              foreach ($userids as $userid) {
5563                  if ($conversation->type == \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF) {
5564                      \core_message\api::unset_favourite_conversation($conversation->id, $userid);
5565                  }
5566              }
5567  
5568              foreach ($config['favourites'] as $userfromindex) {
5569                  $userfrom = $users[$userfromindex];
5570                  $usercontext = \context_user::instance($userfrom->id);
5571                  $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
5572                  $ufservice->create_favourite('core_message', 'message_conversations', $conversation->id, $systemcontext);
5573              }
5574  
5575              $conversations[] = $conversation;
5576          }
5577  
5578          foreach ($deletemessages as $messageindex) {
5579              \core_message\api::delete_message($deleteuser->id, $messageids[$messageindex]);
5580          }
5581  
5582          foreach ($deletedusers as $deleteduser) {
5583              delete_user($users[$deleteduser]);
5584          }
5585  
5586          $counts = core_message_external::get_conversation_counts(...$arguments);
5587          $counts = external_api::clean_returnvalue(core_message_external::get_conversation_counts_returns(), $counts);
5588  
5589          $this->assertEquals($expectedcounts['favourites'], $counts['favourites']);
5590          $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
5591              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]);
5592          $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP],
5593              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]);
5594          $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF],
5595              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]);
5596      }
5597  
5598      /**
5599       * Test the get_unread_conversation_counts() function.
5600       *
5601       * @dataProvider get_conversation_counts_test_cases
5602       * @param array $conversationconfigs Conversations to create
5603       * @param int $deletemessagesuser The user who is deleting the messages
5604       * @param array $deletemessages The list of messages to delete (by index)
5605       * @param array $arguments Arguments for the count conversations function
5606       * @param array $expectedcounts the expected conversation counts
5607       * @param array $expectedunreadcounts the expected unread conversation counts
5608       * @param array $deletedusers the list of users to soft-delete.
5609       */
5610      public function test_get_unread_conversation_counts(
5611          $conversationconfigs,
5612          $deletemessagesuser,
5613          $deletemessages,
5614          $arguments,
5615          $expectedcounts,
5616          $expectedunreadcounts,
5617          $deletedusers
5618      ) {
5619          $this->resetAfterTest();
5620          $generator = $this->getDataGenerator();
5621          $users = [
5622              $generator->create_user(),
5623              $generator->create_user(),
5624              $generator->create_user(),
5625              $generator->create_user(),
5626              $generator->create_user(),
5627              $generator->create_user(),
5628              $generator->create_user(),
5629              $generator->create_user()
5630          ];
5631  
5632          $deleteuser = !is_null($deletemessagesuser) ? $users[$deletemessagesuser] : null;
5633          $this->setUser($users[$arguments[0]]);
5634          $arguments[0] = $users[$arguments[0]]->id;
5635          $systemcontext = \context_system::instance();
5636          $conversations = [];
5637          $messageids = [];
5638  
5639          foreach ($conversationconfigs as $config) {
5640              $conversation = \core_message\api::create_conversation(
5641                  $config['type'],
5642                  array_map(function($userindex) use ($users) {
5643                      return $users[$userindex]->id;
5644                  }, $config['users']),
5645                  null,
5646                  ($config['enabled'] ?? true)
5647              );
5648  
5649              foreach ($config['messages'] as $userfromindex) {
5650                  $userfrom = $users[$userfromindex];
5651                  $messageids[] = testhelper::send_fake_message_to_conversation($userfrom, $conversation->id);
5652              }
5653  
5654              foreach ($config['favourites'] as $userfromindex) {
5655                  $userfrom = $users[$userfromindex];
5656                  $usercontext = \context_user::instance($userfrom->id);
5657                  $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
5658                  $ufservice->create_favourite('core_message', 'message_conversations', $conversation->id, $systemcontext);
5659              }
5660  
5661              $conversations[] = $conversation;
5662          }
5663  
5664          foreach ($deletemessages as $messageindex) {
5665              \core_message\api::delete_message($deleteuser->id, $messageids[$messageindex]);
5666          }
5667  
5668          foreach ($deletedusers as $deleteduser) {
5669              delete_user($users[$deleteduser]);
5670          }
5671  
5672          $counts = core_message_external::get_unread_conversation_counts(...$arguments);
5673          $counts = external_api::clean_returnvalue(core_message_external::get_unread_conversation_counts_returns(), $counts);
5674  
5675          $this->assertEquals($expectedunreadcounts['favourites'], $counts['favourites']);
5676          $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
5677              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]);
5678          $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP],
5679              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]);
5680          $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF],
5681              $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]);
5682      }
5683  
5684      /**
5685       * Test delete_message for all users.
5686       */
5687      public function test_delete_message_for_all_users() {
5688          global $DB;
5689  
5690          $this->resetAfterTest(true);
5691  
5692          // Create fake data to test it.
5693          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
5694  
5695          // Send message as user1 to group conversation.
5696          $messageid1 = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
5697          $messageid2 = testhelper::send_fake_message_to_conversation($user2, $convgroup->id);
5698  
5699          // User1 deletes the first message for all users of group conversation.
5700          // First, we have to allow user1 (Teacher) can delete messages for all users.
5701          $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
5702          assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, \context_system::instance());
5703  
5704          $this->setUser($user1);
5705  
5706          // Now, user1 deletes message for all users.
5707          $return = core_message_external::delete_message_for_all_users($messageid1, $user1->id);
5708          $return = external_api::clean_returnvalue(core_message_external::delete_message_for_all_users_returns(), $return);
5709          // Check if everything is ok.
5710          $this->assertEquals(array(), $return);
5711  
5712          // Check we have 3 records on message_user_actions with the mark MESSAGE_ACTION_DELETED.
5713          $muas = $DB->get_records('message_user_actions', array('messageid' => $messageid1), 'userid ASC');
5714          $this->assertCount(3, $muas);
5715          $mua1 = array_shift($muas);
5716          $mua2 = array_shift($muas);
5717          $mua3 = array_shift($muas);
5718  
5719          $this->assertEquals($user1->id, $mua1->userid);
5720          $this->assertEquals($messageid1, $mua1->messageid);
5721          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action);
5722          $this->assertEquals($user2->id, $mua2->userid);
5723          $this->assertEquals($messageid1, $mua2->messageid);
5724          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action);
5725          $this->assertEquals($user3->id, $mua3->userid);
5726          $this->assertEquals($messageid1, $mua3->messageid);
5727          $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action);
5728      }
5729  
5730      /**
5731       * Test delete_message for all users with messaging disabled.
5732       */
5733      public function test_delete_message_for_all_users_messaging_disabled() {
5734          global $CFG;
5735  
5736          $this->resetAfterTest();
5737  
5738          // Create fake data to test it.
5739          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
5740  
5741          // Send message as user1 to group conversation.
5742          $messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
5743  
5744          $this->setUser($user1);
5745  
5746          // Disable messaging.
5747          $CFG->messaging = 0;
5748  
5749          // Ensure an exception is thrown.
5750          $this->expectException('moodle_exception');
5751          core_message_external::delete_message_for_all_users($messageid, $user1->id);
5752      }
5753  
5754      /**
5755       * Test delete_message for all users with no permission.
5756       */
5757      public function test_delete_message_for_all_users_no_permission() {
5758          $this->resetAfterTest();
5759  
5760          // Create fake data to test it.
5761          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
5762  
5763          // Send message as user1 to group conversation.
5764          $messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id);
5765  
5766          $this->setUser($user2);
5767  
5768          // Try as user2 to delete a message for all users without permission to do it.
5769          $this->expectException('moodle_exception');
5770          $this->expectExceptionMessage('You do not have permission to delete this message for everyone.');
5771          core_message_external::delete_message_for_all_users($messageid, $user2->id);
5772      }
5773  
5774      /**
5775       * Test delete_message for all users in a private conversation.
5776       */
5777      public function test_delete_message_for_all_users_private_conversation() {
5778          global $DB;
5779  
5780          $this->resetAfterTest();
5781  
5782          // Create fake data to test it.
5783          list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data();
5784  
5785          // Send message as user1 to private conversation.
5786          $messageid = testhelper::send_fake_message_to_conversation($user1, $convindividual->id);
5787  
5788          // First, we have to allow user1 (Teacher) can delete messages for all users.
5789          $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']);
5790          assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, \context_system::instance());
5791  
5792          $this->setUser($user1);
5793  
5794          // Try as user1 to delete a private message for all users on individual conversation.
5795          // User1 should not delete message for all users in a private conversations despite being a teacher.
5796          // Because is a teacher in a course and not in a system context.
5797          $this->expectException('moodle_exception');
5798          $this->expectExceptionMessage('You do not have permission to delete this message for everyone.');
5799          core_message_external::delete_message_for_all_users($messageid, $user1->id);
5800      }
5801  
5802      /**
5803       * Test retrieving conversation messages by providing a timefrom higher than last message timecreated. It should return no
5804       * messages but keep the return structure to not break when called from the ws.
5805       */
5806      public function test_get_conversation_messages_timefrom_higher_than_last_timecreated() {
5807          $this->resetAfterTest(true);
5808  
5809          // Create some users.
5810          $user1 = self::getDataGenerator()->create_user();
5811          $user2 = self::getDataGenerator()->create_user();
5812          $user3 = self::getDataGenerator()->create_user();
5813          $user4 = self::getDataGenerator()->create_user();
5814  
5815          // Create group conversation.
5816          $conversation = \core_message\api::create_conversation(
5817              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5818              [$user1->id, $user2->id, $user3->id, $user4->id]
5819          );
5820  
5821          // The person asking for the messages for another user.
5822          $this->setUser($user1);
5823  
5824          // Send some messages back and forth.
5825          $time = 1;
5826          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1);
5827          testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2);
5828          testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3);
5829          testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4);
5830  
5831          // Retrieve the messages.
5832          $result = core_message_external::get_conversation_messages($user1->id, $conversation->id, 0, 0, '', $time + 5);
5833  
5834          // We need to execute the return values cleaning process to simulate the web service server.
5835          $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(), $result);
5836  
5837          // Check the results are correct.
5838          $this->assertEquals($conversation->id, $result['id']);
5839  
5840          // Confirm the message data is correct.
5841          $messages = $result['messages'];
5842          $this->assertEquals(0, count($messages));
5843  
5844          // Confirm that members key is present.
5845          $this->assertArrayHasKey('members', $result);
5846      }
5847  
5848      /**
5849       * Helper to seed the database with initial state with data.
5850       */
5851      protected function create_delete_message_test_data() {
5852          // Create some users.
5853          $user1 = self::getDataGenerator()->create_user();
5854          $user2 = self::getDataGenerator()->create_user();
5855          $user3 = self::getDataGenerator()->create_user();
5856  
5857          // Create a course and enrol the users.
5858          $course = $this->getDataGenerator()->create_course();
5859          $coursecontext = \context_course::instance($course->id);
5860          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'editingteacher');
5861          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
5862          $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student');
5863  
5864          // Create a group and added the users into.
5865          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
5866          groups_add_member($group1->id, $user1->id);
5867          groups_add_member($group1->id, $user2->id);
5868          groups_add_member($group1->id, $user3->id);
5869  
5870          // Create a group conversation linked with the course.
5871          $convgroup = \core_message\api::create_conversation(
5872              \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
5873              [$user1->id, $user2->id, $user3->id],
5874              'Group test delete for everyone', \core_message\api::MESSAGE_CONVERSATION_ENABLED,
5875              'core_group',
5876              'groups',
5877              $group1->id,
5878              \context_course::instance($course->id)->id
5879          );
5880  
5881          // Create and individual conversation.
5882          $convindividual = \core_message\api::create_conversation(
5883              \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
5884              [$user1->id, $user2->id]
5885          );
5886  
5887          return [$user1, $user2, $user3, $convgroup, $convindividual];
5888      }
5889  }