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 * Unit tests for mod/lesson/lib.php. 19 * 20 * @package mod_lesson 21 * @category test 22 * @copyright 2017 Jun Pataleta 23 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 require_once($CFG->dirroot . '/mod/lesson/lib.php'); 30 31 /** 32 * Unit tests for mod/lesson/lib.php. 33 * 34 * @copyright 2017 Jun Pataleta 35 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 36 */ 37 class mod_lesson_lib_testcase extends advanced_testcase { 38 /** 39 * Test for lesson_get_group_override_priorities(). 40 */ 41 public function test_lesson_get_group_override_priorities() { 42 global $DB; 43 $this->resetAfterTest(); 44 $this->setAdminUser(); 45 46 $dg = $this->getDataGenerator(); 47 $course = $dg->create_course(); 48 $lessonmodule = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id)); 49 50 $this->assertNull(lesson_get_group_override_priorities($lessonmodule->id)); 51 52 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 53 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 54 55 $now = 100; 56 $override1 = (object)[ 57 'lessonid' => $lessonmodule->id, 58 'groupid' => $group1->id, 59 'available' => $now, 60 'deadline' => $now + 20 61 ]; 62 $DB->insert_record('lesson_overrides', $override1); 63 64 $override2 = (object)[ 65 'lessonid' => $lessonmodule->id, 66 'groupid' => $group2->id, 67 'available' => $now - 10, 68 'deadline' => $now + 10 69 ]; 70 $DB->insert_record('lesson_overrides', $override2); 71 72 $priorities = lesson_get_group_override_priorities($lessonmodule->id); 73 $this->assertNotEmpty($priorities); 74 75 $openpriorities = $priorities['open']; 76 // Override 2's time open has higher priority since it is sooner than override 1's. 77 $this->assertEquals(2, $openpriorities[$override1->available]); 78 $this->assertEquals(1, $openpriorities[$override2->available]); 79 80 $closepriorities = $priorities['close']; 81 // Override 1's time close has higher priority since it is later than override 2's. 82 $this->assertEquals(1, $closepriorities[$override1->deadline]); 83 $this->assertEquals(2, $closepriorities[$override2->deadline]); 84 } 85 86 /** 87 * Test check_updates_since callback. 88 */ 89 public function test_check_updates_since() { 90 global $DB; 91 92 $this->resetAfterTest(); 93 $this->setAdminUser(); 94 $course = new stdClass(); 95 $course->groupmode = SEPARATEGROUPS; 96 $course->groupmodeforce = true; 97 $course = $this->getDataGenerator()->create_course($course); 98 99 // Create user. 100 $studentg1 = self::getDataGenerator()->create_user(); 101 $teacherg1 = self::getDataGenerator()->create_user(); 102 $studentg2 = self::getDataGenerator()->create_user(); 103 104 // User enrolment. 105 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 106 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 107 $this->getDataGenerator()->enrol_user($studentg1->id, $course->id, $studentrole->id, 'manual'); 108 $this->getDataGenerator()->enrol_user($teacherg1->id, $course->id, $teacherrole->id, 'manual'); 109 $this->getDataGenerator()->enrol_user($studentg2->id, $course->id, $studentrole->id, 'manual'); 110 111 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 112 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 113 groups_add_member($group1, $studentg1); 114 groups_add_member($group2, $studentg2); 115 116 $this->setCurrentTimeStart(); 117 $record = array( 118 'course' => $course->id, 119 'custom' => 0, 120 'feedback' => 1, 121 ); 122 $lessonmodule = $this->getDataGenerator()->create_module('lesson', $record); 123 // Convert to a lesson object. 124 $lesson = new lesson($lessonmodule); 125 $cm = $lesson->cm; 126 $cm = cm_info::create($cm); 127 128 // Check that upon creation, the updates are only about the new configuration created. 129 $onehourago = time() - HOURSECS; 130 $updates = lesson_check_updates_since($cm, $onehourago); 131 foreach ($updates as $el => $val) { 132 if ($el == 'configuration') { 133 $this->assertTrue($val->updated); 134 $this->assertTimeCurrent($val->timeupdated); 135 } else { 136 $this->assertFalse($val->updated); 137 } 138 } 139 140 // Set up a generator to create content. 141 $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); 142 $tfrecord = $generator->create_question_truefalse($lesson); 143 144 // Check now for pages and answers. 145 $updates = lesson_check_updates_since($cm, $onehourago); 146 $this->assertTrue($updates->pages->updated); 147 $this->assertCount(1, $updates->pages->itemids); 148 149 $this->assertTrue($updates->answers->updated); 150 $this->assertCount(2, $updates->answers->itemids); 151 152 // Now, do something in the lesson with the two users. 153 $this->setUser($studentg1); 154 mod_lesson_external::launch_attempt($lesson->id); 155 $data = array( 156 array( 157 'name' => 'answerid', 158 'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)), 159 ), 160 array( 161 'name' => '_qf__lesson_display_answer_form_truefalse', 162 'value' => 1, 163 ) 164 ); 165 mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data); 166 mod_lesson_external::finish_attempt($lesson->id); 167 168 $this->setUser($studentg2); 169 mod_lesson_external::launch_attempt($lesson->id); 170 $data = array( 171 array( 172 'name' => 'answerid', 173 'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)), 174 ), 175 array( 176 'name' => '_qf__lesson_display_answer_form_truefalse', 177 'value' => 1, 178 ) 179 ); 180 mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data); 181 mod_lesson_external::finish_attempt($lesson->id); 182 183 $this->setUser($studentg1); 184 $updates = lesson_check_updates_since($cm, $onehourago); 185 186 // Check question attempts, timers and new grades. 187 $this->assertTrue($updates->questionattempts->updated); 188 $this->assertCount(1, $updates->questionattempts->itemids); 189 190 $this->assertTrue($updates->grades->updated); 191 $this->assertCount(1, $updates->grades->itemids); 192 193 $this->assertTrue($updates->timers->updated); 194 $this->assertCount(1, $updates->timers->itemids); 195 196 // Now, as teacher, check that I can see the two users (even in separate groups). 197 $this->setUser($teacherg1); 198 $updates = lesson_check_updates_since($cm, $onehourago); 199 $this->assertTrue($updates->userquestionattempts->updated); 200 $this->assertCount(2, $updates->userquestionattempts->itemids); 201 202 $this->assertTrue($updates->usergrades->updated); 203 $this->assertCount(2, $updates->usergrades->itemids); 204 205 $this->assertTrue($updates->usertimers->updated); 206 $this->assertCount(2, $updates->usertimers->itemids); 207 208 // Now, teacher can't access all groups. 209 groups_add_member($group1, $teacherg1); 210 assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, context_module::instance($cm->id)); 211 accesslib_clear_all_caches_for_unit_testing(); 212 $updates = lesson_check_updates_since($cm, $onehourago); 213 // I will see only the studentg1 updates. 214 $this->assertTrue($updates->userquestionattempts->updated); 215 $this->assertCount(1, $updates->userquestionattempts->itemids); 216 217 $this->assertTrue($updates->usergrades->updated); 218 $this->assertCount(1, $updates->usergrades->itemids); 219 220 $this->assertTrue($updates->usertimers->updated); 221 $this->assertCount(1, $updates->usertimers->itemids); 222 } 223 224 public function test_lesson_core_calendar_provide_event_action_open() { 225 $this->resetAfterTest(); 226 $this->setAdminUser(); 227 // Create a course. 228 $course = $this->getDataGenerator()->create_course(); 229 // Create a teacher and enrol into the course. 230 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 231 // Create a lesson activity. 232 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 233 'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS)); 234 // Create a calendar event. 235 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 236 237 // Log in as the teacher. 238 $this->setUser($teacher); 239 // Create an action factory. 240 $factory = new \core_calendar\action_factory(); 241 // Decorate action event. 242 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory); 243 // Confirm the event was decorated. 244 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 245 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 246 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 247 $this->assertEquals(1, $actionevent->get_item_count()); 248 $this->assertTrue($actionevent->is_actionable()); 249 } 250 251 public function test_lesson_core_calendar_provide_event_action_open_as_non_user() { 252 global $CFG; 253 254 $this->resetAfterTest(); 255 $this->setAdminUser(); 256 257 // Create a course. 258 $course = $this->getDataGenerator()->create_course(); 259 260 // Create a lesson activity. 261 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 262 'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS)); 263 264 // Create a calendar event. 265 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 266 267 // Now, log out. 268 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 269 $this->setUser(); 270 271 // Create an action factory. 272 $factory = new \core_calendar\action_factory(); 273 274 // Decorate action event. 275 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory); 276 277 // Confirm the event is not shown at all. 278 $this->assertNull($actionevent); 279 } 280 281 public function test_lesson_core_calendar_provide_event_action_open_for_user() { 282 global $CFG; 283 284 $this->resetAfterTest(); 285 $this->setAdminUser(); 286 287 // Create a course. 288 $course = $this->getDataGenerator()->create_course(); 289 290 // Create a student. 291 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 292 293 // Create a lesson activity. 294 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 295 'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS)); 296 297 // Create a calendar event. 298 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 299 300 // Now, log out. 301 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 302 $this->setUser(); 303 304 // Create an action factory. 305 $factory = new \core_calendar\action_factory(); 306 307 // Decorate action event for the student. 308 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id); 309 310 // Confirm the event was decorated. 311 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 312 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 313 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 314 $this->assertEquals(1, $actionevent->get_item_count()); 315 $this->assertTrue($actionevent->is_actionable()); 316 } 317 318 public function test_lesson_core_calendar_provide_event_action_open_in_hidden_section() { 319 $this->resetAfterTest(); 320 $this->setAdminUser(); 321 322 // Create a course. 323 $course = $this->getDataGenerator()->create_course(); 324 325 // Create a student. 326 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 327 328 // Create a lesson activity. 329 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 330 'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS)); 331 332 // Create a calendar event. 333 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 334 335 // Set sections 0 as hidden. 336 set_section_visible($course->id, 0, 0); 337 338 // Create an action factory. 339 $factory = new \core_calendar\action_factory(); 340 341 // Decorate action event for the student. 342 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id); 343 344 // Confirm the event is not shown at all. 345 $this->assertNull($actionevent); 346 } 347 348 public function test_lesson_core_calendar_provide_event_action_closed() { 349 $this->resetAfterTest(); 350 $this->setAdminUser(); 351 352 // Create a course. 353 $course = $this->getDataGenerator()->create_course(); 354 // Create a teacher and enrol. 355 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 356 357 // Create a lesson activity. 358 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 359 'deadline' => time() - DAYSECS)); 360 361 // Create a calendar event. 362 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 363 364 // Now, log in as teacher. 365 $this->setUser($teacher); 366 // Create an action factory. 367 $factory = new \core_calendar\action_factory(); 368 369 // Decorate action event. 370 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory); 371 372 // Confirm the event was decorated. 373 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 374 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 375 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 376 $this->assertEquals(1, $actionevent->get_item_count()); 377 $this->assertFalse($actionevent->is_actionable()); 378 } 379 380 public function test_lesson_core_calendar_provide_event_action_closed_for_user() { 381 global $CFG; 382 383 $this->resetAfterTest(); 384 $this->setAdminUser(); 385 386 // Create a course. 387 $course = $this->getDataGenerator()->create_course(); 388 389 // Create a student. 390 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 391 392 // Create a lesson activity. 393 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 394 'deadline' => time() - DAYSECS)); 395 396 // Create a calendar event. 397 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 398 399 // Now, log out. 400 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 401 $this->setUser(); 402 403 // Create an action factory. 404 $factory = new \core_calendar\action_factory(); 405 406 // Decorate action event. 407 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id); 408 409 // Confirm the event was decorated. 410 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 411 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 412 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 413 $this->assertEquals(1, $actionevent->get_item_count()); 414 $this->assertFalse($actionevent->is_actionable()); 415 } 416 417 public function test_lesson_core_calendar_provide_event_action_open_in_future() { 418 $this->resetAfterTest(); 419 $this->setAdminUser(); 420 421 // Create a course. 422 $course = $this->getDataGenerator()->create_course(); 423 // Create a teacher and enrol into the course. 424 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 425 // Create a lesson activity. 426 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 427 'available' => time() + DAYSECS)); 428 429 // Create a calendar event. 430 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 431 432 // Now, log in as teacher. 433 $this->setUser($teacher); 434 // Create an action factory. 435 $factory = new \core_calendar\action_factory(); 436 437 // Decorate action event. 438 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory); 439 440 // Confirm the event was decorated. 441 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 442 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 443 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 444 $this->assertEquals(1, $actionevent->get_item_count()); 445 $this->assertFalse($actionevent->is_actionable()); 446 } 447 448 public function test_lesson_core_calendar_provide_event_action_open_in_future_for_user() { 449 global $CFG; 450 451 $this->resetAfterTest(); 452 $this->setAdminUser(); 453 454 // Create a course. 455 $course = $this->getDataGenerator()->create_course(); 456 457 // Create a student. 458 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 459 460 // Create a lesson activity. 461 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id, 462 'available' => time() + DAYSECS)); 463 464 // Create a calendar event. 465 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 466 467 // Now, log out. 468 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 469 $this->setUser(); 470 471 // Create an action factory. 472 $factory = new \core_calendar\action_factory(); 473 474 // Decorate action event. 475 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id); 476 477 // Confirm the event was decorated. 478 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 479 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 480 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 481 $this->assertEquals(1, $actionevent->get_item_count()); 482 $this->assertFalse($actionevent->is_actionable()); 483 } 484 485 public function test_lesson_core_calendar_provide_event_action_no_time_specified() { 486 $this->resetAfterTest(); 487 $this->setAdminUser(); 488 489 // Create a course. 490 $course = $this->getDataGenerator()->create_course(); 491 // Create a teacher and enrol into the course. 492 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 493 // Create a lesson activity. 494 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id)); 495 496 // Create a calendar event. 497 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 498 // Now, log in as teacher. 499 $this->setUser($teacher); 500 // Create an action factory. 501 $factory = new \core_calendar\action_factory(); 502 503 // Decorate action event. 504 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory); 505 506 // Confirm the event was decorated. 507 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 508 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 509 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 510 $this->assertEquals(1, $actionevent->get_item_count()); 511 $this->assertTrue($actionevent->is_actionable()); 512 } 513 514 public function test_lesson_core_calendar_provide_event_action_no_time_specified_for_user() { 515 global $CFG; 516 517 $this->resetAfterTest(); 518 $this->setAdminUser(); 519 520 // Create a course. 521 $course = $this->getDataGenerator()->create_course(); 522 523 // Create a student. 524 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 525 526 // Create a lesson activity. 527 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id)); 528 529 // Create a calendar event. 530 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 531 532 // Now, log out. 533 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 534 $this->setUser(); 535 536 // Create an action factory. 537 $factory = new \core_calendar\action_factory(); 538 539 // Decorate action event. 540 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id); 541 542 // Confirm the event was decorated. 543 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 544 $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name()); 545 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 546 $this->assertEquals(1, $actionevent->get_item_count()); 547 $this->assertTrue($actionevent->is_actionable()); 548 } 549 550 public function test_lesson_core_calendar_provide_event_action_after_attempt() { 551 global $DB; 552 553 $this->resetAfterTest(); 554 $this->setAdminUser(); 555 556 // Create a course. 557 $course = $this->getDataGenerator()->create_course(); 558 559 // Create user. 560 $student = self::getDataGenerator()->create_user(); 561 562 // Create a lesson activity. 563 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id)); 564 565 // Create a calendar event. 566 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 567 568 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 569 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); 570 571 $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); 572 $tfrecord = $generator->create_question_truefalse($lesson); 573 574 // Now, do something in the lesson. 575 $this->setUser($student); 576 mod_lesson_external::launch_attempt($lesson->id); 577 $data = array( 578 array( 579 'name' => 'answerid', 580 'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)), 581 ), 582 array( 583 'name' => '_qf__lesson_display_answer_form_truefalse', 584 'value' => 1, 585 ) 586 ); 587 mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data); 588 mod_lesson_external::finish_attempt($lesson->id); 589 590 // Create an action factory. 591 $factory = new \core_calendar\action_factory(); 592 593 // Decorate action event. 594 $action = mod_lesson_core_calendar_provide_event_action($event, $factory); 595 596 // Confirm there was no action for the user. 597 $this->assertNull($action); 598 } 599 600 public function test_lesson_core_calendar_provide_event_action_after_attempt_for_user() { 601 global $DB; 602 603 $this->resetAfterTest(); 604 $this->setAdminUser(); 605 606 // Create a course. 607 $course = $this->getDataGenerator()->create_course(); 608 609 // Create 2 students in the course. 610 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 611 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 612 613 // Create a lesson activity. 614 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id)); 615 616 // Create a calendar event. 617 $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN); 618 619 $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); 620 $tfrecord = $generator->create_question_truefalse($lesson); 621 622 // Now, do something in the lesson as student1. 623 $this->setUser($student1); 624 mod_lesson_external::launch_attempt($lesson->id); 625 $data = array( 626 array( 627 'name' => 'answerid', 628 'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)), 629 ), 630 array( 631 'name' => '_qf__lesson_display_answer_form_truefalse', 632 'value' => 1, 633 ) 634 ); 635 mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data); 636 mod_lesson_external::finish_attempt($lesson->id); 637 638 // Now, log in as the other student. 639 $this->setUser($student2); 640 641 // Create an action factory. 642 $factory = new \core_calendar\action_factory(); 643 644 // Decorate action event. 645 $action = mod_lesson_core_calendar_provide_event_action($event, $factory, $student1->id); 646 647 // Confirm there was no action for the user. 648 $this->assertNull($action); 649 } 650 651 public function test_lesson_core_calendar_provide_event_action_already_completed() { 652 $this->resetAfterTest(); 653 set_config('enablecompletion', 1); 654 $this->setAdminUser(); 655 656 // Create the activity. 657 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 658 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id), 659 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 660 661 // Get some additional data. 662 $cm = get_coursemodule_from_instance('lesson', $lesson->id); 663 664 // Create a calendar event. 665 $event = $this->create_action_event($course->id, $lesson->id, 666 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 667 668 // Mark the activity as completed. 669 $completion = new completion_info($course); 670 $completion->set_module_viewed($cm); 671 672 // Create an action factory. 673 $factory = new \core_calendar\action_factory(); 674 675 // Decorate action event. 676 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory); 677 678 // Ensure result was null. 679 $this->assertNull($actionevent); 680 } 681 682 public function test_lesson_core_calendar_provide_event_action_already_completed_for_user() { 683 $this->resetAfterTest(); 684 set_config('enablecompletion', 1); 685 $this->setAdminUser(); 686 687 // Create the activity. 688 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 689 $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id), 690 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 691 692 // Enrol a student in the course. 693 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 694 695 // Get some additional data. 696 $cm = get_coursemodule_from_instance('lesson', $lesson->id); 697 698 // Create a calendar event. 699 $event = $this->create_action_event($course->id, $lesson->id, 700 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 701 702 // Mark the activity as completed for the student. 703 $completion = new completion_info($course); 704 $completion->set_module_viewed($cm, $student->id); 705 706 // Create an action factory. 707 $factory = new \core_calendar\action_factory(); 708 709 // Decorate action event for the student. 710 $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id); 711 712 // Ensure result was null. 713 $this->assertNull($actionevent); 714 } 715 716 /** 717 * Creates an action event. 718 * 719 * @param int $courseid 720 * @param int $instanceid The lesson id. 721 * @param string $eventtype The event type. eg. LESSON_EVENT_TYPE_OPEN. 722 * @return bool|calendar_event 723 */ 724 private function create_action_event($courseid, $instanceid, $eventtype) { 725 $event = new stdClass(); 726 $event->name = 'Calendar event'; 727 $event->modulename = 'lesson'; 728 $event->courseid = $courseid; 729 $event->instance = $instanceid; 730 $event->type = CALENDAR_EVENT_TYPE_ACTION; 731 $event->eventtype = $eventtype; 732 $event->timestart = time(); 733 return calendar_event::create($event); 734 } 735 736 /** 737 * Test the callback responsible for returning the completion rule descriptions. 738 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 739 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 740 */ 741 public function test_mod_lesson_completion_get_active_rule_descriptions() { 742 $this->resetAfterTest(); 743 $this->setAdminUser(); 744 745 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 746 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]); 747 $lesson1 = $this->getDataGenerator()->create_module('lesson', [ 748 'course' => $course->id, 749 'completion' => 2, 750 'completionendreached' => 1, 751 'completiontimespent' => 3600 752 ]); 753 $lesson2 = $this->getDataGenerator()->create_module('lesson', [ 754 'course' => $course->id, 755 'completion' => 2, 756 'completionendreached' => 0, 757 'completiontimespent' => 0 758 ]); 759 $cm1 = cm_info::create(get_coursemodule_from_instance('lesson', $lesson1->id)); 760 $cm2 = cm_info::create(get_coursemodule_from_instance('lesson', $lesson2->id)); 761 762 // Data for the stdClass input type. 763 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 764 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 765 $moddefaults = new stdClass(); 766 $moddefaults->customdata = ['customcompletionrules' => [ 767 'completionendreached' => 1, 768 'completiontimespent' => 3600 769 ]]; 770 $moddefaults->completion = 2; 771 772 $activeruledescriptions = [ 773 get_string('completionendreached_desc', 'lesson'), 774 get_string('completiontimespentdesc', 'lesson', format_time(3600)), 775 ]; 776 $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 777 $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions($cm2), []); 778 $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 779 $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions(new stdClass()), []); 780 } 781 782 /** 783 * An unknown event type should not change the lesson instance. 784 */ 785 public function test_mod_lesson_core_calendar_event_timestart_updated_unknown_event() { 786 global $CFG, $DB; 787 require_once($CFG->dirroot . "/calendar/lib.php"); 788 789 $this->resetAfterTest(true); 790 $this->setAdminUser(); 791 $generator = $this->getDataGenerator(); 792 $course = $generator->create_course(); 793 $lessongenerator = $generator->get_plugin_generator('mod_lesson'); 794 $timeopen = time(); 795 $timeclose = $timeopen + DAYSECS; 796 $lesson = $lessongenerator->create_instance(['course' => $course->id]); 797 $lesson->available = $timeopen; 798 $lesson->deadline = $timeclose; 799 $DB->update_record('lesson', $lesson); 800 801 // Create a valid event. 802 $event = new \calendar_event([ 803 'name' => 'Test event', 804 'description' => '', 805 'format' => 1, 806 'courseid' => $course->id, 807 'groupid' => 0, 808 'userid' => 2, 809 'modulename' => 'lesson', 810 'instance' => $lesson->id, 811 'eventtype' => LESSON_EVENT_TYPE_OPEN . "SOMETHING ELSE", 812 'timestart' => 1, 813 'timeduration' => 86400, 814 'visible' => 1 815 ]); 816 817 mod_lesson_core_calendar_event_timestart_updated($event, $lesson); 818 $lesson = $DB->get_record('lesson', ['id' => $lesson->id]); 819 $this->assertEquals($timeopen, $lesson->available); 820 $this->assertEquals($timeclose, $lesson->deadline); 821 } 822 823 /** 824 * A LESSON_EVENT_TYPE_OPEN event should update the available property of the lesson activity. 825 */ 826 public function test_mod_lesson_core_calendar_event_timestart_updated_open_event() { 827 global $CFG, $DB; 828 require_once($CFG->dirroot . "/calendar/lib.php"); 829 830 $this->resetAfterTest(true); 831 $this->setAdminUser(); 832 $generator = $this->getDataGenerator(); 833 $course = $generator->create_course(); 834 $lessongenerator = $generator->get_plugin_generator('mod_lesson'); 835 $timeopen = time(); 836 $timeclose = $timeopen + DAYSECS; 837 $timemodified = 1; 838 $newtimeopen = $timeopen - DAYSECS; 839 $lesson = $lessongenerator->create_instance(['course' => $course->id]); 840 $lesson->available = $timeopen; 841 $lesson->deadline = $timeclose; 842 $lesson->timemodified = $timemodified; 843 $DB->update_record('lesson', $lesson); 844 845 // Create a valid event. 846 $event = new \calendar_event([ 847 'name' => 'Test event', 848 'description' => '', 849 'format' => 1, 850 'courseid' => $course->id, 851 'groupid' => 0, 852 'userid' => 2, 853 'modulename' => 'lesson', 854 'instance' => $lesson->id, 855 'eventtype' => LESSON_EVENT_TYPE_OPEN, 856 'timestart' => $newtimeopen, 857 'timeduration' => 86400, 858 'visible' => 1 859 ]); 860 861 // Trigger and capture the event when adding a contact. 862 $sink = $this->redirectEvents(); 863 mod_lesson_core_calendar_event_timestart_updated($event, $lesson); 864 $triggeredevents = $sink->get_events(); 865 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 866 return is_a($e, 'core\event\course_module_updated'); 867 }); 868 $lesson = $DB->get_record('lesson', ['id' => $lesson->id]); 869 870 // Ensure the available property matches the event timestart. 871 $this->assertEquals($newtimeopen, $lesson->available); 872 873 // Ensure the deadline isn't changed. 874 $this->assertEquals($timeclose, $lesson->deadline); 875 876 // Ensure the timemodified property has been changed. 877 $this->assertNotEquals($timemodified, $lesson->timemodified); 878 879 // Confirm that a module updated event is fired when the module is changed. 880 $this->assertNotEmpty($moduleupdatedevents); 881 } 882 883 /** 884 * A LESSON_EVENT_TYPE_CLOSE event should update the deadline property of the lesson activity. 885 */ 886 public function test_mod_lesson_core_calendar_event_timestart_updated_close_event() { 887 global $CFG, $DB; 888 require_once($CFG->dirroot . "/calendar/lib.php"); 889 $this->resetAfterTest(true); 890 $this->setAdminUser(); 891 $generator = $this->getDataGenerator(); 892 $course = $generator->create_course(); 893 $lessongenerator = $generator->get_plugin_generator('mod_lesson'); 894 $timeopen = time(); 895 $timeclose = $timeopen + DAYSECS; 896 $timemodified = 1; 897 $newtimeclose = $timeclose + DAYSECS; 898 $lesson = $lessongenerator->create_instance(['course' => $course->id]); 899 $lesson->available = $timeopen; 900 $lesson->deadline = $timeclose; 901 $lesson->timemodified = $timemodified; 902 $DB->update_record('lesson', $lesson); 903 // Create a valid event. 904 $event = new \calendar_event([ 905 'name' => 'Test event', 906 'description' => '', 907 'format' => 1, 908 'courseid' => $course->id, 909 'groupid' => 0, 910 'userid' => 2, 911 'modulename' => 'lesson', 912 'instance' => $lesson->id, 913 'eventtype' => LESSON_EVENT_TYPE_CLOSE, 914 'timestart' => $newtimeclose, 915 'timeduration' => 86400, 916 'visible' => 1 917 ]); 918 // Trigger and capture the event when adding a contact. 919 $sink = $this->redirectEvents(); 920 mod_lesson_core_calendar_event_timestart_updated($event, $lesson); 921 $triggeredevents = $sink->get_events(); 922 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 923 return is_a($e, 'core\event\course_module_updated'); 924 }); 925 $lesson = $DB->get_record('lesson', ['id' => $lesson->id]); 926 // Ensure the deadline property matches the event timestart. 927 $this->assertEquals($newtimeclose, $lesson->deadline); 928 // Ensure the available isn't changed. 929 $this->assertEquals($timeopen, $lesson->available); 930 // Ensure the timemodified property has been changed. 931 $this->assertNotEquals($timemodified, $lesson->timemodified); 932 // Confirm that a module updated event is fired when the module is changed. 933 $this->assertNotEmpty($moduleupdatedevents); 934 } 935 936 /** 937 * An unknown event type should not have any limits. 938 */ 939 public function test_mod_lesson_core_calendar_get_valid_event_timestart_range_unknown_event() { 940 global $CFG; 941 require_once($CFG->dirroot . "/calendar/lib.php"); 942 943 $this->resetAfterTest(true); 944 $this->setAdminUser(); 945 $generator = $this->getDataGenerator(); 946 $course = $generator->create_course(); 947 $timeopen = time(); 948 $timeclose = $timeopen + DAYSECS; 949 $lesson = new \stdClass(); 950 $lesson->available = $timeopen; 951 $lesson->deadline = $timeclose; 952 953 // Create a valid event. 954 $event = new \calendar_event([ 955 'name' => 'Test event', 956 'description' => '', 957 'format' => 1, 958 'courseid' => $course->id, 959 'groupid' => 0, 960 'userid' => 2, 961 'modulename' => 'lesson', 962 'instance' => 1, 963 'eventtype' => LESSON_EVENT_TYPE_OPEN . "SOMETHING ELSE", 964 'timestart' => 1, 965 'timeduration' => 86400, 966 'visible' => 1 967 ]); 968 969 list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson); 970 $this->assertNull($min); 971 $this->assertNull($max); 972 } 973 974 /** 975 * The open event should be limited by the lesson's deadline property, if it's set. 976 */ 977 public function test_mod_lesson_core_calendar_get_valid_event_timestart_range_open_event() { 978 global $CFG; 979 require_once($CFG->dirroot . "/calendar/lib.php"); 980 981 $this->resetAfterTest(true); 982 $this->setAdminUser(); 983 $generator = $this->getDataGenerator(); 984 $course = $generator->create_course(); 985 $timeopen = time(); 986 $timeclose = $timeopen + DAYSECS; 987 $lesson = new \stdClass(); 988 $lesson->available = $timeopen; 989 $lesson->deadline = $timeclose; 990 991 // Create a valid event. 992 $event = new \calendar_event([ 993 'name' => 'Test event', 994 'description' => '', 995 'format' => 1, 996 'courseid' => $course->id, 997 'groupid' => 0, 998 'userid' => 2, 999 'modulename' => 'lesson', 1000 'instance' => 1, 1001 'eventtype' => LESSON_EVENT_TYPE_OPEN, 1002 'timestart' => 1, 1003 'timeduration' => 86400, 1004 'visible' => 1 1005 ]); 1006 1007 // The max limit should be bounded by the timeclose value. 1008 list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson); 1009 $this->assertNull($min); 1010 $this->assertEquals($timeclose, $max[0]); 1011 1012 // No timeclose value should result in no upper limit. 1013 $lesson->deadline = 0; 1014 list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson); 1015 $this->assertNull($min); 1016 $this->assertNull($max); 1017 } 1018 1019 /** 1020 * The close event should be limited by the lesson's available property, if it's set. 1021 */ 1022 public function test_mod_lesson_core_calendar_get_valid_event_timestart_range_close_event() { 1023 global $CFG; 1024 require_once($CFG->dirroot . "/calendar/lib.php"); 1025 1026 $this->resetAfterTest(true); 1027 $this->setAdminUser(); 1028 $generator = $this->getDataGenerator(); 1029 $course = $generator->create_course(); 1030 $timeopen = time(); 1031 $timeclose = $timeopen + DAYSECS; 1032 $lesson = new \stdClass(); 1033 $lesson->available = $timeopen; 1034 $lesson->deadline = $timeclose; 1035 1036 // Create a valid event. 1037 $event = new \calendar_event([ 1038 'name' => 'Test event', 1039 'description' => '', 1040 'format' => 1, 1041 'courseid' => $course->id, 1042 'groupid' => 0, 1043 'userid' => 2, 1044 'modulename' => 'lesson', 1045 'instance' => 1, 1046 'eventtype' => LESSON_EVENT_TYPE_CLOSE, 1047 'timestart' => 1, 1048 'timeduration' => 86400, 1049 'visible' => 1 1050 ]); 1051 1052 // The max limit should be bounded by the timeclose value. 1053 list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson); 1054 $this->assertEquals($timeopen, $min[0]); 1055 $this->assertNull($max); 1056 1057 // No deadline value should result in no upper limit. 1058 $lesson->available = 0; 1059 list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson); 1060 $this->assertNull($min); 1061 $this->assertNull($max); 1062 } 1063 1064 /** 1065 * A user who does not have capabilities to add events to the calendar should be able to create an lesson. 1066 */ 1067 public function test_creation_with_no_calendar_capabilities() { 1068 $this->resetAfterTest(); 1069 $course = self::getDataGenerator()->create_course(); 1070 $context = context_course::instance($course->id); 1071 $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); 1072 $roleid = self::getDataGenerator()->create_role(); 1073 self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); 1074 assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); 1075 $generator = self::getDataGenerator()->get_plugin_generator('mod_lesson'); 1076 // Create an instance as a user without the calendar capabilities. 1077 $this->setUser($user); 1078 $time = time(); 1079 $params = array( 1080 'course' => $course->id, 1081 'available' => $time + 200, 1082 'deadline' => $time + 2000, 1083 ); 1084 $generator->create_instance($params); 1085 } 1086 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body