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