Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Event container tests.
  19   *
  20   * @package    core_calendar
  21   * @copyright  2017 Cameron Ball <cameron@cameron1729.xyz>
  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  
  29  require_once($CFG->dirroot . '/calendar/lib.php');
  30  
  31  use core_calendar\local\event\entities\action_event;
  32  use core_calendar\local\event\entities\event;
  33  use core_calendar\local\event\entities\event_interface;
  34  use core_calendar\local\event\factories\event_factory;
  35  use core_calendar\local\event\factories\event_factory_interface;
  36  use core_calendar\local\event\mappers\event_mapper;
  37  use core_calendar\local\event\mappers\event_mapper_interface;
  38  
  39  /**
  40   * Core container testcase.
  41   *
  42   * @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
  43   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class core_calendar_container_testcase extends advanced_testcase {
  46  
  47      /**
  48       * Test setup.
  49       */
  50      public function setUp(): void {
  51          $this->resetAfterTest();
  52          $this->setAdminUser();
  53      }
  54  
  55      /**
  56       * Test getting the event factory.
  57       */
  58      public function test_get_event_factory() {
  59          $factory = \core_calendar\local\event\container::get_event_factory();
  60  
  61          // Test that the container is returning the right type.
  62          $this->assertInstanceOf(event_factory_interface::class, $factory);
  63          // Test that the container is returning the right implementation.
  64          $this->assertInstanceOf(event_factory::class, $factory);
  65  
  66          // Test that getting the factory a second time returns the same instance.
  67          $factory2 = \core_calendar\local\event\container::get_event_factory();
  68          $this->assertTrue($factory === $factory2);
  69      }
  70  
  71      /**
  72       * Test that the event factory correctly creates instances of events.
  73       *
  74       * @dataProvider get_event_factory_testcases()
  75       * @param \stdClass $dbrow Row from the "database".
  76       */
  77      public function test_event_factory_create_instance($dbrow) {
  78          $legacyevent = $this->create_event($dbrow);
  79          $factory = \core_calendar\local\event\container::get_event_factory();
  80          $course = $this->getDataGenerator()->create_course();
  81          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
  82          $moduleinstance = $generator->create_instance(['course' => $course->id]);
  83  
  84          // Set some of the fake dbrow properties to match real data in the DB
  85          // this is necessary as the factory hides things that modinfo doesn't
  86          // know about.
  87          $dbrow->id = $legacyevent->id;
  88          $dbrow->courseid = $course->id;
  89          $dbrow->instance = $moduleinstance->id;
  90          $dbrow->modulename = 'assign';
  91          $event = $factory->create_instance($dbrow);
  92  
  93          // Test that the factory is returning the right type.
  94          $this->assertInstanceOf(event_interface::class, $event);
  95          // Test that the factory is returning the right implementation.
  96          $this->assertTrue($event instanceof event || $event instanceof action_event);
  97  
  98          // Test that the event created has the correct properties.
  99          $this->assertEquals($legacyevent->id, $event->get_id());
 100          $this->assertEquals($dbrow->description, $event->get_description()->get_value());
 101          $this->assertEquals($dbrow->format, $event->get_description()->get_format());
 102          $this->assertEquals($dbrow->courseid, $event->get_course()->get('id'));
 103          $this->assertEquals($dbrow->location, $event->get_location());
 104  
 105          if ($dbrow->groupid == 0) {
 106              $this->assertNull($event->get_group());
 107          } else {
 108              $this->assertEquals($dbrow->groupid, $event->get_group()->get('id'));
 109          }
 110  
 111          $this->assertEquals($dbrow->userid, $event->get_user()->get('id'));
 112          $this->assertEquals(null, $event->get_repeats());
 113          $this->assertEquals($dbrow->modulename, $event->get_course_module()->get('modname'));
 114          $this->assertEquals($dbrow->instance, $event->get_course_module()->get('instance'));
 115          $this->assertEquals($dbrow->timestart, $event->get_times()->get_start_time()->getTimestamp());
 116          $this->assertEquals($dbrow->timemodified, $event->get_times()->get_modified_time()->getTimestamp());
 117          $this->assertEquals($dbrow->timesort, $event->get_times()->get_sort_time()->getTimestamp());
 118  
 119          if ($dbrow->visible == 1) {
 120              $this->assertTrue($event->is_visible());
 121          } else {
 122              $this->assertFalse($event->is_visible());
 123          }
 124  
 125          if (!$dbrow->subscriptionid) {
 126              $this->assertNull($event->get_subscription());
 127          } else {
 128              $this->assertEquals($event->get_subscription()->get('id'));
 129          }
 130      }
 131  
 132      /**
 133       * Test that the event factory deals with invisible modules properly as admin.
 134       *
 135       * @dataProvider get_event_factory_testcases()
 136       * @param \stdClass $dbrow Row from the "database".
 137       */
 138      public function test_event_factory_when_module_visibility_is_toggled_as_admin($dbrow) {
 139          $legacyevent = $this->create_event($dbrow);
 140          $factory = \core_calendar\local\event\container::get_event_factory();
 141          $course = $this->getDataGenerator()->create_course();
 142          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 143          $moduleinstance = $generator->create_instance(['course' => $course->id]);
 144  
 145          $dbrow->id = $legacyevent->id;
 146          $dbrow->courseid = $course->id;
 147          $dbrow->instance = $moduleinstance->id;
 148          $dbrow->modulename = 'assign';
 149  
 150          set_coursemodule_visible($moduleinstance->cmid, 0);
 151  
 152          $event = $factory->create_instance($dbrow);
 153  
 154          // Test that the factory is returning an event as the admin can see hidden course modules.
 155          $this->assertInstanceOf(event_interface::class, $event);
 156      }
 157  
 158      /**
 159       * Test that the event factory deals with invisible modules properly as a guest.
 160       *
 161       * @dataProvider get_event_factory_testcases()
 162       * @param \stdClass $dbrow Row from the "database".
 163       */
 164      public function test_event_factory_when_module_visibility_is_toggled_as_guest($dbrow) {
 165          $legacyevent = $this->create_event($dbrow);
 166          $factory = \core_calendar\local\event\container::get_event_factory();
 167          $course = $this->getDataGenerator()->create_course();
 168          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 169          $moduleinstance = $generator->create_instance(['course' => $course->id]);
 170  
 171          $dbrow->id = $legacyevent->id;
 172          $dbrow->courseid = $course->id;
 173          $dbrow->instance = $moduleinstance->id;
 174          $dbrow->modulename = 'assign';
 175  
 176          set_coursemodule_visible($moduleinstance->cmid, 0);
 177  
 178          // Set to a user who can not view hidden course modules.
 179          $this->setGuestUser();
 180  
 181          $event = $factory->create_instance($dbrow);
 182  
 183          // Module is invisible to guest users so this should return null.
 184          $this->assertNull($event);
 185      }
 186  
 187      /**
 188       * Test that the event factory deals with invisible courses as an admin.
 189       *
 190       * @dataProvider get_event_factory_testcases()
 191       * @param \stdClass $dbrow Row from the "database".
 192       */
 193      public function test_event_factory_when_course_visibility_is_toggled_as_admin($dbrow) {
 194          $legacyevent = $this->create_event($dbrow);
 195          $factory = \core_calendar\local\event\container::get_event_factory();
 196  
 197          // Create a hidden course with an assignment.
 198          $course = $this->getDataGenerator()->create_course(['visible' => 0]);
 199          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 200          $moduleinstance = $generator->create_instance(['course' => $course->id]);
 201  
 202          $dbrow->id = $legacyevent->id;
 203          $dbrow->courseid = $course->id;
 204          $dbrow->instance = $moduleinstance->id;
 205          $dbrow->modulename = 'assign';
 206          $event = $factory->create_instance($dbrow);
 207  
 208          // Module is still visible to admins even if the course is invisible.
 209          $this->assertInstanceOf(event_interface::class, $event);
 210      }
 211  
 212      /**
 213       * Test that the event factory deals with invisible courses as a student.
 214       *
 215       * @dataProvider get_event_factory_testcases()
 216       * @param \stdClass $dbrow Row from the "database".
 217       */
 218      public function test_event_factory_when_course_visibility_is_toggled_as_student($dbrow) {
 219          $legacyevent = $this->create_event($dbrow);
 220          $factory = \core_calendar\local\event\container::get_event_factory();
 221  
 222          // Create a hidden course with an assignment.
 223          $course = $this->getDataGenerator()->create_course(['visible' => 0]);
 224          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 225          $moduleinstance = $generator->create_instance(['course' => $course->id]);
 226  
 227          // Enrol a student into this course.
 228          $student = $this->getDataGenerator()->create_user();
 229          $this->getDataGenerator()->enrol_user($student->id, $course->id);
 230  
 231          // Set the user to the student.
 232          $this->setUser($student);
 233  
 234          $dbrow->id = $legacyevent->id;
 235          $dbrow->courseid = $course->id;
 236          $dbrow->instance = $moduleinstance->id;
 237          $dbrow->modulename = 'assign';
 238          $event = $factory->create_instance($dbrow);
 239  
 240          // Module is invisible to students if the course is invisible.
 241          $this->assertNull($event);
 242      }
 243  
 244      /**
 245       * Test that the event factory deals with invisible categorys as an admin.
 246       */
 247      public function test_event_factory_when_category_visibility_is_toggled_as_admin() {
 248          // Create a hidden category.
 249          $category = $this->getDataGenerator()->create_category(['visible' => 0]);
 250  
 251          $eventdata = [
 252                  'categoryid' => $category->id,
 253                  'eventtype' => 'category',
 254              ];
 255          $legacyevent = $this->create_event($eventdata);
 256  
 257          $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
 258          $dbrow->id = $legacyevent->id;
 259  
 260          $factory = \core_calendar\local\event\container::get_event_factory();
 261          $event = $factory->create_instance($dbrow);
 262  
 263          // Module is still visible to admins even if the category is invisible.
 264          $this->assertInstanceOf(event_interface::class, $event);
 265      }
 266  
 267      /**
 268       * Test that the event factory deals with invisible categorys as an user.
 269       */
 270      public function test_event_factory_when_category_visibility_is_toggled_as_user() {
 271          // Create a hidden category.
 272          $category = $this->getDataGenerator()->create_category(['visible' => 0]);
 273  
 274          $eventdata = [
 275                  'categoryid' => $category->id,
 276                  'eventtype' => 'category',
 277              ];
 278          $legacyevent = $this->create_event($eventdata);
 279  
 280          $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
 281          $dbrow->id = $legacyevent->id;
 282  
 283          // Use a standard user.
 284          $user = $this->getDataGenerator()->create_user();
 285  
 286          // Set the user to the student.
 287          $this->setUser($user);
 288  
 289          $factory = \core_calendar\local\event\container::get_event_factory();
 290          $event = $factory->create_instance($dbrow);
 291  
 292          // Module is invisible to non-privileged users.
 293          $this->assertNull($event);
 294      }
 295  
 296      /**
 297       * Test that the event factory deals with invisible categorys as an guest.
 298       */
 299      public function test_event_factory_when_category_visibility_is_toggled_as_guest() {
 300          // Create a hidden category.
 301          $category = $this->getDataGenerator()->create_category(['visible' => 0]);
 302  
 303          $eventdata = [
 304                  'categoryid' => $category->id,
 305                  'eventtype' => 'category',
 306              ];
 307          $legacyevent = $this->create_event($eventdata);
 308  
 309          $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
 310          $dbrow->id = $legacyevent->id;
 311  
 312          // Set the user to the student.
 313          $this->setGuestUser();
 314  
 315          $factory = \core_calendar\local\event\container::get_event_factory();
 316          $event = $factory->create_instance($dbrow);
 317  
 318          // Module is invisible to guests.
 319          $this->assertNull($event);
 320      }
 321  
 322      /**
 323       * Test that the event factory deals with completion related events properly.
 324       */
 325      public function test_event_factory_with_completion_related_event() {
 326          global $CFG;
 327  
 328          $CFG->enablecompletion = true;
 329  
 330          // Create the course we will be using.
 331          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 332  
 333          // Add the assignment.
 334          $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 335          $assign = $generator->create_instance(array('course' => $course->id), array('completion' => 1));
 336  
 337          // Create a completion event.
 338          $event = new \stdClass();
 339          $event->name = 'An event';
 340          $event->description = 'Event description';
 341          $event->location = 'Event location';
 342          $event->format = FORMAT_HTML;
 343          $event->eventtype = \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED;
 344          $event->userid = 1;
 345          $event->modulename = 'assign';
 346          $event->instance = $assign->id;
 347          $event->categoryid = 0;
 348          $event->courseid = $course->id;
 349          $event->groupid = 0;
 350          $event->timestart = time();
 351          $event->timesort = time();
 352          $event->timemodified = time();
 353          $event->timeduration = 0;
 354          $event->subscriptionid = null;
 355          $event->repeatid = 0;
 356          $legacyevent = $this->create_event($event);
 357  
 358          // Update the id of the event that was created.
 359          $event->id = $legacyevent->id;
 360  
 361          // Create the factory we are going to be testing the behaviour of.
 362          $factory = \core_calendar\local\event\container::get_event_factory();
 363  
 364          // Check that we get the correct instance.
 365          $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
 366  
 367          // Now, disable completion.
 368          $CFG->enablecompletion = false;
 369  
 370          // The result should now be null since we have disabled completion.
 371          $this->assertNull($factory->create_instance($event));
 372      }
 373  
 374      /**
 375       * Test that the event factory only returns an event if the logged in user
 376       * is enrolled in the course.
 377       */
 378      public function test_event_factory_unenrolled_user() {
 379          $user = $this->getDataGenerator()->create_user();
 380          // Create the course we will be using.
 381          $course = $this->getDataGenerator()->create_course();
 382  
 383          // Add the assignment.
 384          $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
 385          $lesson = $generator->create_instance(array('course' => $course->id));
 386  
 387          // Create a user override event for the lesson.
 388          $event = new \stdClass();
 389          $event->name = 'An event';
 390          $event->description = 'Event description';
 391          $event->location = 'Event location';
 392          $event->format = FORMAT_HTML;
 393          $event->eventtype = 'close';
 394          $event->userid = $user->id;
 395          $event->modulename = 'lesson';
 396          $event->instance = $lesson->id;
 397          $event->categoryid = 0;
 398          $event->courseid = $course->id;
 399          $event->groupid = 0;
 400          $event->timestart = time();
 401          $event->timesort = time();
 402          $event->timemodified = time();
 403          $event->timeduration = 0;
 404          $event->subscriptionid = null;
 405          $event->repeatid = 0;
 406          $legacyevent = $this->create_event($event);
 407  
 408          // Update the id of the event that was created.
 409          $event->id = $legacyevent->id;
 410  
 411          // Set the logged in user to the one we created.
 412          $this->setUser($user);
 413  
 414          // Create the factory we are going to be testing the behaviour of.
 415          $factory = \core_calendar\local\event\container::get_event_factory();
 416  
 417          // The result should be null since the user is not enrolled in the
 418          // course the event is for.
 419          $this->assertNull($factory->create_instance($event));
 420  
 421          // Now enrol the user in the course.
 422          $this->getDataGenerator()->enrol_user($user->id, $course->id);
 423  
 424          // Check that we get the correct instance.
 425          $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
 426      }
 427  
 428      /**
 429       * Test that when course module is deleted all events are also deleted.
 430       */
 431      public function test_delete_module_delete_events() {
 432          global $DB;
 433          $user = $this->getDataGenerator()->create_user();
 434          // Create the course we will be using.
 435          $course = $this->getDataGenerator()->create_course();
 436          $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
 437  
 438          foreach (core_component::get_plugin_list('mod') as $modname => $unused) {
 439              try {
 440                  $generator = $this->getDataGenerator()->get_plugin_generator('mod_'.$modname);
 441              } catch (coding_exception $e) {
 442                  // Module generator is not implemented.
 443                  continue;
 444              }
 445              $module = $generator->create_instance(['course' => $course->id]);
 446  
 447              // Create bunch of events of different type (user override, group override, module event).
 448              $this->create_event(['userid' => $user->id, 'modulename' => $modname, 'instance' => $module->id]);
 449              $this->create_event(['groupid' => $group->id, 'modulename' => $modname, 'instance' => $module->id]);
 450              $this->create_event(['modulename' => $modname, 'instance' => $module->id]);
 451              $this->create_event(['modulename' => $modname, 'instance' => $module->id, 'courseid' => $course->id]);
 452  
 453              // Delete module and make sure all events are deleted.
 454              course_delete_module($module->cmid);
 455              $this->assertEmpty($DB->get_record('event', ['modulename' => $modname, 'instance' => $module->id]));
 456          }
 457      }
 458  
 459      /**
 460       * Test getting the event mapper.
 461       */
 462      public function test_get_event_mapper() {
 463          $mapper = \core_calendar\local\event\container::get_event_mapper();
 464  
 465          $this->assertInstanceOf(event_mapper_interface::class, $mapper);
 466          $this->assertInstanceOf(event_mapper::class, $mapper);
 467  
 468          $mapper2 = \core_calendar\local\event\container::get_event_mapper();
 469  
 470          $this->assertTrue($mapper === $mapper2);
 471      }
 472  
 473      /**
 474       * Test cases for the get event factory test.
 475       */
 476      public function get_event_factory_testcases() {
 477          return [
 478              'Data set 1' => [
 479                  'dbrow' => (object)[
 480                      'name' => 'Test event',
 481                      'description' => 'Hello',
 482                      'format' => 1,
 483                      'categoryid' => 0,
 484                      'courseid' => 1,
 485                      'groupid' => 0,
 486                      'userid' => 1,
 487                      'repeatid' => 0,
 488                      'modulename' => 'assign',
 489                      'instance' => 2,
 490                      'eventtype' => 'due',
 491                      'timestart' => 1486396800,
 492                      'timeduration' => 0,
 493                      'timesort' => 1486396800,
 494                      'visible' => 1,
 495                      'timemodified' => 1485793098,
 496                      'subscriptionid' => null,
 497                      'location' => 'Test location',
 498                  ]
 499              ],
 500  
 501              'Data set 2' => [
 502                  'dbrow' => (object)[
 503                      'name' => 'Test event',
 504                      'description' => 'Hello',
 505                      'format' => 1,
 506                      'categoryid' => 0,
 507                      'courseid' => 1,
 508                      'groupid' => 1,
 509                      'userid' => 1,
 510                      'repeatid' => 0,
 511                      'modulename' => 'assign',
 512                      'instance' => 2,
 513                      'eventtype' => 'due',
 514                      'timestart' => 1486396800,
 515                      'timeduration' => 0,
 516                      'timesort' => 1486396800,
 517                      'visible' => 1,
 518                      'timemodified' => 1485793098,
 519                      'subscriptionid' => null,
 520                      'location' => 'Test location',
 521                  ]
 522              ]
 523          ];
 524      }
 525  
 526      /**
 527       * Helper function to create calendar events using the old code.
 528       *
 529       * @param array $properties A list of calendar event properties to set
 530       * @return calendar_event|bool
 531       */
 532      protected function create_event($properties = []) {
 533          $record = new \stdClass();
 534          $record->name = 'event name';
 535          $record->eventtype = 'site';
 536          $record->timestart = time();
 537          $record->timeduration = 0;
 538          $record->timesort = 0;
 539          $record->type = 1;
 540          $record->courseid = 0;
 541          $record->categoryid = 0;
 542  
 543          foreach ($properties as $name => $value) {
 544              $record->$name = $value;
 545          }
 546  
 547          $event = new calendar_event($record);
 548          return $event->create($record, false);
 549      }
 550  
 551      /**
 552       * Pad out a basic DB row with basic information.
 553       *
 554       * @param   \stdClass   $skeleton the current skeleton
 555       * @return  \stdClass
 556       */
 557      protected function get_dbrow_from_skeleton($skeleton) {
 558          $dbrow = (object) [
 559              'name' => 'Name',
 560              'description' => 'Description',
 561              'format' => 1,
 562              'categoryid' => 0,
 563              'courseid' => 0,
 564              'groupid' => 0,
 565              'userid' => 0,
 566              'repeatid' => 0,
 567              'modulename' => '',
 568              'instance' => 0,
 569              'eventtype' => 'user',
 570              'timestart' => 1486396800,
 571              'timeduration' => 0,
 572              'timesort' => 1486396800,
 573              'visible' => 1,
 574              'timemodified' => 1485793098,
 575              'subscriptionid' => null,
 576              'location' => 'Test location',
 577          ];
 578  
 579          foreach ((array) $skeleton as $key => $value) {
 580              $dbrow->$key = $value;
 581          }
 582  
 583          return $dbrow;
 584      }
 585  }