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.
   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\task;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  
  23  require_once($CFG->dirroot . '/message/tests/messagelib_test.php');
  24  
  25  /**
  26   * Class for testing the migrate message data task.
  27   *
  28   * @package core_message
  29   * @category test
  30   * @copyright 2018 Mark Nelson <markn@moodle.com>
  31   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  class migrate_message_data_test extends \advanced_testcase {
  34  
  35      /**
  36       * Test set up.
  37       *
  38       * This is executed before running any test in this file.
  39       */
  40      public function setUp(): void {
  41          $this->resetAfterTest();
  42      }
  43  
  44      /**
  45       * Test migrating legacy messages.
  46       */
  47      public function test_migrating_messages() {
  48          global $DB;
  49  
  50          // Create users to test with.
  51          $user1 = $this->getDataGenerator()->create_user();
  52          $user2 = $this->getDataGenerator()->create_user();
  53          $user3 = $this->getDataGenerator()->create_user();
  54  
  55          // Get the current time minus some, to make sure our data is migrated accurately and just not using the current timestamp.
  56          $now = time();
  57          $timedeleted1 = $now - (2 * DAYSECS);
  58          $timedeleted2 = $now - (2 * DAYSECS) + 1;
  59          $timeread1 = $now - DAYSECS;
  60          $timeread2 = $now - DAYSECS + 1;
  61          $timeread3 = $now - DAYSECS + 2;
  62  
  63          // Send messages from user 1 to user 2.
  64          $m1 = $this->create_legacy_message_or_notification($user1->id, $user2->id, 1, false, $timeread1);
  65          $m2 = $this->create_legacy_message_or_notification($user1->id, $user2->id, 2);
  66          $m3 = $this->create_legacy_message_or_notification($user1->id, $user2->id, 3);
  67  
  68          // Send messages from user 3 to user 1.
  69          $m4 = $this->create_legacy_message_or_notification($user3->id, $user1->id, 4, false, $timeread2);
  70          $m5 = $this->create_legacy_message_or_notification($user3->id, $user1->id, 5);
  71          $m6 = $this->create_legacy_message_or_notification($user3->id, $user1->id, 6);
  72  
  73          // Send messages from user 3 to user 2.
  74          $m7 = $this->create_legacy_message_or_notification($user3->id, $user2->id, 7, false, $timeread3);
  75          $m8 = $this->create_legacy_message_or_notification($user3->id, $user2->id, 8);
  76          $m9 = $this->create_legacy_message_or_notification($user3->id, $user2->id, 9);
  77  
  78          // Let's delete some messages, not using API here as it does not use the legacy tables.
  79          $messageupdate = new \stdClass();
  80          $messageupdate->id = $m1;
  81          $messageupdate->timeusertodeleted = $timedeleted1;
  82          $DB->update_record('message_read', $messageupdate);
  83  
  84          $messageupdate = new \stdClass();
  85          $messageupdate->id = $m5;
  86          $messageupdate->timeuserfromdeleted = $timedeleted2;
  87          $DB->update_record('message', $messageupdate);
  88  
  89          // Now, let's execute the task for user 1.
  90          $task = new migrate_message_data();
  91          $task->set_custom_data(
  92              [
  93                  'userid' => $user1->id
  94              ]
  95          );
  96          $task->execute();
  97  
  98          // Ok, now we need to confirm all is good.
  99          // Remember - we are only converting the messages related to user 1.
 100          $this->assertEquals(2, $DB->count_records('message'));
 101          $this->assertEquals(1, $DB->count_records('message_read'));
 102          $this->assertEquals(6, $DB->count_records('messages'));
 103          $this->assertEquals(0, $DB->count_records('notifications'));
 104          $this->assertEquals(0, $DB->count_records('message_popup_notifications'));
 105  
 106          // Get the conversations.
 107          $conversation1 = \core_message\api::get_conversation_between_users([$user1->id, $user2->id]);
 108          $conversation2 = \core_message\api::get_conversation_between_users([$user1->id, $user3->id]);
 109  
 110          // Confirm what we have in the messages table is correct.
 111          $messages = $DB->get_records('messages', [], 'timecreated ASC');
 112          $i = 1;
 113          foreach ($messages as $message) {
 114              $useridfrom = $user1->id;
 115              $conversationid = $conversation1;
 116              if ($i > 3) {
 117                  $useridfrom = $user3->id;
 118                  $conversationid = $conversation2;
 119              }
 120  
 121              if ($i == 1) {
 122                  $messagereadid1 = $message->id;
 123                  $messagedeletedid1 = $message->id;
 124              } else if ($i == 4) {
 125                  $messagereadid2 = $message->id;
 126              } else if ($i == 5) {
 127                  $messagedeletedid2 = $message->id;
 128              }
 129  
 130              $this->assertEquals($useridfrom, $message->useridfrom);
 131              $this->assertEquals($conversationid, $message->conversationid);
 132              $this->assertEquals('Subject ' . $i, $message->subject);
 133              $this->assertEquals('Full message ' . $i, $message->fullmessage);
 134              $this->assertEquals(FORMAT_PLAIN, $message->fullmessageformat);
 135              $this->assertEquals('Full message HTML '. $i, $message->fullmessagehtml);
 136              $this->assertEquals('Small message ' . $i, $message->smallmessage);
 137              $this->assertEquals($i, $message->timecreated);
 138              $i++;
 139          }
 140  
 141          // Confirm there are 4 actions.
 142          $this->assertEquals(4, $DB->count_records('message_user_actions'));
 143  
 144          // Confirm the messages that were marked as read have actions associated with them.
 145          $muas = $DB->get_records('message_user_actions', ['action' => \core_message\api::MESSAGE_ACTION_READ], 'timecreated DESC');
 146          $this->assertCount(2, $muas);
 147  
 148          // Message user action for message read by user 1 (referring to $m4).
 149          $mua1 = array_shift($muas);
 150          // Message user action for message read by user 2 (referring to $m1).
 151          $mua2 = array_shift($muas);
 152  
 153          $this->assertEquals($user1->id, $mua1->userid);
 154          $this->assertEquals($messagereadid2, $mua1->messageid);
 155          $this->assertEquals($timeread2, $mua1->timecreated);
 156  
 157          $this->assertEquals($user2->id, $mua2->userid);
 158          $this->assertEquals($messagereadid1, $mua2->messageid);
 159          $this->assertEquals($timeread1, $mua2->timecreated);
 160  
 161          // Confirm the messages that were deleted have actions associated with them.
 162          $muas = $DB->get_records('message_user_actions', ['action' => \core_message\api::MESSAGE_ACTION_DELETED],
 163              'timecreated DESC');
 164          $this->assertCount(2, $muas);
 165  
 166          // Message user action for message deleted by user 3 (referring to $m5).
 167          $mua1 = array_shift($muas);
 168          // Message user action for message deleted by user 2 (referring to $m1).
 169          $mua2 = array_shift($muas);
 170  
 171          $this->assertEquals($user3->id, $mua1->userid);
 172          $this->assertEquals($messagedeletedid2, $mua1->messageid);
 173          $this->assertEquals($timedeleted2, $mua1->timecreated);
 174  
 175          $this->assertEquals($user2->id, $mua2->userid);
 176          $this->assertEquals($messagedeletedid1, $mua2->messageid);
 177          $this->assertEquals($timedeleted1, $mua2->timecreated);
 178      }
 179  
 180      /**
 181       * Test migrating legacy notifications.
 182       */
 183      public function test_migrating_notifications() {
 184          global $DB;
 185  
 186          // Create users to test with.
 187          $user1 = $this->getDataGenerator()->create_user();
 188          $user2 = $this->getDataGenerator()->create_user();
 189          $user3 = $this->getDataGenerator()->create_user();
 190  
 191          // Get the current time minus some, to make sure our data is migrated accurately and just not using the current timestamp.
 192          $timeread = time() - DAYSECS;
 193  
 194          // Send notifications from user 1 to user 2.
 195          $this->create_legacy_message_or_notification($user1->id, $user2->id, 1, true, $timeread);
 196          $this->create_legacy_message_or_notification($user1->id, $user2->id, 2, true);
 197          $this->create_legacy_message_or_notification($user1->id, $user2->id, 3, true);
 198  
 199          // Send notifications from user 3 to user 1.
 200          $this->create_legacy_message_or_notification($user3->id, $user1->id, 4, true, $timeread);
 201          $this->create_legacy_message_or_notification($user3->id, $user1->id, 5, true);
 202          $this->create_legacy_message_or_notification($user3->id, $user1->id, 6, true);
 203  
 204          // Send notifications from user 3 to user 2.
 205          $this->create_legacy_message_or_notification($user3->id, $user2->id, 7, true, $timeread);
 206          $this->create_legacy_message_or_notification($user3->id, $user2->id, 8, true);
 207          $this->create_legacy_message_or_notification($user3->id, $user2->id, 9, true);
 208  
 209          // Now, let's execute the task for user 1.
 210          $task = new migrate_message_data();
 211          $task->set_custom_data(
 212              [
 213                  'userid' => $user1->id
 214              ]
 215          );
 216          $task->execute();
 217  
 218          // Ok, now we need to confirm all is good.
 219          // Remember - we are only converting the notifications related to user 1.
 220          $this->assertEquals(2, $DB->count_records('message'));
 221          $this->assertEquals(1, $DB->count_records('message_read'));
 222          $this->assertEquals(3, $DB->count_records('message_popup'));
 223          $this->assertEquals(6, $DB->count_records('notifications'));
 224          $this->assertEquals(6, $DB->count_records('message_popup_notifications'));
 225  
 226          // Confirm what we have in the notifications table is correct.
 227          $notifications = $DB->get_records('notifications', [], 'timecreated ASC');
 228          $popupnotifications = $DB->get_records('message_popup_notifications', [], 'notificationid ASC', 'notificationid');
 229          $i = 1;
 230          foreach ($notifications as $notification) {
 231              // Assert the correct id is stored in the 'message_popup_notifications' table.
 232              $this->assertArrayHasKey($notification->id, $popupnotifications);
 233  
 234              $useridfrom = $user1->id;
 235              $useridto = $user2->id;
 236              if ($i > 3) {
 237                  $useridfrom = $user3->id;
 238                  $useridto = $user1->id;
 239              }
 240  
 241              $this->assertEquals($useridfrom, $notification->useridfrom);
 242              $this->assertEquals($useridto, $notification->useridto);
 243              $this->assertEquals('Subject ' . $i, $notification->subject);
 244              $this->assertEquals('Full message ' . $i, $notification->fullmessage);
 245              $this->assertEquals(FORMAT_PLAIN, $notification->fullmessageformat);
 246              $this->assertEquals('Full message HTML '. $i, $notification->fullmessagehtml);
 247              $this->assertEquals('Small message ' . $i, $notification->smallmessage);
 248              $this->assertEquals('mod_assign', $notification->component);
 249              $this->assertEquals('assign_notification', $notification->eventtype);
 250              $this->assertEquals('https://www.google.com', $notification->contexturl);
 251              $this->assertEquals('google', $notification->contexturlname);
 252              $this->assertEquals($i, $notification->timecreated);
 253  
 254              if (($i == 1) || ($i == 4)) {
 255                  $this->assertEquals($timeread, $notification->timeread);
 256              } else {
 257                  $this->assertNull($notification->timeread);
 258              }
 259  
 260              $i++;
 261          }
 262      }
 263  
 264      /**
 265       * Test migrating a legacy message that contains null as the format.
 266       */
 267      public function test_migrating_message_null_format() {
 268          global $DB;
 269  
 270          // Create users to test with.
 271          $user1 = $this->getDataGenerator()->create_user();
 272          $user2 = $this->getDataGenerator()->create_user();
 273  
 274          $this->create_legacy_message_or_notification($user1->id, $user2->id, null, false, null, null);
 275  
 276          // Now, let's execute the task for user 1.
 277          $task = new migrate_message_data();
 278          $task->set_custom_data(
 279              [
 280                  'userid' => $user1->id
 281              ]
 282          );
 283          $task->execute();
 284  
 285          $messages = $DB->get_records('messages');
 286          $this->assertCount(1, $messages);
 287  
 288          $message = reset($messages);
 289          $this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
 290      }
 291  
 292      /**
 293       * Test migrating a legacy notification that contains null as the format.
 294       */
 295      public function test_migrating_notification_null_format() {
 296          global $DB;
 297  
 298          // Create users to test with.
 299          $user1 = $this->getDataGenerator()->create_user();
 300          $user2 = $this->getDataGenerator()->create_user();
 301  
 302          $this->create_legacy_message_or_notification($user1->id, $user2->id, null, true, null, null);
 303  
 304          // Now, let's execute the task for user 1.
 305          $task = new migrate_message_data();
 306          $task->set_custom_data(
 307              [
 308                  'userid' => $user1->id
 309              ]
 310          );
 311          $task->execute();
 312  
 313          $notifications = $DB->get_records('notifications');
 314          $this->assertCount(1, $notifications);
 315  
 316          $notification = reset($notifications);
 317          $this->assertEquals(FORMAT_MOODLE, $notification->fullmessageformat);
 318      }
 319  
 320      /**
 321       * Test migrating a legacy message that a user sent to themselves then deleted.
 322       */
 323      public function test_migrating_message_deleted_message_sent_to_self() {
 324          global $DB;
 325  
 326          // Create user to test with.
 327          $user1 = $this->getDataGenerator()->create_user();
 328  
 329          $m1 = $this->create_legacy_message_or_notification($user1->id, $user1->id, null, false, null, null);
 330  
 331          // Let's delete the message for the 'user to' and 'user from' which in this case is the same user.
 332          $messageupdate = new \stdClass();
 333          $messageupdate->id = $m1;
 334          $messageupdate->timeuserfromdeleted = time();
 335          $messageupdate->timeusertodeleted = time();
 336          $DB->update_record('message', $messageupdate);
 337  
 338          // Now, let's execute the task for the user.
 339          $task = new migrate_message_data();
 340          $task->set_custom_data(
 341              [
 342                  'userid' => $user1->id
 343              ]
 344          );
 345          $task->execute();
 346  
 347          $this->assertEquals(0, $DB->count_records('message'));
 348          $this->assertEquals(1, $DB->count_records('message_user_actions'));
 349      }
 350  
 351      /**
 352       * Creates a legacy message or notification to be used for testing.
 353       *
 354       * @param int $useridfrom The user id from
 355       * @param int $useridto The user id to
 356       * @param int $timecreated
 357       * @param bool $notification
 358       * @param int|null $timeread The time the message/notification was read, null if it hasn't been.
 359       * @param string|int|null $format The format of the message.
 360       * @return int The id of the message (in either the message or message_read table)
 361       * @throws dml_exception
 362       */
 363      private function create_legacy_message_or_notification($useridfrom, $useridto, $timecreated = null,
 364              $notification = false, $timeread = null, $format = FORMAT_PLAIN) {
 365          global $DB;
 366  
 367          $tabledata = new \stdClass();
 368  
 369          if (is_null($timecreated)) {
 370              $timecreated = time();
 371          }
 372  
 373          if (!is_null($timeread)) {
 374              $table = 'message_read';
 375              $tabledata->timeread = $timeread;
 376          } else {
 377              $table = 'message';
 378          }
 379  
 380          if ($notification) {
 381              $tabledata->eventtype = 'assign_notification';
 382              $tabledata->component = 'mod_assign';
 383              $tabledata->notification = 1;
 384              $tabledata->contexturl = 'https://www.google.com';
 385              $tabledata->contexturlname = 'google';
 386          } else {
 387              $tabledata->eventtype = 'instantmessage';
 388              $tabledata->component = 'moodle';
 389              $tabledata->notification = 0;
 390          }
 391  
 392          $tabledata->useridfrom = $useridfrom;
 393          $tabledata->useridto = $useridto;
 394          $tabledata->subject = 'Subject ' . $timecreated;
 395          $tabledata->fullmessage = 'Full message ' . $timecreated;
 396          $tabledata->fullmessageformat = $format;
 397          $tabledata->fullmessagehtml = 'Full message HTML ' . $timecreated;
 398          $tabledata->smallmessage = 'Small message ' . $timecreated;
 399          $tabledata->timecreated = $timecreated;
 400  
 401          $id = $DB->insert_record($table, $tabledata);
 402  
 403          // Insert into the legacy 'message_popup' table if it is a notification.
 404          if ($notification) {
 405              $mp = new \stdClass();
 406              $mp->messageid = $id;
 407              $mp->isread = (!is_null($timeread)) ? 1 : 0;
 408  
 409              $DB->insert_record('message_popup', $mp);
 410          }
 411  
 412          return $id;
 413      }
 414  }