Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This file contains the class that handles testing of the calendar event vault.
  19   *
  20   * @package core_calendar
  21   * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
  22   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  global $CFG;
  28  require_once($CFG->dirroot . '/calendar/tests/helpers.php');
  29  
  30  use core_calendar\local\event\data_access\event_vault;
  31  use core_calendar\local\event\strategies\raw_event_retrieval_strategy;
  32  
  33  /**
  34   * This file contains the class that handles testing of the calendar event vault.
  35   *
  36   * @package core_calendar
  37   * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
  38   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class core_calendar_event_vault_testcase extends advanced_testcase {
  41  
  42      /**
  43       * Test that get_action_events_by_timesort returns events after the
  44       * provided timesort value.
  45       */
  46      public function test_get_action_events_by_timesort_after_time() {
  47          $this->resetAfterTest(true);
  48  
  49          $user = $this->getDataGenerator()->create_user();
  50          $factory = new action_event_test_factory();
  51          $strategy = new raw_event_retrieval_strategy();
  52          $vault = new event_vault($factory, $strategy);
  53  
  54          $this->setUser($user);
  55  
  56          for ($i = 1; $i < 6; $i++) {
  57              create_event([
  58                  'name' => sprintf('Event %d', $i),
  59                  'eventtype' => 'user',
  60                  'userid' => $user->id,
  61                  'timesort' => $i,
  62                  'type' => CALENDAR_EVENT_TYPE_ACTION
  63              ]);
  64          }
  65  
  66          $events = $vault->get_action_events_by_timesort($user, 3);
  67  
  68          $this->assertCount(3, $events);
  69          $this->assertEquals('Event 3', $events[0]->get_name());
  70          $this->assertEquals('Event 4', $events[1]->get_name());
  71          $this->assertEquals('Event 5', $events[2]->get_name());
  72  
  73          $events = $vault->get_action_events_by_timesort($user, 3, null, null, 1);
  74  
  75          $this->assertCount(1, $events);
  76          $this->assertEquals('Event 3', $events[0]->get_name());
  77  
  78          $events = $vault->get_action_events_by_timesort($user, 6);
  79  
  80          $this->assertCount(0, $events);
  81      }
  82  
  83      /**
  84       * Test that get_action_events_by_timesort returns events before the
  85       * provided timesort value.
  86       */
  87      public function test_get_action_events_by_timesort_before_time() {
  88          $this->resetAfterTest(true);
  89          $this->setAdminuser();
  90  
  91          $user = $this->getDataGenerator()->create_user();
  92          $factory = new action_event_test_factory();
  93          $strategy = new raw_event_retrieval_strategy();
  94          $vault = new event_vault($factory, $strategy);
  95  
  96          for ($i = 1; $i < 6; $i++) {
  97              create_event([
  98                  'name' => sprintf('Event %d', $i),
  99                  'eventtype' => 'user',
 100                  'userid' => $user->id,
 101                  'timesort' => $i,
 102                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 103                  'courseid' => 1
 104              ]);
 105          }
 106  
 107          $events = $vault->get_action_events_by_timesort($user, null, 3);
 108  
 109          $this->assertCount(3, $events);
 110          $this->assertEquals('Event 1', $events[0]->get_name());
 111          $this->assertEquals('Event 2', $events[1]->get_name());
 112          $this->assertEquals('Event 3', $events[2]->get_name());
 113  
 114          $events = $vault->get_action_events_by_timesort($user, null, 3, null, 1);
 115  
 116          $this->assertCount(1, $events);
 117          $this->assertEquals('Event 1', $events[0]->get_name());
 118  
 119          $events = $vault->get_action_events_by_timesort($user, 6);
 120  
 121          $this->assertCount(0, $events);
 122      }
 123  
 124      /**
 125       * Test that get_action_events_by_timesort returns events between the
 126       * provided timesort values.
 127       */
 128      public function test_get_action_events_by_timesort_between_time() {
 129          $this->resetAfterTest(true);
 130          $this->setAdminuser();
 131  
 132          $user = $this->getDataGenerator()->create_user();
 133          $factory = new action_event_test_factory();
 134          $strategy = new raw_event_retrieval_strategy();
 135          $vault = new event_vault($factory, $strategy);
 136  
 137          for ($i = 1; $i < 6; $i++) {
 138              create_event([
 139                  'name' => sprintf('Event %d', $i),
 140                  'eventtype' => 'user',
 141                  'userid' => $user->id,
 142                  'timesort' => $i,
 143                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 144                  'courseid' => 1
 145              ]);
 146          }
 147  
 148          $events = $vault->get_action_events_by_timesort($user, 2, 4);
 149  
 150          $this->assertCount(3, $events);
 151          $this->assertEquals('Event 2', $events[0]->get_name());
 152          $this->assertEquals('Event 3', $events[1]->get_name());
 153          $this->assertEquals('Event 4', $events[2]->get_name());
 154  
 155          $events = $vault->get_action_events_by_timesort($user, 2, 4, null, 1);
 156  
 157          $this->assertCount(1, $events);
 158          $this->assertEquals('Event 2', $events[0]->get_name());
 159      }
 160  
 161      /**
 162       * Test that get_action_events_by_timesort returns events between the
 163       * provided timesort values and after the last seen event when one is
 164       * provided.
 165       */
 166      public function test_get_action_events_by_timesort_between_time_after_event() {
 167          $this->resetAfterTest(true);
 168          $this->setAdminuser();
 169  
 170          $user = $this->getDataGenerator()->create_user();
 171          $factory = new action_event_test_factory();
 172          $strategy = new raw_event_retrieval_strategy();
 173          $vault = new event_vault($factory, $strategy);
 174  
 175          $records = [];
 176          for ($i = 1; $i < 21; $i++) {
 177              $records[] = create_event([
 178                  'name' => sprintf('Event %d', $i),
 179                  'eventtype' => 'user',
 180                  'userid' => $user->id,
 181                  'timesort' => $i,
 182                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 183                  'courseid' => 1
 184              ]);
 185          }
 186  
 187          $aftereventid = $records[6]->id;
 188          $afterevent = $vault->get_event_by_id($aftereventid);
 189          $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent);
 190  
 191          $this->assertCount(8, $events);
 192          $this->assertEquals('Event 8', $events[0]->get_name());
 193  
 194          $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent, 3);
 195  
 196          $this->assertCount(3, $events);
 197      }
 198  
 199      /**
 200       * Test that get_action_events_by_timesort returns events between the
 201       * provided timesort values and the last seen event can be provided to
 202       * get paginated results.
 203       */
 204      public function test_get_action_events_by_timesort_between_time_skip_even_records() {
 205          $this->resetAfterTest(true);
 206          $this->setAdminuser();
 207  
 208          $user = $this->getDataGenerator()->create_user();
 209          // The factory will return every event that is divisible by 2.
 210          $factory = new action_event_test_factory(function($actionevent) {
 211              static $count = 0;
 212              $count++;
 213              return ($count % 2) ? true : false;
 214          });
 215          $strategy = new raw_event_retrieval_strategy();
 216          $vault = new event_vault($factory, $strategy);
 217  
 218          for ($i = 1; $i < 41; $i++) {
 219              create_event([
 220                  'name' => sprintf('Event %d', $i),
 221                  'eventtype' => 'user',
 222                  'userid' => $user->id,
 223                  'timesort' => $i,
 224                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 225                  'courseid' => 1
 226              ]);
 227          }
 228  
 229          $events = $vault->get_action_events_by_timesort($user, 3, 35, null, 5);
 230  
 231          $this->assertCount(5, $events);
 232          $this->assertEquals('Event 3', $events[0]->get_name());
 233          $this->assertEquals('Event 5', $events[1]->get_name());
 234          $this->assertEquals('Event 7', $events[2]->get_name());
 235          $this->assertEquals('Event 9', $events[3]->get_name());
 236          $this->assertEquals('Event 11', $events[4]->get_name());
 237  
 238          $afterevent = $events[4];
 239          $events = $vault->get_action_events_by_timesort($user, 3, 35, $afterevent, 5);
 240  
 241          $this->assertCount(5, $events);
 242          $this->assertEquals('Event 13', $events[0]->get_name());
 243          $this->assertEquals('Event 15', $events[1]->get_name());
 244          $this->assertEquals('Event 17', $events[2]->get_name());
 245          $this->assertEquals('Event 19', $events[3]->get_name());
 246          $this->assertEquals('Event 21', $events[4]->get_name());
 247      }
 248  
 249      /**
 250       * Test that get_action_events_by_timesort returns events between the
 251       * provided timesort values. The database will continue to be read until the
 252       * number of events requested has been satisfied. In this case the first
 253       * five events are rejected so it should require two database requests.
 254       */
 255      public function test_get_action_events_by_timesort_between_time_skip_first_records() {
 256          $this->resetAfterTest(true);
 257          $this->setAdminuser();
 258  
 259          $user = $this->getDataGenerator()->create_user();
 260          $limit = 5;
 261          $seen = 0;
 262          // The factory will skip the first $limit events.
 263          $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {
 264              if ($seen < $limit) {
 265                  $seen++;
 266                  return false;
 267              } else {
 268                  return true;
 269              }
 270          });
 271          $strategy = new raw_event_retrieval_strategy();
 272          $vault = new event_vault($factory, $strategy);
 273  
 274          for ($i = 1; $i < 21; $i++) {
 275              create_event([
 276                  'name' => sprintf('Event %d', $i),
 277                  'eventtype' => 'user',
 278                  'userid' => $user->id,
 279                  'timesort' => $i,
 280                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 281                  'courseid' => 1
 282              ]);
 283          }
 284  
 285          $events = $vault->get_action_events_by_timesort($user, 1, 20, null, $limit);
 286  
 287          $this->assertCount($limit, $events);
 288          $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());
 289          $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());
 290          $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());
 291          $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());
 292          $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());
 293      }
 294  
 295      /**
 296       * Test that get_action_events_by_timesort returns events between the
 297       * provided timesort values and after the last seen event when one is
 298       * provided. This should work even when the event ids aren't ordered the
 299       * same as the timesort order.
 300       */
 301      public function test_get_action_events_by_timesort_non_consecutive_ids() {
 302          $this->resetAfterTest(true);
 303          $this->setAdminuser();
 304  
 305          $user = $this->getDataGenerator()->create_user();
 306          $factory = new action_event_test_factory();
 307          $strategy = new raw_event_retrieval_strategy();
 308          $vault = new event_vault($factory, $strategy);
 309  
 310          /*
 311           * The events should be ordered by timesort as follows:
 312           *
 313           * 1 event 1
 314           * 2 event 1
 315           * 1 event 2
 316           * 2 event 2
 317           * 1 event 3
 318           * 2 event 3
 319           * 1 event 4
 320           * 2 event 4
 321           * 1 event 5
 322           * 2 event 5
 323           * 1 event 6
 324           * 2 event 6
 325           * 1 event 7
 326           * 2 event 7
 327           * 1 event 8
 328           * 2 event 8
 329           * 1 event 9
 330           * 2 event 9
 331           * 1 event 10
 332           * 2 event 10
 333           */
 334          $records = [];
 335          for ($i = 1; $i < 11; $i++) {
 336              $records[] = create_event([
 337                  'name' => sprintf('1 event %d', $i),
 338                  'eventtype' => 'user',
 339                  'userid' => $user->id,
 340                  'timesort' => $i,
 341                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 342                  'courseid' => 1
 343              ]);
 344          }
 345  
 346          for ($i = 1; $i < 11; $i++) {
 347              $records[] = create_event([
 348                  'name' => sprintf('2 event %d', $i),
 349                  'eventtype' => 'user',
 350                  'userid' => $user->id,
 351                  'timesort' => $i,
 352                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 353                  'courseid' => 1
 354              ]);
 355          }
 356  
 357          /*
 358           * Expected result set:
 359           *
 360           * 2 event 4
 361           * 1 event 5
 362           * 2 event 5
 363           * 1 event 6
 364           * 2 event 6
 365           * 1 event 7
 366           * 2 event 7
 367           * 1 event 8
 368           * 2 event 8
 369           */
 370          $aftereventid = $records[3]->id;
 371          $afterevent = $vault->get_event_by_id($aftereventid);
 372          // Offset results by event with name "1 event 4" which has the same timesort
 373          // value as the lower boundary of this query (3). Confirm that the given
 374          // $afterevent is used to ignore events with the same timesortfrom values.
 375          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
 376  
 377          $this->assertCount(9, $events);
 378          $this->assertEquals('2 event 4', $events[0]->get_name());
 379          $this->assertEquals('2 event 8', $events[8]->get_name());
 380  
 381          /*
 382           * Expected result set:
 383           *
 384           * 2 event 4
 385           * 1 event 5
 386           */
 387          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent, 2);
 388  
 389          $this->assertCount(2, $events);
 390          $this->assertEquals('2 event 4', $events[0]->get_name());
 391          $this->assertEquals('1 event 5', $events[1]->get_name());
 392  
 393          /*
 394           * Expected result set:
 395           *
 396           * 2 event 8
 397           */
 398          $aftereventid = $records[7]->id;
 399          $afterevent = $vault->get_event_by_id($aftereventid);
 400          // Offset results by event with name "1 event 8" which has the same timesort
 401          // value as the upper boundary of this query (8). Confirm that the given
 402          // $afterevent is used to ignore events with the same timesortto values.
 403          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
 404  
 405          $this->assertCount(1, $events);
 406          $this->assertEquals('2 event 8', $events[0]->get_name());
 407  
 408          /*
 409           * Expected empty result set.
 410           */
 411          $aftereventid = $records[18]->id;
 412          $afterevent = $vault->get_event_by_id($aftereventid);
 413          // Offset results by event with name "2 event 9" which has a timesort
 414          // value larger than the upper boundary of this query (9 > 8). Confirm
 415          // that the given $afterevent is used for filtering events.
 416          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
 417          $this->assertEmpty($events);
 418      }
 419  
 420      /**
 421       * There are subtle cases where the priority of an event override may be identical to another.
 422       * For example, if you duplicate a group override, but make it apply to a different group. Now
 423       * there are two overrides with exactly the same overridden dates. In this case the priority of
 424       * both is 1.
 425       *
 426       * In this situation:
 427       * - A user in group A should see only the A override
 428       * - A user in group B should see only the B override
 429       * - A user in both A and B should see both
 430       */
 431      public function test_get_action_events_by_timesort_with_identical_group_override_priorities() {
 432          $this->resetAfterTest();
 433          $this->setAdminuser();
 434  
 435          $course = $this->getDataGenerator()->create_course();
 436  
 437          // Create an assign instance.
 438          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 439          $assigninstance = $assigngenerator->create_instance(['course' => $course->id]);
 440  
 441          // Create users.
 442          $users = [
 443              'Only in group A'  => $this->getDataGenerator()->create_user(),
 444              'Only in group B'  => $this->getDataGenerator()->create_user(),
 445              'In group A and B' => $this->getDataGenerator()->create_user(),
 446              'In no groups'     => $this->getDataGenerator()->create_user()
 447          ];
 448  
 449          // Enrol users.
 450          foreach ($users as $user) {
 451              $this->getDataGenerator()->enrol_user($user->id, $course->id);
 452          }
 453  
 454          // Create groups.
 455          $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
 456          $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
 457  
 458          // Add members to groups.
 459          // Group A.
 460          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
 461          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);
 462  
 463          // Group B.
 464          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
 465          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);
 466  
 467          // Events with the same module name, instance and event type.
 468          $events = [
 469              [
 470                  'name' => 'Assignment 1 due date - Group A override',
 471                  'description' => '',
 472                  'format' => 1,
 473                  'courseid' => $course->id,
 474                  'groupid' => $groupa->id,
 475                  'userid' => 2,
 476                  'modulename' => 'assign',
 477                  'instance' => $assigninstance->id,
 478                  'eventtype' => 'due',
 479                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 480                  'timestart' => 1,
 481                  'timeduration' => 0,
 482                  'visible' => 1,
 483                  'priority' => 1
 484              ],
 485              [
 486                  'name' => 'Assignment 1 due date - Group B override',
 487                  'description' => '',
 488                  'format' => 1,
 489                  'courseid' => $course->id,
 490                  'groupid' => $groupb->id,
 491                  'userid' => 2,
 492                  'modulename' => 'assign',
 493                  'instance' => $assigninstance->id,
 494                  'eventtype' => 'due',
 495                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 496                  'timestart' => 1,
 497                  'timeduration' => 0,
 498                  'visible' => 1,
 499                  'priority' => 1
 500              ],
 501              [
 502                  'name' => 'Assignment 1 due date',
 503                  'description' => '',
 504                  'format' => 1,
 505                  'courseid' => $course->id,
 506                  'groupid' => 0,
 507                  'userid' => 2,
 508                  'modulename' => 'assign',
 509                  'instance' => $assigninstance->id,
 510                  'eventtype' => 'due',
 511                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 512                  'timestart' => 1,
 513                  'timeduration' => 0,
 514                  'visible' => 1,
 515                  'priority' => null,
 516              ]
 517          ];
 518  
 519          foreach ($events as $event) {
 520              calendar_event::create($event, false);
 521          }
 522  
 523          $factory = new action_event_test_factory();
 524          $strategy = new raw_event_retrieval_strategy();
 525          $vault = new event_vault($factory, $strategy);
 526  
 527          $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $vault) {
 528              // NB: This is currently needed to make get_action_events_by_timesort return the right thing.
 529              // It needs to be fixed, see MDL-58736.
 530              $this->setUser($users[$description]);
 531              return $carry + ['For user ' . lcfirst($description) => $vault->get_action_events_by_timesort($users[$description])];
 532          }, []);
 533  
 534          foreach ($usersevents as $description => $userevents) {
 535              if ($description == 'For user in group A and B') {
 536                  // User is in both A and B, so they should see the override for both
 537                  // given that the priority is the same.
 538                  $this->assertCount(2, $userevents);
 539                  continue;
 540              }
 541  
 542              // Otherwise there should be only one assign event for each user.
 543              $this->assertCount(1, $userevents);
 544          }
 545  
 546          // User in only group A should see the group A override.
 547          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());
 548  
 549          // User in only group B should see the group B override.
 550          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());
 551  
 552          // User in group A and B should see see both overrides since the priorities are the same.
 553          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());
 554          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());
 555  
 556          // User in no groups should see the plain assignment event.
 557          $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());
 558      }
 559  
 560      /**
 561       * Test that if a user is suspended that events related to that course are not shown.
 562       * User 1 is suspended. User 2 is active.
 563       */
 564      public function test_get_action_events_by_timesort_with_suspended_user() {
 565          $this->resetAfterTest();
 566          $user1 = $this->getDataGenerator()->create_user();
 567          $user2 = $this->getDataGenerator()->create_user();
 568          $course = $this->getDataGenerator()->create_course();
 569          $this->setAdminuser();
 570          $lesson = $this->getDataGenerator()->create_module('lesson', [
 571                  'name' => 'Lesson 1',
 572                  'course' => $course->id,
 573                  'available' => time(),
 574                  'deadline' => (time() + (60 * 60 * 24 * 5))
 575              ]
 576          );
 577          $this->getDataGenerator()->enrol_user($user1->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
 578          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 579  
 580          $factory = new action_event_test_factory();
 581          $strategy = new raw_event_retrieval_strategy();
 582          $vault = new event_vault($factory, $strategy);
 583  
 584          $user1events = $vault->get_action_events_by_timesort($user1, null, null, null, 20, true);
 585          $this->assertEmpty($user1events);
 586          $user2events = $vault->get_action_events_by_timesort($user2, null, null, null, 20, true);
 587          $this->assertCount(1, $user2events);
 588          $this->assertEquals('Lesson 1 closes', $user2events[0]->get_name());
 589      }
 590  
 591      /**
 592       * Test that get_action_events_by_course returns events after the
 593       * provided timesort value.
 594       */
 595      public function test_get_action_events_by_course_after_time() {
 596          $user = $this->getDataGenerator()->create_user();
 597          $course1 = $this->getDataGenerator()->create_course();
 598          $course2 = $this->getDataGenerator()->create_course();
 599          $factory = new action_event_test_factory();
 600          $strategy = new raw_event_retrieval_strategy();
 601          $vault = new event_vault($factory, $strategy);
 602  
 603          $this->resetAfterTest(true);
 604          $this->setAdminuser();
 605          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 606          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 607  
 608          for ($i = 1; $i < 6; $i++) {
 609              create_event([
 610                  'name' => sprintf('Event %d', $i),
 611                  'eventtype' => 'user',
 612                  'userid' => $user->id,
 613                  'timesort' => $i,
 614                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 615                  'courseid' => $course1->id,
 616              ]);
 617          }
 618  
 619          for ($i = 6; $i < 12; $i++) {
 620              create_event([
 621                  'name' => sprintf('Event %d', $i),
 622                  'eventtype' => 'user',
 623                  'userid' => $user->id,
 624                  'timesort' => $i,
 625                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 626                  'courseid' => $course2->id,
 627              ]);
 628          }
 629  
 630          $events = $vault->get_action_events_by_course($user, $course1, 3);
 631          $this->assertCount(3, $events);
 632          $this->assertEquals('Event 3', $events[0]->get_name());
 633          $this->assertEquals('Event 4', $events[1]->get_name());
 634          $this->assertEquals('Event 5', $events[2]->get_name());
 635  
 636          $events = $vault->get_action_events_by_course($user, $course1, 3, null, null, 1);
 637  
 638          $this->assertCount(1, $events);
 639          $this->assertEquals('Event 3', $events[0]->get_name());
 640  
 641          $events = $vault->get_action_events_by_course($user, $course1, 6);
 642  
 643          $this->assertCount(0, $events);
 644      }
 645  
 646      /**
 647       * Test that get_action_events_by_course returns events before the
 648       * provided timesort value.
 649       */
 650      public function test_get_action_events_by_course_before_time() {
 651          $user = $this->getDataGenerator()->create_user();
 652          $course1 = $this->getDataGenerator()->create_course();
 653          $course2 = $this->getDataGenerator()->create_course();
 654          $factory = new action_event_test_factory();
 655          $strategy = new raw_event_retrieval_strategy();
 656          $vault = new event_vault($factory, $strategy);
 657  
 658          $this->resetAfterTest(true);
 659          $this->setAdminuser();
 660          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 661          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 662  
 663          for ($i = 1; $i < 6; $i++) {
 664              create_event([
 665                  'name' => sprintf('Event %d', $i),
 666                  'eventtype' => 'user',
 667                  'userid' => $user->id,
 668                  'timesort' => $i,
 669                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 670                  'courseid' => $course1->id,
 671              ]);
 672          }
 673  
 674          for ($i = 6; $i < 12; $i++) {
 675              create_event([
 676                  'name' => sprintf('Event %d', $i),
 677                  'eventtype' => 'user',
 678                  'userid' => $user->id,
 679                  'timesort' => $i,
 680                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 681                  'courseid' => $course2->id,
 682              ]);
 683          }
 684  
 685          $events = $vault->get_action_events_by_course($user, $course1, null, 3);
 686  
 687          $this->assertCount(3, $events);
 688          $this->assertEquals('Event 1', $events[0]->get_name());
 689          $this->assertEquals('Event 2', $events[1]->get_name());
 690          $this->assertEquals('Event 3', $events[2]->get_name());
 691  
 692          $events = $vault->get_action_events_by_course($user, $course1, null, 3, null, 1);
 693  
 694          $this->assertCount(1, $events);
 695          $this->assertEquals('Event 1', $events[0]->get_name());
 696  
 697          $events = $vault->get_action_events_by_course($user, $course1, 6);
 698  
 699          $this->assertCount(0, $events);
 700      }
 701  
 702      /**
 703       * Test that get_action_events_by_course returns events between the
 704       * provided timesort values.
 705       */
 706      public function test_get_action_events_by_course_between_time() {
 707          $user = $this->getDataGenerator()->create_user();
 708          $course1 = $this->getDataGenerator()->create_course();
 709          $course2 = $this->getDataGenerator()->create_course();
 710          $factory = new action_event_test_factory();
 711          $strategy = new raw_event_retrieval_strategy();
 712          $vault = new event_vault($factory, $strategy);
 713  
 714          $this->resetAfterTest(true);
 715          $this->setAdminuser();
 716          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 717          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 718  
 719          for ($i = 1; $i < 6; $i++) {
 720              create_event([
 721                  'name' => sprintf('Event %d', $i),
 722                  'eventtype' => 'user',
 723                  'userid' => $user->id,
 724                  'timesort' => $i,
 725                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 726                  'courseid' => $course1->id,
 727              ]);
 728          }
 729  
 730          for ($i = 6; $i < 12; $i++) {
 731              create_event([
 732                  'name' => sprintf('Event %d', $i),
 733                  'eventtype' => 'user',
 734                  'userid' => $user->id,
 735                  'timesort' => $i,
 736                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 737                  'courseid' => $course2->id,
 738              ]);
 739          }
 740  
 741          $events = $vault->get_action_events_by_course($user, $course1, 2, 4);
 742  
 743          $this->assertCount(3, $events);
 744          $this->assertEquals('Event 2', $events[0]->get_name());
 745          $this->assertEquals('Event 3', $events[1]->get_name());
 746          $this->assertEquals('Event 4', $events[2]->get_name());
 747  
 748          $events = $vault->get_action_events_by_course($user, $course1, 2, 4, null, 1);
 749  
 750          $this->assertCount(1, $events);
 751          $this->assertEquals('Event 2', $events[0]->get_name());
 752      }
 753  
 754      /**
 755       * Test that get_action_events_by_course returns events between the
 756       * provided timesort values and after the last seen event when one is
 757       * provided.
 758       */
 759      public function test_get_action_events_by_course_between_time_after_event() {
 760          $user = $this->getDataGenerator()->create_user();
 761          $course1 = $this->getDataGenerator()->create_course();
 762          $course2 = $this->getDataGenerator()->create_course();
 763          $factory = new action_event_test_factory();
 764          $strategy = new raw_event_retrieval_strategy();
 765          $vault = new event_vault($factory, $strategy);
 766          $records = [];
 767  
 768          $this->resetAfterTest(true);
 769          $this->setAdminuser();
 770          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 771          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 772  
 773          for ($i = 1; $i < 21; $i++) {
 774              $records[] = create_event([
 775                  'name' => sprintf('Event %d', $i),
 776                  'eventtype' => 'user',
 777                  'userid' => $user->id,
 778                  'timesort' => $i,
 779                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 780                  'courseid' => $course1->id,
 781              ]);
 782          }
 783  
 784          for ($i = 21; $i < 41; $i++) {
 785              $records[] = create_event([
 786                  'name' => sprintf('Event %d', $i),
 787                  'eventtype' => 'user',
 788                  'userid' => $user->id,
 789                  'timesort' => $i,
 790                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 791                  'courseid' => $course2->id,
 792              ]);
 793          }
 794  
 795          $aftereventid = $records[6]->id;
 796          $afterevent = $vault->get_event_by_id($aftereventid);
 797          $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent);
 798  
 799          $this->assertCount(8, $events);
 800          $this->assertEquals('Event 8', $events[0]->get_name());
 801  
 802          $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent, 3);
 803  
 804          $this->assertCount(3, $events);
 805      }
 806  
 807      /**
 808       * Test that get_action_events_by_course returns events between the
 809       * provided timesort values and the last seen event can be provided to
 810       * get paginated results.
 811       */
 812      public function test_get_action_events_by_course_between_time_skip_even_records() {
 813          $user = $this->getDataGenerator()->create_user();
 814          $course1 = $this->getDataGenerator()->create_course();
 815          $course2 = $this->getDataGenerator()->create_course();
 816          // The factory will return every event that is divisible by 2.
 817          $factory = new action_event_test_factory(function($actionevent) {
 818              static $count = 0;
 819              $count++;
 820              return ($count % 2) ? true : false;
 821          });
 822          $strategy = new raw_event_retrieval_strategy();
 823          $vault = new event_vault($factory, $strategy);
 824  
 825          $this->resetAfterTest(true);
 826          $this->setAdminuser();
 827          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 828          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 829  
 830          for ($i = 1; $i < 41; $i++) {
 831              create_event([
 832                  'name' => sprintf('Event %d', $i),
 833                  'eventtype' => 'user',
 834                  'userid' => $user->id,
 835                  'timesort' => $i,
 836                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 837                  'courseid' => $course1->id,
 838              ]);
 839          }
 840  
 841          for ($i = 41; $i < 81; $i++) {
 842              create_event([
 843                  'name' => sprintf('Event %d', $i),
 844                  'eventtype' => 'user',
 845                  'userid' => $user->id,
 846                  'timesort' => $i,
 847                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 848                  'courseid' => $course2->id,
 849              ]);
 850          }
 851  
 852          $events = $vault->get_action_events_by_course($user, $course1, 3, 35, null, 5);
 853  
 854          $this->assertCount(5, $events);
 855          $this->assertEquals('Event 3', $events[0]->get_name());
 856          $this->assertEquals('Event 5', $events[1]->get_name());
 857          $this->assertEquals('Event 7', $events[2]->get_name());
 858          $this->assertEquals('Event 9', $events[3]->get_name());
 859          $this->assertEquals('Event 11', $events[4]->get_name());
 860  
 861          $afterevent = $events[4];
 862          $events = $vault->get_action_events_by_course($user, $course1, 3, 35, $afterevent, 5);
 863  
 864          $this->assertCount(5, $events);
 865          $this->assertEquals('Event 13', $events[0]->get_name());
 866          $this->assertEquals('Event 15', $events[1]->get_name());
 867          $this->assertEquals('Event 17', $events[2]->get_name());
 868          $this->assertEquals('Event 19', $events[3]->get_name());
 869          $this->assertEquals('Event 21', $events[4]->get_name());
 870      }
 871  
 872      /**
 873       * Test that get_action_events_by_course returns events between the
 874       * provided timesort values. The database will continue to be read until the
 875       * number of events requested has been satisfied. In this case the first
 876       * five events are rejected so it should require two database requests.
 877       */
 878      public function test_get_action_events_by_course_between_time_skip_first_records() {
 879          $user = $this->getDataGenerator()->create_user();
 880          $course1 = $this->getDataGenerator()->create_course();
 881          $course2 = $this->getDataGenerator()->create_course();
 882          $limit = 5;
 883          $seen = 0;
 884          // The factory will skip the first $limit events.
 885          $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {
 886              if ($seen < $limit) {
 887                  $seen++;
 888                  return false;
 889              } else {
 890                  return true;
 891              }
 892          });
 893          $strategy = new raw_event_retrieval_strategy();
 894          $vault = new event_vault($factory, $strategy);
 895  
 896          $this->resetAfterTest(true);
 897          $this->setAdminuser();
 898          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 899          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 900  
 901          for ($i = 1; $i < 21; $i++) {
 902              create_event([
 903                  'name' => sprintf('Event %d', $i),
 904                  'eventtype' => 'user',
 905                  'userid' => $user->id,
 906                  'timesort' => $i,
 907                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 908                  'courseid' => $course1->id,
 909              ]);
 910          }
 911  
 912          for ($i = 21; $i < 41; $i++) {
 913              create_event([
 914                  'name' => sprintf('Event %d', $i),
 915                  'eventtype' => 'user',
 916                  'userid' => $user->id,
 917                  'timesort' => $i,
 918                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 919                  'courseid' => $course2->id,
 920              ]);
 921          }
 922  
 923          $events = $vault->get_action_events_by_course($user, $course1, 1, 20, null, $limit);
 924  
 925          $this->assertCount($limit, $events);
 926          $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());
 927          $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());
 928          $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());
 929          $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());
 930          $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());
 931      }
 932  
 933      /**
 934       * Test that get_action_events_by_course returns events between the
 935       * provided timesort values and after the last seen event when one is
 936       * provided. This should work even when the event ids aren't ordered the
 937       * same as the timesort order.
 938       */
 939      public function test_get_action_events_by_course_non_consecutive_ids() {
 940          $this->resetAfterTest(true);
 941          $this->setAdminuser();
 942  
 943          $user = $this->getDataGenerator()->create_user();
 944          $course1 = $this->getDataGenerator()->create_course();
 945          $course2 = $this->getDataGenerator()->create_course();
 946          $factory = new action_event_test_factory();
 947          $strategy = new raw_event_retrieval_strategy();
 948          $vault = new event_vault($factory, $strategy);
 949  
 950          $this->setAdminuser();
 951          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
 952          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
 953  
 954          /*
 955           * The events should be ordered by timesort as follows:
 956           *
 957           * 1 event 1
 958           * 2 event 1
 959           * 1 event 2
 960           * 2 event 2
 961           * 1 event 3
 962           * 2 event 3
 963           * 1 event 4
 964           * 2 event 4
 965           * 1 event 5
 966           * 2 event 5
 967           * 1 event 6
 968           * 2 event 6
 969           * 1 event 7
 970           * 2 event 7
 971           * 1 event 8
 972           * 2 event 8
 973           * 1 event 9
 974           * 2 event 9
 975           * 1 event 10
 976           * 2 event 10
 977           */
 978          $records = [];
 979          for ($i = 1; $i < 11; $i++) {
 980              $records[] = create_event([
 981                  'name' => sprintf('1 event %d', $i),
 982                  'eventtype' => 'user',
 983                  'userid' => $user->id,
 984                  'timesort' => $i,
 985                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 986                  'courseid' => $course1->id,
 987              ]);
 988          }
 989  
 990          for ($i = 1; $i < 11; $i++) {
 991              $records[] = create_event([
 992                  'name' => sprintf('2 event %d', $i),
 993                  'eventtype' => 'user',
 994                  'userid' => $user->id,
 995                  'timesort' => $i,
 996                  'type' => CALENDAR_EVENT_TYPE_ACTION,
 997                  'courseid' => $course1->id,
 998              ]);
 999          }
1000  
1001          // Create events for the other course.
1002          for ($i = 1; $i < 11; $i++) {
1003              $records[] = create_event([
1004                  'name' => sprintf('3 event %d', $i),
1005                  'eventtype' => 'user',
1006                  'userid' => $user->id,
1007                  'timesort' => $i,
1008                  'type' => CALENDAR_EVENT_TYPE_ACTION,
1009                  'courseid' => $course2->id,
1010              ]);
1011          }
1012  
1013          /*
1014           * Expected result set:
1015           *
1016           * 2 event 4
1017           * 1 event 5
1018           * 2 event 5
1019           * 1 event 6
1020           * 2 event 6
1021           * 1 event 7
1022           * 2 event 7
1023           * 1 event 8
1024           * 2 event 8
1025           */
1026          $aftereventid = $records[3]->id;
1027          $afterevent = $vault->get_event_by_id($aftereventid);
1028          // Offset results by event with name "1 event 4" which has the same timesort
1029          // value as the lower boundary of this query (3). Confirm that the given
1030          // $afterevent is used to ignore events with the same timesortfrom values.
1031          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
1032  
1033          $this->assertCount(9, $events);
1034          $this->assertEquals('2 event 4', $events[0]->get_name());
1035          $this->assertEquals('2 event 8', $events[8]->get_name());
1036  
1037          /*
1038           * Expected result set:
1039           *
1040           * 2 event 4
1041           * 1 event 5
1042           */
1043          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent, 2);
1044  
1045          $this->assertCount(2, $events);
1046          $this->assertEquals('2 event 4', $events[0]->get_name());
1047          $this->assertEquals('1 event 5', $events[1]->get_name());
1048  
1049          /*
1050           * Expected result set:
1051           *
1052           * 2 event 8
1053           */
1054          $aftereventid = $records[7]->id;
1055          $afterevent = $vault->get_event_by_id($aftereventid);
1056          // Offset results by event with name "1 event 8" which has the same timesort
1057          // value as the upper boundary of this query (8). Confirm that the given
1058          // $afterevent is used to ignore events with the same timesortto values.
1059          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
1060  
1061          $this->assertCount(1, $events);
1062          $this->assertEquals('2 event 8', $events[0]->get_name());
1063  
1064          /*
1065           * Expected empty result set.
1066           */
1067          $aftereventid = $records[18]->id;
1068          $afterevent = $vault->get_event_by_id($aftereventid);
1069          // Offset results by event with name "2 event 9" which has a timesort
1070          // value larger than the upper boundary of this query (9 > 8). Confirm
1071          // that the given $afterevent is used for filtering events.
1072          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
1073  
1074          $this->assertEmpty($events);
1075      }
1076  
1077      /**
1078       * There are subtle cases where the priority of an event override may be identical to another.
1079       * For example, if you duplicate a group override, but make it apply to a different group. Now
1080       * there are two overrides with exactly the same overridden dates. In this case the priority of
1081       * both is 1.
1082       *
1083       * In this situation:
1084       * - A user in group A should see only the A override
1085       * - A user in group B should see only the B override
1086       * - A user in both A and B should see both
1087       */
1088      public function test_get_action_events_by_course_with_identical_group_override_priorities() {
1089          $this->resetAfterTest();
1090          $this->setAdminuser();
1091  
1092          $course = $this->getDataGenerator()->create_course();
1093  
1094          // Create an assign instance.
1095          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1096          $assigninstance = $assigngenerator->create_instance(['course' => $course->id]);
1097  
1098          // Create users.
1099          $users = [
1100              'Only in group A'  => $this->getDataGenerator()->create_user(),
1101              'Only in group B'  => $this->getDataGenerator()->create_user(),
1102              'In group A and B' => $this->getDataGenerator()->create_user(),
1103              'In no groups'     => $this->getDataGenerator()->create_user()
1104          ];
1105  
1106          // Enrol users.
1107          foreach ($users as $user) {
1108              $this->getDataGenerator()->enrol_user($user->id, $course->id);
1109          }
1110  
1111          // Create groups.
1112          $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1113          $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1114  
1115          // Add members to groups.
1116          // Group A.
1117          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
1118          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);
1119  
1120          // Group B.
1121          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
1122          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);
1123  
1124          // Events with the same module name, instance and event type.
1125          $events = [
1126              [
1127                  'name' => 'Assignment 1 due date - Group A override',
1128                  'description' => '',
1129                  'format' => 1,
1130                  'courseid' => $course->id,
1131                  'groupid' => $groupa->id,
1132                  'userid' => 2,
1133                  'modulename' => 'assign',
1134                  'instance' => $assigninstance->id,
1135                  'eventtype' => 'due',
1136                  'type' => CALENDAR_EVENT_TYPE_ACTION,
1137                  'timestart' => 1,
1138                  'timeduration' => 0,
1139                  'visible' => 1,
1140                  'priority' => 1
1141              ],
1142              [
1143                  'name' => 'Assignment 1 due date - Group B override',
1144                  'description' => '',
1145                  'format' => 1,
1146                  'courseid' => $course->id,
1147                  'groupid' => $groupb->id,
1148                  'userid' => 2,
1149                  'modulename' => 'assign',
1150                  'instance' => $assigninstance->id,
1151                  'eventtype' => 'due',
1152                  'type' => CALENDAR_EVENT_TYPE_ACTION,
1153                  'timestart' => 1,
1154                  'timeduration' => 0,
1155                  'visible' => 1,
1156                  'priority' => 1
1157              ],
1158              [
1159                  'name' => 'Assignment 1 due date',
1160                  'description' => '',
1161                  'format' => 1,
1162                  'courseid' => $course->id,
1163                  'groupid' => 0,
1164                  'userid' => 2,
1165                  'modulename' => 'assign',
1166                  'instance' => $assigninstance->id,
1167                  'eventtype' => 'due',
1168                  'type' => CALENDAR_EVENT_TYPE_ACTION,
1169                  'timestart' => 1,
1170                  'timeduration' => 0,
1171                  'visible' => 1,
1172                  'priority' => null,
1173              ]
1174          ];
1175  
1176          foreach ($events as $event) {
1177              calendar_event::create($event, false);
1178          }
1179  
1180          $factory = new action_event_test_factory();
1181          $strategy = new raw_event_retrieval_strategy();
1182          $vault = new event_vault($factory, $strategy);
1183  
1184          $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $course, $vault) {
1185              // NB: This is currently needed to make get_action_events_by_timesort return the right thing.
1186              // It needs to be fixed, see MDL-58736.
1187              $this->setUser($users[$description]);
1188              return $carry + [
1189                  'For user ' . lcfirst($description) => $vault->get_action_events_by_course($users[$description], $course)
1190              ];
1191          }, []);
1192  
1193          foreach ($usersevents as $description => $userevents) {
1194              if ($description == 'For user in group A and B') {
1195                  // User is in both A and B, so they should see the override for both
1196                  // given that the priority is the same.
1197                  $this->assertCount(2, $userevents);
1198                  continue;
1199              }
1200  
1201              // Otherwise there should be only one assign event for each user.
1202              $this->assertCount(1, $userevents);
1203          }
1204  
1205          // User in only group A should see the group A override.
1206          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());
1207  
1208          // User in only group B should see the group B override.
1209          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());
1210  
1211          // User in group A and B should see see both overrides since the priorities are the same.
1212          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());
1213          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());
1214  
1215          // User in no groups should see the plain assignment event.
1216          $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());
1217      }
1218  }