Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

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