2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for lib/modinfolib.php.
22 * @copyright 2012 Andrew Davis
25 defined('MOODLE_INTERNAL') ||
die();
28 require_once($CFG->libdir
. '/modinfolib.php');
29 require_once($CFG->libdir
. '/conditionlib.php');
32 * Unit tests for modinfolib.php
34 * @copyright 2012 Andrew Davis
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 class core_modinfolib_testcase
extends advanced_testcase
{
38 public function test_section_info_properties() {
41 $this->resetAfterTest();
42 $oldcfgenableavailability = $CFG->enableavailability
;
43 $oldcfgenablecompletion = $CFG->enablecompletion
;
44 set_config('enableavailability', 1);
45 set_config('enablecompletion', 1);
46 $this->setAdminUser();
48 // Generate the course and pre-requisite module.
49 $course = $this->getDataGenerator()->create_course(
50 array('format' => 'topics',
52 'enablecompletion' => 1,
53 'groupmode' => SEPARATEGROUPS
,
54 'forcegroupmode' => 0),
55 array('createsections' => true));
56 $coursecontext = context_course
::instance($course->id
);
57 $prereqforum = $this->getDataGenerator()->create_module('forum',
58 array('course' => $course->id
),
59 array('completion' => 1));
61 // Generate the module and add availability conditions.
62 $conditionscompletion = array($prereqforum->cmid
=> COMPLETION_COMPLETE
);
63 $conditionsgrade = array(666 => (object)array('min' => 0.4, 'max' => null, 'name' => '!missing'));
64 $conditionsfield = array('email' => (object)array(
65 'fieldname' => 'email',
66 'operator' => 'contains',
69 $sectiondb = $DB->get_record('course_sections', array('course' => $course->id
, 'section' => 2));
70 $ci = new condition_info_section((object)array('id' => $sectiondb->id
), CONDITION_MISSING_EVERYTHING
);
71 foreach ($conditionscompletion as $cmid => $requiredcompletion) {
72 $ci->add_completion_condition($cmid, $requiredcompletion);
74 foreach ($conditionsgrade as $gradeid => $conditiongrade) {
75 $ci->add_grade_condition($gradeid, $conditiongrade->min
, $conditiongrade->max
, true);
77 foreach ($conditionsfield as $conditionfield) {
78 $ci->add_user_field_condition($conditionfield->fieldname
, $conditionfield->operator
, $conditionfield->value
);
80 // Direct calls to condition_info_section methods do not reset the course cache. Do it manually.
81 rebuild_course_cache($course->id
, true);
83 // Create and enrol a student.
84 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST
);
85 $student = $this->getDataGenerator()->create_user();
86 role_assign($studentrole->id
, $student->id
, $coursecontext);
87 $enrolplugin = enrol_get_plugin('manual');
88 $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id
, 'enrol' => 'manual'));
89 $enrolplugin->enrol_user($enrolinstance, $student->id
);
90 $this->setUser($student);
93 $modinfo = get_fast_modinfo($course->id
);
94 $si = $modinfo->get_section_info(2);
96 $this->assertEquals($sectiondb->id
, $si->id
);
97 $this->assertEquals($sectiondb->course
, $si->course
);
98 $this->assertEquals($sectiondb->section
, $si->section
);
99 $this->assertEquals($sectiondb->name
, $si->name
);
100 $this->assertEquals($sectiondb->visible
, $si->visible
);
101 $this->assertEquals($sectiondb->summary
, $si->summary
);
102 $this->assertEquals($sectiondb->summaryformat
, $si->summaryformat
);
103 $this->assertEquals($sectiondb->showavailability
, $si->showavailability
);
104 $this->assertEquals($sectiondb->availablefrom
, $si->availablefrom
);
105 $this->assertEquals($sectiondb->availableuntil
, $si->availableuntil
);
106 $this->assertEquals($sectiondb->groupingid
, $si->groupingid
);
107 $this->assertEquals($sectiondb->sequence
, $si->sequence
); // Since this section does not contain invalid modules.
108 $this->assertEquals($conditionscompletion, $si->conditionscompletion
);
109 $this->assertEquals($conditionsgrade, $si->conditionsgrade
);
110 $this->assertEquals($conditionsfield, $si->conditionsfield
);
112 // Dynamic fields, just test that they can be retrieved (must be carefully tested in each activity type).
113 $this->assertEquals(0, $si->available
);
114 $this->assertNotEmpty($si->availableinfo
); // Lists all unmet availability conditions.
115 $this->assertEquals(0, $si->uservisible
);
118 set_config('enableavailability', $oldcfgenableavailability);
119 set_config('enablecompletion', $oldcfgenablecompletion);
122 public function test_cm_info_properties() {
125 $this->resetAfterTest();
126 $oldcfgenableavailability = $CFG->enableavailability
;
127 $oldcfgenablecompletion = $CFG->enablecompletion
;
128 set_config('enableavailability', 1);
129 set_config('enablecompletion', 1);
130 $this->setAdminUser();
132 // Generate the course and pre-requisite module.
133 $course = $this->getDataGenerator()->create_course(
134 array('format' => 'topics',
136 'enablecompletion' => 1,
137 'groupmode' => SEPARATEGROUPS
,
138 'forcegroupmode' => 0),
139 array('createsections' => true));
140 $coursecontext = context_course
::instance($course->id
);
141 $prereqforum = $this->getDataGenerator()->create_module('forum',
142 array('course' => $course->id
),
143 array('completion' => 1));
145 // Generate the module and add availability conditions.
146 $conditionscompletion = array($prereqforum->cmid
=> COMPLETION_COMPLETE
);
147 $conditionsgrade = array(666 => (object)array('min' => 0.4, 'max' => null, 'name' => '!missing'));
148 $conditionsfield = array('email' => (object)array(
149 'fieldname' => 'email',
150 'operator' => 'contains',
153 $assign = $this->getDataGenerator()->create_module('assign',
154 array('course' => $course->id
),
155 array('idnumber' => 123,
156 'groupmode' => VISIBLEGROUPS
,
157 'availablefrom' => time() +
3600,
158 'availableuntil' => time() +
5*3600));
159 $ci = new condition_info((object)array('id' => $assign->cmid
), CONDITION_MISSING_EVERYTHING
);
160 foreach ($conditionscompletion as $cmid => $requiredcompletion) {
161 $ci->add_completion_condition($cmid, $requiredcompletion);
163 foreach ($conditionsgrade as $gradeid => $conditiongrade) {
164 $ci->add_grade_condition($gradeid, $conditiongrade->min
, $conditiongrade->max
, true);
166 foreach ($conditionsfield as $conditionfield) {
167 $ci->add_user_field_condition($conditionfield->fieldname
, $conditionfield->operator
, $conditionfield->value
);
169 // Direct access to condition_info functions does not reset course cache, do it manually.
170 rebuild_course_cache($course->id
, true);
172 // Retrieve all related records from DB.
173 $assigndb = $DB->get_record('assign', array('id' => $assign->id
));
174 $moduletypedb = $DB->get_record('modules', array('name' => 'assign'));
175 $moduledb = $DB->get_record('course_modules', array('module' => $moduletypedb->id
, 'instance' => $assign->id
));
176 $sectiondb = $DB->get_record('course_sections', array('id' => $moduledb->section
));
177 $modnamessingular = get_module_types_names(false);
178 $modnamesplural = get_module_types_names(true);
180 // Create and enrol a student.
181 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST
);
182 $student = $this->getDataGenerator()->create_user();
183 role_assign($studentrole->id
, $student->id
, $coursecontext);
184 $enrolplugin = enrol_get_plugin('manual');
185 $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id
, 'enrol' => 'manual'));
186 $enrolplugin->enrol_user($enrolinstance, $student->id
);
187 $this->setUser($student);
189 // Emulate data used in building course cache to receive the same instance of cached_cm_info as was used in building modinfo.
190 $rawmods = get_course_mods($course->id
);
191 $cachedcminfo = assign_get_coursemodule_info($rawmods[$moduledb->id
]);
194 $modinfo = get_fast_modinfo($course->id
);
195 $cm = $modinfo->instances
['assign'][$assign->id
];
197 $this->assertEquals($moduledb->id
, $cm->id
);
198 $this->assertEquals($assigndb->id
, $cm->instance
);
199 $this->assertEquals($moduledb->course
, $cm->course
);
200 $this->assertEquals($moduledb->idnumber
, $cm->idnumber
);
201 $this->assertEquals($moduledb->added
, $cm->added
);
202 $this->assertEquals($moduledb->visible
, $cm->visible
);
203 $this->assertEquals($moduledb->visibleold
, $cm->visibleold
);
204 $this->assertEquals($moduledb->groupmode
, $cm->groupmode
);
205 $this->assertEquals(VISIBLEGROUPS
, $cm->groupmode
);
206 $this->assertEquals($moduledb->groupingid
, $cm->groupingid
);
207 $this->assertEquals($moduledb->groupmembersonly
, $cm->groupmembersonly
);
208 $this->assertEquals($course->groupmodeforce
, $cm->coursegroupmodeforce
);
209 $this->assertEquals($course->groupmode
, $cm->coursegroupmode
);
210 $this->assertEquals(SEPARATEGROUPS
, $cm->coursegroupmode
);
211 $this->assertEquals($course->groupmodeforce ?
$course->groupmode
: $moduledb->groupmode
,
212 $cm->effectivegroupmode
); // (since mod_assign supports groups).
213 $this->assertEquals(VISIBLEGROUPS
, $cm->effectivegroupmode
);
214 $this->assertEquals($moduledb->indent
, $cm->indent
);
215 $this->assertEquals($moduledb->completion
, $cm->completion
);
216 $this->assertEquals($moduledb->completiongradeitemnumber
, $cm->completiongradeitemnumber
);
217 $this->assertEquals($moduledb->completionview
, $cm->completionview
);
218 $this->assertEquals($moduledb->completionexpected
, $cm->completionexpected
);
219 $this->assertEquals($moduledb->availablefrom
, $cm->availablefrom
);
220 $this->assertEquals($moduledb->availableuntil
, $cm->availableuntil
);
221 $this->assertEquals($moduledb->showavailability
, $cm->showavailability
);
222 $this->assertEquals($moduledb->showdescription
, $cm->showdescription
);
223 $this->assertEquals(null, $cm->extra
); // Deprecated field. Used in module types that don't return cached_cm_info.
224 $this->assertEquals($cachedcminfo->icon
, $cm->icon
);
225 $this->assertEquals($cachedcminfo->iconcomponent
, $cm->iconcomponent
);
226 $this->assertEquals('assign', $cm->modname
);
227 $this->assertEquals($moduledb->module
, $cm->module
);
228 $this->assertEquals($cachedcminfo->name
, $cm->name
);
229 $this->assertEquals($sectiondb->section
, $cm->sectionnum
);
230 $this->assertEquals($moduledb->section
, $cm->section
);
231 $this->assertEquals($conditionscompletion, $cm->conditionscompletion
);
232 $this->assertEquals($conditionsgrade, $cm->conditionsgrade
);
233 $this->assertEquals($conditionsfield, $cm->conditionsfield
);
234 $this->assertEquals(context_module
::instance($moduledb->id
), $cm->context
);
235 $this->assertEquals($modnamessingular['assign'], $cm->modfullname
);
236 $this->assertEquals($modnamesplural['assign'], $cm->modplural
);
237 $this->assertEquals(new moodle_url('/mod/assign/view.php', array('id' => $moduledb->id
)), $cm->url
);
238 $this->assertEquals($cachedcminfo->customdata
, $cm->customdata
);
240 // Dynamic fields, just test that they can be retrieved (must be carefully tested in each activity type).
241 $this->assertNotEmpty($cm->availableinfo
); // Lists all unmet availability conditions.
242 $this->assertEquals(0, $cm->uservisible
);
243 $this->assertEquals('', $cm->extraclasses
);
244 $this->assertEquals('', $cm->onclick
);
245 $this->assertEquals(null, $cm->afterlink
);
246 $this->assertEquals(null, $cm->afterediticons
);
247 $this->assertEquals('', $cm->content
);
249 // Attempt to access and set non-existing field.
250 $this->assertTrue(empty($modinfo->somefield
));
251 $this->assertFalse(isset($modinfo->somefield
));
253 $this->assertDebuggingCalled();
254 $cm->somefield
= 'Some value';
255 $this->assertDebuggingCalled();
256 $this->assertEmpty($cm->somefield
);
257 $this->assertDebuggingCalled();
259 // Attempt to overwrite an existing field.
260 $prevvalue = $cm->name
;
261 $this->assertNotEmpty($cm->name
);
262 $this->assertFalse(empty($cm->name
));
263 $this->assertTrue(isset($cm->name
));
264 $cm->name
= 'Illegal overwriting';
265 $this->assertDebuggingCalled();
266 $this->assertEquals($prevvalue, $cm->name
);
267 $this->assertDebuggingNotCalled();
270 set_config('enableavailability', $oldcfgenableavailability);
271 set_config('enablecompletion', $oldcfgenablecompletion);
274 public function test_matching_cacherev() {
277 $this->resetAfterTest();
278 $this->setAdminUser();
279 $cache = cache
::make('core', 'coursemodinfo');
281 // Generate the course and pre-requisite module.
282 $course = $this->getDataGenerator()->create_course(
283 array('format' => 'topics',
285 array('createsections' => true));
287 // Make sure the cacherev is set.
288 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
289 $this->assertGreaterThan(0, $cacherev);
290 $prevcacherev = $cacherev;
292 // Reset course cache and make sure cacherev is bumped up but cache is empty.
293 rebuild_course_cache($course->id
, true);
294 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
295 $this->assertGreaterThan($prevcacherev, $cacherev);
296 $this->assertEmpty($cache->get($course->id
));
297 $prevcacherev = $cacherev;
299 // Build course cache. Cacherev should not change but cache is now not empty. Make sure cacherev is the same everywhere.
300 $modinfo = get_fast_modinfo($course->id
);
301 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
302 $this->assertEquals($prevcacherev, $cacherev);
303 $cachedvalue = $cache->get($course->id
);
304 $this->assertNotEmpty($cachedvalue);
305 $this->assertEquals($cacherev, $cachedvalue->cacherev
);
306 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev
);
307 $prevcacherev = $cacherev;
309 // Little trick to check that cache is not rebuilt druing the next step - substitute the value in MUC and later check that it is still there.
310 $cache->set($course->id
, (object)array_merge((array)$cachedvalue, array('secretfield' => 1)));
312 // Clear static cache and call get_fast_modinfo() again (pretend we are in another request). Cache should not be rebuilt.
313 course_modinfo
::clear_instance_cache();
314 $modinfo = get_fast_modinfo($course->id
);
315 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
316 $this->assertEquals($prevcacherev, $cacherev);
317 $cachedvalue = $cache->get($course->id
);
318 $this->assertNotEmpty($cachedvalue);
319 $this->assertEquals($cacherev, $cachedvalue->cacherev
);
320 $this->assertNotEmpty($cachedvalue->secretfield
);
321 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev
);
322 $prevcacherev = $cacherev;
324 // Rebuild course cache. Cacherev must be incremented everywhere.
325 rebuild_course_cache($course->id
);
326 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
327 $this->assertGreaterThan($prevcacherev, $cacherev);
328 $cachedvalue = $cache->get($course->id
);
329 $this->assertNotEmpty($cachedvalue);
330 $this->assertEquals($cacherev, $cachedvalue->cacherev
);
331 $modinfo = get_fast_modinfo($course->id
);
332 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev
);
333 $prevcacherev = $cacherev;
335 // Update cacherev in DB and make sure the cache will be rebuilt on the next call to get_fast_modinfo().
336 increment_revision_number('course', 'cacherev', 'id = ?', array($course->id
));
337 // We need to clear static cache for course_modinfo instances too.
338 course_modinfo
::clear_instance_cache();
339 $modinfo = get_fast_modinfo($course->id
);
340 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
341 $this->assertGreaterThan($prevcacherev, $cacherev);
342 $cachedvalue = $cache->get($course->id
);
343 $this->assertNotEmpty($cachedvalue);
344 $this->assertEquals($cacherev, $cachedvalue->cacherev
);
345 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev
);
346 $prevcacherev = $cacherev;
348 // Reset cache for all courses and make sure this course cache is reset.
349 rebuild_course_cache(0, true);
350 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
351 $this->assertGreaterThan($prevcacherev, $cacherev);
352 $this->assertEmpty($cache->get($course->id
));
354 $modinfo = get_fast_modinfo($course->id
);
355 $cachedvalue = $cache->get($course->id
);
356 $this->assertNotEmpty($cachedvalue);
357 $this->assertEquals($cacherev, $cachedvalue->cacherev
);
358 $this->assertEquals($cacherev, $modinfo->get_course()->cacherev
);
359 $prevcacherev = $cacherev;
361 // Purge all caches and make sure cacherev is increased and data from MUC erased.
363 $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id
));
364 $this->assertGreaterThan($prevcacherev, $cacherev);
365 $this->assertEmpty($cache->get($course->id
));
368 public function test_course_modinfo_properties() {
371 $this->resetAfterTest();
372 $this->setAdminUser();
374 // Generate the course and some modules. Make one section hidden.
375 $course = $this->getDataGenerator()->create_course(
376 array('format' => 'topics',
378 array('createsections' => true));
379 $DB->execute('UPDATE {course_sections} SET visible = 0 WHERE course = ? and section = ?',
380 array($course->id
, 3));
381 $coursecontext = context_course
::instance($course->id
);
382 $forum0 = $this->getDataGenerator()->create_module('forum',
383 array('course' => $course->id
), array('section' => 0));
384 $assign0 = $this->getDataGenerator()->create_module('assign',
385 array('course' => $course->id
), array('section' => 0, 'visible' => 0));
386 $forum1 = $this->getDataGenerator()->create_module('forum',
387 array('course' => $course->id
), array('section' => 1));
388 $assign1 = $this->getDataGenerator()->create_module('assign',
389 array('course' => $course->id
), array('section' => 1));
390 $page1 = $this->getDataGenerator()->create_module('page',
391 array('course' => $course->id
), array('section' => 1));
392 $page3 = $this->getDataGenerator()->create_module('page',
393 array('course' => $course->id
), array('section' => 3));
395 $modinfo = get_fast_modinfo($course->id
);
397 $this->assertEquals(array($forum0->cmid
, $assign0->cmid
, $forum1->cmid
, $assign1->cmid
, $page1->cmid
, $page3->cmid
),
398 array_keys($modinfo->cms
));
399 $this->assertEquals($course->id
, $modinfo->courseid
);
400 $this->assertEquals($USER->id
, $modinfo->userid
);
401 $this->assertEquals(array(0 => array($forum0->cmid
, $assign0->cmid
),
402 1 => array($forum1->cmid
, $assign1->cmid
, $page1->cmid
), 3 => array($page3->cmid
)), $modinfo->sections
);
403 $this->assertEquals(array('forum', 'assign', 'page'), array_keys($modinfo->instances
));
404 $this->assertEquals(array($assign0->id
, $assign1->id
), array_keys($modinfo->instances
['assign']));
405 $this->assertEquals(array($forum0->id
, $forum1->id
), array_keys($modinfo->instances
['forum']));
406 $this->assertEquals(array($page1->id
, $page3->id
), array_keys($modinfo->instances
['page']));
407 $this->assertEquals(groups_get_user_groups($course->id
), $modinfo->groups
);
408 $this->assertEquals(array(0 => array($forum0->cmid
, $assign0->cmid
),
409 1 => array($forum1->cmid
, $assign1->cmid
, $page1->cmid
),
410 3 => array($page3->cmid
)), $modinfo->get_sections());
411 $this->assertEquals(array(0, 1, 2, 3), array_keys($modinfo->get_section_info_all()));
412 $this->assertEquals($forum0->cmid
. ',' . $assign0->cmid
, $modinfo->get_section_info(0)->sequence
);
413 $this->assertEquals($forum1->cmid
. ',' . $assign1->cmid
. ',' . $page1->cmid
, $modinfo->get_section_info(1)->sequence
);
414 $this->assertEquals('', $modinfo->get_section_info(2)->sequence
);
415 $this->assertEquals($page3->cmid
, $modinfo->get_section_info(3)->sequence
);
416 $this->assertEquals($course->id
, $modinfo->get_course()->id
);
417 $this->assertEquals(array('assign', 'forum', 'page'),
418 array_keys($modinfo->get_used_module_names()));
419 $this->assertEquals(array('assign', 'forum', 'page'),
420 array_keys($modinfo->get_used_module_names(true)));
421 // Admin can see hidden modules/sections.
422 $this->assertTrue($modinfo->cms
[$assign0->cmid
]->uservisible
);
423 $this->assertTrue($modinfo->get_section_info(3)->uservisible
);
425 // Get modinfo for non-current user (without capability to view hidden activities/sections).
426 $user = $this->getDataGenerator()->create_user();
427 $modinfo = get_fast_modinfo($course->id
, $user->id
);
428 $this->assertEquals($user->id
, $modinfo->userid
);
429 $this->assertFalse($modinfo->cms
[$assign0->cmid
]->uservisible
);
430 $this->assertFalse($modinfo->get_section_info(3)->uservisible
);
432 // Attempt to access and set non-existing field.
433 $this->assertTrue(empty($modinfo->somefield
));
434 $this->assertFalse(isset($modinfo->somefield
));
436 $this->assertDebuggingCalled();
437 $modinfo->somefield
= 'Some value';
438 $this->assertDebuggingCalled();
439 $this->assertEmpty($modinfo->somefield
);
440 $this->assertDebuggingCalled();
442 // Attempt to overwrite existing field.
443 $this->assertFalse(empty($modinfo->cms
));
444 $this->assertTrue(isset($modinfo->cms
));
445 $modinfo->cms
= 'Illegal overwriting';
446 $this->assertDebuggingCalled();
447 $this->assertNotEquals('Illegal overwriting', $modinfo->cms
);
451 * Test is_user_access_restricted_by_group()
453 * The underlying groups system is more thoroughly tested in lib/tests/grouplib_test.php
455 public function test_is_user_access_restricted_by_group() {
456 global $DB, $CFG, $USER;
458 $this->resetAfterTest();
461 $course = $this->getDataGenerator()->create_course();
462 $coursecontext = context_course
::instance($course->id
);
464 // Create a mod_assign instance.
465 $assign = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
));
466 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign->id
];
468 // Create and enrol a student.
469 // Enrolment is necessary for groups to work.
470 $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST
);
471 $student = $this->getDataGenerator()->create_user();
472 role_assign($studentrole->id
, $student->id
, $coursecontext);
473 $enrolplugin = enrol_get_plugin('manual');
474 $enrolplugin->add_instance($course);
475 $enrolinstances = enrol_get_instances($course->id
, false);
476 foreach ($enrolinstances as $enrolinstance) {
477 if ($enrolinstance->enrol
=== 'manual') {
481 $enrolplugin->enrol_user($enrolinstance, $student->id
);
483 // Switch to a student and reload the context info.
484 $this->setUser($student);
485 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign->id
];
487 // Create up a teacher.
488 $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST
);
489 $teacher = $this->getDataGenerator()->create_user();
490 role_assign($teacherrole->id
, $teacher->id
, $coursecontext);
492 // Create 2 groupings.
493 $grouping1 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
, 'name' => 'grouping1'));
494 $grouping2 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
, 'name' => 'grouping2'));
496 // Create 2 groups and put them in the groupings.
497 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id
, 'idnumber' => 'group1'));
498 groups_assign_grouping($grouping1->id
, $group1->id
);
499 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id
, 'idnumber' => 'group2'));
500 groups_assign_grouping($grouping2->id
, $group2->id
);
502 // If groups are disabled, the activity isn't restricted.
503 $CFG->enablegroupmembersonly
= false;
504 $this->assertFalse($cm_info->is_user_access_restricted_by_group());
506 // Turn groups setting on.
507 $CFG->enablegroupmembersonly
= true;
508 // Create a mod_assign instance with "group members only", the activity should not be restricted.
509 $assignnogroups = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
),
510 array('groupmembersonly' => NOGROUPS
));
511 $cm_info = get_fast_modinfo($course->id
)->instances
['assign'][$assignnogroups->id
];
512 $this->assertFalse($cm_info->is_user_access_restricted_by_group());
514 // If "group members only" is on but user is in the wrong group, the activity is restricted.
515 $assignsepgroups = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
),
516 array('groupmembersonly' => SEPARATEGROUPS
, 'groupingid' => $grouping1->id
));
517 $this->assertTrue(groups_add_member($group2, $USER));
518 get_fast_modinfo($course->id
, 0, true);
519 $cm_info = get_fast_modinfo($course->id
)->instances
['assign'][$assignsepgroups->id
];
520 $this->assertEquals($grouping1->id
, $cm_info->groupingid
);
521 $this->assertTrue($cm_info->is_user_access_restricted_by_group());
523 // If the user is in the required group, the activity isn't restricted.
524 groups_remove_member($group2, $USER);
525 $this->assertTrue(groups_add_member($group1, $USER));
526 get_fast_modinfo($course->id
, 0, true);
527 $cm_info = get_fast_modinfo($course->id
)->instances
['assign'][$assignsepgroups->id
];
528 $this->assertFalse($cm_info->is_user_access_restricted_by_group());
530 // Switch to a teacher and reload the context info.
531 $this->setUser($teacher);
532 $cm_info = get_fast_modinfo($course->id
)->instances
['assign'][$assignsepgroups->id
];
534 // If the user isn't in the required group but has 'moodle/site:accessallgroups', the activity isn't restricted.
535 $this->assertTrue(has_capability('moodle/site:accessallgroups', $coursecontext));
536 $this->assertFalse($cm_info->is_user_access_restricted_by_group());
540 * Test is_user_access_restricted_by_conditional_access()
542 * The underlying conditional access system is more thoroughly tested in lib/tests/conditionlib_test.php
544 public function test_is_user_access_restricted_by_conditional_access() {
547 $this->resetAfterTest();
549 // Enable conditional availability before creating modules, otherwise the condition data is not written in DB.
550 $CFG->enableavailability
= true;
553 $course = $this->getDataGenerator()->create_course();
554 // 1. Create an activity that is currently unavailable and hidden entirely (for students).
555 $assign1 = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
),
556 array('availablefrom' => time() +
10000, 'showavailability' => CONDITION_STUDENTVIEW_HIDE
));
557 // 2. Create an activity that is currently available.
558 $assign2 = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
));
559 // 3. Create an activity that is currently unavailable and set to be greyed out.
560 $assign3 = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
),
561 array('availablefrom' => time() +
10000, 'showavailability' => CONDITION_STUDENTVIEW_SHOW
));
564 $coursecontext = context_course
::instance($course->id
);
565 $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST
);
566 $teacher = $this->getDataGenerator()->create_user();
567 role_assign($teacherrole->id
, $teacher->id
, $coursecontext);
569 // If conditional availability is disabled the activity will always be unrestricted.
570 $CFG->enableavailability
= false;
571 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign1->id
];
572 $this->assertFalse($cm_info->is_user_access_restricted_by_conditional_access());
574 // Turn on conditional availability and reset the get_fast_modinfo cache.
575 $CFG->enableavailability
= true;
576 get_fast_modinfo($course, 0, true);
578 // The unavailable, hidden entirely activity should now be restricted.
579 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign1->id
];
580 $this->assertFalse($cm_info->available
);
581 $this->assertEquals(CONDITION_STUDENTVIEW_HIDE
, $cm_info->showavailability
);
582 $this->assertTrue($cm_info->is_user_access_restricted_by_conditional_access());
584 // If the activity is available it should not be restricted.
585 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign2->id
];
586 $this->assertTrue($cm_info->available
);
587 $this->assertFalse($cm_info->is_user_access_restricted_by_conditional_access());
589 // If the activity is unavailable and set to be greyed out it should not be restricted.
590 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign3->id
];
591 $this->assertFalse($cm_info->available
);
592 $this->assertEquals(CONDITION_STUDENTVIEW_SHOW
, $cm_info->showavailability
);
593 $this->assertFalse($cm_info->is_user_access_restricted_by_conditional_access());
595 // If the activity is unavailable and set to be hidden entirely its restricted unless user has 'moodle/course:viewhiddenactivities'.
596 // Switch to a teacher and reload the context info.
597 $this->setUser($teacher);
598 $cm_info = get_fast_modinfo($course)->instances
['assign'][$assign1->id
];
599 $this->assertFalse($cm_info->available
);
600 $this->assertEquals(CONDITION_STUDENTVIEW_HIDE
, $cm_info->showavailability
);
602 $this->assertTrue(has_capability('moodle/course:viewhiddenactivities', $coursecontext));
603 $this->assertFalse($cm_info->is_user_access_restricted_by_conditional_access());
606 public function test_is_user_access_restricted_by_capability() {
609 $this->resetAfterTest();
611 // Create a course and a mod_assign instance.
612 $course = $this->getDataGenerator()->create_course();
613 $assign = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id
));
615 // Create and enrol a student.
616 $coursecontext = context_course
::instance($course->id
);
617 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST
);
618 $student = $this->getDataGenerator()->create_user();
619 role_assign($studentrole->id
, $student->id
, $coursecontext);
620 $enrolplugin = enrol_get_plugin('manual');
621 $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id
, 'enrol' => 'manual'));
622 $enrolplugin->enrol_user($enrolinstance, $student->id
);
623 $this->setUser($student);
625 // Make sure student can see the module.
626 $cm = get_fast_modinfo($course->id
)->instances
['assign'][$assign->id
];
627 $this->assertTrue($cm->uservisible
);
628 $this->assertFalse($cm->is_user_access_restricted_by_capability());
630 // Prohibit student to view mod_assign for the course.
631 role_change_permission($studentrole->id
, $coursecontext, 'mod/assign:view', CAP_PROHIBIT
);
632 get_fast_modinfo($course->id
, 0, true);
633 $cm = get_fast_modinfo($course->id
)->instances
['assign'][$assign->id
];
634 $this->assertFalse($cm->uservisible
);
635 $this->assertTrue($cm->is_user_access_restricted_by_capability());
637 // Restore permission to student to view mod_assign for the course.
638 role_change_permission($studentrole->id
, $coursecontext, 'mod/assign:view', CAP_INHERIT
);
639 get_fast_modinfo($course->id
, 0, true);
640 $cm = get_fast_modinfo($course->id
)->instances
['assign'][$assign->id
];
641 $this->assertTrue($cm->uservisible
);
642 $this->assertFalse($cm->is_user_access_restricted_by_capability());
644 // Prohibit student to view mod_assign for the particular module.
645 role_change_permission($studentrole->id
, context_module
::instance($cm->id
), 'mod/assign:view', CAP_PROHIBIT
);
646 get_fast_modinfo($course->id
, 0, true);
647 $cm = get_fast_modinfo($course->id
)->instances
['assign'][$assign->id
];
648 $this->assertFalse($cm->uservisible
);
649 $this->assertTrue($cm->is_user_access_restricted_by_capability());
651 // Check calling get_fast_modinfo() for different user:
652 $this->setAdminUser();
653 $cm = get_fast_modinfo($course->id
)->instances
['assign'][$assign->id
];
654 $this->assertTrue($cm->uservisible
);
655 $this->assertFalse($cm->is_user_access_restricted_by_capability());
656 $cm = get_fast_modinfo($course->id
, $student->id
)->instances
['assign'][$assign->id
];
657 $this->assertFalse($cm->uservisible
);
658 $this->assertTrue($cm->is_user_access_restricted_by_capability());