[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * Course related unit tests 19 * 20 * @package core 21 * @category phpunit 22 * @copyright 2012 Petr Skoda {@link http://skodak.org} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 require_once($CFG->dirroot . '/course/lib.php'); 30 require_once($CFG->dirroot . '/course/tests/fixtures/course_capability_assignment.php'); 31 require_once($CFG->dirroot . '/enrol/imsenterprise/tests/imsenterprise_test.php'); 32 33 class core_course_courselib_testcase extends advanced_testcase { 34 35 /** 36 * Set forum specific test values for calling create_module(). 37 * 38 * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference. 39 */ 40 private function forum_create_set_values(&$moduleinfo) { 41 // Completion specific to forum - optional. 42 $moduleinfo->completionposts = 3; 43 $moduleinfo->completiondiscussions = 1; 44 $moduleinfo->completionreplies = 2; 45 46 // Specific values to the Forum module. 47 $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE; 48 $moduleinfo->type = 'single'; 49 $moduleinfo->trackingtype = FORUM_TRACKING_FORCED; 50 $moduleinfo->maxbytes = 10240; 51 $moduleinfo->maxattachments = 2; 52 53 // Post threshold for blocking - specific to forum. 54 $moduleinfo->blockperiod = 60*60*24; 55 $moduleinfo->blockafter = 10; 56 $moduleinfo->warnafter = 5; 57 } 58 59 /** 60 * Execute test asserts on the saved DB data by create_module($forum). 61 * 62 * @param object $moduleinfo - the specific forum values that were used to create a forum. 63 * @param object $dbmodinstance - the DB values of the created forum. 64 */ 65 private function forum_create_run_asserts($moduleinfo, $dbmodinstance) { 66 // Compare values specific to forums. 67 $this->assertEquals($moduleinfo->forcesubscribe, $dbmodinstance->forcesubscribe); 68 $this->assertEquals($moduleinfo->type, $dbmodinstance->type); 69 $this->assertEquals($moduleinfo->assessed, $dbmodinstance->assessed); 70 $this->assertEquals($moduleinfo->completionposts, $dbmodinstance->completionposts); 71 $this->assertEquals($moduleinfo->completiondiscussions, $dbmodinstance->completiondiscussions); 72 $this->assertEquals($moduleinfo->completionreplies, $dbmodinstance->completionreplies); 73 $this->assertEquals($moduleinfo->scale, $dbmodinstance->scale); 74 $this->assertEquals($moduleinfo->assesstimestart, $dbmodinstance->assesstimestart); 75 $this->assertEquals($moduleinfo->assesstimefinish, $dbmodinstance->assesstimefinish); 76 $this->assertEquals($moduleinfo->rsstype, $dbmodinstance->rsstype); 77 $this->assertEquals($moduleinfo->rssarticles, $dbmodinstance->rssarticles); 78 $this->assertEquals($moduleinfo->trackingtype, $dbmodinstance->trackingtype); 79 $this->assertEquals($moduleinfo->maxbytes, $dbmodinstance->maxbytes); 80 $this->assertEquals($moduleinfo->maxattachments, $dbmodinstance->maxattachments); 81 $this->assertEquals($moduleinfo->blockperiod, $dbmodinstance->blockperiod); 82 $this->assertEquals($moduleinfo->blockafter, $dbmodinstance->blockafter); 83 $this->assertEquals($moduleinfo->warnafter, $dbmodinstance->warnafter); 84 } 85 86 /** 87 * Set assign module specific test values for calling create_module(). 88 * 89 * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference. 90 */ 91 private function assign_create_set_values(&$moduleinfo) { 92 // Specific values to the Assign module. 93 $moduleinfo->alwaysshowdescription = true; 94 $moduleinfo->submissiondrafts = true; 95 $moduleinfo->requiresubmissionstatement = true; 96 $moduleinfo->sendnotifications = true; 97 $moduleinfo->sendlatenotifications = true; 98 $moduleinfo->duedate = time() + (7 * 24 * 3600); 99 $moduleinfo->cutoffdate = time() + (7 * 24 * 3600); 100 $moduleinfo->allowsubmissionsfromdate = time(); 101 $moduleinfo->teamsubmission = true; 102 $moduleinfo->requireallteammemberssubmit = true; 103 $moduleinfo->teamsubmissiongroupingid = true; 104 $moduleinfo->blindmarking = true; 105 $moduleinfo->markingworkflow = true; 106 $moduleinfo->markingallocation = true; 107 $moduleinfo->assignsubmission_onlinetext_enabled = true; 108 $moduleinfo->assignsubmission_file_enabled = true; 109 $moduleinfo->assignsubmission_file_maxfiles = 1; 110 $moduleinfo->assignsubmission_file_maxsizebytes = 1000000; 111 $moduleinfo->assignsubmission_comments_enabled = true; 112 $moduleinfo->assignfeedback_comments_enabled = true; 113 $moduleinfo->assignfeedback_offline_enabled = true; 114 $moduleinfo->assignfeedback_file_enabled = true; 115 116 // Advanced grading. 117 $gradingmethods = grading_manager::available_methods(); 118 $moduleinfo->advancedgradingmethod_submissions = current(array_keys($gradingmethods)); 119 } 120 121 /** 122 * Execute test asserts on the saved DB data by create_module($assign). 123 * 124 * @param object $moduleinfo - the specific assign module values that were used to create an assign module. 125 * @param object $dbmodinstance - the DB values of the created assign module. 126 */ 127 private function assign_create_run_asserts($moduleinfo, $dbmodinstance) { 128 global $DB; 129 130 $this->assertEquals($moduleinfo->alwaysshowdescription, $dbmodinstance->alwaysshowdescription); 131 $this->assertEquals($moduleinfo->submissiondrafts, $dbmodinstance->submissiondrafts); 132 $this->assertEquals($moduleinfo->requiresubmissionstatement, $dbmodinstance->requiresubmissionstatement); 133 $this->assertEquals($moduleinfo->sendnotifications, $dbmodinstance->sendnotifications); 134 $this->assertEquals($moduleinfo->duedate, $dbmodinstance->duedate); 135 $this->assertEquals($moduleinfo->cutoffdate, $dbmodinstance->cutoffdate); 136 $this->assertEquals($moduleinfo->allowsubmissionsfromdate, $dbmodinstance->allowsubmissionsfromdate); 137 $this->assertEquals($moduleinfo->teamsubmission, $dbmodinstance->teamsubmission); 138 $this->assertEquals($moduleinfo->requireallteammemberssubmit, $dbmodinstance->requireallteammemberssubmit); 139 $this->assertEquals($moduleinfo->teamsubmissiongroupingid, $dbmodinstance->teamsubmissiongroupingid); 140 $this->assertEquals($moduleinfo->blindmarking, $dbmodinstance->blindmarking); 141 $this->assertEquals($moduleinfo->markingworkflow, $dbmodinstance->markingworkflow); 142 $this->assertEquals($moduleinfo->markingallocation, $dbmodinstance->markingallocation); 143 // The goal not being to fully test assign_add_instance() we'll stop here for the assign tests - to avoid too many DB queries. 144 145 // Advanced grading. 146 $cm = get_coursemodule_from_instance('assign', $dbmodinstance->id); 147 $contextmodule = context_module::instance($cm->id); 148 $advancedgradingmethod = $DB->get_record('grading_areas', 149 array('contextid' => $contextmodule->id, 150 'activemethod' => $moduleinfo->advancedgradingmethod_submissions)); 151 $this->assertEquals($moduleinfo->advancedgradingmethod_submissions, $advancedgradingmethod); 152 } 153 154 /** 155 * Run some asserts test for a specific module for the function create_module(). 156 * 157 * The function has been created (and is called) for $this->test_create_module(). 158 * Note that the call to MODULE_create_set_values and MODULE_create_run_asserts are done after the common set values/run asserts. 159 * So if you want, you can overwrite the default values/asserts in the respective functions. 160 * @param string $modulename Name of the module ('forum', 'assign', 'book'...). 161 */ 162 private function create_specific_module_test($modulename) { 163 global $DB, $CFG; 164 165 $this->resetAfterTest(true); 166 167 $this->setAdminUser(); 168 169 // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard. 170 require_once($CFG->dirroot.'/mod/'. $modulename .'/lib.php'); 171 172 // Enable avaibility. 173 // If not enabled all conditional fields will be ignored. 174 set_config('enableavailability', 1); 175 176 // Enable course completion. 177 // If not enabled all completion settings will be ignored. 178 set_config('enablecompletion', COMPLETION_ENABLED); 179 180 // Enable forum RSS feeds. 181 set_config('enablerssfeeds', 1); 182 set_config('forum_enablerssfeeds', 1); 183 184 $course = $this->getDataGenerator()->create_course(array('numsections'=>1, 'enablecompletion' => COMPLETION_ENABLED), 185 array('createsections'=>true)); 186 187 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id)); 188 189 // Create assign module instance for test. 190 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); 191 $params['course'] = $course->id; 192 $instance = $generator->create_instance($params); 193 $assigncm = get_coursemodule_from_instance('assign', $instance->id); 194 195 // Module test values. 196 $moduleinfo = new stdClass(); 197 198 // Always mandatory generic values to any module. 199 $moduleinfo->modulename = $modulename; 200 $moduleinfo->section = 1; // This is the section number in the course. Not the section id in the database. 201 $moduleinfo->course = $course->id; 202 $moduleinfo->groupingid = $grouping->id; 203 $moduleinfo->visible = true; 204 205 // Sometimes optional generic values for some modules. 206 $moduleinfo->name = 'My test module'; 207 $moduleinfo->showdescription = 1; // standard boolean 208 require_once($CFG->libdir . '/gradelib.php'); 209 $gradecats = grade_get_categories_menu($moduleinfo->course, false); 210 $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats 211 $moduleinfo->gradecat = $gradecatid; 212 $moduleinfo->groupmode = VISIBLEGROUPS; 213 $moduleinfo->cmidnumber = 'idnumber_XXX'; 214 215 // Completion common to all module. 216 $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC; 217 $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED; 218 $moduleinfo->completiongradeitemnumber = 1; 219 $moduleinfo->completionexpected = time() + (7 * 24 * 3600); 220 221 // Conditional activity. 222 $moduleinfo->availability = '{"op":"&","showc":[true,true],"c":[' . 223 '{"type":"date","d":">=","t":' . time() . '},' . 224 '{"type":"date","d":"<","t":' . (time() + (7 * 24 * 3600)) . '}' . 225 ']}'; 226 $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself. 227 $moduleinfo->conditiongradegroup = array(array('conditiongradeitemid' => $coursegradeitem->id, 'conditiongrademin' => 10, 'conditiongrademax' => 80)); 228 $moduleinfo->conditionfieldgroup = array(array('conditionfield' => 'email', 'conditionfieldoperator' => \availability_profile\condition::OP_CONTAINS, 'conditionfieldvalue' => '@')); 229 $moduleinfo->conditioncompletiongroup = array(array('conditionsourcecmid' => $assigncm->id, 'conditionrequiredcompletion' => COMPLETION_COMPLETE)); // "conditionsourcecmid == 0" => none 230 231 // Grading and Advanced grading. 232 require_once($CFG->dirroot . '/rating/lib.php'); 233 $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE; 234 $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number. 235 $moduleinfo->assesstimestart = time(); 236 $moduleinfo->assesstimefinish = time() + (7 * 24 * 3600); 237 238 // RSS. 239 $moduleinfo->rsstype = 2; 240 $moduleinfo->rssarticles = 10; 241 242 // Optional intro editor (depends of module). 243 $draftid_editor = 0; 244 file_prepare_draft_area($draftid_editor, null, null, null, null); 245 $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor); 246 247 // Following is the advanced grading method area called 'submissions' for the 'assign' module. 248 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) { 249 $moduleinfo->grade = 100; 250 } 251 252 // Plagiarism form values. 253 // No plagiarism plugin installed by default. Use this space to make your own test. 254 255 // Values specific to the module. 256 $modulesetvalues = $modulename.'_create_set_values'; 257 $this->$modulesetvalues($moduleinfo); 258 259 // Create the module. 260 $result = create_module($moduleinfo); 261 262 // Retrieve the module info. 263 $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance)); 264 $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance); 265 // We passed the course section number to create_courses but $dbcm contain the section id. 266 // We need to retrieve the db course section number. 267 $section = $DB->get_record('course_sections', array('course' => $dbcm->course, 'id' => $dbcm->section)); 268 // Retrieve the grade item. 269 $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course, 270 'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename)); 271 272 // Compare the values common to all module instances. 273 $this->assertEquals($moduleinfo->modulename, $dbcm->modname); 274 $this->assertEquals($moduleinfo->section, $section->section); 275 $this->assertEquals($moduleinfo->course, $dbcm->course); 276 $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid); 277 $this->assertEquals($moduleinfo->visible, $dbcm->visible); 278 $this->assertEquals($moduleinfo->completion, $dbcm->completion); 279 $this->assertEquals($moduleinfo->completionview, $dbcm->completionview); 280 $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber); 281 $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected); 282 $this->assertEquals($moduleinfo->availability, $dbcm->availability); 283 $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription); 284 $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode); 285 $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber); 286 $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid); 287 288 // Optional grade testing. 289 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) { 290 $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade); 291 } 292 293 // Some optional (but quite common) to some module. 294 $this->assertEquals($moduleinfo->name, $dbmodinstance->name); 295 $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro); 296 $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat); 297 298 // Test specific to the module. 299 $modulerunasserts = $modulename.'_create_run_asserts'; 300 $this->$modulerunasserts($moduleinfo, $dbmodinstance); 301 return $moduleinfo; 302 } 303 304 /** 305 * Test create_module() for multiple modules defined in the $modules array (first declaration of the function). 306 */ 307 public function test_create_module() { 308 // Add the module name you want to test here. 309 // Create the match MODULENAME_create_set_values() and MODULENAME_create_run_asserts(). 310 $modules = array('forum', 'assign'); 311 // Run all tests. 312 foreach ($modules as $modulename) { 313 $this->create_specific_module_test($modulename); 314 } 315 } 316 317 /** 318 * Test update_module() for multiple modules defined in the $modules array (first declaration of the function). 319 */ 320 public function test_update_module() { 321 // Add the module name you want to test here. 322 // Create the match MODULENAME_update_set_values() and MODULENAME_update_run_asserts(). 323 $modules = array('forum'); 324 // Run all tests. 325 foreach ($modules as $modulename) { 326 $this->update_specific_module_test($modulename); 327 } 328 } 329 330 /** 331 * Set forum specific test values for calling update_module(). 332 * 333 * @param object $moduleinfo - the moduleinfo to add some specific values - passed in reference. 334 */ 335 private function forum_update_set_values(&$moduleinfo) { 336 // Completion specific to forum - optional. 337 $moduleinfo->completionposts = 3; 338 $moduleinfo->completiondiscussions = 1; 339 $moduleinfo->completionreplies = 2; 340 341 // Specific values to the Forum module. 342 $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE; 343 $moduleinfo->type = 'single'; 344 $moduleinfo->trackingtype = FORUM_TRACKING_FORCED; 345 $moduleinfo->maxbytes = 10240; 346 $moduleinfo->maxattachments = 2; 347 348 // Post threshold for blocking - specific to forum. 349 $moduleinfo->blockperiod = 60*60*24; 350 $moduleinfo->blockafter = 10; 351 $moduleinfo->warnafter = 5; 352 } 353 354 /** 355 * Execute test asserts on the saved DB data by update_module($forum). 356 * 357 * @param object $moduleinfo - the specific forum values that were used to update a forum. 358 * @param object $dbmodinstance - the DB values of the updated forum. 359 */ 360 private function forum_update_run_asserts($moduleinfo, $dbmodinstance) { 361 // Compare values specific to forums. 362 $this->assertEquals($moduleinfo->forcesubscribe, $dbmodinstance->forcesubscribe); 363 $this->assertEquals($moduleinfo->type, $dbmodinstance->type); 364 $this->assertEquals($moduleinfo->assessed, $dbmodinstance->assessed); 365 $this->assertEquals($moduleinfo->completionposts, $dbmodinstance->completionposts); 366 $this->assertEquals($moduleinfo->completiondiscussions, $dbmodinstance->completiondiscussions); 367 $this->assertEquals($moduleinfo->completionreplies, $dbmodinstance->completionreplies); 368 $this->assertEquals($moduleinfo->scale, $dbmodinstance->scale); 369 $this->assertEquals($moduleinfo->assesstimestart, $dbmodinstance->assesstimestart); 370 $this->assertEquals($moduleinfo->assesstimefinish, $dbmodinstance->assesstimefinish); 371 $this->assertEquals($moduleinfo->rsstype, $dbmodinstance->rsstype); 372 $this->assertEquals($moduleinfo->rssarticles, $dbmodinstance->rssarticles); 373 $this->assertEquals($moduleinfo->trackingtype, $dbmodinstance->trackingtype); 374 $this->assertEquals($moduleinfo->maxbytes, $dbmodinstance->maxbytes); 375 $this->assertEquals($moduleinfo->maxattachments, $dbmodinstance->maxattachments); 376 $this->assertEquals($moduleinfo->blockperiod, $dbmodinstance->blockperiod); 377 $this->assertEquals($moduleinfo->blockafter, $dbmodinstance->blockafter); 378 $this->assertEquals($moduleinfo->warnafter, $dbmodinstance->warnafter); 379 } 380 381 382 383 /** 384 * Test a specific type of module. 385 * 386 * @param string $modulename - the module name to test 387 */ 388 private function update_specific_module_test($modulename) { 389 global $DB, $CFG; 390 391 $this->resetAfterTest(true); 392 393 $this->setAdminUser(); 394 395 // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard. 396 require_once($CFG->dirroot.'/mod/'. $modulename .'/lib.php'); 397 398 // Enable avaibility. 399 // If not enabled all conditional fields will be ignored. 400 set_config('enableavailability', 1); 401 402 // Enable course completion. 403 // If not enabled all completion settings will be ignored. 404 set_config('enablecompletion', COMPLETION_ENABLED); 405 406 // Enable forum RSS feeds. 407 set_config('enablerssfeeds', 1); 408 set_config('forum_enablerssfeeds', 1); 409 410 $course = $this->getDataGenerator()->create_course(array('numsections'=>1, 'enablecompletion' => COMPLETION_ENABLED), 411 array('createsections'=>true)); 412 413 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id)); 414 415 // Create assign module instance for testing gradeitem. 416 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); 417 $params['course'] = $course->id; 418 $instance = $generator->create_instance($params); 419 $assigncm = get_coursemodule_from_instance('assign', $instance->id); 420 421 // Create the test forum to update. 422 $initvalues = new stdClass(); 423 $initvalues->introformat = FORMAT_HTML; 424 $initvalues->course = $course->id; 425 $forum = self::getDataGenerator()->create_module('forum', $initvalues); 426 427 // Retrieve course module. 428 $cm = get_coursemodule_from_instance('forum', $forum->id); 429 430 // Module test values. 431 $moduleinfo = new stdClass(); 432 433 // Always mandatory generic values to any module. 434 $moduleinfo->coursemodule = $cm->id; 435 $moduleinfo->modulename = $modulename; 436 $moduleinfo->course = $course->id; 437 $moduleinfo->groupingid = $grouping->id; 438 $moduleinfo->visible = true; 439 440 // Sometimes optional generic values for some modules. 441 $moduleinfo->name = 'My test module'; 442 $moduleinfo->showdescription = 1; // standard boolean 443 require_once($CFG->libdir . '/gradelib.php'); 444 $gradecats = grade_get_categories_menu($moduleinfo->course, false); 445 $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats 446 $moduleinfo->gradecat = $gradecatid; 447 $moduleinfo->groupmode = VISIBLEGROUPS; 448 $moduleinfo->cmidnumber = 'idnumber_XXX'; 449 450 // Completion common to all module. 451 $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC; 452 $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED; 453 $moduleinfo->completiongradeitemnumber = 1; 454 $moduleinfo->completionexpected = time() + (7 * 24 * 3600); 455 $moduleinfo->completionunlocked = 1; 456 457 // Conditional activity. 458 $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself. 459 $moduleinfo->availability = json_encode(\core_availability\tree::get_root_json( 460 array(\availability_date\condition::get_json('>=', time()), 461 \availability_date\condition::get_json('<', time() + (7 * 24 * 3600)), 462 \availability_grade\condition::get_json($coursegradeitem->id, 10, 80), 463 \availability_profile\condition::get_json(false, 'email', 'contains', '@'), 464 \availability_completion\condition::get_json($assigncm->id, COMPLETION_COMPLETE)), '&')); 465 466 // Grading and Advanced grading. 467 require_once($CFG->dirroot . '/rating/lib.php'); 468 $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE; 469 $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number. 470 $moduleinfo->assesstimestart = time(); 471 $moduleinfo->assesstimefinish = time() + (7 * 24 * 3600); 472 473 // RSS. 474 $moduleinfo->rsstype = 2; 475 $moduleinfo->rssarticles = 10; 476 477 // Optional intro editor (depends of module). 478 $draftid_editor = 0; 479 file_prepare_draft_area($draftid_editor, null, null, null, null); 480 $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor); 481 482 // Following is the advanced grading method area called 'submissions' for the 'assign' module. 483 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) { 484 $moduleinfo->grade = 100; 485 } 486 // Plagiarism form values. 487 // No plagiarism plugin installed by default. Use this space to make your own test. 488 489 // Values specific to the module. 490 $modulesetvalues = $modulename.'_update_set_values'; 491 $this->$modulesetvalues($moduleinfo); 492 493 // Create the module. 494 $result = update_module($moduleinfo); 495 496 // Retrieve the module info. 497 $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance)); 498 $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance); 499 // Retrieve the grade item. 500 $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course, 501 'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename)); 502 503 // Compare the values common to all module instances. 504 $this->assertEquals($moduleinfo->modulename, $dbcm->modname); 505 $this->assertEquals($moduleinfo->course, $dbcm->course); 506 $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid); 507 $this->assertEquals($moduleinfo->visible, $dbcm->visible); 508 $this->assertEquals($moduleinfo->completion, $dbcm->completion); 509 $this->assertEquals($moduleinfo->completionview, $dbcm->completionview); 510 $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber); 511 $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected); 512 $this->assertEquals($moduleinfo->availability, $dbcm->availability); 513 $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription); 514 $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode); 515 $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber); 516 $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid); 517 518 // Optional grade testing. 519 if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) { 520 $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade); 521 } 522 523 // Some optional (but quite common) to some module. 524 $this->assertEquals($moduleinfo->name, $dbmodinstance->name); 525 $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro); 526 $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat); 527 528 // Test specific to the module. 529 $modulerunasserts = $modulename.'_update_run_asserts'; 530 $this->$modulerunasserts($moduleinfo, $dbmodinstance); 531 return $moduleinfo; 532 } 533 534 /** 535 * Data provider for course_delete module 536 * 537 * @return array An array of arrays contain test data 538 */ 539 public function provider_course_delete_module() { 540 $data = array(); 541 542 $data['assign'] = array('assign', array('duedate' => time())); 543 $data['quiz'] = array('quiz', array('duedate' => time())); 544 545 return $data; 546 } 547 548 /** 549 * Test the create_course function 550 */ 551 public function test_create_course() { 552 global $DB; 553 $this->resetAfterTest(true); 554 $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0"); 555 556 $course = new stdClass(); 557 $course->fullname = 'Apu loves Unit TÉ™sts'; 558 $course->shortname = 'Spread the lÅve'; 559 $course->idnumber = '123'; 560 $course->summary = 'Awesome!'; 561 $course->summaryformat = FORMAT_PLAIN; 562 $course->format = 'topics'; 563 $course->newsitems = 0; 564 $course->numsections = 5; 565 $course->category = $defaultcategory; 566 $original = (array) $course; 567 568 $created = create_course($course); 569 $context = context_course::instance($created->id); 570 571 // Compare original and created. 572 $this->assertEquals($original, array_intersect_key((array) $created, $original)); 573 574 // Ensure default section is created. 575 $sectioncreated = $DB->record_exists('course_sections', array('course' => $created->id, 'section' => 0)); 576 $this->assertTrue($sectioncreated); 577 578 // Ensure blocks have been associated to the course. 579 $blockcount = $DB->count_records('block_instances', array('parentcontextid' => $context->id)); 580 $this->assertGreaterThan(0, $blockcount); 581 582 // Ensure that the shortname isn't duplicated. 583 try { 584 $created = create_course($course); 585 $this->fail('Exception expected'); 586 } catch (moodle_exception $e) { 587 $this->assertSame(get_string('shortnametaken', 'error', $course->shortname), $e->getMessage()); 588 } 589 590 // Ensure that the idnumber isn't duplicated. 591 $course->shortname .= '1'; 592 try { 593 $created = create_course($course); 594 $this->fail('Exception expected'); 595 } catch (moodle_exception $e) { 596 $this->assertSame(get_string('courseidnumbertaken', 'error', $course->idnumber), $e->getMessage()); 597 } 598 } 599 600 public function test_create_course_with_generator() { 601 global $DB; 602 $this->resetAfterTest(true); 603 $course = $this->getDataGenerator()->create_course(); 604 605 // Ensure default section is created. 606 $sectioncreated = $DB->record_exists('course_sections', array('course' => $course->id, 'section' => 0)); 607 $this->assertTrue($sectioncreated); 608 } 609 610 public function test_create_course_sections() { 611 global $DB; 612 $this->resetAfterTest(true); 613 614 $course = $this->getDataGenerator()->create_course( 615 array('shortname' => 'GrowingCourse', 616 'fullname' => 'Growing Course', 617 'numsections' => 5), 618 array('createsections' => true)); 619 620 // Ensure all 6 (0-5) sections were created and course content cache works properly 621 $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all()); 622 $this->assertEquals(range(0, $course->numsections), $sectionscreated); 623 624 // this will do nothing, section already exists 625 $this->assertFalse(course_create_sections_if_missing($course, $course->numsections)); 626 627 // this will create new section 628 $this->assertTrue(course_create_sections_if_missing($course, $course->numsections + 1)); 629 630 // Ensure all 7 (0-6) sections were created and modinfo/sectioninfo cache works properly 631 $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all()); 632 $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated); 633 } 634 635 public function test_update_course() { 636 global $DB; 637 638 $this->resetAfterTest(); 639 640 $defaultcategory = $DB->get_field_select('course_categories', 'MIN(id)', 'parent = 0'); 641 642 $course = new stdClass(); 643 $course->fullname = 'Apu loves Unit TÉ™sts'; 644 $course->shortname = 'test1'; 645 $course->idnumber = '1'; 646 $course->summary = 'Awesome!'; 647 $course->summaryformat = FORMAT_PLAIN; 648 $course->format = 'topics'; 649 $course->newsitems = 0; 650 $course->numsections = 5; 651 $course->category = $defaultcategory; 652 653 $created = create_course($course); 654 // Ensure the checks only work on idnumber/shortname that are not already ours. 655 update_course($created); 656 657 $course->shortname = 'test2'; 658 $course->idnumber = '2'; 659 660 $created2 = create_course($course); 661 662 // Test duplicate idnumber. 663 $created2->idnumber = '1'; 664 try { 665 update_course($created2); 666 $this->fail('Expected exception when trying to update a course with duplicate idnumber'); 667 } catch (moodle_exception $e) { 668 $this->assertEquals(get_string('courseidnumbertaken', 'error', $created2->idnumber), $e->getMessage()); 669 } 670 671 // Test duplicate shortname. 672 $created2->idnumber = '2'; 673 $created2->shortname = 'test1'; 674 try { 675 update_course($created2); 676 $this->fail('Expected exception when trying to update a course with a duplicate shortname'); 677 } catch (moodle_exception $e) { 678 $this->assertEquals(get_string('shortnametaken', 'error', $created2->shortname), $e->getMessage()); 679 } 680 } 681 682 public function test_course_add_cm_to_section() { 683 global $DB; 684 $this->resetAfterTest(true); 685 686 // Create course with 1 section. 687 $course = $this->getDataGenerator()->create_course( 688 array('shortname' => 'GrowingCourse', 689 'fullname' => 'Growing Course', 690 'numsections' => 1), 691 array('createsections' => true)); 692 693 // Trash modinfo. 694 rebuild_course_cache($course->id, true); 695 696 // Create some cms for testing. 697 $cmids = array(); 698 for ($i=0; $i<4; $i++) { 699 $cmids[$i] = $DB->insert_record('course_modules', array('course' => $course->id)); 700 } 701 702 // Add it to section that exists. 703 course_add_cm_to_section($course, $cmids[0], 1); 704 705 // Check it got added to sequence. 706 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1)); 707 $this->assertEquals($cmids[0], $sequence); 708 709 // Add a second, this time using courseid variant of parameters. 710 $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); 711 course_add_cm_to_section($course->id, $cmids[1], 1); 712 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1)); 713 $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence); 714 715 // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly). 716 $this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id))); 717 $this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id)); 718 719 // Add one to section that doesn't exist (this might rebuild modinfo). 720 course_add_cm_to_section($course, $cmids[2], 2); 721 $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id))); 722 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2)); 723 $this->assertEquals($cmids[2], $sequence); 724 725 // Add using the 'before' option. 726 course_add_cm_to_section($course, $cmids[3], 2, $cmids[2]); 727 $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id))); 728 $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2)); 729 $this->assertEquals($cmids[3] . ',' . $cmids[2], $sequence); 730 } 731 732 public function test_reorder_sections() { 733 global $DB; 734 $this->resetAfterTest(true); 735 736 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true)); 737 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true)); 738 $oldsections = array(); 739 $sections = array(); 740 foreach ($DB->get_records('course_sections', array('course'=>$course->id), 'id') as $section) { 741 $oldsections[$section->section] = $section->id; 742 $sections[$section->id] = $section->section; 743 } 744 ksort($oldsections); 745 746 $neworder = reorder_sections($sections, 2, 4); 747 $neworder = array_keys($neworder); 748 $this->assertEquals($oldsections[0], $neworder[0]); 749 $this->assertEquals($oldsections[1], $neworder[1]); 750 $this->assertEquals($oldsections[2], $neworder[4]); 751 $this->assertEquals($oldsections[3], $neworder[2]); 752 $this->assertEquals($oldsections[4], $neworder[3]); 753 $this->assertEquals($oldsections[5], $neworder[5]); 754 $this->assertEquals($oldsections[6], $neworder[6]); 755 756 $neworder = reorder_sections($sections, 4, 2); 757 $neworder = array_keys($neworder); 758 $this->assertEquals($oldsections[0], $neworder[0]); 759 $this->assertEquals($oldsections[1], $neworder[1]); 760 $this->assertEquals($oldsections[2], $neworder[3]); 761 $this->assertEquals($oldsections[3], $neworder[4]); 762 $this->assertEquals($oldsections[4], $neworder[2]); 763 $this->assertEquals($oldsections[5], $neworder[5]); 764 $this->assertEquals($oldsections[6], $neworder[6]); 765 766 $neworder = reorder_sections(1, 2, 4); 767 $this->assertFalse($neworder); 768 } 769 770 public function test_move_section_down() { 771 global $DB; 772 $this->resetAfterTest(true); 773 774 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true)); 775 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true)); 776 $oldsections = array(); 777 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) { 778 $oldsections[$section->section] = $section->id; 779 } 780 ksort($oldsections); 781 782 // Test move section down.. 783 move_section_to($course, 2, 4); 784 $sections = array(); 785 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) { 786 $sections[$section->section] = $section->id; 787 } 788 ksort($sections); 789 790 $this->assertEquals($oldsections[0], $sections[0]); 791 $this->assertEquals($oldsections[1], $sections[1]); 792 $this->assertEquals($oldsections[2], $sections[4]); 793 $this->assertEquals($oldsections[3], $sections[2]); 794 $this->assertEquals($oldsections[4], $sections[3]); 795 $this->assertEquals($oldsections[5], $sections[5]); 796 $this->assertEquals($oldsections[6], $sections[6]); 797 } 798 799 public function test_move_section_up() { 800 global $DB; 801 $this->resetAfterTest(true); 802 803 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true)); 804 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true)); 805 $oldsections = array(); 806 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) { 807 $oldsections[$section->section] = $section->id; 808 } 809 ksort($oldsections); 810 811 // Test move section up.. 812 move_section_to($course, 6, 4); 813 $sections = array(); 814 foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) { 815 $sections[$section->section] = $section->id; 816 } 817 ksort($sections); 818 819 $this->assertEquals($oldsections[0], $sections[0]); 820 $this->assertEquals($oldsections[1], $sections[1]); 821 $this->assertEquals($oldsections[2], $sections[2]); 822 $this->assertEquals($oldsections[3], $sections[3]); 823 $this->assertEquals($oldsections[4], $sections[5]); 824 $this->assertEquals($oldsections[5], $sections[6]); 825 $this->assertEquals($oldsections[6], $sections[4]); 826 } 827 828 public function test_move_section_marker() { 829 global $DB; 830 $this->resetAfterTest(true); 831 832 $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true)); 833 $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true)); 834 835 // Set course marker to the section we are going to move.. 836 course_set_marker($course->id, 2); 837 // Verify that the course marker is set correctly. 838 $course = $DB->get_record('course', array('id' => $course->id)); 839 $this->assertEquals(2, $course->marker); 840 841 // Test move the marked section down.. 842 move_section_to($course, 2, 4); 843 844 // Verify that the coruse marker has been moved along with the section.. 845 $course = $DB->get_record('course', array('id' => $course->id)); 846 $this->assertEquals(4, $course->marker); 847 848 // Test move the marked section up.. 849 move_section_to($course, 4, 3); 850 851 // Verify that the course marker has been moved along with the section.. 852 $course = $DB->get_record('course', array('id' => $course->id)); 853 $this->assertEquals(3, $course->marker); 854 855 // Test moving a non-marked section above the marked section.. 856 move_section_to($course, 4, 2); 857 858 // Verify that the course marker has been moved down to accomodate.. 859 $course = $DB->get_record('course', array('id' => $course->id)); 860 $this->assertEquals(4, $course->marker); 861 862 // Test moving a non-marked section below the marked section.. 863 move_section_to($course, 3, 6); 864 865 // Verify that the course marker has been up to accomodate.. 866 $course = $DB->get_record('course', array('id' => $course->id)); 867 $this->assertEquals(3, $course->marker); 868 } 869 870 public function test_course_can_delete_section() { 871 global $DB; 872 $this->resetAfterTest(true); 873 874 $generator = $this->getDataGenerator(); 875 876 $courseweeks = $generator->create_course( 877 array('numsections' => 5, 'format' => 'weeks'), 878 array('createsections' => true)); 879 $assign1 = $generator->create_module('assign', array('course' => $courseweeks, 'section' => 1)); 880 $assign2 = $generator->create_module('assign', array('course' => $courseweeks, 'section' => 2)); 881 882 $coursetopics = $generator->create_course( 883 array('numsections' => 5, 'format' => 'topics'), 884 array('createsections' => true)); 885 886 $coursesingleactivity = $generator->create_course( 887 array('format' => 'singleactivity'), 888 array('createsections' => true)); 889 890 // Enrol student and teacher. 891 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); 892 $student = $generator->create_user(); 893 $teacher = $generator->create_user(); 894 895 $generator->enrol_user($student->id, $courseweeks->id, $roleids['student']); 896 $generator->enrol_user($teacher->id, $courseweeks->id, $roleids['editingteacher']); 897 898 $generator->enrol_user($student->id, $coursetopics->id, $roleids['student']); 899 $generator->enrol_user($teacher->id, $coursetopics->id, $roleids['editingteacher']); 900 901 $generator->enrol_user($student->id, $coursesingleactivity->id, $roleids['student']); 902 $generator->enrol_user($teacher->id, $coursesingleactivity->id, $roleids['editingteacher']); 903 904 // Teacher should be able to delete sections (except for 0) in topics and weeks format. 905 $this->setUser($teacher); 906 907 // For topics and weeks formats will return false for section 0 and true for any other section. 908 $this->assertFalse(course_can_delete_section($courseweeks, 0)); 909 $this->assertTrue(course_can_delete_section($courseweeks, 1)); 910 911 $this->assertFalse(course_can_delete_section($coursetopics, 0)); 912 $this->assertTrue(course_can_delete_section($coursetopics, 1)); 913 914 // For singleactivity course format no section can be deleted. 915 $this->assertFalse(course_can_delete_section($coursesingleactivity, 0)); 916 $this->assertFalse(course_can_delete_section($coursesingleactivity, 1)); 917 918 // Now let's revoke a capability from teacher to manage activity in section 1. 919 $modulecontext = context_module::instance($assign1->cmid); 920 assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleids['editingteacher'], 921 $modulecontext); 922 $modulecontext->mark_dirty(); 923 $this->assertFalse(course_can_delete_section($courseweeks, 1)); 924 $this->assertTrue(course_can_delete_section($courseweeks, 2)); 925 926 // Student does not have permissions to delete sections. 927 $this->setUser($student); 928 $this->assertFalse(course_can_delete_section($courseweeks, 1)); 929 $this->assertFalse(course_can_delete_section($coursetopics, 1)); 930 $this->assertFalse(course_can_delete_section($coursesingleactivity, 1)); 931 } 932 933 public function test_course_delete_section() { 934 global $DB; 935 $this->resetAfterTest(true); 936 937 $generator = $this->getDataGenerator(); 938 939 $course = $generator->create_course(array('numsections' => 6, 'format' => 'topics'), 940 array('createsections' => true)); 941 $assign0 = $generator->create_module('assign', array('course' => $course, 'section' => 0)); 942 $assign1 = $generator->create_module('assign', array('course' => $course, 'section' => 1)); 943 $assign21 = $generator->create_module('assign', array('course' => $course, 'section' => 2)); 944 $assign22 = $generator->create_module('assign', array('course' => $course, 'section' => 2)); 945 $assign3 = $generator->create_module('assign', array('course' => $course, 'section' => 3)); 946 $assign5 = $generator->create_module('assign', array('course' => $course, 'section' => 5)); 947 $assign6 = $generator->create_module('assign', array('course' => $course, 'section' => 6)); 948 949 $this->setAdminUser(); 950 951 // Attempt to delete non-existing section. 952 $this->assertFalse(course_delete_section($course, 10, false)); 953 $this->assertFalse(course_delete_section($course, 9, true)); 954 955 // Attempt to delete 0-section. 956 $this->assertFalse(course_delete_section($course, 0, true)); 957 $this->assertTrue($DB->record_exists('course_modules', array('id' => $assign0->cmid))); 958 959 // Delete last section. 960 $this->assertTrue(course_delete_section($course, 6, true)); 961 $this->assertFalse($DB->record_exists('course_modules', array('id' => $assign6->cmid))); 962 $this->assertEquals(5, course_get_format($course)->get_course()->numsections); 963 964 // Delete empty section. 965 $this->assertTrue(course_delete_section($course, 4, false)); 966 $this->assertEquals(4, course_get_format($course)->get_course()->numsections); 967 968 // Delete section in the middle (2). 969 $this->assertFalse(course_delete_section($course, 2, false)); 970 $this->assertTrue(course_delete_section($course, 2, true)); 971 $this->assertFalse($DB->record_exists('course_modules', array('id' => $assign21->cmid))); 972 $this->assertFalse($DB->record_exists('course_modules', array('id' => $assign22->cmid))); 973 $this->assertEquals(3, course_get_format($course)->get_course()->numsections); 974 $this->assertEquals(array(0 => array($assign0->cmid), 975 1 => array($assign1->cmid), 976 2 => array($assign3->cmid), 977 3 => array($assign5->cmid)), get_fast_modinfo($course)->sections); 978 979 // Make last section orphaned. 980 update_course((object)array('id' => $course->id, 'numsections' => 2)); 981 $this->assertEquals(2, course_get_format($course)->get_course()->numsections); 982 983 // Remove orphaned section. 984 $this->assertTrue(course_delete_section($course, 3, true)); 985 $this->assertEquals(2, course_get_format($course)->get_course()->numsections); 986 987 // Remove marked section. 988 course_set_marker($course->id, 1); 989 $this->assertTrue(course_get_format($course)->is_section_current(1)); 990 $this->assertTrue(course_delete_section($course, 1, true)); 991 $this->assertFalse(course_get_format($course)->is_section_current(1)); 992 } 993 994 public function test_get_course_display_name_for_list() { 995 global $CFG; 996 $this->resetAfterTest(true); 997 998 $course = $this->getDataGenerator()->create_course(array('shortname' => 'FROG101', 'fullname' => 'Introduction to pond life')); 999 1000 $CFG->courselistshortnames = 0; 1001 $this->assertEquals('Introduction to pond life', get_course_display_name_for_list($course)); 1002 1003 $CFG->courselistshortnames = 1; 1004 $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course)); 1005 } 1006 1007 public function test_move_module_in_course() { 1008 global $DB; 1009 1010 $this->resetAfterTest(true); 1011 // Setup fixture 1012 $course = $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections' => true)); 1013 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id)); 1014 1015 $cms = get_fast_modinfo($course)->get_cms(); 1016 $cm = reset($cms); 1017 1018 $newsection = get_fast_modinfo($course)->get_section_info(3); 1019 $oldsectionid = $cm->section; 1020 1021 // Perform the move 1022 moveto_module($cm, $newsection); 1023 1024 $cms = get_fast_modinfo($course)->get_cms(); 1025 $cm = reset($cms); 1026 1027 // Check that the cached modinfo contains the correct section info 1028 $modinfo = get_fast_modinfo($course); 1029 $this->assertTrue(empty($modinfo->sections[0])); 1030 $this->assertFalse(empty($modinfo->sections[3])); 1031 1032 // Check that the old section's sequence no longer contains this ID 1033 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid)); 1034 $oldsequences = explode(',', $newsection->sequence); 1035 $this->assertFalse(in_array($cm->id, $oldsequences)); 1036 1037 // Check that the new section's sequence now contains this ID 1038 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id)); 1039 $newsequences = explode(',', $newsection->sequence); 1040 $this->assertTrue(in_array($cm->id, $newsequences)); 1041 1042 // Check that the section number has been changed in the cm 1043 $this->assertEquals($newsection->id, $cm->section); 1044 1045 1046 // Perform a second move as some issues were only seen on the second move 1047 $newsection = get_fast_modinfo($course)->get_section_info(2); 1048 $oldsectionid = $cm->section; 1049 moveto_module($cm, $newsection); 1050 1051 $cms = get_fast_modinfo($course)->get_cms(); 1052 $cm = reset($cms); 1053 1054 // Check that the cached modinfo contains the correct section info 1055 $modinfo = get_fast_modinfo($course); 1056 $this->assertTrue(empty($modinfo->sections[0])); 1057 $this->assertFalse(empty($modinfo->sections[2])); 1058 1059 // Check that the old section's sequence no longer contains this ID 1060 $oldsection = $DB->get_record('course_sections', array('id' => $oldsectionid)); 1061 $oldsequences = explode(',', $newsection->sequence); 1062 $this->assertFalse(in_array($cm->id, $oldsequences)); 1063 1064 // Check that the new section's sequence now contains this ID 1065 $newsection = $DB->get_record('course_sections', array('id' => $newsection->id)); 1066 $newsequences = explode(',', $newsection->sequence); 1067 $this->assertTrue(in_array($cm->id, $newsequences)); 1068 } 1069 1070 public function test_module_visibility() { 1071 $this->setAdminUser(); 1072 $this->resetAfterTest(true); 1073 1074 // Create course and modules. 1075 $course = $this->getDataGenerator()->create_course(array('numsections' => 5)); 1076 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 1077 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id)); 1078 $modules = compact('forum', 'assign'); 1079 1080 // Hiding the modules. 1081 foreach ($modules as $mod) { 1082 set_coursemodule_visible($mod->cmid, 0); 1083 $this->check_module_visibility($mod, 0, 0); 1084 } 1085 1086 // Showing the modules. 1087 foreach ($modules as $mod) { 1088 set_coursemodule_visible($mod->cmid, 1); 1089 $this->check_module_visibility($mod, 1, 1); 1090 } 1091 } 1092 1093 public function test_section_visibility_events() { 1094 $this->setAdminUser(); 1095 $this->resetAfterTest(true); 1096 1097 $course = $this->getDataGenerator()->create_course(array('numsections' => 1), array('createsections' => true)); 1098 $sectionnumber = 1; 1099 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 1100 array('section' => $sectionnumber)); 1101 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 1102 'course' => $course->id), array('section' => $sectionnumber)); 1103 $sink = $this->redirectEvents(); 1104 set_section_visible($course->id, $sectionnumber, 0); 1105 $events = $sink->get_events(); 1106 1107 // Extract the number of events related to what we are testing, other events 1108 // such as course_section_updated could have been triggered. 1109 $count = 0; 1110 foreach ($events as $event) { 1111 if ($event instanceof \core\event\course_module_updated) { 1112 $count++; 1113 } 1114 } 1115 $this->assertSame(2, $count); 1116 $sink->close(); 1117 } 1118 1119 public function test_section_visibility() { 1120 $this->setAdminUser(); 1121 $this->resetAfterTest(true); 1122 1123 // Create course. 1124 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true)); 1125 1126 $sink = $this->redirectEvents(); 1127 1128 // Testing an empty section. 1129 $sectionnumber = 1; 1130 set_section_visible($course->id, $sectionnumber, 0); 1131 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1132 $this->assertEquals($section_info->visible, 0); 1133 set_section_visible($course->id, $sectionnumber, 1); 1134 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1135 $this->assertEquals($section_info->visible, 1); 1136 1137 // Checking that an event was fired. 1138 $events = $sink->get_events(); 1139 $this->assertInstanceOf('\core\event\course_section_updated', $events[0]); 1140 $sink->close(); 1141 1142 // Testing a section with visible modules. 1143 $sectionnumber = 2; 1144 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 1145 array('section' => $sectionnumber)); 1146 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 1147 'course' => $course->id), array('section' => $sectionnumber)); 1148 $modules = compact('forum', 'assign'); 1149 set_section_visible($course->id, $sectionnumber, 0); 1150 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1151 $this->assertEquals($section_info->visible, 0); 1152 foreach ($modules as $mod) { 1153 $this->check_module_visibility($mod, 0, 1); 1154 } 1155 set_section_visible($course->id, $sectionnumber, 1); 1156 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1157 $this->assertEquals($section_info->visible, 1); 1158 foreach ($modules as $mod) { 1159 $this->check_module_visibility($mod, 1, 1); 1160 } 1161 1162 // Testing a section with hidden modules, which should stay hidden. 1163 $sectionnumber = 3; 1164 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 1165 array('section' => $sectionnumber)); 1166 $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 1167 'course' => $course->id), array('section' => $sectionnumber)); 1168 $modules = compact('forum', 'assign'); 1169 foreach ($modules as $mod) { 1170 set_coursemodule_visible($mod->cmid, 0); 1171 $this->check_module_visibility($mod, 0, 0); 1172 } 1173 set_section_visible($course->id, $sectionnumber, 0); 1174 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1175 $this->assertEquals($section_info->visible, 0); 1176 foreach ($modules as $mod) { 1177 $this->check_module_visibility($mod, 0, 0); 1178 } 1179 set_section_visible($course->id, $sectionnumber, 1); 1180 $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber); 1181 $this->assertEquals($section_info->visible, 1); 1182 foreach ($modules as $mod) { 1183 $this->check_module_visibility($mod, 0, 0); 1184 } 1185 } 1186 1187 /** 1188 * Helper function to assert that a module has correctly been made visible, or hidden. 1189 * 1190 * @param stdClass $mod module information 1191 * @param int $visibility the current state of the module 1192 * @param int $visibleold the current state of the visibleold property 1193 * @return void 1194 */ 1195 public function check_module_visibility($mod, $visibility, $visibleold) { 1196 global $DB; 1197 $cm = get_fast_modinfo($mod->course)->get_cm($mod->cmid); 1198 $this->assertEquals($visibility, $cm->visible); 1199 $this->assertEquals($visibleold, $cm->visibleold); 1200 1201 // Check the module grade items. 1202 $grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $cm->modname, 1203 'iteminstance' => $cm->instance, 'courseid' => $cm->course)); 1204 if ($grade_items) { 1205 foreach ($grade_items as $grade_item) { 1206 if ($visibility) { 1207 $this->assertFalse($grade_item->is_hidden(), "$cm->modname grade_item not visible"); 1208 } else { 1209 $this->assertTrue($grade_item->is_hidden(), "$cm->modname grade_item not hidden"); 1210 } 1211 } 1212 } 1213 1214 // Check the events visibility. 1215 if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $cm->modname))) { 1216 foreach ($events as $event) { 1217 $calevent = new calendar_event($event); 1218 $this->assertEquals($visibility, $calevent->visible, "$cm->modname calendar_event visibility"); 1219 } 1220 } 1221 } 1222 1223 public function test_course_page_type_list() { 1224 global $DB; 1225 $this->resetAfterTest(true); 1226 1227 // Create a category. 1228 $category = new stdClass(); 1229 $category->name = 'Test Category'; 1230 1231 $testcategory = $this->getDataGenerator()->create_category($category); 1232 1233 // Create a course. 1234 $course = new stdClass(); 1235 $course->fullname = 'Apu loves Unit TÉ™sts'; 1236 $course->shortname = 'Spread the lÅve'; 1237 $course->idnumber = '123'; 1238 $course->summary = 'Awesome!'; 1239 $course->summaryformat = FORMAT_PLAIN; 1240 $course->format = 'topics'; 1241 $course->newsitems = 0; 1242 $course->numsections = 5; 1243 $course->category = $testcategory->id; 1244 1245 $testcourse = $this->getDataGenerator()->create_course($course); 1246 1247 // Create contexts. 1248 $coursecontext = context_course::instance($testcourse->id); 1249 $parentcontext = $coursecontext->get_parent_context(); // Not actually used. 1250 $pagetype = 'page-course-x'; // Not used either. 1251 $pagetypelist = course_page_type_list($pagetype, $parentcontext, $coursecontext); 1252 1253 // Page type lists for normal courses. 1254 $testpagetypelist1 = array(); 1255 $testpagetypelist1['*'] = 'Any page'; 1256 $testpagetypelist1['course-*'] = 'Any course page'; 1257 $testpagetypelist1['course-view-*'] = 'Any type of course main page'; 1258 1259 $this->assertEquals($testpagetypelist1, $pagetypelist); 1260 1261 // Get the context for the front page course. 1262 $sitecoursecontext = context_course::instance(SITEID); 1263 $pagetypelist = course_page_type_list($pagetype, $parentcontext, $sitecoursecontext); 1264 1265 // Page type list for the front page course. 1266 $testpagetypelist2 = array('*' => 'Any page'); 1267 $this->assertEquals($testpagetypelist2, $pagetypelist); 1268 1269 // Make sure that providing no current context to the function doesn't result in an error. 1270 // Calls made from generate_page_type_patterns() may provide null values. 1271 $pagetypelist = course_page_type_list($pagetype, null, null); 1272 $this->assertEquals($pagetypelist, $testpagetypelist1); 1273 } 1274 1275 public function test_compare_activities_by_time_desc() { 1276 1277 // Let's create some test data. 1278 $activitiesivities = array(); 1279 $x = new stdClass(); 1280 $x->timestamp = null; 1281 $activities[] = $x; 1282 1283 $x = new stdClass(); 1284 $x->timestamp = 1; 1285 $activities[] = $x; 1286 1287 $x = new stdClass(); 1288 $x->timestamp = 3; 1289 $activities[] = $x; 1290 1291 $x = new stdClass(); 1292 $x->timestamp = 0; 1293 $activities[] = $x; 1294 1295 $x = new stdClass(); 1296 $x->timestamp = 5; 1297 $activities[] = $x; 1298 1299 $x = new stdClass(); 1300 $activities[] = $x; 1301 1302 $x = new stdClass(); 1303 $x->timestamp = 5; 1304 $activities[] = $x; 1305 1306 // Do the sorting. 1307 usort($activities, 'compare_activities_by_time_desc'); 1308 1309 // Let's check the result. 1310 $last = 10; 1311 foreach($activities as $activity) { 1312 if (empty($activity->timestamp)) { 1313 $activity->timestamp = 0; 1314 } 1315 $this->assertLessThanOrEqual($last, $activity->timestamp); 1316 } 1317 } 1318 1319 public function test_compare_activities_by_time_asc() { 1320 1321 // Let's create some test data. 1322 $activities = array(); 1323 $x = new stdClass(); 1324 $x->timestamp = null; 1325 $activities[] = $x; 1326 1327 $x = new stdClass(); 1328 $x->timestamp = 1; 1329 $activities[] = $x; 1330 1331 $x = new stdClass(); 1332 $x->timestamp = 3; 1333 $activities[] = $x; 1334 1335 $x = new stdClass(); 1336 $x->timestamp = 0; 1337 $activities[] = $x; 1338 1339 $x = new stdClass(); 1340 $x->timestamp = 5; 1341 $activities[] = $x; 1342 1343 $x = new stdClass(); 1344 $activities[] = $x; 1345 1346 $x = new stdClass(); 1347 $x->timestamp = 5; 1348 $activities[] = $x; 1349 1350 // Do the sorting. 1351 usort($activities, 'compare_activities_by_time_asc'); 1352 1353 // Let's check the result. 1354 $last = 0; 1355 foreach($activities as $activity) { 1356 if (empty($activity->timestamp)) { 1357 $activity->timestamp = 0; 1358 } 1359 $this->assertGreaterThanOrEqual($last, $activity->timestamp); 1360 } 1361 } 1362 1363 /** 1364 * Tests moving a module between hidden/visible sections and 1365 * verifies that the course/module visiblity seettings are 1366 * retained. 1367 */ 1368 public function test_moveto_module_between_hidden_sections() { 1369 global $DB; 1370 1371 $this->resetAfterTest(true); 1372 1373 $course = $this->getDataGenerator()->create_course(array('numsections' => 4), array('createsections' => true)); 1374 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 1375 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id)); 1376 $quiz= $this->getDataGenerator()->create_module('quiz', array('course' => $course->id)); 1377 1378 // Set the page as hidden 1379 set_coursemodule_visible($page->cmid, 0); 1380 1381 // Set sections 3 as hidden. 1382 set_section_visible($course->id, 3, 0); 1383 1384 $modinfo = get_fast_modinfo($course); 1385 1386 $hiddensection = $modinfo->get_section_info(3); 1387 // New section is definitely not visible: 1388 $this->assertEquals($hiddensection->visible, 0); 1389 1390 $forumcm = $modinfo->cms[$forum->cmid]; 1391 $pagecm = $modinfo->cms[$page->cmid]; 1392 1393 // Move the forum and the page to a hidden section, make sure moveto_module returns 0 as new visibility state. 1394 $this->assertEquals(0, moveto_module($forumcm, $hiddensection)); 1395 $this->assertEquals(0, moveto_module($pagecm, $hiddensection)); 1396 1397 $modinfo = get_fast_modinfo($course); 1398 1399 // Verify that forum and page have been moved to the hidden section and quiz has not. 1400 $this->assertContains($forum->cmid, $modinfo->sections[3]); 1401 $this->assertContains($page->cmid, $modinfo->sections[3]); 1402 $this->assertNotContains($quiz->cmid, $modinfo->sections[3]); 1403 1404 // Verify that forum has been made invisible. 1405 $forumcm = $modinfo->cms[$forum->cmid]; 1406 $this->assertEquals($forumcm->visible, 0); 1407 // Verify that old state has been retained. 1408 $this->assertEquals($forumcm->visibleold, 1); 1409 1410 // Verify that page has stayed invisible. 1411 $pagecm = $modinfo->cms[$page->cmid]; 1412 $this->assertEquals($pagecm->visible, 0); 1413 // Verify that old state has been retained. 1414 $this->assertEquals($pagecm->visibleold, 0); 1415 1416 // Verify that quiz has been unaffected. 1417 $quizcm = $modinfo->cms[$quiz->cmid]; 1418 $this->assertEquals($quizcm->visible, 1); 1419 1420 // Move forum and page back to visible section. 1421 // Make sure the visibility is restored to the original value (visible for forum and hidden for page). 1422 $visiblesection = $modinfo->get_section_info(2); 1423 $this->assertEquals(1, moveto_module($forumcm, $visiblesection)); 1424 $this->assertEquals(0, moveto_module($pagecm, $visiblesection)); 1425 1426 $modinfo = get_fast_modinfo($course); 1427 1428 // Double check that forum has been made visible. 1429 $forumcm = $modinfo->cms[$forum->cmid]; 1430 $this->assertEquals($forumcm->visible, 1); 1431 1432 // Double check that page has stayed invisible. 1433 $pagecm = $modinfo->cms[$page->cmid]; 1434 $this->assertEquals($pagecm->visible, 0); 1435 1436 // Move the page in the same section (this is what mod duplicate does). 1437 // Visibility of page remains 0. 1438 $this->assertEquals(0, moveto_module($pagecm, $visiblesection, $forumcm)); 1439 1440 // Double check that the the page is still hidden. 1441 $modinfo = get_fast_modinfo($course); 1442 $pagecm = $modinfo->cms[$page->cmid]; 1443 $this->assertEquals($pagecm->visible, 0); 1444 } 1445 1446 /** 1447 * Tests moving a module around in the same section. moveto_module() 1448 * is called this way in modduplicate. 1449 */ 1450 public function test_moveto_module_in_same_section() { 1451 global $DB; 1452 1453 $this->resetAfterTest(true); 1454 1455 $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true)); 1456 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id)); 1457 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id)); 1458 1459 // Simulate inconsistent visible/visibleold values (MDL-38713). 1460 $cm = $DB->get_record('course_modules', array('id' => $page->cmid), '*', MUST_EXIST); 1461 $cm->visible = 0; 1462 $cm->visibleold = 1; 1463 $DB->update_record('course_modules', $cm); 1464 1465 $modinfo = get_fast_modinfo($course); 1466 $forumcm = $modinfo->cms[$forum->cmid]; 1467 $pagecm = $modinfo->cms[$page->cmid]; 1468 1469 // Verify that page is hidden. 1470 $this->assertEquals($pagecm->visible, 0); 1471 1472 // Verify section 0 is where all mods added. 1473 $section = $modinfo->get_section_info(0); 1474 $this->assertEquals($section->id, $forumcm->section); 1475 $this->assertEquals($section->id, $pagecm->section); 1476 1477 1478 // Move the page inside the hidden section. Make sure it is hidden. 1479 $this->assertEquals(0, moveto_module($pagecm, $section, $forumcm)); 1480 1481 // Double check that the the page is still hidden. 1482 $modinfo = get_fast_modinfo($course); 1483 $pagecm = $modinfo->cms[$page->cmid]; 1484 $this->assertEquals($pagecm->visible, 0); 1485 } 1486 1487 /** 1488 * Tests the function that deletes a course module 1489 * 1490 * @param string $type The type of module for the test 1491 * @param array $options The options for the module creation 1492 * @dataProvider provider_course_delete_module 1493 */ 1494 public function test_course_delete_module($type, $options) { 1495 global $DB; 1496 1497 $this->resetAfterTest(true); 1498 $this->setAdminUser(); 1499 1500 // Create course and modules. 1501 $course = $this->getDataGenerator()->create_course(array('numsections' => 5)); 1502 $options['course'] = $course->id; 1503 1504 // Generate an assignment with due date (will generate a course event). 1505 $module = $this->getDataGenerator()->create_module($type, $options); 1506 1507 // Get the module context. 1508 $modcontext = context_module::instance($module->cmid); 1509 1510 // Verify context exists. 1511 $this->assertInstanceOf('context_module', $modcontext); 1512 1513 // Make module specific messes. 1514 switch ($type) { 1515 case 'assign': 1516 // Add some tags to this assignment. 1517 core_tag_tag::set_item_tags('mod_assign', 'assign', $module->id, $modcontext, array('Tag 1', 'Tag 2', 'Tag 3')); 1518 core_tag_tag::set_item_tags('core', 'course_modules', $module->cmid, $modcontext, array('Tag 3', 'Tag 4', 'Tag 5')); 1519 1520 // Confirm the tag instances were added. 1521 $criteria = array('component' => 'mod_assign', 'itemtype' => 'assign', 'contextid' => $modcontext->id); 1522 $this->assertEquals(3, $DB->count_records('tag_instance', $criteria)); 1523 $criteria = array('component' => 'core', 'itemtype' => 'course_modules', 'contextid' => $modcontext->id); 1524 $this->assertEquals(3, $DB->count_records('tag_instance', $criteria)); 1525 1526 // Verify event assignment event has been generated. 1527 $eventcount = $DB->count_records('event', array('instance' => $module->id, 'modulename' => $type)); 1528 $this->assertEquals(1, $eventcount); 1529 1530 break; 1531 case 'quiz': 1532 $qgen = $this->getDataGenerator()->get_plugin_generator('core_question'); 1533 $qcat = $qgen->create_question_category(array('contextid' => $modcontext->id)); 1534 $questions = array( 1535 $qgen->create_question('shortanswer', null, array('category' => $qcat->id)), 1536 $qgen->create_question('shortanswer', null, array('category' => $qcat->id)), 1537 ); 1538 $this->expectOutputRegex('/'.get_string('unusedcategorydeleted', 'question').'/'); 1539 break; 1540 default: 1541 break; 1542 } 1543 1544 // Run delete.. 1545 course_delete_module($module->cmid); 1546 1547 // Verify the context has been removed. 1548 $this->assertFalse(context_module::instance($module->cmid, IGNORE_MISSING)); 1549 1550 // Verify the course_module record has been deleted. 1551 $cmcount = $DB->count_records('course_modules', array('id' => $module->cmid)); 1552 $this->assertEmpty($cmcount); 1553 1554 // Test clean up of module specific messes. 1555 switch ($type) { 1556 case 'assign': 1557 // Verify event assignment events have been removed. 1558 $eventcount = $DB->count_records('event', array('instance' => $module->id, 'modulename' => $type)); 1559 $this->assertEmpty($eventcount); 1560 1561 // Verify the tag instances were deleted. 1562 $criteria = array('component' => 'mod_assign', 'contextid' => $modcontext->id); 1563 $this->assertEquals(0, $DB->count_records('tag_instance', $criteria)); 1564 1565 $criteria = array('component' => 'core', 'itemtype' => 'course_modules', 'contextid' => $modcontext->id); 1566 $this->assertEquals(0, $DB->count_records('tag_instance', $criteria)); 1567 break; 1568 case 'quiz': 1569 // Verify category deleted. 1570 $criteria = array('contextid' => $modcontext->id); 1571 $this->assertEquals(0, $DB->count_records('question_categories', $criteria)); 1572 1573 // Verify questions deleted. 1574 $criteria = array('category' => $qcat->id); 1575 $this->assertEquals(0, $DB->count_records('question', $criteria)); 1576 break; 1577 default: 1578 break; 1579 } 1580 } 1581 1582 /** 1583 * Test that triggering a course_created event works as expected. 1584 */ 1585 public function test_course_created_event() { 1586 global $DB; 1587 1588 $this->resetAfterTest(); 1589 1590 // Catch the events. 1591 $sink = $this->redirectEvents(); 1592 1593 // Create the course with an id number which is used later when generating a course via the imsenterprise plugin. 1594 $data = new stdClass(); 1595 $data->idnumber = 'idnumber'; 1596 $course = $this->getDataGenerator()->create_course($data); 1597 // Get course from DB for comparison. 1598 $course = $DB->get_record('course', array('id' => $course->id)); 1599 1600 // Capture the event. 1601 $events = $sink->get_events(); 1602 $sink->close(); 1603 1604 // Validate the event. 1605 $event = $events[0]; 1606 $this->assertInstanceOf('\core\event\course_created', $event); 1607 $this->assertEquals('course', $event->objecttable); 1608 $this->assertEquals($course->id, $event->objectid); 1609 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1610 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); 1611 $this->assertEquals('course_created', $event->get_legacy_eventname()); 1612 $this->assertEventLegacyData($course, $event); 1613 $expectedlog = array(SITEID, 'course', 'new', 'view.php?id=' . $course->id, $course->fullname . ' (ID ' . $course->id . ')'); 1614 $this->assertEventLegacyLogData($expectedlog, $event); 1615 1616 // Now we want to trigger creating a course via the imsenterprise. 1617 // Delete the course we created earlier, as we want the imsenterprise plugin to create this. 1618 // We do not want print out any of the text this function generates while doing this, which is why 1619 // we are using ob_start() and ob_end_clean(). 1620 ob_start(); 1621 delete_course($course); 1622 ob_end_clean(); 1623 1624 // Create the XML file we want to use. 1625 $course->category = (array)$course->category; 1626 $imstestcase = new enrol_imsenterprise_testcase(); 1627 $imstestcase->imsplugin = enrol_get_plugin('imsenterprise'); 1628 $imstestcase->set_test_config(); 1629 $imstestcase->set_xml_file(false, array($course)); 1630 1631 // Capture the event. 1632 $sink = $this->redirectEvents(); 1633 $imstestcase->imsplugin->cron(); 1634 $events = $sink->get_events(); 1635 $sink->close(); 1636 $event = null; 1637 foreach ($events as $eventinfo) { 1638 if ($eventinfo instanceof \core\event\course_created ) { 1639 $event = $eventinfo; 1640 break; 1641 } 1642 } 1643 1644 // Validate the event triggered is \core\event\course_created. There is no need to validate the other values 1645 // as they have already been validated in the previous steps. Here we only want to make sure that when the 1646 // imsenterprise plugin creates a course an event is triggered. 1647 $this->assertInstanceOf('\core\event\course_created', $event); 1648 $this->assertEventContextNotUsed($event); 1649 } 1650 1651 /** 1652 * Test that triggering a course_updated event works as expected. 1653 */ 1654 public function test_course_updated_event() { 1655 global $DB; 1656 1657 $this->resetAfterTest(); 1658 1659 // Create a course. 1660 $course = $this->getDataGenerator()->create_course(); 1661 1662 // Create a category we are going to move this course to. 1663 $category = $this->getDataGenerator()->create_category(); 1664 1665 // Create a hidden category we are going to move this course to. 1666 $categoryhidden = $this->getDataGenerator()->create_category(array('visible' => 0)); 1667 1668 // Update course and catch course_updated event. 1669 $sink = $this->redirectEvents(); 1670 update_course($course); 1671 $events = $sink->get_events(); 1672 $sink->close(); 1673 1674 // Get updated course information from the DB. 1675 $updatedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1676 // Validate event. 1677 $event = array_shift($events); 1678 $this->assertInstanceOf('\core\event\course_updated', $event); 1679 $this->assertEquals('course', $event->objecttable); 1680 $this->assertEquals($updatedcourse->id, $event->objectid); 1681 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1682 $url = new moodle_url('/course/edit.php', array('id' => $event->objectid)); 1683 $this->assertEquals($url, $event->get_url()); 1684 $this->assertEquals($updatedcourse, $event->get_record_snapshot('course', $event->objectid)); 1685 $this->assertEquals('course_updated', $event->get_legacy_eventname()); 1686 $this->assertEventLegacyData($updatedcourse, $event); 1687 $expectedlog = array($updatedcourse->id, 'course', 'update', 'edit.php?id=' . $course->id, $course->id); 1688 $this->assertEventLegacyLogData($expectedlog, $event); 1689 1690 // Move course and catch course_updated event. 1691 $sink = $this->redirectEvents(); 1692 move_courses(array($course->id), $category->id); 1693 $events = $sink->get_events(); 1694 $sink->close(); 1695 1696 // Return the moved course information from the DB. 1697 $movedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1698 // Validate event. 1699 $event = array_shift($events); 1700 $this->assertInstanceOf('\core\event\course_updated', $event); 1701 $this->assertEquals('course', $event->objecttable); 1702 $this->assertEquals($movedcourse->id, $event->objectid); 1703 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1704 $this->assertEquals($movedcourse, $event->get_record_snapshot('course', $movedcourse->id)); 1705 $this->assertEquals('course_updated', $event->get_legacy_eventname()); 1706 $this->assertEventLegacyData($movedcourse, $event); 1707 $expectedlog = array($movedcourse->id, 'course', 'move', 'edit.php?id=' . $movedcourse->id, $movedcourse->id); 1708 $this->assertEventLegacyLogData($expectedlog, $event); 1709 1710 // Move course to hidden category and catch course_updated event. 1711 $sink = $this->redirectEvents(); 1712 move_courses(array($course->id), $categoryhidden->id); 1713 $events = $sink->get_events(); 1714 $sink->close(); 1715 1716 // Return the moved course information from the DB. 1717 $movedcoursehidden = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1718 // Validate event. 1719 $event = array_shift($events); 1720 $this->assertInstanceOf('\core\event\course_updated', $event); 1721 $this->assertEquals('course', $event->objecttable); 1722 $this->assertEquals($movedcoursehidden->id, $event->objectid); 1723 $this->assertEquals(context_course::instance($course->id), $event->get_context()); 1724 $this->assertEquals($movedcoursehidden, $event->get_record_snapshot('course', $movedcoursehidden->id)); 1725 $this->assertEquals('course_updated', $event->get_legacy_eventname()); 1726 $this->assertEventLegacyData($movedcoursehidden, $event); 1727 $expectedlog = array($movedcoursehidden->id, 'course', 'move', 'edit.php?id=' . $movedcoursehidden->id, $movedcoursehidden->id); 1728 $this->assertEventLegacyLogData($expectedlog, $event); 1729 $this->assertEventContextNotUsed($event); 1730 } 1731 1732 /** 1733 * Test that triggering a course_deleted event works as expected. 1734 */ 1735 public function test_course_deleted_event() { 1736 $this->resetAfterTest(); 1737 1738 // Create the course. 1739 $course = $this->getDataGenerator()->create_course(); 1740 1741 // Save the course context before we delete the course. 1742 $coursecontext = context_course::instance($course->id); 1743 1744 // Catch the update event. 1745 $sink = $this->redirectEvents(); 1746 1747 // Call delete_course() which will trigger the course_deleted event and the course_content_deleted 1748 // event. This function prints out data to the screen, which we do not want during a PHPUnit test, 1749 // so use ob_start and ob_end_clean to prevent this. 1750 ob_start(); 1751 delete_course($course); 1752 ob_end_clean(); 1753 1754 // Capture the event. 1755 $events = $sink->get_events(); 1756 $sink->close(); 1757 1758 // Validate the event. 1759 $event = array_pop($events); 1760 $this->assertInstanceOf('\core\event\course_deleted', $event); 1761 $this->assertEquals('course', $event->objecttable); 1762 $this->assertEquals($course->id, $event->objectid); 1763 $this->assertEquals($coursecontext->id, $event->contextid); 1764 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); 1765 $this->assertEquals('course_deleted', $event->get_legacy_eventname()); 1766 $eventdata = $event->get_data(); 1767 $this->assertSame($course->idnumber, $eventdata['other']['idnumber']); 1768 $this->assertSame($course->fullname, $eventdata['other']['fullname']); 1769 $this->assertSame($course->shortname, $eventdata['other']['shortname']); 1770 1771 // The legacy data also passed the context in the course object and substitutes timemodified with the current date. 1772 $expectedlegacy = clone($course); 1773 $expectedlegacy->context = $coursecontext; 1774 $expectedlegacy->timemodified = $event->timecreated; 1775 $this->assertEventLegacyData($expectedlegacy, $event); 1776 1777 // Validate legacy log data. 1778 $expectedlog = array(SITEID, 'course', 'delete', 'view.php?id=' . $course->id, $course->fullname . '(ID ' . $course->id . ')'); 1779 $this->assertEventLegacyLogData($expectedlog, $event); 1780 $this->assertEventContextNotUsed($event); 1781 } 1782 1783 /** 1784 * Test that triggering a course_content_deleted event works as expected. 1785 */ 1786 public function test_course_content_deleted_event() { 1787 global $DB; 1788 1789 $this->resetAfterTest(); 1790 1791 // Create the course. 1792 $course = $this->getDataGenerator()->create_course(); 1793 1794 // Get the course from the DB. The data generator adds some extra properties, such as 1795 // numsections, to the course object which will fail the assertions later on. 1796 $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); 1797 1798 // Save the course context before we delete the course. 1799 $coursecontext = context_course::instance($course->id); 1800 1801 // Catch the update event. 1802 $sink = $this->redirectEvents(); 1803 1804 remove_course_contents($course->id, false); 1805 1806 // Capture the event. 1807 $events = $sink->get_events(); 1808 $sink->close(); 1809 1810 // Validate the event. 1811 $event = array_pop($events); 1812 $this->assertInstanceOf('\core\event\course_content_deleted', $event); 1813 $this->assertEquals('course', $event->objecttable); 1814 $this->assertEquals($course->id, $event->objectid); 1815 $this->assertEquals($coursecontext->id, $event->contextid); 1816 $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); 1817 $this->assertEquals('course_content_removed', $event->get_legacy_eventname()); 1818 // The legacy data also passed the context and options in the course object. 1819 $course->context = $coursecontext; 1820 $course->options = array(); 1821 $this->assertEventLegacyData($course, $event); 1822 $this->assertEventContextNotUsed($event); 1823 } 1824 1825 /** 1826 * Test that triggering a course_category_deleted event works as expected. 1827 */ 1828 public function test_course_category_deleted_event() { 1829 $this->resetAfterTest(); 1830 1831 // Create a category. 1832 $category = $this->getDataGenerator()->create_category(); 1833 1834 // Save the context before it is deleted. 1835 $categorycontext = context_coursecat::instance($category->id); 1836 1837 // Catch the update event. 1838 $sink = $this->redirectEvents(); 1839 1840 // Delete the category. 1841 $category->delete_full(); 1842 1843 // Capture the event. 1844 $events = $sink->get_events(); 1845 $sink->close(); 1846 1847 // Validate the event. 1848 $event = $events[0]; 1849 $this->assertInstanceOf('\core\event\course_category_deleted', $event); 1850 $this->assertEquals('course_categories', $event->objecttable); 1851 $this->assertEquals($category->id, $event->objectid); 1852 $this->assertEquals($categorycontext->id, $event->contextid); 1853 $this->assertEquals('course_category_deleted', $event->get_legacy_eventname()); 1854 $this->assertEquals(null, $event->get_url()); 1855 $this->assertEventLegacyData($category, $event); 1856 $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category->name . '(ID ' . $category->id . ')'); 1857 $this->assertEventLegacyLogData($expectedlog, $event); 1858 1859 // Create two categories. 1860 $category = $this->getDataGenerator()->create_category(); 1861 $category2 = $this->getDataGenerator()->create_category(); 1862 1863 // Save the context before it is moved and then deleted. 1864 $category2context = context_coursecat::instance($category2->id); 1865 1866 // Catch the update event. 1867 $sink = $this->redirectEvents(); 1868 1869 // Move the category. 1870 $category2->delete_move($category->id); 1871 1872 // Capture the event. 1873 $events = $sink->get_events(); 1874 $sink->close(); 1875 1876 // Validate the event. 1877 $event = $events[0]; 1878 $this->assertInstanceOf('\core\event\course_category_deleted', $event); 1879 $this->assertEquals('course_categories', $event->objecttable); 1880 $this->assertEquals($category2->id, $event->objectid); 1881 $this->assertEquals($category2context->id, $event->contextid); 1882 $this->assertEquals('course_category_deleted', $event->get_legacy_eventname()); 1883 $this->assertEventLegacyData($category2, $event); 1884 $expectedlog = array(SITEID, 'category', 'delete', 'index.php', $category2->name . '(ID ' . $category2->id . ')'); 1885 $this->assertEventLegacyLogData($expectedlog, $event); 1886 $this->assertEventContextNotUsed($event); 1887 } 1888 1889 /** 1890 * Test that triggering a course_restored event works as expected. 1891 */ 1892 public function test_course_restored_event() { 1893 global $CFG; 1894 1895 // Get the necessary files to perform backup and restore. 1896 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 1897 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 1898 1899 $this->resetAfterTest(); 1900 1901 // Set to admin user. 1902 $this->setAdminUser(); 1903 1904 // The user id is going to be 2 since we are the admin user. 1905 $userid = 2; 1906 1907 // Create a course. 1908 $course = $this->getDataGenerator()->create_course(); 1909 1910 // Create backup file and save it to the backup location. 1911 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, 1912 backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid); 1913 $bc->execute_plan(); 1914 $results = $bc->get_results(); 1915 $file = $results['backup_destination']; 1916 $fp = get_file_packer('application/vnd.moodle.backup'); 1917 $filepath = $CFG->dataroot . '/temp/backup/test-restore-course-event'; 1918 $file->extract_to_pathname($fp, $filepath); 1919 $bc->destroy(); 1920 1921 // Now we want to catch the restore course event. 1922 $sink = $this->redirectEvents(); 1923 1924 // Now restore the course to trigger the event. 1925 $rc = new restore_controller('test-restore-course-event', $course->id, backup::INTERACTIVE_NO, 1926 backup::MODE_GENERAL, $userid, backup::TARGET_NEW_COURSE); 1927 $rc->execute_precheck(); 1928 $rc->execute_plan(); 1929 1930 // Capture the event. 1931 $events = $sink->get_events(); 1932 $sink->close(); 1933 1934 // Validate the event. 1935 $event = array_pop($events); 1936 $this->assertInstanceOf('\core\event\course_restored', $event); 1937 $this->assertEquals('course', $event->objecttable); 1938 $this->assertEquals($rc->get_courseid(), $event->objectid); 1939 $this->assertEquals(context_course::instance($rc->get_courseid())->id, $event->contextid); 1940 $this->assertEquals('course_restored', $event->get_legacy_eventname()); 1941 $legacydata = (object) array( 1942 'courseid' => $rc->get_courseid(), 1943 'userid' => $rc->get_userid(), 1944 'type' => $rc->get_type(), 1945 'target' => $rc->get_target(), 1946 'mode' => $rc->get_mode(), 1947 'operation' => $rc->get_operation(), 1948 'samesite' => $rc->is_samesite() 1949 ); 1950 $url = new moodle_url('/course/view.php', array('id' => $event->objectid)); 1951 $this->assertEquals($url, $event->get_url()); 1952 $this->assertEventLegacyData($legacydata, $event); 1953 $this->assertEventContextNotUsed($event); 1954 1955 // Destroy the resource controller since we are done using it. 1956 $rc->destroy(); 1957 } 1958 1959 /** 1960 * Test that triggering a course_section_updated event works as expected. 1961 */ 1962 public function test_course_section_updated_event() { 1963 global $DB; 1964 1965 $this->resetAfterTest(); 1966 1967 // Create the course with sections. 1968 $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true)); 1969 $sections = $DB->get_records('course_sections', array('course' => $course->id)); 1970 1971 $coursecontext = context_course::instance($course->id); 1972 1973 $section = array_pop($sections); 1974 $section->name = 'Test section'; 1975 $section->summary = 'Test section summary'; 1976 $DB->update_record('course_sections', $section); 1977 1978 // Trigger an event for course section update. 1979 $event = \core\event\course_section_updated::create( 1980 array( 1981 'objectid' => $section->id, 1982 'courseid' => $course->id, 1983 'context' => context_course::instance($course->id), 1984 'other' => array( 1985 'sectionnum' => $section->section 1986 ) 1987 ) 1988 ); 1989 $event->add_record_snapshot('course_sections', $section); 1990 // Trigger and catch event. 1991 $sink = $this->redirectEvents(); 1992 $event->trigger(); 1993 $events = $sink->get_events(); 1994 $sink->close(); 1995 1996 // Validate the event. 1997 $event = $events[0]; 1998 $this->assertInstanceOf('\core\event\course_section_updated', $event); 1999 $this->assertEquals('course_sections', $event->objecttable); 2000 $this->assertEquals($section->id, $event->objectid); 2001 $this->assertEquals($course->id, $event->courseid); 2002 $this->assertEquals($coursecontext->id, $event->contextid); 2003 $this->assertEquals($section->section, $event->other['sectionnum']); 2004 $expecteddesc = "The user with id '{$event->userid}' updated section number '{$event->other['sectionnum']}' for the course with id '{$event->courseid}'"; 2005 $this->assertEquals($expecteddesc, $event->get_description()); 2006 $url = new moodle_url('/course/editsection.php', array('id' => $event->objectid)); 2007 $this->assertEquals($url, $event->get_url()); 2008 $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid)); 2009 $id = $section->id; 2010 $sectionnum = $section->section; 2011 $expectedlegacydata = array($course->id, "course", "editsection", 'editsection.php?id=' . $id, $sectionnum); 2012 $this->assertEventLegacyLogData($expectedlegacydata, $event); 2013 $this->assertEventContextNotUsed($event); 2014 } 2015 2016 /** 2017 * Test that triggering a course_section_deleted event works as expected. 2018 */ 2019 public function test_course_section_deleted_event() { 2020 global $USER, $DB; 2021 $this->resetAfterTest(); 2022 $sink = $this->redirectEvents(); 2023 2024 // Create the course with sections. 2025 $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true)); 2026 $sections = $DB->get_records('course_sections', array('course' => $course->id)); 2027 $coursecontext = context_course::instance($course->id); 2028 $section = array_pop($sections); 2029 course_delete_section($course, $section); 2030 $events = $sink->get_events(); 2031 $event = array_pop($events); // Delete section event. 2032 $sink->close(); 2033 2034 // Validate event data. 2035 $this->assertInstanceOf('\core\event\course_section_deleted', $event); 2036 $this->assertEquals('course_sections', $event->objecttable); 2037 $this->assertEquals($section->id, $event->objectid); 2038 $this->assertEquals($course->id, $event->courseid); 2039 $this->assertEquals($coursecontext->id, $event->contextid); 2040 $this->assertEquals($section->section, $event->other['sectionnum']); 2041 $expecteddesc = "The user with id '{$event->userid}' deleted section number '{$event->other['sectionnum']}' " . 2042 "(section name '{$event->other['sectionname']}') for the course with id '{$event->courseid}'"; 2043 $this->assertEquals($expecteddesc, $event->get_description()); 2044 $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid)); 2045 $this->assertNull($event->get_url()); 2046 2047 // Test legacy data. 2048 $sectionnum = $section->section; 2049 $expectedlegacydata = array($course->id, "course", "delete section", 'view.php?id=' . $course->id, $sectionnum); 2050 $this->assertEventLegacyLogData($expectedlegacydata, $event); 2051 $this->assertEventContextNotUsed($event); 2052 } 2053 2054 public function test_course_integrity_check() { 2055 global $DB; 2056 2057 $this->resetAfterTest(true); 2058 $course = $this->getDataGenerator()->create_course(array('numsections' => 1), 2059 array('createsections'=>true)); 2060 2061 $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), 2062 array('section' => 0)); 2063 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id), 2064 array('section' => 0)); 2065 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id), 2066 array('section' => 0)); 2067 $correctseq = join(',', array($forum->cmid, $page->cmid, $quiz->cmid)); 2068 2069 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2070 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2071 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2072 $this->assertEquals($correctseq, $section0->sequence); 2073 $this->assertEmpty($section1->sequence); 2074 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2075 $this->assertEquals($section0->id, $cms[$page->cmid]->section); 2076 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2077 $this->assertEmpty(course_integrity_check($course->id)); 2078 2079 // Now let's make manual change in DB and let course_integrity_check() fix it: 2080 2081 // 1. Module appears twice in one section. 2082 $DB->update_record('course_sections', array('id' => $section0->id, 'sequence' => $section0->sequence. ','. $page->cmid)); 2083 $this->assertEquals( 2084 array('Failed integrity check for course ['. $course->id. 2085 ']. Sequence for course section ['. $section0->id. '] is "'. 2086 $section0->sequence. ','. $page->cmid. '", must be "'. 2087 $section0->sequence. '"'), 2088 course_integrity_check($course->id)); 2089 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2090 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2091 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2092 $this->assertEquals($correctseq, $section0->sequence); 2093 $this->assertEmpty($section1->sequence); 2094 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2095 $this->assertEquals($section0->id, $cms[$page->cmid]->section); 2096 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2097 2098 // 2. Module appears in two sections (last section wins). 2099 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => ''. $page->cmid)); 2100 // First message about double mentioning in sequence, second message about wrong section field for $page. 2101 $this->assertEquals(array( 2102 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 2103 '] must be removed from sequence of section ['. $section0->id. 2104 '] because it is also present in sequence of section ['. $section1->id. ']', 2105 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 2106 '] points to section ['. $section0->id. '] instead of ['. $section1->id. ']'), 2107 course_integrity_check($course->id)); 2108 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2109 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2110 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2111 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 2112 $this->assertEquals(''. $page->cmid, $section1->sequence); 2113 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2114 $this->assertEquals($section1->id, $cms[$page->cmid]->section); 2115 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2116 2117 // 3. Module id is not present in course_section.sequence (integrity check with $fullcheck = false). 2118 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => '')); 2119 $this->assertEmpty(course_integrity_check($course->id)); // Not an error! 2120 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2121 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2122 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2123 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 2124 $this->assertEmpty($section1->sequence); 2125 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2126 $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed. 2127 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2128 2129 // 4. Module id is not present in course_section.sequence (integrity check with $fullcheck = true). 2130 $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['. 2131 $page->cmid. '] is missing from sequence of section ['. $section1->id. ']'), 2132 course_integrity_check($course->id, null, null, true)); // Error! 2133 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2134 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2135 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2136 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 2137 $this->assertEquals(''. $page->cmid, $section1->sequence); // Yay, module added to section. 2138 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2139 $this->assertEquals($section1->id, $cms[$page->cmid]->section); // Not changed. 2140 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2141 2142 // 5. Module id is not present in course_section.sequence and it's section is invalid (integrity check with $fullcheck = true). 2143 $DB->update_record('course_modules', array('id' => $page->cmid, 'section' => 8765)); 2144 $DB->update_record('course_sections', array('id' => $section1->id, 'sequence' => '')); 2145 $this->assertEquals(array( 2146 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 2147 '] is missing from sequence of section ['. $section0->id. ']', 2148 'Failed integrity check for course ['. $course->id. ']. Course module ['. $page->cmid. 2149 '] points to section [8765] instead of ['. $section0->id. ']'), 2150 course_integrity_check($course->id, null, null, true)); 2151 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2152 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2153 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2154 $this->assertEquals($forum->cmid. ','. $quiz->cmid. ','. $page->cmid, $section0->sequence); // Module added to section. 2155 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2156 $this->assertEquals($section0->id, $cms[$page->cmid]->section); // Section changed to section0. 2157 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2158 2159 // 6. Module is deleted from course_modules but not deleted in sequence (integrity check with $fullcheck = true). 2160 $DB->delete_records('course_modules', array('id' => $page->cmid)); 2161 $this->assertEquals(array('Failed integrity check for course ['. $course->id. ']. Course module ['. 2162 $page->cmid. '] does not exist but is present in the sequence of section ['. $section0->id. ']'), 2163 course_integrity_check($course->id, null, null, true)); 2164 $section0 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 0)); 2165 $section1 = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1)); 2166 $cms = $DB->get_records('course_modules', array('course' => $course->id), 'id', 'id,section'); 2167 $this->assertEquals($forum->cmid. ','. $quiz->cmid, $section0->sequence); 2168 $this->assertEmpty($section1->sequence); 2169 $this->assertEquals($section0->id, $cms[$forum->cmid]->section); 2170 $this->assertEquals($section0->id, $cms[$quiz->cmid]->section); 2171 $this->assertEquals(2, count($cms)); 2172 } 2173 2174 /** 2175 * Tests for event related to course module creation. 2176 */ 2177 public function test_course_module_created_event() { 2178 global $USER, $DB; 2179 $this->resetAfterTest(); 2180 2181 // Create an assign module. 2182 $sink = $this->redirectEvents(); 2183 $modinfo = $this->create_specific_module_test('assign'); 2184 $events = $sink->get_events(); 2185 $event = array_pop($events); 2186 2187 $cm = get_coursemodule_from_id('assign', $modinfo->coursemodule, 0, false, MUST_EXIST); 2188 $mod = $DB->get_record('assign', array('id' => $modinfo->instance), '*', MUST_EXIST); 2189 2190 // Validate event data. 2191 $this->assertInstanceOf('\core\event\course_module_created', $event); 2192 $this->assertEquals($cm->id, $event->objectid); 2193 $this->assertEquals($USER->id, $event->userid); 2194 $this->assertEquals('course_modules', $event->objecttable); 2195 $url = new moodle_url('/mod/assign/view.php', array('id' => $cm->id)); 2196 $this->assertEquals($url, $event->get_url()); 2197 2198 // Test legacy data. 2199 $this->assertSame('mod_created', $event->get_legacy_eventname()); 2200 $eventdata = new stdClass(); 2201 $eventdata->modulename = 'assign'; 2202 $eventdata->name = $mod->name; 2203 $eventdata->cmid = $cm->id; 2204 $eventdata->courseid = $cm->course; 2205 $eventdata->userid = $USER->id; 2206 $this->assertEventLegacyData($eventdata, $event); 2207 2208 $arr = array( 2209 array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance"), 2210 array($cm->course, "assign", "add", "view.php?id=$cm->id", $cm->instance, $cm->id) 2211 ); 2212 $this->assertEventLegacyLogData($arr, $event); 2213 $this->assertEventContextNotUsed($event); 2214 2215 // Let us see if duplicating an activity results in a nice course module created event. 2216 $sink->clear(); 2217 $course = get_course($mod->course); 2218 $newcm = duplicate_module($course, $cm); 2219 $events = $sink->get_events(); 2220 $event = array_pop($events); 2221 $sink->close(); 2222 2223 // Validate event data. 2224 $this->assertInstanceOf('\core\event\course_module_created', $event); 2225 $this->assertEquals($newcm->id, $event->objectid); 2226 $this->assertEquals($USER->id, $event->userid); 2227 $this->assertEquals($course->id, $event->courseid); 2228 $url = new moodle_url('/mod/assign/view.php', array('id' => $newcm->id)); 2229 $this->assertEquals($url, $event->get_url()); 2230 } 2231 2232 /** 2233 * Tests for event validations related to course module creation. 2234 */ 2235 public function test_course_module_created_event_exceptions() { 2236 2237 $this->resetAfterTest(); 2238 2239 // Generate data. 2240 $modinfo = $this->create_specific_module_test('assign'); 2241 $context = context_module::instance($modinfo->coursemodule); 2242 2243 // Test not setting instanceid. 2244 try { 2245 $event = \core\event\course_module_created::create(array( 2246 'courseid' => $modinfo->course, 2247 'context' => $context, 2248 'objectid' => $modinfo->coursemodule, 2249 'other' => array( 2250 'modulename' => 'assign', 2251 'name' => 'My assignment', 2252 ) 2253 )); 2254 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without 2255 other['instanceid']"); 2256 } catch (coding_exception $e) { 2257 $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage()); 2258 } 2259 2260 // Test not setting modulename. 2261 try { 2262 $event = \core\event\course_module_created::create(array( 2263 'courseid' => $modinfo->course, 2264 'context' => $context, 2265 'objectid' => $modinfo->coursemodule, 2266 'other' => array( 2267 'instanceid' => $modinfo->instance, 2268 'name' => 'My assignment', 2269 ) 2270 )); 2271 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without 2272 other['modulename']"); 2273 } catch (coding_exception $e) { 2274 $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage()); 2275 } 2276 2277 // Test not setting name. 2278 2279 try { 2280 $event = \core\event\course_module_created::create(array( 2281 'courseid' => $modinfo->course, 2282 'context' => $context, 2283 'objectid' => $modinfo->coursemodule, 2284 'other' => array( 2285 'modulename' => 'assign', 2286 'instanceid' => $modinfo->instance, 2287 ) 2288 )); 2289 $this->fail("Event validation should not allow \\core\\event\\course_module_created to be triggered without 2290 other['name']"); 2291 } catch (coding_exception $e) { 2292 $this->assertContains("The 'name' value must be set in other.", $e->getMessage()); 2293 } 2294 2295 } 2296 2297 /** 2298 * Tests for event related to course module updates. 2299 */ 2300 public function test_course_module_updated_event() { 2301 global $USER, $DB; 2302 $this->resetAfterTest(); 2303 2304 // Update a forum module. 2305 $sink = $this->redirectEvents(); 2306 $modinfo = $this->update_specific_module_test('forum'); 2307 $events = $sink->get_events(); 2308 $event = array_pop($events); 2309 $sink->close(); 2310 2311 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST); 2312 $mod = $DB->get_record('forum', array('id' => $cm->instance), '*', MUST_EXIST); 2313 2314 // Validate event data. 2315 $this->assertInstanceOf('\core\event\course_module_updated', $event); 2316 $this->assertEquals($cm->id, $event->objectid); 2317 $this->assertEquals($USER->id, $event->userid); 2318 $this->assertEquals('course_modules', $event->objecttable); 2319 $url = new moodle_url('/mod/forum/view.php', array('id' => $cm->id)); 2320 $this->assertEquals($url, $event->get_url()); 2321 2322 // Test legacy data. 2323 $this->assertSame('mod_updated', $event->get_legacy_eventname()); 2324 $eventdata = new stdClass(); 2325 $eventdata->modulename = 'forum'; 2326 $eventdata->name = $mod->name; 2327 $eventdata->cmid = $cm->id; 2328 $eventdata->courseid = $cm->course; 2329 $eventdata->userid = $USER->id; 2330 $this->assertEventLegacyData($eventdata, $event); 2331 2332 $arr = array( 2333 array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$cm->id", "forum $cm->instance"), 2334 array($cm->course, "forum", "update", "view.php?id=$cm->id", $cm->instance, $cm->id) 2335 ); 2336 $this->assertEventLegacyLogData($arr, $event); 2337 $this->assertEventContextNotUsed($event); 2338 } 2339 2340 /** 2341 * Tests for create_from_cm method. 2342 */ 2343 public function test_course_module_create_from_cm() { 2344 $this->resetAfterTest(); 2345 $this->setAdminUser(); 2346 2347 // Create course and modules. 2348 $course = $this->getDataGenerator()->create_course(array('numsections' => 5)); 2349 2350 // Generate an assignment. 2351 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); 2352 2353 // Get the module context. 2354 $modcontext = context_module::instance($assign->cmid); 2355 2356 // Get course module. 2357 $cm = get_coursemodule_from_id(null, $assign->cmid, $course->id, false, MUST_EXIST); 2358 2359 // Create an event from course module. 2360 $event = \core\event\course_module_updated::create_from_cm($cm, $modcontext); 2361 2362 // Trigger the events. 2363 $sink = $this->redirectEvents(); 2364 $event->trigger(); 2365 $events = $sink->get_events(); 2366 $event2 = array_pop($events); 2367 2368 // Test event data. 2369 $this->assertInstanceOf('\core\event\course_module_updated', $event); 2370 $this->assertEquals($cm->id, $event2->objectid); 2371 $this->assertEquals($modcontext, $event2->get_context()); 2372 $this->assertEquals($cm->modname, $event2->other['modulename']); 2373 $this->assertEquals($cm->instance, $event2->other['instanceid']); 2374 $this->assertEquals($cm->name, $event2->other['name']); 2375 $this->assertEventContextNotUsed($event2); 2376 $this->assertSame('mod_updated', $event2->get_legacy_eventname()); 2377 $arr = array( 2378 array($cm->course, "course", "update mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance"), 2379 array($cm->course, "assign", "update", "view.php?id=$cm->id", $cm->instance, $cm->id) 2380 ); 2381 $this->assertEventLegacyLogData($arr, $event); 2382 } 2383 2384 /** 2385 * Tests for event validations related to course module update. 2386 */ 2387 public function test_course_module_updated_event_exceptions() { 2388 2389 $this->resetAfterTest(); 2390 2391 // Generate data. 2392 $modinfo = $this->create_specific_module_test('assign'); 2393 $context = context_module::instance($modinfo->coursemodule); 2394 2395 // Test not setting instanceid. 2396 try { 2397 $event = \core\event\course_module_updated::create(array( 2398 'courseid' => $modinfo->course, 2399 'context' => $context, 2400 'objectid' => $modinfo->coursemodule, 2401 'other' => array( 2402 'modulename' => 'assign', 2403 'name' => 'My assignment', 2404 ) 2405 )); 2406 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without 2407 other['instanceid']"); 2408 } catch (coding_exception $e) { 2409 $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage()); 2410 } 2411 2412 // Test not setting modulename. 2413 try { 2414 $event = \core\event\course_module_updated::create(array( 2415 'courseid' => $modinfo->course, 2416 'context' => $context, 2417 'objectid' => $modinfo->coursemodule, 2418 'other' => array( 2419 'instanceid' => $modinfo->instance, 2420 'name' => 'My assignment', 2421 ) 2422 )); 2423 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without 2424 other['modulename']"); 2425 } catch (coding_exception $e) { 2426 $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage()); 2427 } 2428 2429 // Test not setting name. 2430 2431 try { 2432 $event = \core\event\course_module_updated::create(array( 2433 'courseid' => $modinfo->course, 2434 'context' => $context, 2435 'objectid' => $modinfo->coursemodule, 2436 'other' => array( 2437 'modulename' => 'assign', 2438 'instanceid' => $modinfo->instance, 2439 ) 2440 )); 2441 $this->fail("Event validation should not allow \\core\\event\\course_module_updated to be triggered without 2442 other['name']"); 2443 } catch (coding_exception $e) { 2444 $this->assertContains("The 'name' value must be set in other.", $e->getMessage()); 2445 } 2446 2447 } 2448 2449 /** 2450 * Tests for event related to course module delete. 2451 */ 2452 public function test_course_module_deleted_event() { 2453 global $USER, $DB; 2454 $this->resetAfterTest(); 2455 2456 // Create and delete a module. 2457 $sink = $this->redirectEvents(); 2458 $modinfo = $this->create_specific_module_test('forum'); 2459 $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST); 2460 course_delete_module($modinfo->coursemodule); 2461 $events = $sink->get_events(); 2462 $event = array_pop($events); // delete module event.; 2463 $sink->close(); 2464 2465 // Validate event data. 2466 $this->assertInstanceOf('\core\event\course_module_deleted', $event); 2467 $this->assertEquals($cm->id, $event->objectid); 2468 $this->assertEquals($USER->id, $event->userid); 2469 $this->assertEquals('course_modules', $event->objecttable); 2470 $this->assertEquals(null, $event->get_url()); 2471 $this->assertEquals($cm, $event->get_record_snapshot('course_modules', $cm->id)); 2472 2473 // Test legacy data. 2474 $this->assertSame('mod_deleted', $event->get_legacy_eventname()); 2475 $eventdata = new stdClass(); 2476 $eventdata->modulename = 'forum'; 2477 $eventdata->cmid = $cm->id; 2478 $eventdata->courseid = $cm->course; 2479 $eventdata->userid = $USER->id; 2480 $this->assertEventLegacyData($eventdata, $event); 2481 2482 $arr = array($cm->course, 'course', "delete mod", "view.php?id=$cm->course", "forum $cm->instance", $cm->id); 2483 $this->assertEventLegacyLogData($arr, $event); 2484 2485 } 2486 2487 /** 2488 * Tests for event validations related to course module deletion. 2489 */ 2490 public function test_course_module_deleted_event_exceptions() { 2491 2492 $this->resetAfterTest(); 2493 2494 // Generate data. 2495 $modinfo = $this->create_specific_module_test('assign'); 2496 $context = context_module::instance($modinfo->coursemodule); 2497 2498 // Test not setting instanceid. 2499 try { 2500 $event = \core\event\course_module_deleted::create(array( 2501 'courseid' => $modinfo->course, 2502 'context' => $context, 2503 'objectid' => $modinfo->coursemodule, 2504 'other' => array( 2505 'modulename' => 'assign', 2506 'name' => 'My assignment', 2507 ) 2508 )); 2509 $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without 2510 other['instanceid']"); 2511 } catch (coding_exception $e) { 2512 $this->assertContains("The 'instanceid' value must be set in other.", $e->getMessage()); 2513 } 2514 2515 // Test not setting modulename. 2516 try { 2517 $event = \core\event\course_module_deleted::create(array( 2518 'courseid' => $modinfo->course, 2519 'context' => $context, 2520 'objectid' => $modinfo->coursemodule, 2521 'other' => array( 2522 'instanceid' => $modinfo->instance, 2523 'name' => 'My assignment', 2524 ) 2525 )); 2526 $this->fail("Event validation should not allow \\core\\event\\course_module_deleted to be triggered without 2527 other['modulename']"); 2528 } catch (coding_exception $e) { 2529 $this->assertContains("The 'modulename' value must be set in other.", $e->getMessage()); 2530 } 2531 } 2532 2533 /** 2534 * Returns a user object and its assigned new role. 2535 * 2536 * @param testing_data_generator $generator 2537 * @param $contextid 2538 * @return array The user object and the role ID 2539 */ 2540 protected function get_user_objects(testing_data_generator $generator, $contextid) { 2541 global $USER; 2542 2543 if (empty($USER->id)) { 2544 $user = $generator->create_user(); 2545 $this->setUser($user); 2546 } 2547 $roleid = create_role('Test role', 'testrole', 'Test role description'); 2548 if (!is_array($contextid)) { 2549 $contextid = array($contextid); 2550 } 2551 foreach ($contextid as $cid) { 2552 $assignid = role_assign($roleid, $user->id, $cid); 2553 } 2554 return array($user, $roleid); 2555 } 2556 2557 /** 2558 * Test course move after course. 2559 */ 2560 public function test_course_change_sortorder_after_course() { 2561 global $DB; 2562 2563 $this->resetAfterTest(true); 2564 2565 $generator = $this->getDataGenerator(); 2566 $category = $generator->create_category(); 2567 $course3 = $generator->create_course(array('category' => $category->id)); 2568 $course2 = $generator->create_course(array('category' => $category->id)); 2569 $course1 = $generator->create_course(array('category' => $category->id)); 2570 $context = $category->get_context(); 2571 2572 list($user, $roleid) = $this->get_user_objects($generator, $context->id); 2573 $caps = course_capability_assignment::allow('moodle/category:manage', $roleid, $context->id); 2574 2575 $courses = $category->get_courses(); 2576 $this->assertInternalType('array', $courses); 2577 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2578 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2579 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2580 2581 // Test moving down. 2582 $this->assertTrue(course_change_sortorder_after_course($course1->id, $course3->id)); 2583 $courses = $category->get_courses(); 2584 $this->assertInternalType('array', $courses); 2585 $this->assertEquals(array($course2->id, $course3->id, $course1->id), array_keys($courses)); 2586 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2587 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2588 2589 // Test moving up. 2590 $this->assertTrue(course_change_sortorder_after_course($course1->id, $course2->id)); 2591 $courses = $category->get_courses(); 2592 $this->assertInternalType('array', $courses); 2593 $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses)); 2594 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2595 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2596 2597 // Test moving to the top. 2598 $this->assertTrue(course_change_sortorder_after_course($course1->id, 0)); 2599 $courses = $category->get_courses(); 2600 $this->assertInternalType('array', $courses); 2601 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2602 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2603 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2604 } 2605 2606 /** 2607 * Tests changing the visibility of a course. 2608 */ 2609 public function test_course_change_visibility() { 2610 global $DB; 2611 2612 $this->resetAfterTest(true); 2613 2614 $generator = $this->getDataGenerator(); 2615 $category = $generator->create_category(); 2616 $course = $generator->create_course(array('category' => $category->id)); 2617 2618 $this->assertEquals('1', $course->visible); 2619 $this->assertEquals('1', $course->visibleold); 2620 2621 $this->assertTrue(course_change_visibility($course->id, false)); 2622 $course = $DB->get_record('course', array('id' => $course->id)); 2623 $this->assertEquals('0', $course->visible); 2624 $this->assertEquals('0', $course->visibleold); 2625 2626 $this->assertTrue(course_change_visibility($course->id, true)); 2627 $course = $DB->get_record('course', array('id' => $course->id)); 2628 $this->assertEquals('1', $course->visible); 2629 $this->assertEquals('1', $course->visibleold); 2630 } 2631 2632 /** 2633 * Tests moving the course up and down by one. 2634 */ 2635 public function test_course_change_sortorder_by_one() { 2636 global $DB; 2637 2638 $this->resetAfterTest(true); 2639 2640 $generator = $this->getDataGenerator(); 2641 $category = $generator->create_category(); 2642 $course3 = $generator->create_course(array('category' => $category->id)); 2643 $course2 = $generator->create_course(array('category' => $category->id)); 2644 $course1 = $generator->create_course(array('category' => $category->id)); 2645 2646 $courses = $category->get_courses(); 2647 $this->assertInternalType('array', $courses); 2648 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2649 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2650 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2651 2652 // Test moving down. 2653 $course1 = get_course($course1->id); 2654 $this->assertTrue(course_change_sortorder_by_one($course1, false)); 2655 $courses = $category->get_courses(); 2656 $this->assertInternalType('array', $courses); 2657 $this->assertEquals(array($course2->id, $course1->id, $course3->id), array_keys($courses)); 2658 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2659 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2660 2661 // Test moving up. 2662 $course1 = get_course($course1->id); 2663 $this->assertTrue(course_change_sortorder_by_one($course1, true)); 2664 $courses = $category->get_courses(); 2665 $this->assertInternalType('array', $courses); 2666 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2667 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2668 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2669 2670 // Test moving the top course up one. 2671 $course1 = get_course($course1->id); 2672 $this->assertFalse(course_change_sortorder_by_one($course1, true)); 2673 // Check nothing changed. 2674 $courses = $category->get_courses(); 2675 $this->assertInternalType('array', $courses); 2676 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2677 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2678 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2679 2680 // Test moving the bottom course up down. 2681 $course3 = get_course($course3->id); 2682 $this->assertFalse(course_change_sortorder_by_one($course3, false)); 2683 // Check nothing changed. 2684 $courses = $category->get_courses(); 2685 $this->assertInternalType('array', $courses); 2686 $this->assertEquals(array($course1->id, $course2->id, $course3->id), array_keys($courses)); 2687 $dbcourses = $DB->get_records('course', array('category' => $category->id), 'sortorder', 'id'); 2688 $this->assertEquals(array_keys($dbcourses), array_keys($courses)); 2689 } 2690 2691 public function test_view_resources_list() { 2692 $this->resetAfterTest(); 2693 2694 $course = self::getDataGenerator()->create_course(); 2695 $coursecontext = context_course::instance($course->id); 2696 2697 $event = \core\event\course_resources_list_viewed::create(array('context' => context_course::instance($course->id))); 2698 $event->set_legacy_logdata(array('book', 'page', 'resource')); 2699 $sink = $this->redirectEvents(); 2700 $event->trigger(); 2701 $events = $sink->get_events(); 2702 $sink->close(); 2703 2704 // Validate the event. 2705 $event = $events[0]; 2706 $this->assertInstanceOf('\core\event\course_resources_list_viewed', $event); 2707 $this->assertEquals(null, $event->objecttable); 2708 $this->assertEquals(null, $event->objectid); 2709 $this->assertEquals($course->id, $event->courseid); 2710 $this->assertEquals($coursecontext->id, $event->contextid); 2711 $expectedlegacydata = array( 2712 array($course->id, "book", "view all", 'index.php?id=' . $course->id, ''), 2713 array($course->id, "page", "view all", 'index.php?id=' . $course->id, ''), 2714 array($course->id, "resource", "view all", 'index.php?id=' . $course->id, ''), 2715 ); 2716 $this->assertEventLegacyLogData($expectedlegacydata, $event); 2717 $this->assertEventContextNotUsed($event); 2718 } 2719 2720 /** 2721 * Test duplicate_module() 2722 */ 2723 public function test_duplicate_module() { 2724 $this->setAdminUser(); 2725 $this->resetAfterTest(); 2726 $course = self::getDataGenerator()->create_course(); 2727 $res = self::getDataGenerator()->create_module('resource', array('course' => $course)); 2728 $cm = get_coursemodule_from_id('resource', $res->cmid, 0, false, MUST_EXIST); 2729 2730 $newcm = duplicate_module($course, $cm); 2731 2732 // Make sure they are the same, except obvious id changes. 2733 foreach ($cm as $prop => $value) { 2734 if ($prop == 'id' || $prop == 'url' || $prop == 'instance' || $prop == 'added') { 2735 // Ignore obviously different properties. 2736 continue; 2737 } 2738 $this->assertEquals($value, $newcm->$prop); 2739 } 2740 } 2741 2742 /** 2743 * Tests that when creating or updating a module, if the availability settings 2744 * are present but set to an empty tree, availability is set to null in 2745 * database. 2746 */ 2747 public function test_empty_availability_settings() { 2748 global $DB; 2749 $this->setAdminUser(); 2750 $this->resetAfterTest(); 2751 2752 // Enable availability. 2753 set_config('enableavailability', 1); 2754 2755 // Test add. 2756 $emptyavailability = json_encode(\core_availability\tree::get_root_json(array())); 2757 $course = self::getDataGenerator()->create_course(); 2758 $label = self::getDataGenerator()->create_module('label', array( 2759 'course' => $course, 'availability' => $emptyavailability)); 2760 $this->assertNull($DB->get_field('course_modules', 'availability', 2761 array('id' => $label->cmid))); 2762 2763 // Test update. 2764 $formdata = $DB->get_record('course_modules', array('id' => $label->cmid)); 2765 unset($formdata->availability); 2766 $formdata->availabilityconditionsjson = $emptyavailability; 2767 $formdata->modulename = 'label'; 2768 $formdata->coursemodule = $label->cmid; 2769 $draftid = 0; 2770 file_prepare_draft_area($draftid, context_module::instance($label->cmid)->id, 2771 'mod_label', 'intro', 0); 2772 $formdata->introeditor = array( 2773 'itemid' => $draftid, 2774 'text' => '<p>Yo</p>', 2775 'format' => FORMAT_HTML); 2776 update_module($formdata); 2777 $this->assertNull($DB->get_field('course_modules', 'availability', 2778 array('id' => $label->cmid))); 2779 } 2780 2781 /** 2782 * Test update_inplace_editable() 2783 */ 2784 public function test_update_module_name_inplace() { 2785 global $CFG, $DB, $PAGE; 2786 require_once($CFG->dirroot . '/lib/external/externallib.php'); 2787 2788 $this->setUser($this->getDataGenerator()->create_user()); 2789 2790 $this->resetAfterTest(true); 2791 $course = $this->getDataGenerator()->create_course(); 2792 $forum = self::getDataGenerator()->create_module('forum', array('course' => $course->id, 'name' => 'forum name')); 2793 2794 // Call service for core_course component without necessary permissions. 2795 try { 2796 core_external::update_inplace_editable('core_course', 'activityname', $forum->cmid, 'New forum name'); 2797 $this->fail('Exception expected'); 2798 } catch (moodle_exception $e) { 2799 $this->assertEquals('Course or activity not accessible. (Not enrolled)', 2800 $e->getMessage()); 2801 } 2802 2803 // Change to admin user and make sure that cm name can be updated using web service update_inplace_editable(). 2804 $this->setAdminUser(); 2805 $res = core_external::update_inplace_editable('core_course', 'activityname', $forum->cmid, 'New forum name'); 2806 $res = external_api::clean_returnvalue(core_external::update_inplace_editable_returns(), $res); 2807 $this->assertEquals('New forum name', $res['value']); 2808 $this->assertEquals('New forum name', $DB->get_field('forum', 'name', array('id' => $forum->id))); 2809 } 2810 2811 /** 2812 * Testing function course_get_tagged_course_modules - search tagged course modules 2813 */ 2814 public function test_course_get_tagged_course_modules() { 2815 global $DB; 2816 $this->resetAfterTest(); 2817 $course3 = $this->getDataGenerator()->create_course(); 2818 $course2 = $this->getDataGenerator()->create_course(); 2819 $course1 = $this->getDataGenerator()->create_course(); 2820 $cm11 = $this->getDataGenerator()->create_module('assign', array('course' => $course1->id, 2821 'tags' => 'Cat, Dog')); 2822 $cm12 = $this->getDataGenerator()->create_module('page', array('course' => $course1->id, 2823 'tags' => 'Cat, Mouse', 'visible' => 0)); 2824 $cm13 = $this->getDataGenerator()->create_module('page', array('course' => $course1->id, 2825 'tags' => 'Cat, Mouse, Dog')); 2826 $cm21 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id, 2827 'tags' => 'Cat, Mouse')); 2828 $cm31 = $this->getDataGenerator()->create_module('forum', array('course' => $course3->id, 2829 'tags' => 'Cat, Mouse')); 2830 2831 // Admin is able to view everything. 2832 $this->setAdminUser(); 2833 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2834 /*$exclusivemode = */false, /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$page = */0); 2835 $this->assertRegExp('/'.$cm11->name.'/', $res->content); 2836 $this->assertRegExp('/'.$cm12->name.'/', $res->content); 2837 $this->assertRegExp('/'.$cm13->name.'/', $res->content); 2838 $this->assertRegExp('/'.$cm21->name.'/', $res->content); 2839 $this->assertRegExp('/'.$cm31->name.'/', $res->content); 2840 // Results from course1 are returned before results from course2. 2841 $this->assertTrue(strpos($res->content, $cm11->name) < strpos($res->content, $cm21->name)); 2842 2843 // Ordinary user is not able to see anything. 2844 $user = $this->getDataGenerator()->create_user(); 2845 $this->setUser($user); 2846 2847 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2848 /*$exclusivemode = */false, /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$page = */0); 2849 $this->assertNull($res); 2850 2851 // Enrol user as student in course1 and course2. 2852 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); 2853 $this->getDataGenerator()->enrol_user($user->id, $course1->id, $roleids['student']); 2854 $this->getDataGenerator()->enrol_user($user->id, $course2->id, $roleids['student']); 2855 core_tag_index_builder::reset_caches(); 2856 2857 // Searching in the course context returns visible modules in this course. 2858 $context = context_course::instance($course1->id); 2859 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2860 /*$exclusivemode = */false, /*$fromctx = */0, /*$ctx = */$context->id, /*$rec = */1, /*$page = */0); 2861 $this->assertRegExp('/'.$cm11->name.'/', $res->content); 2862 $this->assertNotRegExp('/'.$cm12->name.'/', $res->content); 2863 $this->assertRegExp('/'.$cm13->name.'/', $res->content); 2864 $this->assertNotRegExp('/'.$cm21->name.'/', $res->content); 2865 $this->assertNotRegExp('/'.$cm31->name.'/', $res->content); 2866 2867 // Searching FROM the course context returns visible modules in all courses. 2868 $context = context_course::instance($course2->id); 2869 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2870 /*$exclusivemode = */false, /*$fromctx = */$context->id, /*$ctx = */0, /*$rec = */1, /*$page = */0); 2871 $this->assertRegExp('/'.$cm11->name.'/', $res->content); 2872 $this->assertNotRegExp('/'.$cm12->name.'/', $res->content); 2873 $this->assertRegExp('/'.$cm13->name.'/', $res->content); 2874 $this->assertRegExp('/'.$cm21->name.'/', $res->content); 2875 $this->assertNotRegExp('/'.$cm31->name.'/', $res->content); // No access to course3. 2876 // Results from course2 are returned before results from course1. 2877 $this->assertTrue(strpos($res->content, $cm21->name) < strpos($res->content, $cm11->name)); 2878 2879 // Enrol user in course1 as a teacher - now he should be able to see hidden module. 2880 $this->getDataGenerator()->enrol_user($user->id, $course1->id, $roleids['editingteacher']); 2881 get_fast_modinfo(0,0,true); 2882 2883 $context = context_course::instance($course1->id); 2884 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2885 /*$exclusivemode = */false, /*$fromctx = */$context->id, /*$ctx = */0, /*$rec = */1, /*$page = */0); 2886 $this->assertRegExp('/'.$cm12->name.'/', $res->content); 2887 2888 // Create more modules and try pagination. 2889 $cm14 = $this->getDataGenerator()->create_module('assign', array('course' => $course1->id, 2890 'tags' => 'Cat, Dog')); 2891 $cm15 = $this->getDataGenerator()->create_module('page', array('course' => $course1->id, 2892 'tags' => 'Cat, Mouse', 'visible' => 0)); 2893 $cm16 = $this->getDataGenerator()->create_module('page', array('course' => $course1->id, 2894 'tags' => 'Cat, Mouse, Dog')); 2895 2896 $context = context_course::instance($course1->id); 2897 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2898 /*$exclusivemode = */false, /*$fromctx = */0, /*$ctx = */$context->id, /*$rec = */1, /*$page = */0); 2899 $this->assertRegExp('/'.$cm11->name.'/', $res->content); 2900 $this->assertRegExp('/'.$cm12->name.'/', $res->content); 2901 $this->assertRegExp('/'.$cm13->name.'/', $res->content); 2902 $this->assertNotRegExp('/'.$cm21->name.'/', $res->content); 2903 $this->assertRegExp('/'.$cm14->name.'/', $res->content); 2904 $this->assertRegExp('/'.$cm15->name.'/', $res->content); 2905 $this->assertNotRegExp('/'.$cm16->name.'/', $res->content); 2906 $this->assertNotRegExp('/'.$cm31->name.'/', $res->content); // No access to course3. 2907 $this->assertEmpty($res->prevpageurl); 2908 $this->assertNotEmpty($res->nextpageurl); 2909 2910 $res = course_get_tagged_course_modules(core_tag_tag::get_by_name(0, 'Cat'), 2911 /*$exclusivemode = */false, /*$fromctx = */0, /*$ctx = */$context->id, /*$rec = */1, /*$page = */1); 2912 $this->assertNotRegExp('/'.$cm11->name.'/', $res->content); 2913 $this->assertNotRegExp('/'.$cm12->name.'/', $res->content); 2914 $this->assertNotRegExp('/'.$cm13->name.'/', $res->content); 2915 $this->assertNotRegExp('/'.$cm21->name.'/', $res->content); 2916 $this->assertNotRegExp('/'.$cm14->name.'/', $res->content); 2917 $this->assertNotRegExp('/'.$cm15->name.'/', $res->content); 2918 $this->assertRegExp('/'.$cm16->name.'/', $res->content); 2919 $this->assertNotRegExp('/'.$cm31->name.'/', $res->content); // No access to course3. 2920 $this->assertNotEmpty($res->prevpageurl); 2921 $this->assertEmpty($res->nextpageurl); 2922 } 2923 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |