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