Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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

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

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