See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 /** 17 * Unit tests for (some of) mod/feedback/lib.php. 18 * 19 * @package mod_feedback 20 * @copyright 2016 Stephen Bourget 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 namespace mod_feedback; 24 25 use mod_feedback_completion; 26 27 defined('MOODLE_INTERNAL') || die(); 28 global $CFG; 29 require_once($CFG->dirroot . '/mod/feedback/lib.php'); 30 31 /** 32 * Unit tests for (some of) mod/feedback/lib.php. 33 * 34 * @copyright 2016 Stephen Bourget 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class lib_test extends \advanced_testcase { 38 39 public function test_feedback_initialise() { 40 $this->resetAfterTest(); 41 $this->setAdminUser(); 42 43 $course = $this->getDataGenerator()->create_course(); 44 $params['course'] = $course->id; 45 $params['timeopen'] = time() - 5 * MINSECS; 46 $params['timeclose'] = time() + DAYSECS; 47 $params['anonymous'] = 1; 48 $params['intro'] = 'Some introduction text'; 49 $feedback = $this->getDataGenerator()->create_module('feedback', $params); 50 51 // Test different ways to construct the structure object. 52 $pseudocm = get_coursemodule_from_instance('feedback', $feedback->id); // Object similar to cm_info. 53 $cm = get_fast_modinfo($course)->instances['feedback'][$feedback->id]; // Instance of cm_info. 54 55 $constructorparams = [ 56 [$feedback, null], 57 [null, $pseudocm], 58 [null, $cm], 59 [$feedback, $pseudocm], 60 [$feedback, $cm], 61 ]; 62 63 foreach ($constructorparams as $params) { 64 $structure = new mod_feedback_completion($params[0], $params[1], 0); 65 $this->assertTrue($structure->is_open()); 66 $this->assertTrue($structure->get_cm() instanceof \cm_info); 67 $this->assertEquals($feedback->cmid, $structure->get_cm()->id); 68 $this->assertEquals($feedback->intro, $structure->get_feedback()->intro); 69 } 70 } 71 72 /** 73 * Tests for mod_feedback_refresh_events. 74 */ 75 public function test_feedback_refresh_events() { 76 global $DB; 77 $this->resetAfterTest(); 78 $this->setAdminUser(); 79 80 $timeopen = time(); 81 $timeclose = time() + 86400; 82 83 $course = $this->getDataGenerator()->create_course(); 84 $generator = $this->getDataGenerator()->get_plugin_generator('mod_feedback'); 85 $params['course'] = $course->id; 86 $params['timeopen'] = $timeopen; 87 $params['timeclose'] = $timeclose; 88 $feedback = $generator->create_instance($params); 89 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 90 $context = \context_module::instance($cm->id); 91 92 // Normal case, with existing course. 93 $this->assertTrue(feedback_refresh_events($course->id)); 94 $eventparams = array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'open'); 95 $openevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST); 96 $this->assertEquals($openevent->timestart, $timeopen); 97 98 $eventparams = array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'close'); 99 $closeevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST); 100 $this->assertEquals($closeevent->timestart, $timeclose); 101 // In case the course ID is passed as a numeric string. 102 $this->assertTrue(feedback_refresh_events('' . $course->id)); 103 // Course ID not provided. 104 $this->assertTrue(feedback_refresh_events()); 105 $eventparams = array('modulename' => 'feedback'); 106 $events = $DB->get_records('event', $eventparams); 107 foreach ($events as $event) { 108 if ($event->modulename === 'feedback' && $event->instance === $feedback->id && $event->eventtype === 'open') { 109 $this->assertEquals($event->timestart, $timeopen); 110 } 111 if ($event->modulename === 'feedback' && $event->instance === $feedback->id && $event->eventtype === 'close') { 112 $this->assertEquals($event->timestart, $timeclose); 113 } 114 } 115 } 116 117 /** 118 * Test check_updates_since callback. 119 */ 120 public function test_check_updates_since() { 121 global $DB; 122 123 $this->resetAfterTest(); 124 $this->setAdminUser(); 125 $course = $this->getDataGenerator()->create_course(); 126 127 // Create user. 128 $student = self::getDataGenerator()->create_user(); 129 130 // User enrolment. 131 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 132 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); 133 134 $this->setCurrentTimeStart(); 135 $record = array( 136 'course' => $course->id, 137 'custom' => 0, 138 'feedback' => 1, 139 ); 140 $feedback = $this->getDataGenerator()->create_module('feedback', $record); 141 $cm = get_coursemodule_from_instance('feedback', $feedback->id, $course->id); 142 $cm = \cm_info::create($cm); 143 144 $this->setUser($student); 145 // Check that upon creation, the updates are only about the new configuration created. 146 $onehourago = time() - HOURSECS; 147 $updates = feedback_check_updates_since($cm, $onehourago); 148 foreach ($updates as $el => $val) { 149 if ($el == 'configuration') { 150 $this->assertTrue($val->updated); 151 $this->assertTimeCurrent($val->timeupdated); 152 } else { 153 $this->assertFalse($val->updated); 154 } 155 } 156 157 $record = [ 158 'feedback' => $feedback->id, 159 'userid' => $student->id, 160 'timemodified' => time(), 161 'random_response' => 0, 162 'anonymous_response' => FEEDBACK_ANONYMOUS_NO, 163 'courseid' => $course->id, 164 ]; 165 $DB->insert_record('feedback_completed', (object)$record); 166 $DB->insert_record('feedback_completedtmp', (object)$record); 167 168 // Check now for finished and unfinished attempts. 169 $updates = feedback_check_updates_since($cm, $onehourago); 170 $this->assertTrue($updates->attemptsunfinished->updated); 171 $this->assertCount(1, $updates->attemptsunfinished->itemids); 172 173 $this->assertTrue($updates->attemptsfinished->updated); 174 $this->assertCount(1, $updates->attemptsfinished->itemids); 175 } 176 177 /** 178 * Test calendar event provide action open. 179 */ 180 public function test_feedback_core_calendar_provide_event_action_open() { 181 $this->resetAfterTest(); 182 $this->setAdminUser(); 183 184 $now = time(); 185 $course = $this->getDataGenerator()->create_course(); 186 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id, 187 'timeopen' => $now - DAYSECS, 'timeclose' => $now + DAYSECS]); 188 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 189 190 $factory = new \core_calendar\action_factory(); 191 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory); 192 193 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 194 $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name()); 195 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 196 $this->assertEquals(1, $actionevent->get_item_count()); 197 $this->assertTrue($actionevent->is_actionable()); 198 } 199 200 /** 201 * Test calendar event provide action open, viewed by a different user. 202 */ 203 public function test_feedback_core_calendar_provide_event_action_open_for_user() { 204 global $DB; 205 206 $this->resetAfterTest(); 207 $this->setAdminUser(); 208 209 $now = time(); 210 $user = $this->getDataGenerator()->create_user(); 211 $user2 = $this->getDataGenerator()->create_user(); 212 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 213 $course = $this->getDataGenerator()->create_course(); 214 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual'); 215 216 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id, 217 'timeopen' => $now - DAYSECS, 'timeclose' => $now + DAYSECS]); 218 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 219 $factory = new \core_calendar\action_factory(); 220 221 $this->setUser($user2); 222 223 // User2 checking their events. 224 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id); 225 $this->assertNull($actionevent); 226 227 // User2 checking $user's events. 228 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id); 229 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 230 $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name()); 231 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 232 $this->assertEquals(1, $actionevent->get_item_count()); 233 $this->assertTrue($actionevent->is_actionable()); 234 } 235 236 /** 237 * Test calendar event provide action closed. 238 */ 239 public function test_feedback_core_calendar_provide_event_action_closed() { 240 $this->resetAfterTest(); 241 $this->setAdminUser(); 242 243 $course = $this->getDataGenerator()->create_course(); 244 $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id, 245 'timeclose' => time() - DAYSECS)); 246 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 247 248 $factory = new \core_calendar\action_factory(); 249 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory); 250 251 // No event on the dashboard if feedback is closed. 252 $this->assertNull($actionevent); 253 } 254 255 /** 256 * Test calendar event provide action closed, viewed by a different user. 257 */ 258 public function test_feedback_core_calendar_provide_event_action_closed_for_user() { 259 global $DB; 260 261 $this->resetAfterTest(); 262 $this->setAdminUser(); 263 264 $course = $this->getDataGenerator()->create_course(); 265 $user = $this->getDataGenerator()->create_user(); 266 $user2 = $this->getDataGenerator()->create_user(); 267 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 268 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual'); 269 270 $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id, 271 'timeclose' => time() - DAYSECS)); 272 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 273 $factory = new \core_calendar\action_factory(); 274 $this->setUser($user2); 275 276 // User2 checking their events. 277 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id); 278 $this->assertNull($actionevent); 279 280 // User2 checking $user's events. 281 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id); 282 283 // No event on the dashboard if feedback is closed. 284 $this->assertNull($actionevent); 285 } 286 287 /** 288 * Test calendar event action open in future. 289 * 290 * @throws coding_exception 291 */ 292 public function test_feedback_core_calendar_provide_event_action_open_in_future() { 293 $this->resetAfterTest(); 294 $this->setAdminUser(); 295 296 $course = $this->getDataGenerator()->create_course(); 297 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id, 298 'timeopen' => time() + DAYSECS]); 299 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 300 301 $factory = new \core_calendar\action_factory(); 302 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory); 303 304 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 305 $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name()); 306 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 307 $this->assertEquals(1, $actionevent->get_item_count()); 308 $this->assertFalse($actionevent->is_actionable()); 309 } 310 311 /** 312 * Test calendar event action open in future, viewed by a different user. 313 * 314 * @throws coding_exception 315 */ 316 public function test_feedback_core_calendar_provide_event_action_open_in_future_for_user() { 317 global $DB; 318 319 $this->resetAfterTest(); 320 $this->setAdminUser(); 321 322 $course = $this->getDataGenerator()->create_course(); 323 $user = $this->getDataGenerator()->create_user(); 324 $user2 = $this->getDataGenerator()->create_user(); 325 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 326 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual'); 327 328 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id, 329 'timeopen' => time() + DAYSECS]); 330 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 331 332 $factory = new \core_calendar\action_factory(); 333 $this->setUser($user2); 334 335 // User2 checking their events. 336 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id); 337 $this->assertNull($actionevent); 338 339 // User2 checking $user's events. 340 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id); 341 342 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 343 $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name()); 344 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 345 $this->assertEquals(1, $actionevent->get_item_count()); 346 $this->assertFalse($actionevent->is_actionable()); 347 } 348 349 /** 350 * Test calendar event with no time specified. 351 * 352 * @throws coding_exception 353 */ 354 public function test_feedback_core_calendar_provide_event_action_no_time_specified() { 355 $this->resetAfterTest(); 356 $this->setAdminUser(); 357 358 $course = $this->getDataGenerator()->create_course(); 359 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]); 360 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 361 362 $factory = new \core_calendar\action_factory(); 363 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory); 364 365 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 366 $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name()); 367 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 368 $this->assertEquals(1, $actionevent->get_item_count()); 369 $this->assertTrue($actionevent->is_actionable()); 370 } 371 372 /** 373 * Test calendar event with no time specified, viewed by a different user. 374 * 375 * @throws coding_exception 376 */ 377 public function test_feedback_core_calendar_provide_event_action_no_time_specified_for_user() { 378 global $DB; 379 380 $this->resetAfterTest(); 381 $this->setAdminUser(); 382 383 $course = $this->getDataGenerator()->create_course(); 384 $user = $this->getDataGenerator()->create_user(); 385 $user2 = $this->getDataGenerator()->create_user(); 386 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 387 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual'); 388 389 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]); 390 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 391 392 $factory = new \core_calendar\action_factory(); 393 $this->setUser($user2); 394 395 // User2 checking their events. 396 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id); 397 $this->assertNull($actionevent); 398 399 // User2 checking $user's events. 400 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id); 401 402 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 403 $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name()); 404 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 405 $this->assertEquals(1, $actionevent->get_item_count()); 406 $this->assertTrue($actionevent->is_actionable()); 407 } 408 409 /** 410 * A user that can not submit feedback should not have an action. 411 */ 412 public function test_feedback_core_calendar_provide_event_action_can_not_submit() { 413 global $DB; 414 415 $this->resetAfterTest(); 416 $this->setAdminUser(); 417 418 $user = $this->getDataGenerator()->create_user(); 419 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 420 $course = $this->getDataGenerator()->create_course(); 421 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]); 422 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 423 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 424 $context = \context_module::instance($cm->id); 425 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual'); 426 427 $this->setUser($user); 428 assign_capability('mod/feedback:complete', CAP_PROHIBIT, $studentrole->id, $context); 429 430 $factory = new \core_calendar\action_factory(); 431 $action = mod_feedback_core_calendar_provide_event_action($event, $factory); 432 433 $this->assertNull($action); 434 } 435 436 /** 437 * A user that can not submit feedback should not have an action, viewed by a different user. 438 */ 439 public function test_feedback_core_calendar_provide_event_action_can_not_submit_for_user() { 440 global $DB; 441 442 $this->resetAfterTest(); 443 $this->setAdminUser(); 444 445 $user = $this->getDataGenerator()->create_user(); 446 $user2 = $this->getDataGenerator()->create_user(); 447 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 448 $course = $this->getDataGenerator()->create_course(); 449 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]); 450 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 451 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 452 $context = \context_module::instance($cm->id); 453 $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual'); 454 455 assign_capability('mod/feedback:complete', CAP_PROHIBIT, $studentrole->id, $context); 456 $factory = new \core_calendar\action_factory(); 457 $this->setUser($user2); 458 459 // User2 checking their events. 460 461 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id); 462 $this->assertNull($actionevent); 463 464 // User2 checking $user's events. 465 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id); 466 467 $this->assertNull($actionevent); 468 } 469 470 /** 471 * A user that has already submitted feedback should not have an action. 472 */ 473 public function test_feedback_core_calendar_provide_event_action_already_submitted() { 474 global $DB; 475 476 $this->resetAfterTest(); 477 $this->setAdminUser(); 478 479 $user = $this->getDataGenerator()->create_user(); 480 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 481 $course = $this->getDataGenerator()->create_course(); 482 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]); 483 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 484 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 485 $context = \context_module::instance($cm->id); 486 487 $this->setUser($user); 488 489 $record = [ 490 'feedback' => $feedback->id, 491 'userid' => $user->id, 492 'timemodified' => time(), 493 'random_response' => 0, 494 'anonymous_response' => FEEDBACK_ANONYMOUS_NO, 495 'courseid' => 0, 496 ]; 497 $DB->insert_record('feedback_completed', (object) $record); 498 499 $factory = new \core_calendar\action_factory(); 500 $action = mod_feedback_core_calendar_provide_event_action($event, $factory); 501 502 $this->assertNull($action); 503 } 504 505 /** 506 * A user that has already submitted feedback should not have an action, viewed by a different user. 507 */ 508 public function test_feedback_core_calendar_provide_event_action_already_submitted_for_user() { 509 global $DB; 510 511 $this->resetAfterTest(); 512 $this->setAdminUser(); 513 514 $user = $this->getDataGenerator()->create_user(); 515 $user2 = $this->getDataGenerator()->create_user(); 516 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 517 $course = $this->getDataGenerator()->create_course(); 518 $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]); 519 $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN); 520 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 521 $context = \context_module::instance($cm->id); 522 523 $this->setUser($user); 524 525 $record = [ 526 'feedback' => $feedback->id, 527 'userid' => $user->id, 528 'timemodified' => time(), 529 'random_response' => 0, 530 'anonymous_response' => FEEDBACK_ANONYMOUS_NO, 531 'courseid' => 0, 532 ]; 533 $DB->insert_record('feedback_completed', (object) $record); 534 535 $factory = new \core_calendar\action_factory(); 536 $this->setUser($user2); 537 538 // User2 checking their events. 539 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id); 540 $this->assertNull($actionevent); 541 542 // User2 checking $user's events. 543 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id); 544 545 $this->assertNull($actionevent); 546 } 547 548 public function test_feedback_core_calendar_provide_event_action_already_completed() { 549 $this->resetAfterTest(); 550 set_config('enablecompletion', 1); 551 $this->setAdminUser(); 552 553 // Create the activity. 554 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 555 $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id), 556 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 557 558 // Get some additional data. 559 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 560 561 // Create a calendar event. 562 $event = $this->create_action_event($course->id, $feedback->id, 563 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 564 565 // Mark the activity as completed. 566 $completion = new \completion_info($course); 567 $completion->set_module_viewed($cm); 568 569 // Create an action factory. 570 $factory = new \core_calendar\action_factory(); 571 572 // Decorate action event. 573 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory); 574 575 // Ensure result was null. 576 $this->assertNull($actionevent); 577 } 578 579 public function test_feedback_core_calendar_provide_event_action_already_completed_for_user() { 580 $this->resetAfterTest(); 581 set_config('enablecompletion', 1); 582 $this->setAdminUser(); 583 584 // Create the activity. 585 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 586 $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id), 587 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 588 589 // Enrol a student in the course. 590 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 591 592 // Get some additional data. 593 $cm = get_coursemodule_from_instance('feedback', $feedback->id); 594 595 // Create a calendar event. 596 $event = $this->create_action_event($course->id, $feedback->id, 597 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 598 599 // Mark the activity as completed for the student. 600 $completion = new \completion_info($course); 601 $completion->set_module_viewed($cm, $student->id); 602 603 // Create an action factory. 604 $factory = new \core_calendar\action_factory(); 605 606 // Decorate action event for the student. 607 $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $student->id); 608 609 // Ensure result was null. 610 $this->assertNull($actionevent); 611 } 612 613 /** 614 * Creates an action event. 615 * 616 * @param int $courseid The course id. 617 * @param int $instanceid The feedback id. 618 * @param string $eventtype The event type. eg. FEEDBACK_EVENT_TYPE_OPEN. 619 * @return bool|calendar_event 620 */ 621 private function create_action_event($courseid, $instanceid, $eventtype) { 622 $event = new \stdClass(); 623 $event->name = 'Calendar event'; 624 $event->modulename = 'feedback'; 625 $event->courseid = $courseid; 626 $event->instance = $instanceid; 627 $event->type = CALENDAR_EVENT_TYPE_ACTION; 628 $event->eventtype = $eventtype; 629 $event->timestart = time(); 630 631 return \calendar_event::create($event); 632 } 633 634 /** 635 * Test the callback responsible for returning the completion rule descriptions. 636 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 637 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 638 */ 639 public function test_mod_feedback_completion_get_active_rule_descriptions() { 640 $this->resetAfterTest(); 641 $this->setAdminUser(); 642 643 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 644 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]); 645 $feedback1 = $this->getDataGenerator()->create_module('feedback', [ 646 'course' => $course->id, 647 'completion' => 2, 648 'completionsubmit' => 1 649 ]); 650 $feedback2 = $this->getDataGenerator()->create_module('feedback', [ 651 'course' => $course->id, 652 'completion' => 2, 653 'completionsubmit' => 0 654 ]); 655 $cm1 = \cm_info::create(get_coursemodule_from_instance('feedback', $feedback1->id)); 656 $cm2 = \cm_info::create(get_coursemodule_from_instance('feedback', $feedback2->id)); 657 658 // Data for the stdClass input type. 659 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 660 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 661 $moddefaults = new \stdClass(); 662 $moddefaults->customdata = ['customcompletionrules' => ['completionsubmit' => 1]]; 663 $moddefaults->completion = 2; 664 665 $activeruledescriptions = [get_string('completionsubmit', 'feedback')]; 666 $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 667 $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions($cm2), []); 668 $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 669 $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions(new \stdClass()), []); 670 } 671 672 /** 673 * An unknown event should not have min or max restrictions. 674 */ 675 public function test_get_valid_event_timestart_range_unknown_event() { 676 global $CFG, $DB; 677 require_once($CFG->dirroot . "/calendar/lib.php"); 678 679 $this->resetAfterTest(true); 680 $this->setAdminUser(); 681 $generator = $this->getDataGenerator(); 682 $course = $generator->create_course(); 683 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 684 $timeopen = time(); 685 $timeclose = $timeopen + DAYSECS; 686 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 687 $feedback->timeopen = $timeopen; 688 $feedback->timeclose = $timeclose; 689 $DB->update_record('feedback', $feedback); 690 691 $event = new \calendar_event([ 692 'name' => 'Test event', 693 'description' => '', 694 'format' => 1, 695 'courseid' => $course->id, 696 'groupid' => 0, 697 'userid' => 2, 698 'modulename' => 'feedback', 699 'instance' => $feedback->id, 700 'eventtype' => 'SOME UNKNOWN EVENT', 701 'timestart' => $timeopen, 702 'timeduration' => 86400, 703 'visible' => 1 704 ]); 705 706 list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback); 707 $this->assertNull($min); 708 $this->assertNull($max); 709 } 710 711 /** 712 * A FEEDBACK_EVENT_TYPE_OPEN should have a max timestart equal to the activity 713 * close time. 714 */ 715 public function test_get_valid_event_timestart_range_event_type_open() { 716 global $CFG, $DB; 717 require_once($CFG->dirroot . "/calendar/lib.php"); 718 719 $this->resetAfterTest(true); 720 $this->setAdminUser(); 721 $generator = $this->getDataGenerator(); 722 $course = $generator->create_course(); 723 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 724 $timeopen = time(); 725 $timeclose = $timeopen + DAYSECS; 726 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 727 $feedback->timeopen = $timeopen; 728 $feedback->timeclose = $timeclose; 729 $DB->update_record('feedback', $feedback); 730 731 $event = new \calendar_event([ 732 'name' => 'Test event', 733 'description' => '', 734 'format' => 1, 735 'courseid' => $course->id, 736 'groupid' => 0, 737 'userid' => 2, 738 'modulename' => 'feedback', 739 'instance' => $feedback->id, 740 'eventtype' => FEEDBACK_EVENT_TYPE_OPEN, 741 'timestart' => $timeopen, 742 'timeduration' => 86400, 743 'visible' => 1 744 ]); 745 746 list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback); 747 $this->assertNull($min); 748 $this->assertEquals($timeclose, $max[0]); 749 $this->assertNotEmpty($max[1]); 750 } 751 752 /** 753 * A FEEDBACK_EVENT_TYPE_OPEN should not have a max timestamp if the activity 754 * doesn't have a close date. 755 */ 756 public function test_get_valid_event_timestart_range_event_type_open_no_close() { 757 global $CFG, $DB; 758 require_once($CFG->dirroot . "/calendar/lib.php"); 759 760 $this->resetAfterTest(true); 761 $this->setAdminUser(); 762 $generator = $this->getDataGenerator(); 763 $course = $generator->create_course(); 764 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 765 $timeopen = time(); 766 $timeclose = $timeopen + DAYSECS; 767 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 768 $feedback->timeopen = $timeopen; 769 $feedback->timeclose = 0; 770 $DB->update_record('feedback', $feedback); 771 772 $event = new \calendar_event([ 773 'name' => 'Test event', 774 'description' => '', 775 'format' => 1, 776 'courseid' => $course->id, 777 'groupid' => 0, 778 'userid' => 2, 779 'modulename' => 'feedback', 780 'instance' => $feedback->id, 781 'eventtype' => FEEDBACK_EVENT_TYPE_OPEN, 782 'timestart' => $timeopen, 783 'timeduration' => 86400, 784 'visible' => 1 785 ]); 786 787 list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback); 788 $this->assertNull($min); 789 $this->assertNull($max); 790 } 791 792 /** 793 * A FEEDBACK_EVENT_TYPE_CLOSE should have a min timestart equal to the activity 794 * open time. 795 */ 796 public function test_get_valid_event_timestart_range_event_type_close() { 797 global $CFG, $DB; 798 require_once($CFG->dirroot . "/calendar/lib.php"); 799 800 $this->resetAfterTest(true); 801 $this->setAdminUser(); 802 $generator = $this->getDataGenerator(); 803 $course = $generator->create_course(); 804 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 805 $timeopen = time(); 806 $timeclose = $timeopen + DAYSECS; 807 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 808 $feedback->timeopen = $timeopen; 809 $feedback->timeclose = $timeclose; 810 $DB->update_record('feedback', $feedback); 811 812 $event = new \calendar_event([ 813 'name' => 'Test event', 814 'description' => '', 815 'format' => 1, 816 'courseid' => $course->id, 817 'groupid' => 0, 818 'userid' => 2, 819 'modulename' => 'feedback', 820 'instance' => $feedback->id, 821 'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE, 822 'timestart' => $timeopen, 823 'timeduration' => 86400, 824 'visible' => 1 825 ]); 826 827 list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback); 828 $this->assertEquals($timeopen, $min[0]); 829 $this->assertNotEmpty($min[1]); 830 $this->assertNull($max); 831 } 832 833 /** 834 * A FEEDBACK_EVENT_TYPE_CLOSE should not have a minimum timestamp if the activity 835 * doesn't have an open date. 836 */ 837 public function test_get_valid_event_timestart_range_event_type_close_no_open() { 838 global $CFG, $DB; 839 require_once($CFG->dirroot . "/calendar/lib.php"); 840 841 $this->resetAfterTest(true); 842 $this->setAdminUser(); 843 $generator = $this->getDataGenerator(); 844 $course = $generator->create_course(); 845 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 846 $timeopen = time(); 847 $timeclose = $timeopen + DAYSECS; 848 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 849 $feedback->timeopen = 0; 850 $feedback->timeclose = $timeclose; 851 $DB->update_record('feedback', $feedback); 852 853 $event = new \calendar_event([ 854 'name' => 'Test event', 855 'description' => '', 856 'format' => 1, 857 'courseid' => $course->id, 858 'groupid' => 0, 859 'userid' => 2, 860 'modulename' => 'feedback', 861 'instance' => $feedback->id, 862 'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE, 863 'timestart' => $timeopen, 864 'timeduration' => 86400, 865 'visible' => 1 866 ]); 867 868 list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback); 869 $this->assertNull($min); 870 $this->assertNull($max); 871 } 872 873 /** 874 * An unkown event type should not change the feedback instance. 875 */ 876 public function test_mod_feedback_core_calendar_event_timestart_updated_unknown_event() { 877 global $CFG, $DB; 878 require_once($CFG->dirroot . "/calendar/lib.php"); 879 880 $this->resetAfterTest(true); 881 $this->setAdminUser(); 882 $generator = $this->getDataGenerator(); 883 $course = $generator->create_course(); 884 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 885 $timeopen = time(); 886 $timeclose = $timeopen + DAYSECS; 887 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 888 $feedback->timeopen = $timeopen; 889 $feedback->timeclose = $timeclose; 890 $DB->update_record('feedback', $feedback); 891 892 // Create a valid event. 893 $event = new \calendar_event([ 894 'name' => 'Test event', 895 'description' => '', 896 'format' => 1, 897 'courseid' => $course->id, 898 'groupid' => 0, 899 'userid' => 2, 900 'modulename' => 'feedback', 901 'instance' => $feedback->id, 902 'eventtype' => FEEDBACK_EVENT_TYPE_OPEN . "SOMETHING ELSE", 903 'timestart' => 1, 904 'timeduration' => 86400, 905 'visible' => 1 906 ]); 907 908 mod_feedback_core_calendar_event_timestart_updated($event, $feedback); 909 910 $feedback = $DB->get_record('feedback', ['id' => $feedback->id]); 911 $this->assertEquals($timeopen, $feedback->timeopen); 912 $this->assertEquals($timeclose, $feedback->timeclose); 913 } 914 915 /** 916 * A FEEDBACK_EVENT_TYPE_OPEN event should update the timeopen property of 917 * the feedback activity. 918 */ 919 public function test_mod_feedback_core_calendar_event_timestart_updated_open_event() { 920 global $CFG, $DB; 921 require_once($CFG->dirroot . "/calendar/lib.php"); 922 923 $this->resetAfterTest(true); 924 $this->setAdminUser(); 925 $generator = $this->getDataGenerator(); 926 $course = $generator->create_course(); 927 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 928 $timeopen = time(); 929 $timeclose = $timeopen + DAYSECS; 930 $timemodified = 1; 931 $newtimeopen = $timeopen - DAYSECS; 932 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 933 $feedback->timeopen = $timeopen; 934 $feedback->timeclose = $timeclose; 935 $feedback->timemodified = $timemodified; 936 $DB->update_record('feedback', $feedback); 937 938 // Create a valid event. 939 $event = new \calendar_event([ 940 'name' => 'Test event', 941 'description' => '', 942 'format' => 1, 943 'courseid' => $course->id, 944 'groupid' => 0, 945 'userid' => 2, 946 'modulename' => 'feedback', 947 'instance' => $feedback->id, 948 'eventtype' => FEEDBACK_EVENT_TYPE_OPEN, 949 'timestart' => $newtimeopen, 950 'timeduration' => 86400, 951 'visible' => 1 952 ]); 953 954 mod_feedback_core_calendar_event_timestart_updated($event, $feedback); 955 956 $feedback = $DB->get_record('feedback', ['id' => $feedback->id]); 957 // Ensure the timeopen property matches the event timestart. 958 $this->assertEquals($newtimeopen, $feedback->timeopen); 959 // Ensure the timeclose isn't changed. 960 $this->assertEquals($timeclose, $feedback->timeclose); 961 // Ensure the timemodified property has been changed. 962 $this->assertNotEquals($timemodified, $feedback->timemodified); 963 } 964 965 /** 966 * A FEEDBACK_EVENT_TYPE_CLOSE event should update the timeclose property of 967 * the feedback activity. 968 */ 969 public function test_mod_feedback_core_calendar_event_timestart_updated_close_event() { 970 global $CFG, $DB; 971 require_once($CFG->dirroot . "/calendar/lib.php"); 972 973 $this->resetAfterTest(true); 974 $this->setAdminUser(); 975 $generator = $this->getDataGenerator(); 976 $course = $generator->create_course(); 977 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 978 $timeopen = time(); 979 $timeclose = $timeopen + DAYSECS; 980 $timemodified = 1; 981 $newtimeclose = $timeclose + DAYSECS; 982 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 983 $feedback->timeopen = $timeopen; 984 $feedback->timeclose = $timeclose; 985 $feedback->timemodified = $timemodified; 986 $DB->update_record('feedback', $feedback); 987 988 // Create a valid event. 989 $event = new \calendar_event([ 990 'name' => 'Test event', 991 'description' => '', 992 'format' => 1, 993 'courseid' => $course->id, 994 'groupid' => 0, 995 'userid' => 2, 996 'modulename' => 'feedback', 997 'instance' => $feedback->id, 998 'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE, 999 'timestart' => $newtimeclose, 1000 'timeduration' => 86400, 1001 'visible' => 1 1002 ]); 1003 1004 mod_feedback_core_calendar_event_timestart_updated($event, $feedback); 1005 1006 $feedback = $DB->get_record('feedback', ['id' => $feedback->id]); 1007 // Ensure the timeclose property matches the event timestart. 1008 $this->assertEquals($newtimeclose, $feedback->timeclose); 1009 // Ensure the timeopen isn't changed. 1010 $this->assertEquals($timeopen, $feedback->timeopen); 1011 // Ensure the timemodified property has been changed. 1012 $this->assertNotEquals($timemodified, $feedback->timemodified); 1013 } 1014 1015 /** 1016 * If a student somehow finds a way to update the calendar event 1017 * then the callback should not be executed to update the activity 1018 * properties as well because that would be a security issue. 1019 */ 1020 public function test_student_role_cant_update_time_close_event() { 1021 global $CFG, $DB; 1022 require_once($CFG->dirroot . '/calendar/lib.php'); 1023 1024 $this->resetAfterTest(); 1025 $this->setAdminUser(); 1026 1027 $generator = $this->getDataGenerator(); 1028 $user = $generator->create_user(); 1029 $course = $generator->create_course(); 1030 $context = \context_course::instance($course->id); 1031 $roleid = $generator->create_role(); 1032 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 1033 $timeopen = time(); 1034 $timeclose = $timeopen + DAYSECS; 1035 $timemodified = 1; 1036 $newtimeclose = $timeclose + DAYSECS; 1037 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 1038 $feedback->timeopen = $timeopen; 1039 $feedback->timeclose = $timeclose; 1040 $feedback->timemodified = $timemodified; 1041 $DB->update_record('feedback', $feedback); 1042 1043 $generator->enrol_user($user->id, $course->id, 'student'); 1044 $generator->role_assign($roleid, $user->id, $context->id); 1045 1046 // Create a valid event. 1047 $event = new \calendar_event([ 1048 'name' => 'Test event', 1049 'description' => '', 1050 'format' => 1, 1051 'courseid' => $course->id, 1052 'groupid' => 0, 1053 'userid' => $user->id, 1054 'modulename' => 'feedback', 1055 'instance' => $feedback->id, 1056 'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE, 1057 'timestart' => $newtimeclose, 1058 'timeduration' => 86400, 1059 'visible' => 1 1060 ]); 1061 1062 assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); 1063 assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleid, $context, true); 1064 1065 $this->setUser($user); 1066 1067 mod_feedback_core_calendar_event_timestart_updated($event, $feedback); 1068 1069 $newfeedback = $DB->get_record('feedback', ['id' => $feedback->id]); 1070 // The activity shouldn't have been updated because the user 1071 // doesn't have permissions to do it. 1072 $this->assertEquals($timeclose, $newfeedback->timeclose); 1073 } 1074 1075 /** 1076 * The activity should update if a teacher modifies the calendar 1077 * event. 1078 */ 1079 public function test_teacher_role_can_update_time_close_event() { 1080 global $CFG, $DB; 1081 require_once($CFG->dirroot . '/calendar/lib.php'); 1082 1083 $this->resetAfterTest(); 1084 $this->setAdminUser(); 1085 1086 $generator = $this->getDataGenerator(); 1087 $user = $generator->create_user(); 1088 $course = $generator->create_course(); 1089 $context = \context_course::instance($course->id); 1090 $roleid = $generator->create_role(); 1091 $feedbackgenerator = $generator->get_plugin_generator('mod_feedback'); 1092 $timeopen = time(); 1093 $timeclose = $timeopen + DAYSECS; 1094 $timemodified = 1; 1095 $newtimeclose = $timeclose + DAYSECS; 1096 $feedback = $feedbackgenerator->create_instance(['course' => $course->id]); 1097 $feedback->timeopen = $timeopen; 1098 $feedback->timeclose = $timeclose; 1099 $feedback->timemodified = $timemodified; 1100 $DB->update_record('feedback', $feedback); 1101 1102 $generator->enrol_user($user->id, $course->id, 'teacher'); 1103 $generator->role_assign($roleid, $user->id, $context->id); 1104 1105 // Create a valid event. 1106 $event = new \calendar_event([ 1107 'name' => 'Test event', 1108 'description' => '', 1109 'format' => 1, 1110 'courseid' => $course->id, 1111 'groupid' => 0, 1112 'userid' => $user->id, 1113 'modulename' => 'feedback', 1114 'instance' => $feedback->id, 1115 'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE, 1116 'timestart' => $newtimeclose, 1117 'timeduration' => 86400, 1118 'visible' => 1 1119 ]); 1120 1121 assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); 1122 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleid, $context, true); 1123 1124 $this->setUser($user); 1125 1126 $sink = $this->redirectEvents(); 1127 1128 mod_feedback_core_calendar_event_timestart_updated($event, $feedback); 1129 1130 $triggeredevents = $sink->get_events(); 1131 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 1132 return is_a($e, 'core\event\course_module_updated'); 1133 }); 1134 1135 $newfeedback = $DB->get_record('feedback', ['id' => $feedback->id]); 1136 // The activity should have been updated because the user 1137 // has permissions to do it. 1138 $this->assertEquals($newtimeclose, $newfeedback->timeclose); 1139 // A course_module_updated event should be fired if the module 1140 // was successfully modified. 1141 $this->assertNotEmpty($moduleupdatedevents); 1142 } 1143 1144 /** 1145 * A user who does not have capabilities to add events to the calendar should be able to create an feedback. 1146 */ 1147 public function test_creation_with_no_calendar_capabilities() { 1148 $this->resetAfterTest(); 1149 $course = self::getDataGenerator()->create_course(); 1150 $context = \context_course::instance($course->id); 1151 $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); 1152 $roleid = self::getDataGenerator()->create_role(); 1153 self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); 1154 assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); 1155 $generator = self::getDataGenerator()->get_plugin_generator('mod_feedback'); 1156 // Create an instance as a user without the calendar capabilities. 1157 $this->setUser($user); 1158 $time = time(); 1159 $params = array( 1160 'course' => $course->id, 1161 'timeopen' => $time + 200, 1162 'timeclose' => $time + 2000, 1163 ); 1164 $generator->create_instance($params); 1165 } 1166 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body