Differences Between: [Versions 310 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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 (some of) mod/assign/lib.php. 19 * 20 * @package mod_assign 21 * @category phpunit 22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 namespace mod_assign; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 require_once($CFG->dirroot . '/mod/assign/lib.php'); 31 require_once($CFG->dirroot . '/mod/assign/locallib.php'); 32 require_once($CFG->dirroot . '/mod/assign/tests/generator.php'); 33 34 use core_calendar\local\api as calendar_local_api; 35 use core_calendar\local\event\container as calendar_event_container; 36 use mod_assign_test_generator; 37 38 /** 39 * Unit tests for (some of) mod/assign/lib.php. 40 * 41 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 */ 44 class lib_test extends \advanced_testcase { 45 46 // Use the generator helper. 47 use mod_assign_test_generator; 48 49 /** 50 * Test that assign_print_recent_activity shows ungraded submitted assignments. 51 */ 52 public function test_print_recent_activity() { 53 $this->resetAfterTest(); 54 $course = $this->getDataGenerator()->create_course(); 55 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 56 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 57 $assign = $this->create_instance($course); 58 $this->submit_for_grading($student, $assign); 59 60 $this->setUser($teacher); 61 $this->expectOutputRegex('/submitted:/'); 62 assign_print_recent_activity($course, true, time() - 3600); 63 } 64 65 /** 66 * Test that assign_print_recent_activity does not display any warnings when a custom fullname has been configured. 67 */ 68 public function test_print_recent_activity_fullname() { 69 $this->resetAfterTest(); 70 $course = $this->getDataGenerator()->create_course(); 71 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 72 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 73 $assign = $this->create_instance($course); 74 $this->submit_for_grading($student, $assign); 75 76 $this->setUser($teacher); 77 $this->expectOutputRegex('/submitted:/'); 78 set_config('fullnamedisplay', 'firstname, lastnamephonetic'); 79 assign_print_recent_activity($course, false, time() - 3600); 80 } 81 82 /** 83 * Test that assign_print_recent_activity shows the blind marking ID. 84 */ 85 public function test_print_recent_activity_fullname_blind_marking() { 86 $this->resetAfterTest(); 87 $course = $this->getDataGenerator()->create_course(); 88 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 89 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 90 91 $assign = $this->create_instance($course, [ 92 'blindmarking' => 1, 93 ]); 94 $this->add_submission($student, $assign); 95 $this->submit_for_grading($student, $assign); 96 97 $this->setUser($teacher); 98 $uniqueid = $assign->get_uniqueid_for_user($student->id); 99 $expectedstr = preg_quote(get_string('participant', 'mod_assign'), '/') . '.*' . $uniqueid; 100 $this->expectOutputRegex("/{$expectedstr}/"); 101 assign_print_recent_activity($course, false, time() - 3600); 102 } 103 104 /** 105 * Test that assign_get_recent_mod_activity fetches the assignment correctly. 106 */ 107 public function test_assign_get_recent_mod_activity() { 108 $this->resetAfterTest(); 109 $course = $this->getDataGenerator()->create_course(); 110 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 111 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 112 $assign = $this->create_instance($course); 113 $this->add_submission($student, $assign); 114 $this->submit_for_grading($student, $assign); 115 116 $index = 1; 117 $activities = [ 118 $index => (object) [ 119 'type' => 'assign', 120 'cmid' => $assign->get_course_module()->id, 121 ], 122 ]; 123 124 $this->setUser($teacher); 125 assign_get_recent_mod_activity($activities, $index, time() - HOURSECS, $course->id, $assign->get_course_module()->id); 126 127 $activity = $activities[1]; 128 $this->assertEquals("assign", $activity->type); 129 $this->assertEquals($student->id, $activity->user->id); 130 } 131 132 /** 133 * Ensure that assign_user_complete displays information about drafts. 134 */ 135 public function test_assign_user_complete() { 136 global $PAGE, $DB; 137 138 $this->resetAfterTest(); 139 $course = $this->getDataGenerator()->create_course(); 140 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 141 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 142 $assign = $this->create_instance($course, ['submissiondrafts' => 1]); 143 $this->add_submission($student, $assign); 144 145 $PAGE->set_url(new \moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id))); 146 147 $submission = $assign->get_user_submission($student->id, true); 148 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT; 149 $DB->update_record('assign_submission', $submission); 150 151 $this->expectOutputRegex('/Draft/'); 152 assign_user_complete($course, $student, $assign->get_course_module(), $assign->get_instance()); 153 } 154 155 /** 156 * Ensure that assign_user_outline fetches updated grades. 157 */ 158 public function test_assign_user_outline() { 159 $this->resetAfterTest(); 160 $course = $this->getDataGenerator()->create_course(); 161 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 162 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 163 $assign = $this->create_instance($course); 164 165 $this->add_submission($student, $assign); 166 $this->submit_for_grading($student, $assign); 167 $this->mark_submission($teacher, $assign, $student, 50.0); 168 169 $this->setUser($teacher); 170 $data = $assign->get_user_grade($student->id, true); 171 $data->grade = '50.5'; 172 $assign->update_grade($data); 173 174 $result = assign_user_outline($course, $student, $assign->get_course_module(), $assign->get_instance()); 175 176 $this->assertMatchesRegularExpression('/50.5/', $result->info); 177 } 178 179 /** 180 * Ensure that assign_get_completion_state reflects the correct status at each point. 181 */ 182 public function test_assign_get_completion_state() { 183 global $DB; 184 185 $this->resetAfterTest(); 186 $course = $this->getDataGenerator()->create_course(); 187 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 188 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 189 $assign = $this->create_instance($course, [ 190 'submissiondrafts' => 0, 191 'completionsubmit' => 1 192 ]); 193 194 $this->setUser($student); 195 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false); 196 $this->assertFalse($result); 197 198 $this->add_submission($student, $assign); 199 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false); 200 $this->assertFalse($result); 201 202 $this->submit_for_grading($student, $assign); 203 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false); 204 $this->assertTrue($result); 205 206 $this->mark_submission($teacher, $assign, $student, 50.0); 207 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false); 208 $this->assertTrue($result); 209 } 210 211 /** 212 * Tests for mod_assign_refresh_events. 213 */ 214 public function test_assign_refresh_events() { 215 global $DB; 216 217 $this->resetAfterTest(); 218 219 $duedate = time(); 220 $newduedate = $duedate + DAYSECS; 221 222 $this->setAdminUser(); 223 224 $course = $this->getDataGenerator()->create_course(); 225 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 226 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 227 $assign = $this->create_instance($course, [ 228 'duedate' => $duedate, 229 ]); 230 231 $instance = $assign->get_instance(); 232 $eventparams = [ 233 'modulename' => 'assign', 234 'instance' => $instance->id, 235 'eventtype' => ASSIGN_EVENT_TYPE_DUE, 236 'groupid' => 0 237 ]; 238 239 // Make sure the calendar event for assignment 1 matches the initial due date. 240 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST); 241 $this->assertEquals($eventtime, $duedate); 242 243 // Manually update assignment 1's due date. 244 $DB->update_record('assign', (object) [ 245 'id' => $instance->id, 246 'duedate' => $newduedate, 247 'course' => $course->id 248 ]); 249 250 // Then refresh the assignment events of assignment 1's course. 251 $this->assertTrue(assign_refresh_events($course->id)); 252 253 // Confirm that the assignment 1's due date event now has the new due date after refresh. 254 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST); 255 $this->assertEquals($eventtime, $newduedate); 256 257 // Create a second course and assignment. 258 $othercourse = $this->getDataGenerator()->create_course();; 259 $otherassign = $this->create_instance($othercourse, [ 260 'duedate' => $duedate, 261 ]); 262 $otherinstance = $otherassign->get_instance(); 263 264 // Manually update assignment 1 and 2's due dates. 265 $newduedate += DAYSECS; 266 $DB->update_record('assign', (object)[ 267 'id' => $instance->id, 268 'duedate' => $newduedate, 269 'course' => $course->id 270 ]); 271 $DB->update_record('assign', (object)[ 272 'id' => $otherinstance->id, 273 'duedate' => $newduedate, 274 'course' => $othercourse->id 275 ]); 276 277 // Refresh events of all courses and check the calendar events matches the new date. 278 $this->assertTrue(assign_refresh_events()); 279 280 // Check the due date calendar event for assignment 1. 281 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST); 282 $this->assertEquals($eventtime, $newduedate); 283 284 // Check the due date calendar event for assignment 2. 285 $eventparams['instance'] = $otherinstance->id; 286 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST); 287 $this->assertEquals($eventtime, $newduedate); 288 289 // In case the course ID is passed as a numeric string. 290 $this->assertTrue(assign_refresh_events('' . $course->id)); 291 292 // Non-existing course ID. 293 $this->assertFalse(assign_refresh_events(-1)); 294 295 // Invalid course ID. 296 $this->assertFalse(assign_refresh_events('aaa')); 297 } 298 299 public function test_assign_core_calendar_is_event_visible_duedate_event_as_teacher() { 300 $this->resetAfterTest(); 301 $course = $this->getDataGenerator()->create_course(); 302 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 303 $assign = $this->create_instance($course); 304 305 $this->setAdminUser(); 306 307 // Create a calendar event. 308 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 309 310 // The teacher should see the due date event. 311 $this->setUser($teacher); 312 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event)); 313 } 314 315 public function test_assign_core_calendar_is_event_visible_duedate_event_for_teacher() { 316 $this->resetAfterTest(); 317 $course = $this->getDataGenerator()->create_course(); 318 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 319 $assign = $this->create_instance($course); 320 321 $this->setAdminUser(); 322 323 // Create a calendar event. 324 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 325 326 // Now, log out. 327 $this->setUser(); 328 329 // The teacher should see the due date event. 330 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id)); 331 } 332 333 public function test_assign_core_calendar_is_event_visible_duedate_event_as_student() { 334 $this->resetAfterTest(); 335 $course = $this->getDataGenerator()->create_course(); 336 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 337 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 338 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); 339 340 $this->setAdminUser(); 341 342 // Create a calendar event. 343 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 344 345 // The student should care about the due date event. 346 $this->setUser($student); 347 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event)); 348 } 349 350 public function test_assign_core_calendar_is_event_visible_duedate_event_for_student() { 351 $this->resetAfterTest(); 352 $course = $this->getDataGenerator()->create_course(); 353 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 354 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 355 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); 356 357 $this->setAdminUser(); 358 359 // Create a calendar event. 360 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 361 362 // Now, log out. 363 $this->setUser(); 364 365 // The student should care about the due date event. 366 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $student->id)); 367 } 368 369 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_teacher() { 370 $this->resetAfterTest(); 371 $course = $this->getDataGenerator()->create_course(); 372 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 373 $assign = $this->create_instance($course); 374 375 // Create a calendar event. 376 $this->setAdminUser(); 377 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 378 379 // The teacher should see the due date event. 380 $this->setUser($teacher); 381 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event)); 382 } 383 384 385 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_teacher() { 386 $this->resetAfterTest(); 387 $course = $this->getDataGenerator()->create_course(); 388 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 389 $assign = $this->create_instance($course); 390 391 // Create a calendar event. 392 $this->setAdminUser(); 393 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 394 395 // Now, log out. 396 $this->setUser(); 397 398 // The teacher should see the due date event. 399 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id)); 400 } 401 402 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_student() { 403 $this->resetAfterTest(); 404 $course = $this->getDataGenerator()->create_course(); 405 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 406 $assign = $this->create_instance($course); 407 408 // Create a calendar event. 409 $this->setAdminUser(); 410 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 411 412 // The student should not see the due date event. 413 $this->setUser($student); 414 $this->assertFalse(mod_assign_core_calendar_is_event_visible($event)); 415 } 416 417 418 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_student() { 419 $this->resetAfterTest(); 420 $course = $this->getDataGenerator()->create_course(); 421 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 422 $assign = $this->create_instance($course); 423 424 // Create a calendar event. 425 $this->setAdminUser(); 426 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 427 428 // Now, log out. 429 $this->setUser(); 430 431 // The student should not see the due date event. 432 $this->assertFalse(mod_assign_core_calendar_is_event_visible($event, $student->id)); 433 } 434 435 public function test_assign_core_calendar_provide_event_action_duedate_as_teacher() { 436 $this->resetAfterTest(); 437 $course = $this->getDataGenerator()->create_course(); 438 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 439 $assign = $this->create_instance($course); 440 441 // Create a calendar event. 442 $this->setAdminUser(); 443 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 444 445 // The teacher should see the event. 446 $this->setUser($teacher); 447 $factory = new \core_calendar\action_factory(); 448 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); 449 450 // The teacher should not have an action for a due date event. 451 $this->assertNull($actionevent); 452 } 453 454 public function test_assign_core_calendar_provide_event_action_duedate_for_teacher() { 455 $this->resetAfterTest(); 456 $course = $this->getDataGenerator()->create_course(); 457 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 458 $assign = $this->create_instance($course); 459 460 // Create a calendar event. 461 $this->setAdminUser(); 462 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 463 464 // Now, log out. 465 $this->setUser(); 466 467 // Decorate action event for a teacher. 468 $factory = new \core_calendar\action_factory(); 469 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id); 470 471 // The teacher should not have an action for a due date event. 472 $this->assertNull($actionevent); 473 } 474 475 public function test_assign_core_calendar_provide_event_action_duedate_as_student() { 476 $this->resetAfterTest(); 477 $course = $this->getDataGenerator()->create_course(); 478 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 479 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); 480 481 // Create a calendar event. 482 $this->setAdminUser(); 483 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 484 485 // The student should see the event. 486 $this->setUser($student); 487 $factory = new \core_calendar\action_factory(); 488 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); 489 490 // Confirm the event was decorated. 491 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 492 $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name()); 493 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 494 $this->assertEquals(1, $actionevent->get_item_count()); 495 $this->assertTrue($actionevent->is_actionable()); 496 } 497 498 public function test_assign_core_calendar_provide_event_action_duedate_for_student() { 499 $this->resetAfterTest(); 500 $course = $this->getDataGenerator()->create_course(); 501 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 502 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); 503 504 // Create a calendar event. 505 $this->setAdminUser(); 506 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 507 508 // Now, log out. 509 $this->setUser(); 510 511 // Decorate action event for a student. 512 $factory = new \core_calendar\action_factory(); 513 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); 514 515 // Confirm the event was decorated. 516 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 517 $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name()); 518 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 519 $this->assertEquals(1, $actionevent->get_item_count()); 520 $this->assertTrue($actionevent->is_actionable()); 521 } 522 523 public function test_assign_core_calendar_provide_event_action_gradingduedate_as_teacher() { 524 $this->resetAfterTest(); 525 $course = $this->getDataGenerator()->create_course(); 526 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 527 $assign = $this->create_instance($course); 528 529 // Create a calendar event. 530 $this->setAdminUser(); 531 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 532 533 $this->setUser($teacher); 534 $factory = new \core_calendar\action_factory(); 535 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); 536 537 // Confirm the event was decorated. 538 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 539 $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); 540 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 541 $this->assertEquals(0, $actionevent->get_item_count()); 542 $this->assertTrue($actionevent->is_actionable()); 543 } 544 545 public function test_assign_core_calendar_provide_event_action_gradingduedate_for_teacher() { 546 $this->resetAfterTest(); 547 $course = $this->getDataGenerator()->create_course(); 548 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 549 $assign = $this->create_instance($course); 550 551 // Create a calendar event. 552 $this->setAdminUser(); 553 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 554 555 // Now, log out. 556 $this->setUser(); 557 558 // Decorate action event for a teacher. 559 $factory = new \core_calendar\action_factory(); 560 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id); 561 562 // Confirm the event was decorated. 563 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 564 $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); 565 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 566 $this->assertEquals(0, $actionevent->get_item_count()); 567 $this->assertTrue($actionevent->is_actionable()); 568 } 569 570 public function test_assign_core_calendar_provide_event_action_gradingduedate_as_student() { 571 $this->resetAfterTest(); 572 $course = $this->getDataGenerator()->create_course(); 573 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 574 $assign = $this->create_instance($course); 575 576 // Create a calendar event. 577 $this->setAdminUser(); 578 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 579 580 $this->setUser($student); 581 $factory = new \core_calendar\action_factory(); 582 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); 583 584 // Confirm the event was decorated. 585 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 586 $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); 587 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 588 $this->assertEquals(0, $actionevent->get_item_count()); 589 $this->assertFalse($actionevent->is_actionable()); 590 } 591 592 public function test_assign_core_calendar_provide_event_action_gradingduedate_for_student() { 593 $this->resetAfterTest(); 594 $course = $this->getDataGenerator()->create_course(); 595 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 596 $assign = $this->create_instance($course); 597 598 // Create a calendar event. 599 $this->setAdminUser(); 600 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE); 601 602 // Now, log out. 603 $this->setUser(); 604 605 // Decorate action event for a student. 606 $factory = new \core_calendar\action_factory(); 607 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); 608 609 // Confirm the event was decorated. 610 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 611 $this->assertEquals(get_string('gradenoun'), $actionevent->get_name()); 612 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 613 $this->assertEquals(0, $actionevent->get_item_count()); 614 $this->assertFalse($actionevent->is_actionable()); 615 } 616 617 public function test_assign_core_calendar_provide_event_action_duedate_as_student_submitted() { 618 $this->resetAfterTest(); 619 $course = $this->getDataGenerator()->create_course(); 620 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 621 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 622 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); 623 624 $this->setAdminUser(); 625 626 // Create a calendar event. 627 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 628 629 // Create an action factory. 630 $factory = new \core_calendar\action_factory(); 631 632 // Submit as the student. 633 $this->add_submission($student, $assign); 634 $this->submit_for_grading($student, $assign); 635 636 // Confirm there was no event to action. 637 $factory = new \core_calendar\action_factory(); 638 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); 639 $this->assertNull($actionevent); 640 } 641 642 public function test_assign_core_calendar_provide_event_action_duedate_for_student_submitted() { 643 $this->resetAfterTest(); 644 $course = $this->getDataGenerator()->create_course(); 645 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 646 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 647 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]); 648 649 $this->setAdminUser(); 650 651 // Create a calendar event. 652 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE); 653 654 // Create an action factory. 655 $factory = new \core_calendar\action_factory(); 656 657 // Submit as the student. 658 $this->add_submission($student, $assign); 659 $this->submit_for_grading($student, $assign); 660 661 // Now, log out. 662 $this->setUser(); 663 664 // Confirm there was no event to action. 665 $factory = new \core_calendar\action_factory(); 666 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); 667 $this->assertNull($actionevent); 668 } 669 670 public function test_assign_core_calendar_provide_event_action_already_completed() { 671 $this->resetAfterTest(); 672 set_config('enablecompletion', 1); 673 $this->setAdminUser(); 674 675 // Create the activity. 676 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 677 $assign = $this->create_instance($course, 678 ['completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS]); 679 680 // Get some additional data. 681 $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id); 682 683 // Create a calendar event. 684 $event = $this->create_action_event($course, $assign, 685 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 686 687 // Mark the activity as completed. 688 $completion = new \completion_info($course); 689 $completion->set_module_viewed($cm); 690 691 // Create an action factory. 692 $factory = new \core_calendar\action_factory(); 693 694 // Decorate action event. 695 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory); 696 697 // Ensure result was null. 698 $this->assertNull($actionevent); 699 } 700 701 public function test_assign_core_calendar_provide_event_action_already_completed_for_user() { 702 $this->resetAfterTest(); 703 set_config('enablecompletion', 1); 704 $this->setAdminUser(); 705 706 // Create the activity. 707 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 708 $assign = $this->create_instance($course, 709 ['completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS]); 710 711 // Enrol a student in the course. 712 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 713 714 // Get some additional data. 715 $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id); 716 717 // Create a calendar event. 718 $event = $this->create_action_event($course, $assign, 719 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 720 721 // Mark the activity as completed for the student. 722 $completion = new \completion_info($course); 723 $completion->set_module_viewed($cm, $student->id); 724 725 // Create an action factory. 726 $factory = new \core_calendar\action_factory(); 727 728 // Decorate action event for the student. 729 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id); 730 731 // Ensure result was null. 732 $this->assertNull($actionevent); 733 } 734 735 /** 736 * Creates an action event. 737 * 738 * @param \stdClass $course The course the assignment is in 739 * @param assign $assign The assignment to create an event for 740 * @param string $eventtype The event type. eg. ASSIGN_EVENT_TYPE_DUE. 741 * @return bool|calendar_event 742 */ 743 private function create_action_event($course, $assign, $eventtype) { 744 $event = new \stdClass(); 745 $event->name = 'Calendar event'; 746 $event->modulename = 'assign'; 747 $event->courseid = $course->id; 748 $event->instance = $assign->get_instance()->id; 749 $event->type = CALENDAR_EVENT_TYPE_ACTION; 750 $event->eventtype = $eventtype; 751 $event->timestart = time(); 752 753 return \calendar_event::create($event); 754 } 755 756 /** 757 * Test the callback responsible for returning the completion rule descriptions. 758 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 759 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 760 */ 761 public function test_mod_assign_completion_get_active_rule_descriptions() { 762 $this->resetAfterTest(); 763 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); 764 765 $this->setAdminUser(); 766 767 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 768 $cm1 = $this->create_instance($course, ['completion' => '2', 'completionsubmit' => '1'])->get_course_module(); 769 $cm2 = $this->create_instance($course, ['completion' => '2', 'completionsubmit' => '0'])->get_course_module(); 770 771 // Data for the stdClass input type. 772 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 773 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 774 $moddefaults = (object) [ 775 'customdata' => [ 776 'customcompletionrules' => [ 777 'completionsubmit' => '1', 778 ], 779 ], 780 'completion' => 2, 781 ]; 782 783 $activeruledescriptions = [get_string('completionsubmit', 'assign')]; 784 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 785 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($cm2), []); 786 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 787 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions(new \stdClass()), []); 788 } 789 790 /** 791 * Test that if some grades are not set, they are left alone and not rescaled 792 */ 793 public function test_assign_rescale_activity_grades_some_unset() { 794 $this->resetAfterTest(); 795 $course = $this->getDataGenerator()->create_course(); 796 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 797 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 798 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student'); 799 800 // As a teacher. 801 $this->setUser($teacher); 802 $assign = $this->create_instance($course); 803 804 // Grade the student. 805 $data = ['grade' => 50]; 806 $assign->testable_apply_grade_to_user((object)$data, $student->id, 0); 807 808 // Try getting another students grade. This will give a grade of ASSIGN_GRADE_NOT_SET (-1). 809 $assign->get_user_grade($otherstudent->id, true); 810 811 // Rescale. 812 assign_rescale_activity_grades($course, $assign->get_course_module(), 0, 100, 0, 10); 813 814 // Get the grades for both students. 815 $studentgrade = $assign->get_user_grade($student->id, true); 816 $otherstudentgrade = $assign->get_user_grade($otherstudent->id, true); 817 818 // Make sure the real grade is scaled, but the ASSIGN_GRADE_NOT_SET stays the same. 819 $this->assertEquals($studentgrade->grade, 5); 820 $this->assertEquals($otherstudentgrade->grade, ASSIGN_GRADE_NOT_SET); 821 } 822 823 /** 824 * Return false when there are not overrides for this assign instance. 825 */ 826 public function test_assign_is_override_calendar_event_no_override() { 827 global $CFG, $DB; 828 require_once($CFG->dirroot . '/calendar/lib.php'); 829 830 $this->resetAfterTest(); 831 $course = $this->getDataGenerator()->create_course(); 832 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 833 834 $this->setAdminUser(); 835 836 $duedate = time(); 837 $assign = $this->create_instance($course, ['duedate' => $duedate]); 838 839 $instance = $assign->get_instance(); 840 $event = new \calendar_event((object)[ 841 'modulename' => 'assign', 842 'instance' => $instance->id, 843 'userid' => $student->id, 844 ]); 845 846 $this->assertFalse($assign->is_override_calendar_event($event)); 847 } 848 849 /** 850 * Return false if the given event isn't an assign module event. 851 */ 852 public function test_assign_is_override_calendar_event_no_nodule_event() { 853 global $CFG, $DB; 854 require_once($CFG->dirroot . '/calendar/lib.php'); 855 856 $this->resetAfterTest(); 857 $course = $this->getDataGenerator()->create_course(); 858 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 859 860 $this->setAdminUser(); 861 862 $userid = $student->id; 863 $duedate = time(); 864 $assign = $this->create_instance($course, ['duedate' => $duedate]); 865 866 $instance = $assign->get_instance(); 867 $event = new \calendar_event((object)[ 868 'userid' => $userid 869 ]); 870 871 $this->assertFalse($assign->is_override_calendar_event($event)); 872 } 873 874 /** 875 * Return false if there is overrides for this use but they belong to another assign 876 * instance. 877 */ 878 public function test_assign_is_override_calendar_event_different_assign_instance() { 879 global $CFG, $DB; 880 require_once($CFG->dirroot . '/calendar/lib.php'); 881 882 $this->resetAfterTest(); 883 $course = $this->getDataGenerator()->create_course(); 884 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 885 886 $this->setAdminUser(); 887 888 $duedate = time(); 889 $assign = $this->create_instance($course, ['duedate' => $duedate]); 890 $instance = $assign->get_instance(); 891 892 $otherassign = $this->create_instance($course, ['duedate' => $duedate]); 893 $otherinstance = $otherassign->get_instance(); 894 895 $event = new \calendar_event((object) [ 896 'modulename' => 'assign', 897 'instance' => $instance->id, 898 'userid' => $student->id, 899 ]); 900 901 $DB->insert_record('assign_overrides', (object) [ 902 'assignid' => $otherinstance->id, 903 'userid' => $student->id, 904 ]); 905 906 $this->assertFalse($assign->is_override_calendar_event($event)); 907 } 908 909 /** 910 * Return true if there is a user override for this event and assign instance. 911 */ 912 public function test_assign_is_override_calendar_event_user_override() { 913 global $CFG, $DB; 914 require_once($CFG->dirroot . '/calendar/lib.php'); 915 916 $this->resetAfterTest(); 917 $course = $this->getDataGenerator()->create_course(); 918 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 919 920 $this->setAdminUser(); 921 922 $duedate = time(); 923 $assign = $this->create_instance($course, ['duedate' => $duedate]); 924 925 $instance = $assign->get_instance(); 926 $event = new \calendar_event((object) [ 927 'modulename' => 'assign', 928 'instance' => $instance->id, 929 'userid' => $student->id, 930 ]); 931 932 933 $DB->insert_record('assign_overrides', (object) [ 934 'assignid' => $instance->id, 935 'userid' => $student->id, 936 ]); 937 938 $this->assertTrue($assign->is_override_calendar_event($event)); 939 } 940 941 /** 942 * Return true if there is a group override for the event and assign instance. 943 */ 944 public function test_assign_is_override_calendar_event_group_override() { 945 global $CFG, $DB; 946 require_once($CFG->dirroot . '/calendar/lib.php'); 947 948 $this->resetAfterTest(); 949 $course = $this->getDataGenerator()->create_course(); 950 951 $this->setAdminUser(); 952 953 $duedate = time(); 954 $assign = $this->create_instance($course, ['duedate' => $duedate]); 955 $instance = $assign->get_instance(); 956 $group = $this->getDataGenerator()->create_group(array('courseid' => $instance->course)); 957 958 $event = new \calendar_event((object) [ 959 'modulename' => 'assign', 960 'instance' => $instance->id, 961 'groupid' => $group->id, 962 ]); 963 964 $DB->insert_record('assign_overrides', (object) [ 965 'assignid' => $instance->id, 966 'groupid' => $group->id, 967 ]); 968 969 $this->assertTrue($assign->is_override_calendar_event($event)); 970 } 971 972 /** 973 * Unknown event types should not have any limit restrictions returned. 974 */ 975 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_unkown_event_type() { 976 global $CFG; 977 require_once($CFG->dirroot . '/calendar/lib.php'); 978 979 $this->resetAfterTest(); 980 $course = $this->getDataGenerator()->create_course(); 981 982 $this->setAdminUser(); 983 984 $duedate = time(); 985 $assign = $this->create_instance($course, ['duedate' => $duedate]); 986 $instance = $assign->get_instance(); 987 988 $event = new \calendar_event((object) [ 989 'courseid' => $instance->course, 990 'modulename' => 'assign', 991 'instance' => $instance->id, 992 'eventtype' => 'SOME RANDOM EVENT' 993 ]); 994 995 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); 996 $this->assertNull($min); 997 $this->assertNull($max); 998 } 999 1000 /** 1001 * Override events should not have any limit restrictions returned. 1002 */ 1003 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_override_event() { 1004 global $CFG, $DB; 1005 require_once($CFG->dirroot . '/calendar/lib.php'); 1006 1007 $this->resetAfterTest(); 1008 $course = $this->getDataGenerator()->create_course(); 1009 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 1010 1011 $this->setAdminUser(); 1012 1013 $duedate = time(); 1014 $assign = $this->create_instance($course, ['duedate' => $duedate]); 1015 $instance = $assign->get_instance(); 1016 1017 $event = new \calendar_event((object) [ 1018 'courseid' => $instance->course, 1019 'modulename' => 'assign', 1020 'instance' => $instance->id, 1021 'userid' => $student->id, 1022 'eventtype' => ASSIGN_EVENT_TYPE_DUE 1023 ]); 1024 1025 $record = (object) [ 1026 'assignid' => $instance->id, 1027 'userid' => $student->id, 1028 ]; 1029 1030 $DB->insert_record('assign_overrides', $record); 1031 1032 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); 1033 $this->assertFalse($min); 1034 $this->assertFalse($max); 1035 } 1036 1037 /** 1038 * Assignments configured without a submissions from and cutoff date should not have 1039 * any limits applied. 1040 */ 1041 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_due_no_limit() { 1042 global $CFG, $DB; 1043 require_once($CFG->dirroot . '/calendar/lib.php'); 1044 1045 $this->resetAfterTest(); 1046 $course = $this->getDataGenerator()->create_course(); 1047 1048 $this->setAdminUser(); 1049 1050 $duedate = time(); 1051 $assign = $this->create_instance($course, [ 1052 'duedate' => $duedate, 1053 'allowsubmissionsfromdate' => 0, 1054 'cutoffdate' => 0, 1055 ]); 1056 $instance = $assign->get_instance(); 1057 1058 $event = new \calendar_event((object) [ 1059 'courseid' => $instance->course, 1060 'modulename' => 'assign', 1061 'instance' => $instance->id, 1062 'eventtype' => ASSIGN_EVENT_TYPE_DUE 1063 ]); 1064 1065 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); 1066 $this->assertNull($min); 1067 $this->assertNull($max); 1068 } 1069 1070 /** 1071 * Assignments should be bottom and top bound by the submissions from date and cutoff date 1072 * respectively. 1073 */ 1074 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_due_with_limits() { 1075 global $CFG, $DB; 1076 require_once($CFG->dirroot . '/calendar/lib.php'); 1077 1078 $this->resetAfterTest(); 1079 $course = $this->getDataGenerator()->create_course(); 1080 1081 $this->setAdminUser(); 1082 1083 $duedate = time(); 1084 $submissionsfromdate = $duedate - DAYSECS; 1085 $cutoffdate = $duedate + DAYSECS; 1086 $assign = $this->create_instance($course, [ 1087 'duedate' => $duedate, 1088 'allowsubmissionsfromdate' => $submissionsfromdate, 1089 'cutoffdate' => $cutoffdate, 1090 ]); 1091 $instance = $assign->get_instance(); 1092 1093 $event = new \calendar_event((object) [ 1094 'courseid' => $instance->course, 1095 'modulename' => 'assign', 1096 'instance' => $instance->id, 1097 'eventtype' => ASSIGN_EVENT_TYPE_DUE 1098 ]); 1099 1100 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); 1101 $this->assertEquals($submissionsfromdate, $min[0]); 1102 $this->assertNotEmpty($min[1]); 1103 $this->assertEquals($cutoffdate, $max[0]); 1104 $this->assertNotEmpty($max[1]); 1105 } 1106 1107 /** 1108 * Assignment grading due date should not have any limits of no due date and cutoff date is set. 1109 */ 1110 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_gradingdue_no_limit() { 1111 global $CFG, $DB; 1112 require_once($CFG->dirroot . '/calendar/lib.php'); 1113 1114 $this->resetAfterTest(); 1115 $course = $this->getDataGenerator()->create_course(); 1116 1117 $this->setAdminUser(); 1118 1119 $assign = $this->create_instance($course, [ 1120 'duedate' => 0, 1121 'allowsubmissionsfromdate' => 0, 1122 'cutoffdate' => 0, 1123 ]); 1124 $instance = $assign->get_instance(); 1125 1126 $event = new \calendar_event((object) [ 1127 'courseid' => $instance->course, 1128 'modulename' => 'assign', 1129 'instance' => $instance->id, 1130 'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE 1131 ]); 1132 1133 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); 1134 $this->assertNull($min); 1135 $this->assertNull($max); 1136 } 1137 1138 /** 1139 * Assignment grading due event is minimum bound by the due date, if it is set. 1140 */ 1141 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_gradingdue_with_due_date() { 1142 global $CFG, $DB; 1143 require_once($CFG->dirroot . '/calendar/lib.php'); 1144 1145 $this->resetAfterTest(); 1146 $course = $this->getDataGenerator()->create_course(); 1147 1148 $this->setAdminUser(); 1149 1150 $duedate = time(); 1151 $assign = $this->create_instance($course, ['duedate' => $duedate]); 1152 $instance = $assign->get_instance(); 1153 1154 $event = new \calendar_event((object) [ 1155 'courseid' => $instance->course, 1156 'modulename' => 'assign', 1157 'instance' => $instance->id, 1158 'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE 1159 ]); 1160 1161 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance); 1162 $this->assertEquals($duedate, $min[0]); 1163 $this->assertNotEmpty($min[1]); 1164 $this->assertNull($max); 1165 } 1166 1167 /** 1168 * Non due date events should not update the assignment due date. 1169 */ 1170 public function test_mod_assign_core_calendar_event_timestart_updated_non_due_event() { 1171 global $CFG, $DB; 1172 require_once($CFG->dirroot . '/calendar/lib.php'); 1173 1174 $this->resetAfterTest(); 1175 $course = $this->getDataGenerator()->create_course(); 1176 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 1177 1178 $this->setAdminUser(); 1179 1180 $duedate = time(); 1181 $submissionsfromdate = $duedate - DAYSECS; 1182 $cutoffdate = $duedate + DAYSECS; 1183 $assign = $this->create_instance($course, [ 1184 'duedate' => $duedate, 1185 'allowsubmissionsfromdate' => $submissionsfromdate, 1186 'cutoffdate' => $cutoffdate, 1187 ]); 1188 $instance = $assign->get_instance(); 1189 1190 $event = new \calendar_event((object) [ 1191 'courseid' => $instance->course, 1192 'modulename' => 'assign', 1193 'instance' => $instance->id, 1194 'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE, 1195 'timestart' => $duedate + 1 1196 ]); 1197 1198 mod_assign_core_calendar_event_timestart_updated($event, $instance); 1199 1200 $newinstance = $DB->get_record('assign', ['id' => $instance->id]); 1201 $this->assertEquals($duedate, $newinstance->duedate); 1202 } 1203 1204 /** 1205 * Due date override events should not change the assignment due date. 1206 */ 1207 public function test_mod_assign_core_calendar_event_timestart_updated_due_event_override() { 1208 global $CFG, $DB; 1209 require_once($CFG->dirroot . '/calendar/lib.php'); 1210 1211 $this->resetAfterTest(); 1212 $course = $this->getDataGenerator()->create_course(); 1213 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 1214 1215 $this->setAdminUser(); 1216 1217 $duedate = time(); 1218 $submissionsfromdate = $duedate - DAYSECS; 1219 $cutoffdate = $duedate + DAYSECS; 1220 $assign = $this->create_instance($course, [ 1221 'duedate' => $duedate, 1222 'allowsubmissionsfromdate' => $submissionsfromdate, 1223 'cutoffdate' => $cutoffdate, 1224 ]); 1225 $instance = $assign->get_instance(); 1226 1227 $event = new \calendar_event((object) [ 1228 'courseid' => $instance->course, 1229 'modulename' => 'assign', 1230 'instance' => $instance->id, 1231 'userid' => $student->id, 1232 'eventtype' => ASSIGN_EVENT_TYPE_DUE, 1233 'timestart' => $duedate + 1 1234 ]); 1235 1236 $record = (object) [ 1237 'assignid' => $instance->id, 1238 'userid' => $student->id, 1239 'duedate' => $duedate + 1, 1240 ]; 1241 1242 $DB->insert_record('assign_overrides', $record); 1243 1244 mod_assign_core_calendar_event_timestart_updated($event, $instance); 1245 1246 $newinstance = $DB->get_record('assign', ['id' => $instance->id]); 1247 $this->assertEquals($duedate, $newinstance->duedate); 1248 } 1249 1250 /** 1251 * Due date events should update the assignment due date. 1252 */ 1253 public function test_mod_assign_core_calendar_event_timestart_updated_due_event() { 1254 global $CFG, $DB; 1255 require_once($CFG->dirroot . '/calendar/lib.php'); 1256 1257 $this->resetAfterTest(); 1258 $course = $this->getDataGenerator()->create_course(); 1259 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 1260 1261 $this->setAdminUser(); 1262 1263 $duedate = time(); 1264 $newduedate = $duedate + 1; 1265 $submissionsfromdate = $duedate - DAYSECS; 1266 $cutoffdate = $duedate + DAYSECS; 1267 $assign = $this->create_instance($course, [ 1268 'duedate' => $duedate, 1269 'allowsubmissionsfromdate' => $submissionsfromdate, 1270 'cutoffdate' => $cutoffdate, 1271 ]); 1272 $instance = $assign->get_instance(); 1273 1274 $event = new \calendar_event((object) [ 1275 'courseid' => $instance->course, 1276 'modulename' => 'assign', 1277 'instance' => $instance->id, 1278 'eventtype' => ASSIGN_EVENT_TYPE_DUE, 1279 'timestart' => $newduedate 1280 ]); 1281 1282 mod_assign_core_calendar_event_timestart_updated($event, $instance); 1283 1284 $newinstance = $DB->get_record('assign', ['id' => $instance->id]); 1285 $this->assertEquals($newduedate, $newinstance->duedate); 1286 } 1287 1288 /** 1289 * If a student somehow finds a way to update the due date calendar event 1290 * then the callback should not be executed to update the assignment due 1291 * date as well otherwise that would be a security issue. 1292 */ 1293 public function test_student_role_cant_update_due_event() { 1294 global $CFG, $DB; 1295 require_once($CFG->dirroot . '/calendar/lib.php'); 1296 1297 $this->resetAfterTest(); 1298 $course = $this->getDataGenerator()->create_course(); 1299 $context = \context_course::instance($course->id); 1300 1301 $roleid = $this->getDataGenerator()->create_role(); 1302 $role = $DB->get_record('role', ['id' => $roleid]); 1303 $user = $this->getDataGenerator()->create_and_enrol($course, $role->shortname); 1304 1305 $this->setAdminUser(); 1306 1307 $mapper = calendar_event_container::get_event_mapper(); 1308 $now = time(); 1309 $duedate = (new \DateTime())->setTimestamp($now); 1310 $newduedate = (new \DateTime())->setTimestamp($now)->modify('+1 day'); 1311 $assign = $this->create_instance($course, [ 1312 'course' => $course->id, 1313 'duedate' => $duedate->getTimestamp(), 1314 ]); 1315 $instance = $assign->get_instance(); 1316 1317 $record = $DB->get_record('event', [ 1318 'courseid' => $course->id, 1319 'modulename' => 'assign', 1320 'instance' => $instance->id, 1321 'eventtype' => ASSIGN_EVENT_TYPE_DUE 1322 ]); 1323 1324 $event = new \calendar_event($record); 1325 1326 assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); 1327 assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleid, $context, true); 1328 1329 $this->setUser($user); 1330 1331 calendar_local_api::update_event_start_day( 1332 $mapper->from_legacy_event_to_event($event), 1333 $newduedate 1334 ); 1335 1336 $newinstance = $DB->get_record('assign', ['id' => $instance->id]); 1337 $newevent = \calendar_event::load($event->id); 1338 // The due date shouldn't have changed even though we updated the calendar 1339 // event. 1340 $this->assertEquals($duedate->getTimestamp(), $newinstance->duedate); 1341 $this->assertEquals($newduedate->getTimestamp(), $newevent->timestart); 1342 } 1343 1344 /** 1345 * A teacher with the capability to modify an assignment module should be 1346 * able to update the assignment due date by changing the due date calendar 1347 * event. 1348 */ 1349 public function test_teacher_role_can_update_due_event() { 1350 global $CFG, $DB; 1351 require_once($CFG->dirroot . '/calendar/lib.php'); 1352 1353 $this->resetAfterTest(); 1354 $course = $this->getDataGenerator()->create_course(); 1355 $context = \context_course::instance($course->id); 1356 $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 1357 $roleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']); 1358 1359 $this->setAdminUser(); 1360 1361 $mapper = calendar_event_container::get_event_mapper(); 1362 $now = time(); 1363 $duedate = (new \DateTime())->setTimestamp($now); 1364 $newduedate = (new \DateTime())->setTimestamp($now)->modify('+1 day'); 1365 $assign = $this->create_instance($course, [ 1366 'course' => $course->id, 1367 'duedate' => $duedate->getTimestamp(), 1368 ]); 1369 $instance = $assign->get_instance(); 1370 1371 $record = $DB->get_record('event', [ 1372 'courseid' => $course->id, 1373 'modulename' => 'assign', 1374 'instance' => $instance->id, 1375 'eventtype' => ASSIGN_EVENT_TYPE_DUE 1376 ]); 1377 1378 $event = new \calendar_event($record); 1379 1380 assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); 1381 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleid, $context, true); 1382 1383 $this->setUser($user); 1384 // Trigger and capture the event when adding a contact. 1385 $sink = $this->redirectEvents(); 1386 1387 calendar_local_api::update_event_start_day( 1388 $mapper->from_legacy_event_to_event($event), 1389 $newduedate 1390 ); 1391 1392 $triggeredevents = $sink->get_events(); 1393 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 1394 return is_a($e, 'core\event\course_module_updated'); 1395 }); 1396 1397 $newinstance = $DB->get_record('assign', ['id' => $instance->id]); 1398 $newevent = \calendar_event::load($event->id); 1399 // The due date shouldn't have changed even though we updated the calendar 1400 // event. 1401 $this->assertEquals($newduedate->getTimestamp(), $newinstance->duedate); 1402 $this->assertEquals($newduedate->getTimestamp(), $newevent->timestart); 1403 // Confirm that a module updated event is fired when the module 1404 // is changed. 1405 $this->assertNotEmpty($moduleupdatedevents); 1406 } 1407 1408 /** 1409 * A user who does not have capabilities to add events to the calendar should be able to create an assignment. 1410 */ 1411 public function test_creation_with_no_calendar_capabilities() { 1412 $this->resetAfterTest(); 1413 $course = self::getDataGenerator()->create_course(); 1414 $context = \context_course::instance($course->id); 1415 $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); 1416 $roleid = self::getDataGenerator()->create_role(); 1417 self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); 1418 assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); 1419 $generator = self::getDataGenerator()->get_plugin_generator('mod_assign'); 1420 // Create an instance as a user without the calendar capabilities. 1421 $this->setUser($user); 1422 $time = time(); 1423 $params = array( 1424 'course' => $course->id, 1425 'allowsubmissionsfromdate' => $time, 1426 'duedate' => $time + 500, 1427 'cutoffdate' => $time + 600, 1428 'gradingduedate' => $time + 700, 1429 ); 1430 $generator->create_instance($params); 1431 } 1432 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body