80df165c0b3673c9a10d67a464e78885184f2811
[moodle.git] / lib / tests / accesslib_test.php
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/>.
17 /**
18  * Full functional accesslib test.
19  *
20  * @package    core
21  * @category   phpunit
22  * @copyright  2011 Petr Skoda {@link http://skodak.org}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
29 /**
30  * Functional test for accesslib.php
31  *
32  * Note: execution may take many minutes especially on slower servers.
33  */
34 class core_accesslib_testcase extends advanced_testcase {
35     /**
36      * Verify comparison of context instances in phpunit asserts.
37      */
38     public function test_context_comparisons() {
39         $frontpagecontext1 = context_course::instance(SITEID);
40         context_helper::reset_caches();
41         $frontpagecontext2 = context_course::instance(SITEID);
42         $this->assertEquals($frontpagecontext1, $frontpagecontext2);
44         $user1 = context_user::instance(1);
45         $user2 = context_user::instance(2);
46         $this->assertNotEquals($user1, $user2);
47     }
49     /**
50      * Test resetting works.
51      */
52     public function test_accesslib_clear_all_caches() {
53         global $ACCESSLIB_PRIVATE;
55         $this->resetAfterTest();
57         $this->setAdminUser();
58         load_all_capabilities();
60         $this->assertNotEmpty($ACCESSLIB_PRIVATE->rolepermissions);
61         $this->assertNotEmpty($ACCESSLIB_PRIVATE->rolepermissions);
62         $this->assertNotEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
63         accesslib_clear_all_caches(true);
64         $this->assertEmpty($ACCESSLIB_PRIVATE->rolepermissions);
65         $this->assertEmpty($ACCESSLIB_PRIVATE->rolepermissions);
66         $this->assertEmpty($ACCESSLIB_PRIVATE->dirtycontexts);
67         $this->assertEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
68     }
70     /**
71      * Test getting of role access
72      */
73     public function test_get_role_access() {
74         global $DB;
76         $roles = $DB->get_records('role');
77         foreach ($roles as $role) {
78             $access = get_role_access($role->id);
80             $this->assertTrue(is_array($access));
81             $this->assertTrue(is_array($access['ra']));
82             $this->assertTrue(is_array($access['rdef']));
83             $this->assertTrue(isset($access['rdef_count']));
84             $this->assertTrue(is_array($access['loaded']));
85             $this->assertTrue(isset($access['time']));
86             $this->assertTrue(is_array($access['rsw']));
87         }
89         // Note: the data is validated in the functional permission evaluation test at the end of this testcase.
90     }
92     /**
93      * Test getting of guest role.
94      */
95     public function test_get_guest_role() {
96         global $CFG;
98         $guest = get_guest_role();
99         $this->assertEquals('guest', $guest->archetype);
100         $this->assertEquals('guest', $guest->shortname);
102         $this->assertEquals($CFG->guestroleid, $guest->id);
103     }
105     /**
106      * Test if user is admin.
107      */
108     public function test_is_siteadmin() {
109         global $DB, $CFG;
111         $this->resetAfterTest();
113         $users = $DB->get_records('user');
115         foreach ($users as $user) {
116             $this->setUser(0);
117             if ($user->username === 'admin') {
118                 $this->assertTrue(is_siteadmin($user));
119                 $this->assertTrue(is_siteadmin($user->id));
120                 $this->setUser($user);
121                 $this->assertTrue(is_siteadmin());
122                 $this->assertTrue(is_siteadmin(null));
123             } else {
124                 $this->assertFalse(is_siteadmin($user));
125                 $this->assertFalse(is_siteadmin($user->id));
126                 $this->setUser($user);
127                 $this->assertFalse(is_siteadmin());
128                 $this->assertFalse(is_siteadmin(null));
129             }
130         }
132         // Change the site admin list and check that it still works with
133         // multiple admins. We do this with userids only (not real user
134         // accounts) because it makes the test simpler.
135         $before = $CFG->siteadmins;
136         set_config('siteadmins', '666,667,668');
137         $this->assertTrue(is_siteadmin(666));
138         $this->assertTrue(is_siteadmin(667));
139         $this->assertTrue(is_siteadmin(668));
140         $this->assertFalse(is_siteadmin(669));
141         set_config('siteadmins', '13');
142         $this->assertTrue(is_siteadmin(13));
143         $this->assertFalse(is_siteadmin(666));
144         set_config('siteadmins', $before);
145     }
147     /**
148      * Test if user is enrolled in a course
149      */
150     public function test_is_enrolled() {
151         global $DB;
153         $this->resetAfterTest();
155         // Generate data.
156         $user = $this->getDataGenerator()->create_user();
157         $course = $this->getDataGenerator()->create_course();
158         $coursecontext = context_course::instance($course->id);
159         $role = $DB->get_record('role', array('shortname'=>'student'));
161         // There should be a manual enrolment as part of the default install.
162         $plugin = enrol_get_plugin('manual');
163         $instance = $DB->get_record('enrol', array(
164             'courseid' => $course->id,
165             'enrol' => 'manual',
166         ));
167         $this->assertNotSame(false, $instance);
169         // Enrol the user in the course.
170         $plugin->enrol_user($instance, $user->id, $role->id);
172         // We'll test with the mod/assign:submit capability.
173         $capability= 'mod/assign:submit';
174         $this->assertTrue($DB->record_exists('capabilities', array('name' => $capability)));
176         // Switch to our user.
177         $this->setUser($user);
179         // Ensure that the user has the capability first.
180         $this->assertTrue(has_capability($capability, $coursecontext, $user->id));
182         // We first test whether the user is enrolled on the course as this
183         // seeds the cache, then we test for the capability.
184         $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
185         $this->assertTrue(is_enrolled($coursecontext, $user, $capability));
187         // Prevent the capability for this user role.
188         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
189         $coursecontext->mark_dirty();
190         $this->assertFalse(has_capability($capability, $coursecontext, $user->id));
192         // Again, we seed the cache first by checking initial enrolment,
193         // and then we test the actual capability.
194         $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
195         $this->assertFalse(is_enrolled($coursecontext, $user, $capability));
196     }
198     /**
199      * Test logged in test.
200      */
201     public function test_isloggedin() {
202         global $USER;
204         $this->resetAfterTest();
206         $USER->id = 0;
207         $this->assertFalse(isloggedin());
208         $USER->id = 1;
209         $this->assertTrue(isloggedin());
210     }
212     /**
213      * Test guest user test.
214      */
215     public function test_isguestuser() {
216         global $DB;
218         $this->resetAfterTest();
220         $guest = $DB->get_record('user', array('username'=>'guest'));
221         $this->setUser(0);
222         $this->assertFalse(isguestuser());
223         $this->setAdminUser();
224         $this->assertFalse(isguestuser());
225         $this->assertTrue(isguestuser($guest));
226         $this->assertTrue(isguestuser($guest->id));
227         $this->setUser($guest);
228         $this->assertTrue(isguestuser());
230         $users = $DB->get_records('user');
231         foreach ($users as $user) {
232             if ($user->username === 'guest') {
233                 continue;
234             }
235             $this->assertFalse(isguestuser($user));
236         }
237     }
239     /**
240      * Test capability riskiness.
241      */
242     public function test_is_safe_capability() {
243         global $DB;
244         // Note: there is not much to test, just make sure no notices are throw for the most dangerous cap.
245         $capability = $DB->get_record('capabilities', array('name'=>'moodle/site:config'), '*', MUST_EXIST);
246         $this->assertFalse(is_safe_capability($capability));
247     }
249     /**
250      * Test context fetching.
251      */
252     public function test_get_context_info_array() {
253         $this->resetAfterTest();
255         $syscontext = context_system::instance();
256         $user = $this->getDataGenerator()->create_user();
257         $usercontext = context_user::instance($user->id);
258         $course = $this->getDataGenerator()->create_course();
259         $catcontext = context_coursecat::instance($course->category);
260         $coursecontext = context_course::instance($course->id);
261         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
262         $modcontext = context_module::instance($page->cmid);
263         $cm = get_coursemodule_from_instance('page', $page->id);
264         $block1 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
265         $block1context = context_block::instance($block1->id);
266         $block2 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$modcontext->id));
267         $block2context = context_block::instance($block2->id);
269         $result = get_context_info_array($syscontext->id);
270         $this->assertCount(3, $result);
271         $this->assertEquals($syscontext, $result[0]);
272         $this->assertNull($result[1]);
273         $this->assertNull($result[2]);
275         $result = get_context_info_array($usercontext->id);
276         $this->assertCount(3, $result);
277         $this->assertEquals($usercontext, $result[0]);
278         $this->assertNull($result[1]);
279         $this->assertNull($result[2]);
281         $result = get_context_info_array($catcontext->id);
282         $this->assertCount(3, $result);
283         $this->assertEquals($catcontext, $result[0]);
284         $this->assertNull($result[1]);
285         $this->assertNull($result[2]);
287         $result = get_context_info_array($coursecontext->id);
288         $this->assertCount(3, $result);
289         $this->assertEquals($coursecontext, $result[0]);
290         $this->assertEquals($course->id, $result[1]->id);
291         $this->assertSame($course->shortname, $result[1]->shortname);
292         $this->assertNull($result[2]);
294         $result = get_context_info_array($block1context->id);
295         $this->assertCount(3, $result);
296         $this->assertEquals($block1context, $result[0]);
297         $this->assertEquals($course->id, $result[1]->id);
298         $this->assertEquals($course->shortname, $result[1]->shortname);
299         $this->assertNull($result[2]);
301         $result = get_context_info_array($modcontext->id);
302         $this->assertCount(3, $result);
303         $this->assertEquals($modcontext, $result[0]);
304         $this->assertEquals($course->id, $result[1]->id);
305         $this->assertSame($course->shortname, $result[1]->shortname);
306         $this->assertEquals($cm->id, $result[2]->id);
307         $this->assertEquals($cm->groupmembersonly, $result[2]->groupmembersonly);
309         $result = get_context_info_array($block2context->id);
310         $this->assertCount(3, $result);
311         $this->assertEquals($block2context, $result[0]);
312         $this->assertEquals($course->id, $result[1]->id);
313         $this->assertSame($course->shortname, $result[1]->shortname);
314         $this->assertEquals($cm->id, $result[2]->id);
315         $this->assertEquals($cm->groupmembersonly, $result[2]->groupmembersonly);
316     }
318     /**
319      * Test looking for course contacts.
320      */
321     public function test_has_coursecontact_role() {
322         global $DB, $CFG;
324         $this->resetAfterTest();
326         $users = $DB->get_records('user');
328         // Nobody is expected to have any course level roles.
329         $this->assertNotEmpty($CFG->coursecontact);
330         foreach ($users as $user) {
331             $this->assertFalse(has_coursecontact_role($user->id));
332         }
334         $user = $this->getDataGenerator()->create_user();
335         $course = $this->getDataGenerator()->create_course();
336         role_assign($CFG->coursecontact, $user->id, context_course::instance($course->id));
337         $this->assertTrue(has_coursecontact_role($user->id));
338     }
340     /**
341      * Test creation of roles.
342      */
343     public function test_create_role() {
344         global $DB;
346         $this->resetAfterTest();
348         $id = create_role('New student role', 'student2', 'New student description', 'student');
349         $role = $DB->get_record('role', array('id'=>$id));
351         $this->assertNotEmpty($role);
352         $this->assertSame('New student role', $role->name);
353         $this->assertSame('student2', $role->shortname);
354         $this->assertSame('New student description', $role->description);
355         $this->assertSame('student', $role->archetype);
356     }
358     /**
359      * Test adding of capabilities to roles.
360      */
361     public function test_assign_capability() {
362         global $DB;
364         $this->resetAfterTest();
366         $user = $this->getDataGenerator()->create_user();
367         $syscontext = context_system::instance();
368         $frontcontext = context_course::instance(SITEID);
369         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
370         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to student by default.
371         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
372         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
374         $this->setUser($user);
375         $result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $frontcontext->id);
376         $this->assertTrue($result);
377         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
378         $this->assertNotEmpty($permission);
379         $this->assertEquals(CAP_ALLOW, $permission->permission);
380         $this->assertEquals($user->id, $permission->modifierid);
382         $this->setUser(0);
383         $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, false);
384         $this->assertTrue($result);
385         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
386         $this->assertNotEmpty($permission);
387         $this->assertEquals(CAP_ALLOW, $permission->permission);
388         $this->assertEquals(3, $permission->modifierid);
390         $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, true);
391         $this->assertTrue($result);
392         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
393         $this->assertNotEmpty($permission);
394         $this->assertEquals(CAP_PROHIBIT, $permission->permission);
395         $this->assertEquals(0, $permission->modifierid);
397         $result = assign_capability('moodle/backup:backupcourse', CAP_INHERIT, $student->id, $frontcontext->id);
398         $this->assertTrue($result);
399         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
400         $this->assertEmpty($permission);
402         // Test event trigger.
403         $rolecapabilityevent = \core\event\role_capabilities_updated::create(array('context' => $syscontext,
404                                                                                   'objectid' => $student->id,
405                                                                                   'other' => array('name' => $student->shortname)
406                                                                                  ));
407         $expectedlegacylog = array(SITEID, 'role', 'view', 'admin/roles/define.php?action=view&roleid=' . $student->id,
408                             $student->shortname, '', $user->id);
409         $rolecapabilityevent->set_legacy_logdata($expectedlegacylog);
410         $rolecapabilityevent->add_record_snapshot('role', $student);
412         $sink = $this->redirectEvents();
413         $rolecapabilityevent->trigger();
414         $events = $sink->get_events();
415         $sink->close();
416         $event = array_pop($events);
418         $this->assertInstanceOf('\core\event\role_capabilities_updated', $event);
419         $expectedurl = new moodle_url('admin/roles/define.php', array('action' => 'view', 'roleid' => $student->id));
420         $this->assertEquals($expectedurl, $event->get_url());
421         $this->assertEventLegacyLogData($expectedlegacylog, $event);
422     }
424     /**
425      * Test removing of capabilities from roles.
426      */
427     public function test_unassign_capability() {
428         global $DB;
430         $this->resetAfterTest();
432         $syscontext = context_system::instance();
433         $frontcontext = context_course::instance(SITEID);
434         $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
435         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to manager by default.
436         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
438         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
439         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
441         $result = unassign_capability('moodle/backup:backupcourse', $manager->id, $syscontext->id);
442         $this->assertTrue($result);
443         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
444         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
445         unassign_capability('moodle/backup:backupcourse', $manager->id, $frontcontext);
446         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
448         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
449         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
450         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
452         $result = unassign_capability('moodle/backup:backupcourse', $manager->id);
453         $this->assertTrue($result);
454         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
455         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
456     }
458     /**
459      * Test role assigning.
460      */
461     public function test_role_assign() {
462         global $DB, $USER;
464         $this->resetAfterTest();
466         $user = $this->getDataGenerator()->create_user();
467         $course = $this->getDataGenerator()->create_course();
468         $role = $DB->get_record('role', array('shortname'=>'student'));
470         $this->setUser(0);
471         $context = context_system::instance();
472         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
473         role_assign($role->id, $user->id, $context->id);
474         $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
475         $this->assertNotEmpty($ras);
476         $this->assertSame('', $ras->component);
477         $this->assertSame('0', $ras->itemid);
478         $this->assertEquals($USER->id, $ras->modifierid);
480         $this->setAdminUser();
481         $context = context_course::instance($course->id);
482         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
483         role_assign($role->id, $user->id, $context->id, 'enrol_self', 1, 666);
484         $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
485         $this->assertNotEmpty($ras);
486         $this->assertSame('enrol_self', $ras->component);
487         $this->assertSame('1', $ras->itemid);
488         $this->assertEquals($USER->id, $ras->modifierid);
489         $this->assertEquals(666, $ras->timemodified);
491         // Test event triggered.
493         $user2 = $this->getDataGenerator()->create_user();
494         $sink = $this->redirectEvents();
495         $raid = role_assign($role->id, $user2->id, $context->id);
496         $events = $sink->get_events();
497         $sink->close();
498         $this->assertCount(1, $events);
499         $event = $events[0];
500         $this->assertInstanceOf('\core\event\role_assigned', $event);
501         $this->assertSame('role', $event->target);
502         $this->assertSame('role', $event->objecttable);
503         $this->assertEquals($role->id, $event->objectid);
504         $this->assertEquals($context->id, $event->contextid);
505         $this->assertEquals($user2->id, $event->relateduserid);
506         $this->assertCount(3, $event->other);
507         $this->assertEquals($raid, $event->other['id']);
508         $this->assertSame('', $event->other['component']);
509         $this->assertEquals(0, $event->other['itemid']);
510         $this->assertInstanceOf('moodle_url', $event->get_url());
511         $this->assertSame('role_assigned', $event::get_legacy_eventname());
512         $roles = get_all_roles();
513         $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
514         $expectedlegacylog = array($course->id, 'role', 'assign',
515             'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
516         $this->assertEventLegacyLogData($expectedlegacylog, $event);
517     }
519     /**
520      * Test role unassigning.
521      */
522     public function test_role_unassign() {
523         global $DB, $USER;
525         $this->resetAfterTest();
527         $user = $this->getDataGenerator()->create_user();
528         $course = $this->getDataGenerator()->create_course();
529         $role = $DB->get_record('role', array('shortname'=>'student'));
531         $context = context_course::instance($course->id);
532         role_assign($role->id, $user->id, $context->id);
533         $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
534         role_unassign($role->id, $user->id, $context->id);
535         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
537         role_assign($role->id, $user->id, $context->id, 'enrol_self', 1);
538         $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
539         role_unassign($role->id, $user->id, $context->id, 'enrol_self', 1);
540         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
542         // Test event triggered.
544         role_assign($role->id, $user->id, $context->id);
545         $sink = $this->redirectEvents();
546         role_unassign($role->id, $user->id, $context->id);
547         $events = $sink->get_events();
548         $sink->close();
549         $this->assertCount(1, $events);
550         $event = $events[0];
551         $this->assertInstanceOf('\core\event\role_unassigned', $event);
552         $this->assertSame('role', $event->target);
553         $this->assertSame('role', $event->objecttable);
554         $this->assertEquals($role->id, $event->objectid);
555         $this->assertEquals($context->id, $event->contextid);
556         $this->assertEquals($user->id, $event->relateduserid);
557         $this->assertCount(3, $event->other);
558         $this->assertSame('', $event->other['component']);
559         $this->assertEquals(0, $event->other['itemid']);
560         $this->assertInstanceOf('moodle_url', $event->get_url());
561         $roles = get_all_roles();
562         $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
563         $expectedlegacylog = array($course->id, 'role', 'unassign',
564             'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
565         $this->assertEventLegacyLogData($expectedlegacylog, $event);
566     }
568     /**
569      * Test role unassigning.
570      */
571     public function test_role_unassign_all() {
572         global $DB;
574         $this->resetAfterTest();
576         $user = $this->getDataGenerator()->create_user();
577         $course = $this->getDataGenerator()->create_course();
578         $role = $DB->get_record('role', array('shortname'=>'student'));
579         $role2 = $DB->get_record('role', array('shortname'=>'teacher'));
580         $syscontext = context_system::instance();
581         $coursecontext = context_course::instance($course->id);
582         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
583         $modcontext = context_module::instance($page->cmid);
585         role_assign($role->id, $user->id, $syscontext->id);
586         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
587         $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
588         role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role->id));
589         $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
591         role_assign($role->id, $user->id, $syscontext->id);
592         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
593         role_assign($role->id, $user->id, $modcontext->id);
594         $this->assertEquals(3, $DB->count_records('role_assignments', array('userid'=>$user->id)));
595         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), false);
596         $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
597         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), true);
598         $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
599         role_unassign_all(array('userid'=>$user->id));
600         $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
602         role_assign($role->id, $user->id, $syscontext->id);
603         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
604         role_assign($role->id, $user->id, $coursecontext->id);
605         role_assign($role->id, $user->id, $modcontext->id);
606         $this->assertEquals(4, $DB->count_records('role_assignments', array('userid'=>$user->id)));
607         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id, 'component'=>'enrol_self'), true, true);
608         $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
610         // Test events triggered.
612         role_assign($role2->id, $user->id, $coursecontext->id);
613         role_assign($role2->id, $user->id, $modcontext->id);
614         $sink = $this->redirectEvents();
615         role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role2->id));
616         $events = $sink->get_events();
617         $sink->close();
618         $this->assertCount(2, $events);
619         $this->assertInstanceOf('\core\event\role_unassigned', $events[0]);
620         $this->assertInstanceOf('\core\event\role_unassigned', $events[1]);
621     }
623     /**
624      * Test role queries.
625      */
626     public function test_get_roles_with_capability() {
627         global $DB;
629         $this->resetAfterTest();
631         $syscontext = context_system::instance();
632         $frontcontext = context_course::instance(SITEID);
633         $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
634         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
636         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
637         $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse'));
639         $roles = get_roles_with_capability('moodle/backup:backupcourse');
640         $this->assertEquals(array(), $roles);
642         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
643         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $manager->id, $frontcontext->id);
644         assign_capability('moodle/backup:backupcourse', CAP_PREVENT, $teacher->id, $frontcontext->id);
646         $roles = get_roles_with_capability('moodle/backup:backupcourse');
647         $this->assertEquals(array($teacher->id, $manager->id), array_keys($roles), '', 0, 10, true);
649         $roles = get_roles_with_capability('moodle/backup:backupcourse', CAP_ALLOW);
650         $this->assertEquals(array($manager->id), array_keys($roles), '', 0, 10, true);
652         $roles = get_roles_with_capability('moodle/backup:backupcourse', null, $syscontext);
653         $this->assertEquals(array($manager->id), array_keys($roles), '', 0, 10, true);
654     }
656     /**
657      * Test deleting of roles.
658      */
659     public function test_delete_role() {
660         global $DB;
662         $this->resetAfterTest();
664         $role = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
665         $user = $this->getDataGenerator()->create_user();
666         role_assign($role->id, $user->id, context_system::instance());
667         $course = $this->getDataGenerator()->create_course();
668         $rolename = (object)array('roleid'=>$role->id, 'name'=>'Man', 'contextid'=>context_course::instance($course->id)->id);
669         $DB->insert_record('role_names', $rolename);
671         $this->assertTrue($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
672         $this->assertTrue($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
673         $this->assertTrue($DB->record_exists('role_names', array('roleid'=>$role->id)));
674         $this->assertTrue($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
675         $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
676         $this->assertTrue($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
677         $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
678         $this->assertTrue($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
680         // Delete role and get event.
681         $sink = $this->redirectEvents();
682         $result = delete_role($role->id);
683         $events = $sink->get_events();
684         $sink->close();
685         $event = array_pop($events);
687         $this->assertTrue($result);
688         $this->assertFalse($DB->record_exists('role', array('id'=>$role->id)));
689         $this->assertFalse($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
690         $this->assertFalse($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
691         $this->assertFalse($DB->record_exists('role_names', array('roleid'=>$role->id)));
692         $this->assertFalse($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
693         $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
694         $this->assertFalse($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
695         $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
696         $this->assertFalse($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
698         // Test triggered event.
699         $this->assertInstanceOf('\core\event\role_deleted', $event);
700         $this->assertSame('role', $event->target);
701         $this->assertSame('role', $event->objecttable);
702         $this->assertSame($role->id, $event->objectid);
703         $this->assertEquals(context_system::instance(), $event->get_context());
704         $this->assertSame($role->shortname, $event->other['shortname']);
705         $this->assertSame($role->description, $event->other['description']);
706         $this->assertSame($role->archetype, $event->other['archetype']);
708         $expectedlegacylog = array(SITEID, 'role', 'delete', 'admin/roles/manage.php?action=delete&roleid='.$role->id,
709                                    $role->shortname, '');
710         $this->assertEventLegacyLogData($expectedlegacylog, $event);
711     }
713     /**
714      * Test fetching of all roles.
715      */
716     public function test_get_all_roles() {
717         global $DB;
719         $this->resetAfterTest();
721         $allroles = get_all_roles();
722         $this->assertInternalType('array', $allroles);
723         $this->assertCount(8, $allroles); // There are 8 roles is standard install.
725         $role = reset($allroles);
726         $role = (array)$role;
728         $this->assertEquals(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype'), array_keys($role), '', 0, 10, true);
730         foreach ($allroles as $roleid => $role) {
731             $this->assertEquals($role->id, $roleid);
732         }
734         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
735         $course = $this->getDataGenerator()->create_course();
736         $coursecontext = context_course::instance($course->id);
737         $otherid = create_role('Other role', 'other', 'Some other role', '');
738         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
739         $DB->insert_record('role_names', $teacherename);
740         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
741         $DB->insert_record('role_names', $otherrename);
742         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
744         $allroles = get_all_roles($coursecontext);
745         $this->assertInternalType('array', $allroles);
746         $this->assertCount(9, $allroles);
747         $role = reset($allroles);
748         $role = (array)$role;
750         $this->assertEquals(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype', 'coursealias'), array_keys($role), '', 0, 10, true);
752         foreach ($allroles as $roleid => $role) {
753             $this->assertEquals($role->id, $roleid);
754             if (isset($renames[$roleid])) {
755                 $this->assertSame($renames[$roleid], $role->coursealias);
756             } else {
757                 $this->assertNull($role->coursealias);
758             }
759         }
760     }
762     /**
763      * Test getting of all archetypes.
764      */
765     public function test_get_role_archetypes() {
766         $archetypes = get_role_archetypes();
767         $this->assertCount(8, $archetypes); // There are 8 archetypes in standard install.
768         foreach ($archetypes as $k => $v) {
769             $this->assertSame($k, $v);
770         }
771     }
773     /**
774      * Test getting of roles with given archetype.
775      */
776     public function test_get_archetype_roles() {
777         $this->resetAfterTest();
779         // New install should have 1 role for each archetype.
780         $archetypes = get_role_archetypes();
781         foreach ($archetypes as $archetype) {
782             $roles = get_archetype_roles($archetype);
783             $this->assertCount(1, $roles);
784             $role = reset($roles);
785             $this->assertSame($archetype, $role->archetype);
786         }
788         create_role('New student role', 'student2', 'New student description', 'student');
789         $roles = get_archetype_roles('student');
790         $this->assertCount(2, $roles);
791     }
793     /**
794      * Test aliased role names.
795      */
796     public function test_role_get_name() {
797         global $DB;
799         $this->resetAfterTest();
801         $allroles = $DB->get_records('role');
802         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
803         $course = $this->getDataGenerator()->create_course();
804         $coursecontext = context_course::instance($course->id);
805         $otherid = create_role('Other role', 'other', 'Some other role', '');
806         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
807         $DB->insert_record('role_names', $teacherename);
808         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
809         $DB->insert_record('role_names', $otherrename);
810         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
812         foreach ($allroles as $role) {
813             // Get localised name from lang pack.
814             $this->assertSame('', $role->name);
815             $name = role_get_name($role, null, ROLENAME_ORIGINAL);
816             $this->assertNotEmpty($name);
817             $this->assertNotEquals($role->shortname, $name);
819             if (isset($renames[$role->id])) {
820                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext));
821                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS));
822                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
823                 $this->assertSame("{$renames[$role->id]} ($name)", role_get_name($role, $coursecontext, ROLENAME_BOTH));
824             } else {
825                 $this->assertSame($name, role_get_name($role, $coursecontext));
826                 $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ALIAS));
827                 $this->assertNull(role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
828                 $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_BOTH));
829             }
830             $this->assertSame($name, role_get_name($role));
831             $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ORIGINAL));
832             $this->assertSame($name, role_get_name($role, null, ROLENAME_ORIGINAL));
833             $this->assertSame($role->shortname, role_get_name($role, $coursecontext, ROLENAME_SHORT));
834             $this->assertSame($role->shortname, role_get_name($role, null, ROLENAME_SHORT));
835             $this->assertSame("$name ($role->shortname)", role_get_name($role, $coursecontext, ROLENAME_ORIGINALANDSHORT));
836             $this->assertSame("$name ($role->shortname)", role_get_name($role, null, ROLENAME_ORIGINALANDSHORT));
837             $this->assertNull(role_get_name($role, null, ROLENAME_ALIAS_RAW));
838         }
839     }
841     /**
842      * Test tweaking of role name arrays.
843      */
844     public function test_role_fix_names() {
845         global $DB;
847         $this->resetAfterTest();
849         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
850         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
851         $otherid = create_role('Other role', 'other', 'Some other role', '');
852         $anotherid = create_role('Another role', 'another', 'Yet another other role', '');
853         $allroles = $DB->get_records('role');
855         $syscontext = context_system::instance();
856         $frontcontext = context_course::instance(SITEID);
857         $course = $this->getDataGenerator()->create_course();
858         $coursecontext = context_course::instance($course->id);
859         $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
860         $categorycontext = context_coursecat::instance($category->id);
862         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
863         $DB->insert_record('role_names', $teacherename);
864         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
865         $DB->insert_record('role_names', $otherrename);
866         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
868         // Make sure all localname contain proper values for each ROLENAME_ constant,
869         // note role_get_name() on frontpage is used to get the original name for future compatibility.
870         $roles = $allroles;
871         unset($roles[$student->id]); // Remove one role to make sure no role is added or removed.
872         $rolenames = array();
873         foreach ($roles as $role) {
874             $rolenames[$role->id] = $role->name;
875         }
877         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
878         foreach ($alltypes as $type) {
879             $fixed = role_fix_names($roles, $coursecontext, $type);
880             $this->assertCount(count($roles), $fixed);
881             foreach ($fixed as $roleid => $rolename) {
882                 $this->assertInstanceOf('stdClass', $rolename);
883                 $role = $allroles[$roleid];
884                 $name = role_get_name($role, $coursecontext, $type);
885                 $this->assertSame($name, $rolename->localname);
886             }
887             $fixed = role_fix_names($rolenames, $coursecontext, $type);
888             $this->assertCount(count($rolenames), $fixed);
889             foreach ($fixed as $roleid => $rolename) {
890                 $role = $allroles[$roleid];
891                 $name = role_get_name($role, $coursecontext, $type);
892                 $this->assertSame($name, $rolename);
893             }
894         }
895     }
897     /**
898      * Test role default allows.
899      */
900     public function test_get_default_role_archetype_allows() {
901         $archetypes = get_role_archetypes();
902         foreach ($archetypes as $archetype) {
904             $result = get_default_role_archetype_allows('assign', $archetype);
905             $this->assertInternalType('array', $result);
907             $result = get_default_role_archetype_allows('override', $archetype);
908             $this->assertInternalType('array', $result);
910             $result = get_default_role_archetype_allows('switch', $archetype);
911             $this->assertInternalType('array', $result);
912         }
914         $result = get_default_role_archetype_allows('assign', '');
915         $this->assertSame(array(), $result);
917         $result = get_default_role_archetype_allows('override', '');
918         $this->assertSame(array(), $result);
920         $result = get_default_role_archetype_allows('switch', '');
921         $this->assertSame(array(), $result);
923         $result = get_default_role_archetype_allows('assign', 'wrongarchetype');
924         $this->assertSame(array(), $result);
925         $this->assertDebuggingCalled();
927         $result = get_default_role_archetype_allows('override', 'wrongarchetype');
928         $this->assertSame(array(), $result);
929         $this->assertDebuggingCalled();
931         $result = get_default_role_archetype_allows('switch', 'wrongarchetype');
932         $this->assertSame(array(), $result);
933         $this->assertDebuggingCalled();
934     }
936     /**
937      * Test allowing of role assignments.
938      */
939     public function test_allow_assign() {
940         global $DB, $CFG;
942         $this->resetAfterTest();
944         $otherid = create_role('Other role', 'other', 'Some other role', '');
945         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
947         $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
948         allow_assign($otherid, $student->id);
949         $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
951         // Test event trigger.
952         $allowroleassignevent = \core\event\role_allow_assign_updated::create(array('context' => context_system::instance()));
953         $sink = $this->redirectEvents();
954         $allowroleassignevent->trigger();
955         $events = $sink->get_events();
956         $sink->close();
957         $event = array_pop($events);
958         $this->assertInstanceOf('\core\event\role_allow_assign_updated', $event);
959         $mode = 'assign';
960         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
961         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
962         $this->assertEventLegacyLogData($expectedlegacylog, $event);
963     }
965     /**
966      * Test allowing of role overrides.
967      */
968     public function test_allow_override() {
969         global $DB, $CFG;
971         $this->resetAfterTest();
973         $otherid = create_role('Other role', 'other', 'Some other role', '');
974         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
976         $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
977         allow_override($otherid, $student->id);
978         $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
980         // Test event trigger.
981         $allowroleassignevent = \core\event\role_allow_override_updated::create(array('context' => context_system::instance()));
982         $sink = $this->redirectEvents();
983         $allowroleassignevent->trigger();
984         $events = $sink->get_events();
985         $sink->close();
986         $event = array_pop($events);
987         $this->assertInstanceOf('\core\event\role_allow_override_updated', $event);
988         $mode = 'override';
989         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
990         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
991         $this->assertEventLegacyLogData($expectedlegacylog, $event);
992     }
994     /**
995      * Test allowing of role switching.
996      */
997     public function test_allow_switch() {
998         global $DB, $CFG;
1000         $this->resetAfterTest();
1002         $otherid = create_role('Other role', 'other', 'Some other role', '');
1003         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1005         $this->assertFalse($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1006         allow_switch($otherid, $student->id);
1007         $this->assertTrue($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1009         // Test event trigger.
1010         $allowroleassignevent = \core\event\role_allow_switch_updated::create(array('context' => context_system::instance()));
1011         $sink = $this->redirectEvents();
1012         $allowroleassignevent->trigger();
1013         $events = $sink->get_events();
1014         $sink->close();
1015         $event = array_pop($events);
1016         $this->assertInstanceOf('\core\event\role_allow_switch_updated', $event);
1017         $mode = 'switch';
1018         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1019         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1020         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1021     }
1023     /**
1024      * Test returning of assignable roles in context.
1025      */
1026     public function test_get_assignable_roles() {
1027         global $DB;
1029         $this->resetAfterTest();
1031         $course = $this->getDataGenerator()->create_course();
1032         $coursecontext = context_course::instance($course->id);
1034         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1035         $teacher = $this->getDataGenerator()->create_user();
1036         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1037         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1038         $DB->insert_record('role_names', $teacherename);
1040         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1041         $student = $this->getDataGenerator()->create_user();
1042         role_assign($studentrole->id, $student->id, $coursecontext);
1044         $contexts = $DB->get_records('context');
1045         $users = $DB->get_records('user');
1046         $allroles = $DB->get_records('role');
1048         // Evaluate all results for all users in all contexts.
1049         foreach ($users as $user) {
1050             $this->setUser($user);
1051             foreach ($contexts as $contextid => $unused) {
1052                 $context = context_helper::instance_by_id($contextid);
1053                 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1054                 foreach ($allroles as $roleid => $role) {
1055                     if (isset($roles[$roleid])) {
1056                         if (is_siteadmin()) {
1057                             $this->assertTrue($DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid)));
1058                         } else {
1059                             $this->assertTrue(user_can_assign($context, $roleid), "u:$user->id r:$roleid");
1060                         }
1061                         $this->assertEquals($role->shortname, $roles[$roleid]);
1062                     } else {
1063                         $allowed = $DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid));
1064                         if (is_siteadmin()) {
1065                             $this->assertFalse($allowed);
1066                         } else {
1067                             $this->assertFalse($allowed and user_can_assign($context, $roleid), "u:$user->id, r:{$allroles[$roleid]->name}, c:$context->contextlevel");
1068                         }
1069                     }
1070                 }
1071             }
1072         }
1074         // Not-logged-in user.
1075         $this->setUser(0);
1076         foreach ($contexts as $contextid => $unused) {
1077             $context = context_helper::instance_by_id($contextid);
1078             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1079             $this->assertSame(array(), $roles);
1080         }
1082         // Test current user.
1083         $this->setUser(0);
1084         $admin = $DB->get_record('user', array('username'=>'admin'), '*', MUST_EXIST);
1085         $roles1 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin);
1086         $roles2 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin->id);
1087         $this->setAdminUser();
1088         $roles3 = get_assignable_roles($coursecontext, ROLENAME_SHORT);
1089         $this->assertSame($roles1, $roles3);
1090         $this->assertSame($roles2, $roles3);
1092         // Test parameter defaults.
1093         $this->setAdminUser();
1094         $roles1 = get_assignable_roles($coursecontext);
1095         $roles2 = get_assignable_roles($coursecontext, ROLENAME_ALIAS, false, $admin);
1096         $this->assertEquals($roles2, $roles1);
1098         // Verify returned names - let's allow all roles everywhere to simplify this a bit.
1099         $alllevels = context_helper::get_all_levels();
1100         $alllevels = array_keys($alllevels);
1101         foreach ($allroles as $roleid => $role) {
1102             set_role_contextlevels($roleid, $alllevels);
1103         }
1104         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1105         foreach ($alltypes as $type) {
1106             $rolenames = role_fix_names($allroles, $coursecontext, $type);
1107             $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1108             foreach ($roles as $roleid => $rolename) {
1109                 $this->assertSame($rolenames[$roleid]->localname, $rolename);
1110             }
1111         }
1113         // Verify counts.
1114         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1115         foreach ($alltypes as $type) {
1116             $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1117             list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($coursecontext, $type, true, $admin);
1118             $this->assertEquals($roles, $rolenames);
1119             foreach ($rolenames as $roleid => $name) {
1120                 if ($roleid == $teacherrole->id or $roleid == $studentrole->id) {
1121                     $this->assertEquals(1, $rolecounts[$roleid]);
1122                 } else {
1123                     $this->assertEquals(0, $rolecounts[$roleid]);
1124                 }
1125                 $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1126             }
1127         }
1128     }
1130     /**
1131      * Test getting of all switchable roles.
1132      */
1133     public function test_get_switchable_roles() {
1134         global $DB;
1136         $this->resetAfterTest();
1138         $course = $this->getDataGenerator()->create_course();
1139         $coursecontext = context_course::instance($course->id);
1141         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1142         $teacher = $this->getDataGenerator()->create_user();
1143         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1144         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1145         $DB->insert_record('role_names', $teacherename);
1147         $contexts = $DB->get_records('context');
1148         $users = $DB->get_records('user');
1149         $allroles = $DB->get_records('role');
1151         // Evaluate all results for all users in all contexts.
1152         foreach ($users as $user) {
1153             $this->setUser($user);
1154             foreach ($contexts as $contextid => $unused) {
1155                 $context = context_helper::instance_by_id($contextid);
1156                 $roles = get_switchable_roles($context);
1157                 foreach ($allroles as $roleid => $role) {
1158                     if (is_siteadmin()) {
1159                         $this->assertTrue(isset($roles[$roleid]));
1160                     } else {
1161                         $parents = $context->get_parent_context_ids(true);
1162                         $pcontexts = implode(',' , $parents);
1163                         $allowed = $DB->record_exists_sql(
1164                             "SELECT r.id
1165                                FROM {role} r
1166                                JOIN {role_allow_switch} ras ON ras.allowswitch = r.id
1167                                JOIN {role_assignments} ra ON ra.roleid = ras.roleid
1168                               WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1169                             ",
1170                             array('userid'=>$user->id, 'roleid'=>$roleid)
1171                         );
1172                         if (isset($roles[$roleid])) {
1173                             $this->assertTrue($allowed);
1174                         } else {
1175                             $this->assertFalse($allowed);
1176                         }
1177                     }
1179                     if (isset($roles[$roleid])) {
1180                         $coursecontext = $context->get_course_context(false);
1181                         $this->assertSame(role_get_name($role, $coursecontext), $roles[$roleid]);
1182                     }
1183                 }
1184             }
1185         }
1186     }
1188     /**
1189      * Test getting of all overridable roles.
1190      */
1191     public function test_get_overridable_roles() {
1192         global $DB;
1194         $this->resetAfterTest();
1196         $course = $this->getDataGenerator()->create_course();
1197         $coursecontext = context_course::instance($course->id);
1199         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1200         $teacher = $this->getDataGenerator()->create_user();
1201         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1202         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1203         $DB->insert_record('role_names', $teacherename);
1204         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
1205         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacher->id, $coursecontext->id);
1207         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1208         $student = $this->getDataGenerator()->create_user();
1209         role_assign($studentrole->id, $student->id, $coursecontext);
1211         $contexts = $DB->get_records('context');
1212         $users = $DB->get_records('user');
1213         $allroles = $DB->get_records('role');
1215         // Evaluate all results for all users in all contexts.
1216         foreach ($users as $user) {
1217             $this->setUser($user);
1218             foreach ($contexts as $contextid => $unused) {
1219                 $context = context_helper::instance_by_id($contextid);
1220                 $roles = get_overridable_roles($context, ROLENAME_SHORT);
1221                 foreach ($allroles as $roleid => $role) {
1222                     $hascap = has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context);
1223                     if (is_siteadmin()) {
1224                         $this->assertTrue(isset($roles[$roleid]));
1225                     } else {
1226                         $parents = $context->get_parent_context_ids(true);
1227                         $pcontexts = implode(',' , $parents);
1228                         $allowed = $DB->record_exists_sql(
1229                             "SELECT r.id
1230                                FROM {role} r
1231                                JOIN {role_allow_override} rao ON r.id = rao.allowoverride
1232                                JOIN {role_assignments} ra ON rao.roleid = ra.roleid
1233                               WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1234                             ",
1235                             array('userid'=>$user->id, 'roleid'=>$roleid)
1236                         );
1237                         if (isset($roles[$roleid])) {
1238                             $this->assertTrue($hascap);
1239                             $this->assertTrue($allowed);
1240                         } else {
1241                             $this->assertFalse($hascap and $allowed);
1242                         }
1243                     }
1245                     if (isset($roles[$roleid])) {
1246                         $this->assertEquals($role->shortname, $roles[$roleid]);
1247                     }
1248                 }
1249             }
1250         }
1252         // Test parameter defaults.
1253         $this->setAdminUser();
1254         $roles1 = get_overridable_roles($coursecontext);
1255         $roles2 = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1256         $this->assertEquals($roles2, $roles1);
1258         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1259         foreach ($alltypes as $type) {
1260             $rolenames = role_fix_names($allroles, $coursecontext, $type);
1261             $roles = get_overridable_roles($coursecontext, $type, false);
1262             foreach ($roles as $roleid => $rolename) {
1263                 $this->assertSame($rolenames[$roleid]->localname, $rolename);
1264             }
1265         }
1267         // Verify counts.
1268         $roles = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1269         list($rolenames, $rolecounts, $nameswithcounts) = get_overridable_roles($coursecontext, ROLENAME_ALIAS, true);
1270         $this->assertEquals($roles, $rolenames);
1271         foreach ($rolenames as $roleid => $name) {
1272             if ($roleid == $teacherrole->id) {
1273                 $this->assertEquals(1, $rolecounts[$roleid]);
1274             } else {
1275                 $this->assertEquals(0, $rolecounts[$roleid]);
1276             }
1277             $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1278         }
1279     }
1281     /**
1282      * Test we have context level defaults.
1283      */
1284     public function test_get_default_contextlevels() {
1285         $archetypes = get_role_archetypes();
1286         $alllevels = context_helper::get_all_levels();
1287         foreach ($archetypes as $archetype) {
1288             $defaults = get_default_contextlevels($archetype);
1289             $this->assertInternalType('array', $defaults);
1290             foreach ($defaults as $level) {
1291                 $this->assertTrue(isset($alllevels[$level]));
1292             }
1293         }
1294     }
1296     /**
1297      * Test role context level setup.
1298      */
1299     public function test_set_role_contextlevels() {
1300         global $DB;
1302         $this->resetAfterTest();
1304         $roleid = create_role('New student role', 'student2', 'New student description', 'student');
1306         $this->assertFalse($DB->record_exists('role_context_levels', array('roleid' => $roleid)));
1308         set_role_contextlevels($roleid, array(CONTEXT_COURSE, CONTEXT_MODULE));
1309         $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1310         $this->assertCount(2, $levels);
1311         $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1312         $this->assertTrue(isset($levels[CONTEXT_MODULE]));
1314         set_role_contextlevels($roleid, array(CONTEXT_COURSE));
1315         $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1316         $this->assertCount(1, $levels);
1317         $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1318     }
1320     /**
1321      * Test getting of role context levels
1322      */
1323     public function test_get_roles_for_contextlevels() {
1324         global $DB;
1326         $allroles = get_all_roles();
1327         foreach (context_helper::get_all_levels() as $level => $unused) {
1328             $roles = get_roles_for_contextlevels($level);
1329             foreach ($allroles as $roleid => $unused) {
1330                 $exists = $DB->record_exists('role_context_levels', array('contextlevel'=>$level, 'roleid'=>$roleid));
1331                 if (in_array($roleid, $roles)) {
1332                     $this->assertTrue($exists);
1333                 } else {
1334                     $this->assertFalse($exists);
1335                 }
1336             }
1337         }
1338     }
1340     /**
1341      * Test default enrol roles.
1342      */
1343     public function test_get_default_enrol_roles() {
1344         $this->resetAfterTest();
1346         $course = $this->getDataGenerator()->create_course();
1347         $coursecontext = context_course::instance($course->id);
1349         $id2 = create_role('New student role', 'student2', 'New student description', 'student');
1350         set_role_contextlevels($id2, array(CONTEXT_COURSE));
1352         $allroles = get_all_roles();
1353         $expected = array($id2=>$allroles[$id2]);
1355         foreach (get_role_archetypes() as $archetype) {
1356             $defaults = get_default_contextlevels($archetype);
1357             if (in_array(CONTEXT_COURSE, $defaults)) {
1358                 $roles = get_archetype_roles($archetype);
1359                 foreach ($roles as $role) {
1360                     $expected[$role->id] = $role;
1361                 }
1362             }
1363         }
1365         $roles = get_default_enrol_roles($coursecontext);
1366         foreach ($allroles as $role) {
1367             $this->assertEquals(isset($expected[$role->id]), isset($roles[$role->id]));
1368             if (isset($roles[$role->id])) {
1369                 $this->assertSame(role_get_name($role, $coursecontext), $roles[$role->id]);
1370             }
1371         }
1372     }
1374     /**
1375      * Test getting of role users.
1376      */
1377     public function test_get_role_users() {
1378         global $DB;
1380         $this->resetAfterTest();
1382         $systemcontext = context_system::instance();
1383         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1384         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1385         $course = $this->getDataGenerator()->create_course();
1386         $coursecontext = context_course::instance($course->id);
1387         $otherid = create_role('Other role', 'other', 'Some other role', '');
1388         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1389         $DB->insert_record('role_names', $teacherrename);
1390         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1391         $DB->insert_record('role_names', $otherrename);
1393         $user1 = $this->getDataGenerator()->create_user(array('firstname'=>'John', 'lastname'=>'Smith'));
1394         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1395         $user2 = $this->getDataGenerator()->create_user(array('firstname'=>'Jan', 'lastname'=>'Kovar'));
1396         role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1397         $user3 = $this->getDataGenerator()->create_user();
1398         $this->getDataGenerator()->enrol_user($user3->id, $course->id, $teacherrole->id);
1399         $user4 = $this->getDataGenerator()->create_user();
1400         $this->getDataGenerator()->enrol_user($user4->id, $course->id, $studentrole->id);
1402         $group = $this->getDataGenerator()->create_group(array('courseid'=>$course->id));
1403         groups_add_member($group, $user3);
1405         $users = get_role_users($teacherrole->id, $coursecontext);
1406         $this->assertCount(2, $users);
1407         $this->assertArrayHasKey($user1->id, $users);
1408         $this->assertEquals($users[$user1->id]->id, $user1->id);
1409         $this->assertEquals($users[$user1->id]->roleid, $teacherrole->id);
1410         $this->assertEquals($users[$user1->id]->rolename, $teacherrole->name);
1411         $this->assertEquals($users[$user1->id]->roleshortname, $teacherrole->shortname);
1412         $this->assertEquals($users[$user1->id]->rolecoursealias, $teacherrename->name);
1413         $this->assertArrayHasKey($user3->id, $users);
1414         $this->assertEquals($users[$user3->id]->id, $user3->id);
1415         $this->assertEquals($users[$user3->id]->roleid, $teacherrole->id);
1416         $this->assertEquals($users[$user3->id]->rolename, $teacherrole->name);
1417         $this->assertEquals($users[$user3->id]->roleshortname, $teacherrole->shortname);
1418         $this->assertEquals($users[$user3->id]->rolecoursealias, $teacherrename->name);
1420         $users = get_role_users($teacherrole->id, $coursecontext, true);
1421         $this->assertCount(3, $users);
1423         $users = get_role_users($teacherrole->id, $coursecontext, true, '', null, null, '', 2, 1);
1424         $this->assertCount(1, $users);
1426         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber');
1427         $this->assertCount(2, $users);
1428         $this->assertArrayHasKey($user1->id, $users);
1429         $this->assertArrayHasKey($user3->id, $users);
1431         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
1432         $this->assertCount(1, $users);
1433         $this->assertArrayHasKey($user3->id, $users);
1435         $users = get_role_users($teacherrole->id, $coursecontext, true, 'u.id, u.email, u.idnumber, u.firstname', 'u.idnumber', null, '', '', '', 'u.firstname = :xfirstname', array('xfirstname'=>'John'));
1436         $this->assertCount(1, $users);
1437         $this->assertArrayHasKey($user1->id, $users);
1438     }
1440     /**
1441      * Test used role query.
1442      */
1443     public function test_get_roles_used_in_context() {
1444         global $DB;
1446         $this->resetAfterTest();
1448         $systemcontext = context_system::instance();
1449         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1450         $course = $this->getDataGenerator()->create_course();
1451         $coursecontext = context_course::instance($course->id);
1452         $otherid = create_role('Other role', 'other', 'Some other role', '');
1453         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1454         $DB->insert_record('role_names', $teacherrename);
1455         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1456         $DB->insert_record('role_names', $otherrename);
1458         $user1 = $this->getDataGenerator()->create_user();
1459         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1461         $roles = get_roles_used_in_context($coursecontext);
1462         $this->assertCount(1, $roles);
1463         $role = reset($roles);
1464         $roleid = key($roles);
1465         $this->assertEquals($roleid, $role->id);
1466         $this->assertEquals($teacherrole->id, $role->id);
1467         $this->assertSame($teacherrole->name, $role->name);
1468         $this->assertSame($teacherrole->shortname, $role->shortname);
1469         $this->assertEquals($teacherrole->sortorder, $role->sortorder);
1470         $this->assertSame($teacherrename->name, $role->coursealias);
1472         $user2 = $this->getDataGenerator()->create_user();
1473         role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1474         role_assign($otherid, $user2->id, $systemcontext->id);
1476         $roles = get_roles_used_in_context($systemcontext);
1477         $this->assertCount(2, $roles);
1478     }
1480     /**
1481      * Test roles used in course.
1482      */
1483     public function test_get_user_roles_in_course() {
1484         global $DB, $CFG;
1486         $this->resetAfterTest();
1488         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1489         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1490         $course = $this->getDataGenerator()->create_course();
1491         $coursecontext = context_course::instance($course->id);
1492         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1493         $DB->insert_record('role_names', $teacherrename);
1495         $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1496         $this->assertTrue(in_array($teacherrole->id, $roleids));
1497         $this->assertTrue(in_array($studentrole->id, $roleids));
1499         $user1 = $this->getDataGenerator()->create_user();
1500         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1501         role_assign($studentrole->id, $user1->id, $coursecontext->id);
1502         $user2 = $this->getDataGenerator()->create_user();
1503         role_assign($studentrole->id, $user2->id, $coursecontext->id);
1504         $user3 = $this->getDataGenerator()->create_user();
1506         $roles = get_user_roles_in_course($user1->id, $course->id);
1507         $this->assertEquals(1, preg_match_all('/,/', $roles, $matches));
1508         $this->assertTrue(strpos($roles, role_get_name($teacherrole, $coursecontext)) !== false);
1510         $roles = get_user_roles_in_course($user2->id, $course->id);
1511         $this->assertEquals(0, preg_match_all('/,/', $roles, $matches));
1512         $this->assertTrue(strpos($roles, role_get_name($studentrole, $coursecontext)) !== false);
1514         $roles = get_user_roles_in_course($user3->id, $course->id);
1515         $this->assertSame('', $roles);
1516     }
1518     /**
1519      * Test has_capability(), has_any_capability() and has_all_capabilities().
1520      */
1521     public function test_has_capability_and_friends() {
1522         global $DB;
1524         $this->resetAfterTest();
1526         $course = $this->getDataGenerator()->create_course();
1527         $coursecontext = context_course::instance($course->id);
1528         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1529         $teacher = $this->getDataGenerator()->create_user();
1530         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1531         $admin = $DB->get_record('user', array('username'=>'admin'));
1533         // Note: Here are used default capabilities, the full test is in permission evaluation bellow,
1534         // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
1536         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupsection')));
1537         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse')));
1538         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/site:approvecourse')));
1540         $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse', 'moodle/site:approvecourse');
1541         $sc = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
1543         $this->setUser(0);
1544         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext));
1545         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext));
1546         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1547         $this->assertFalse(has_any_capability($sca, $coursecontext));
1548         $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1550         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $teacher));
1551         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $teacher));
1552         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $teacher));
1553         $this->assertTrue(has_any_capability($sca, $coursecontext, $teacher));
1554         $this->assertTrue(has_all_capabilities($sc, $coursecontext, $teacher));
1555         $this->assertFalse(has_all_capabilities($sca, $coursecontext, $teacher));
1557         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $admin));
1558         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $admin));
1559         $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext, $admin));
1560         $this->assertTrue(has_any_capability($sca, $coursecontext, $admin));
1561         $this->assertTrue(has_all_capabilities($sc, $coursecontext, $admin));
1562         $this->assertTrue(has_all_capabilities($sca, $coursecontext, $admin));
1564         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, $admin, false));
1565         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, $admin, false));
1566         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $admin, false));
1567         $this->assertFalse(has_any_capability($sca, $coursecontext, $admin, false));
1568         $this->assertFalse(has_all_capabilities($sc, $coursecontext, $admin, false));
1569         $this->assertFalse(has_all_capabilities($sca, $coursecontext, $admin, false));
1571         $this->setUser($teacher);
1572         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1573         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1574         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1575         $this->assertTrue(has_any_capability($sca, $coursecontext));
1576         $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1577         $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1579         $this->setAdminUser();
1580         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1581         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1582         $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext));
1583         $this->assertTrue(has_any_capability($sca, $coursecontext));
1584         $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1585         $this->assertTrue(has_all_capabilities($sca, $coursecontext));
1587         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, 0));
1588         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, 0));
1589         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, 0));
1590         $this->assertFalse(has_any_capability($sca, $coursecontext, 0));
1591         $this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
1592     }
1594     /**
1595      * Test if course creator future capability lookup works.
1596      */
1597     public function test_guess_if_creator_will_have_course_capability() {
1598         global $DB, $CFG, $USER;
1600         $this->resetAfterTest();
1602         $category = $this->getDataGenerator()->create_category();
1603         $course = $this->getDataGenerator()->create_course(array('category'=>$category->id));
1605         $syscontext = context_system::instance();
1606         $categorycontext = context_coursecat::instance($category->id);
1607         $coursecontext = context_course::instance($course->id);
1608         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1609         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1610         $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST);
1611         $managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
1613         $this->assertEquals($teacherrole->id, $CFG->creatornewroleid);
1615         $creator = $this->getDataGenerator()->create_user();
1616         $manager = $this->getDataGenerator()->create_user();
1617         role_assign($managerrole->id, $manager->id, $categorycontext);
1619         $this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator));
1620         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
1621         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
1622         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
1623         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
1624         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
1626         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1627         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager));
1628         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager));
1629         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id));
1630         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id));
1632         $this->assertEquals(0, $USER->id);
1633         $this->assertFalse(has_capability('moodle/course:view', $categorycontext));
1634         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext));
1635         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext));
1636         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext));
1637         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
1638         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
1640         $this->setUser($manager);
1641         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
1642         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
1643         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
1644         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
1645         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
1647         $this->setAdminUser();
1648         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
1649         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
1650         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
1651         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
1652         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
1653         $this->setUser(0);
1655         role_assign($creatorrole->id, $creator->id, $categorycontext);
1657         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
1658         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
1659         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
1660         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
1661         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
1663         $this->setUser($creator);
1664         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, null));
1665         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, null));
1666         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, null));
1667         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, null));
1668         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, null));
1669         $this->setUser(0);
1671         set_config('creatornewroleid', $studentrole->id);
1673         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
1674         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
1675         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
1676         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
1678         set_config('creatornewroleid', $teacherrole->id);
1680         role_change_permission($managerrole->id, $categorycontext, 'moodle/course:visibility', CAP_PREVENT);
1681         role_assign($creatorrole->id, $manager->id, $categorycontext);
1683         $this->assertTrue(has_capability('moodle/course:view', $categorycontext, $manager));
1684         $this->assertTrue(has_capability('moodle/course:view', $coursecontext, $manager));
1685         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1686         $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
1687         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
1688         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
1689         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
1690         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
1692         role_change_permission($managerrole->id, $categorycontext, 'moodle/course:view', CAP_PREVENT);
1693         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1694         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
1695         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
1696         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
1697         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
1699         $this->getDataGenerator()->enrol_user($manager->id, $course->id, 0);
1701         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1702         $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
1703         $this->assertTrue(is_enrolled($coursecontext, $manager));
1704         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
1705         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
1706         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
1707         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
1709         // Test problems.
1711         try {
1712             guess_if_creator_will_have_course_capability('moodle/course:visibility', $syscontext, $creator);
1713             $this->fail('Exception expected when non course/category context passed to guess_if_creator_will_have_course_capability()');
1714         } catch (moodle_exception $e) {
1715             $this->assertInstanceOf('coding_exception', $e);
1716         }
1717     }
1719     /**
1720      * Test require_capability() exceptions.
1721      */
1722     public function test_require_capability() {
1723         $this->resetAfterTest();
1725         $syscontext = context_system::instance();
1727         $this->setUser(0);
1728         $this->assertFalse(has_capability('moodle/site:config', $syscontext));
1729         try {
1730             require_capability('moodle/site:config', $syscontext);
1731             $this->fail('Exception expected from require_capability()');
1732         } catch (moodle_exception $e) {
1733             $this->assertInstanceOf('required_capability_exception', $e);
1734         }
1735         $this->setAdminUser();
1736         $this->assertFalse(has_capability('moodle/site:config', $syscontext, 0));
1737         try {
1738             require_capability('moodle/site:config', $syscontext, 0);
1739             $this->fail('Exception expected from require_capability()');
1740         } catch (moodle_exception $e) {
1741             $this->assertInstanceOf('required_capability_exception', $e);
1742         }
1743         $this->assertFalse(has_capability('moodle/site:config', $syscontext, null, false));
1744         try {
1745             require_capability('moodle/site:config', $syscontext, null, false);
1746             $this->fail('Exception expected from require_capability()');
1747         } catch (moodle_exception $e) {
1748             $this->assertInstanceOf('required_capability_exception', $e);
1749         }
1750     }
1752     /**
1753      * A small functional test of permission evaluations.
1754      */
1755     public function test_permission_evaluation() {
1756         global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE;
1758         $this->resetAfterTest();
1760         $generator = $this->getDataGenerator();
1762         // Fill the site with some real data.
1763         $testcategories = array();
1764         $testcourses = array();
1765         $testpages = array();
1766         $testblocks = array();
1767         $allroles = $DB->get_records_menu('role', array(), 'id', 'archetype, id');
1769         $systemcontext = context_system::instance();
1770         $frontpagecontext = context_course::instance(SITEID);
1772         // Add block to system context.
1773         $bi = $generator->create_block('online_users');
1774         context_block::instance($bi->id);
1775         $testblocks[] = $bi->id;
1777         // Some users.
1778         $testusers = array();
1779         for ($i=0; $i<20; $i++) {
1780             $user = $generator->create_user();
1781             $testusers[$i] = $user->id;
1782             $usercontext = context_user::instance($user->id);
1784             // Add block to user profile.
1785             $bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id));
1786             $testblocks[] = $bi->id;
1787         }
1788         // Deleted user - should be ignored everywhere, can not have context.
1789         $generator->create_user(array('deleted'=>1));
1791         // Add block to frontpage.
1792         $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id));
1793         $frontpageblockcontext = context_block::instance($bi->id);
1794         $testblocks[] = $bi->id;
1796         // Add a resource to frontpage.
1797         $page = $generator->create_module('page', array('course'=>$SITE->id));
1798         $testpages[] = $page->id;
1799         $frontpagepagecontext = context_module::instance($page->cmid);
1801         // Add block to frontpage resource.
1802         $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id));
1803         $frontpagepageblockcontext = context_block::instance($bi->id);
1804         $testblocks[] = $bi->id;
1806         // Some nested course categories with courses.
1807         $manualenrol = enrol_get_plugin('manual');
1808         $parentcat = 0;
1809         for ($i=0; $i<5; $i++) {
1810             $cat = $generator->create_category(array('parent'=>$parentcat));
1811             $testcategories[] = $cat->id;
1812             $catcontext = context_coursecat::instance($cat->id);
1813             $parentcat = $cat->id;
1815             if ($i >= 4) {
1816                 continue;
1817             }
1819             // Add resource to each category.
1820             $bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id));
1821             context_block::instance($bi->id);
1823             // Add a few courses to each category.
1824             for ($j=0; $j<6; $j++) {
1825                 $course = $generator->create_course(array('category'=>$cat->id));
1826                 $testcourses[] = $course->id;
1827                 $coursecontext = context_course::instance($course->id);
1829                 if ($j >= 5) {
1830                     continue;
1831                 }
1832                 // Add manual enrol instance.
1833                 $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id)));
1835                 // Add block to each course.
1836                 $bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
1837                 $testblocks[] = $bi->id;
1839                 // Add a resource to each course.
1840                 $page = $generator->create_module('page', array('course'=>$course->id));
1841                 $testpages[] = $page->id;
1842                 $modcontext = context_module::instance($page->cmid);
1844                 // Add block to each module.
1845                 $bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id));
1846                 $testblocks[] = $bi->id;
1847             }
1848         }
1850         // Make sure all contexts were created properly.
1851         $count = 1; // System.
1852         $count += $DB->count_records('user', array('deleted'=>0));
1853         $count += $DB->count_records('course_categories');
1854         $count += $DB->count_records('course');
1855         $count += $DB->count_records('course_modules');
1856         $count += $DB->count_records('block_instances');
1857         $this->assertEquals($count, $DB->count_records('context'));
1858         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
1859         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
1862         // Test context_helper::get_level_name() method.
1864         $levels = context_helper::get_all_levels();
1865         foreach ($levels as $level => $classname) {
1866             $name = context_helper::get_level_name($level);
1867             $this->assertNotEmpty($name);
1868         }
1871         // Test context::instance_by_id(), context_xxx::instance() methods.
1873         $context = context::instance_by_id($frontpagecontext->id);
1874         $this->assertSame(CONTEXT_COURSE, $context->contextlevel);
1875         $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING));
1876         try {
1877             context::instance_by_id(-1);
1878             $this->fail('exception expected');
1879         } catch (moodle_exception $e) {
1880             $this->assertTrue(true);
1881         }
1882         $this->assertInstanceOf('context_system', context_system::instance());
1883         $this->assertInstanceOf('context_coursecat', context_coursecat::instance($testcategories[0]));
1884         $this->assertInstanceOf('context_course', context_course::instance($testcourses[0]));
1885         $this->assertInstanceOf('context_module', context_module::instance($testpages[0]));
1886         $this->assertInstanceOf('context_block', context_block::instance($testblocks[0]));
1888         $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING));
1889         $this->assertFalse(context_course::instance(-1, IGNORE_MISSING));
1890         $this->assertFalse(context_module::instance(-1, IGNORE_MISSING));
1891         $this->assertFalse(context_block::instance(-1, IGNORE_MISSING));
1892         try {
1893             context_coursecat::instance(-1);
1894             $this->fail('exception expected');
1895         } catch (moodle_exception $e) {
1896             $this->assertTrue(true);
1897         }
1898         try {
1899             context_course::instance(-1);
1900             $this->fail('exception expected');
1901         } catch (moodle_exception $e) {
1902             $this->assertTrue(true);
1903         }
1904         try {
1905             context_module::instance(-1);
1906             $this->fail('exception expected');
1907         } catch (moodle_exception $e) {
1908             $this->assertTrue(true);
1909         }
1910         try {
1911             context_block::instance(-1);
1912             $this->fail('exception expected');
1913         } catch (moodle_exception $e) {
1914             $this->assertTrue(true);
1915         }
1918         // Test $context->get_url(), $context->get_context_name(), $context->get_capabilities() methods.
1920         $testcontexts = array();
1921         $testcontexts[CONTEXT_SYSTEM]    = context_system::instance();
1922         $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]);
1923         $testcontexts[CONTEXT_COURSE]    = context_course::instance($testcourses[0]);
1924         $testcontexts[CONTEXT_MODULE]    = context_module::instance($testpages[0]);
1925         $testcontexts[CONTEXT_BLOCK]     = context_block::instance($testblocks[0]);
1927         foreach ($testcontexts as $context) {
1928             $name = $context->get_context_name(true, true);
1929             $this->assertNotEmpty($name);
1931             $this->assertInstanceOf('moodle_url', $context->get_url());
1933             $caps = $context->get_capabilities();
1934             $this->assertTrue(is_array($caps));
1935             foreach ($caps as $cap) {
1936                 $cap = (array)$cap;
1937                 $this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask'));
1938             }
1939         }
1940         unset($testcontexts);
1942         // Test $context->get_course_context() method.
1944         $this->assertFalse($systemcontext->get_course_context(false));
1945         try {
1946             $systemcontext->get_course_context();
1947             $this->fail('exception expected');
1948         } catch (moodle_exception $e) {
1949             $this->assertInstanceOf('coding_exception', $e);
1950         }
1951         $context = context_coursecat::instance($testcategories[0]);
1952         $this->assertFalse($context->get_course_context(false));
1953         try {
1954             $context->get_course_context();
1955             $this->fail('exception expected');
1956         } catch (moodle_exception $e) {
1957             $this->assertInstanceOf('coding_exception', $e);
1958         }
1959         $this->assertEquals($frontpagecontext, $frontpagecontext->get_course_context(true));
1960         $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_course_context(true));
1961         $this->assertEquals($frontpagecontext, $frontpagepageblockcontext->get_course_context(true));
1964         // Test $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() methods.
1966         $userid = reset($testusers);
1967         $usercontext = context_user::instance($userid);
1968         $this->assertEquals($systemcontext, $usercontext->get_parent_context());
1969         $this->assertEquals(array($systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts());
1970         $this->assertEquals(array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts(true));
1972         $this->assertEquals(array(), $systemcontext->get_parent_contexts());
1973         $this->assertEquals(array($systemcontext->id=>$systemcontext), $systemcontext->get_parent_contexts(true));
1974         $this->assertEquals(array(), $systemcontext->get_parent_context_ids());
1975         $this->assertEquals(array($systemcontext->id), $systemcontext->get_parent_context_ids(true));
1977         $this->assertEquals($systemcontext, $frontpagecontext->get_parent_context());
1978         $this->assertEquals(array($systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts());
1979         $this->assertEquals(array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts(true));
1980         $this->assertEquals(array($systemcontext->id), $frontpagecontext->get_parent_context_ids());
1981         $this->assertEquals(array($frontpagecontext->id, $systemcontext->id), $frontpagecontext->get_parent_context_ids(true));
1983         $this->assertFalse($systemcontext->get_parent_context());
1984         $frontpagecontext = context_course::instance($SITE->id);
1985         $parent = $systemcontext;
1986         foreach ($testcategories as $catid) {
1987             $catcontext = context_coursecat::instance($catid);
1988             $this->assertEquals($parent, $catcontext->get_parent_context());
1989             $parent = $catcontext;
1990         }
1991         $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_parent_context());
1992         $this->assertEquals($frontpagecontext, $frontpageblockcontext->get_parent_context());
1993         $this->assertEquals($frontpagepagecontext, $frontpagepageblockcontext->get_parent_context());
1996         // Test $context->get_child_contexts() method.
1998         $children = $systemcontext->get_child_contexts();
1999         $this->resetDebugging();
2000         $this->assertEquals(count($children)+1, $DB->count_records('context'));
2002         $context = context_coursecat::instance($testcategories[3]);
2003         $children = $context->get_child_contexts();
2004         $countcats    = 0;
2005         $countcourses = 0;
2006         $countblocks  = 0;
2007         foreach ($children as $child) {
2008             if ($child->contextlevel == CONTEXT_COURSECAT) {
2009                 $countcats++;
2010             }
2011             if ($child->contextlevel == CONTEXT_COURSE) {
2012                 $countcourses++;
2013             }
2014             if ($child->contextlevel == CONTEXT_BLOCK) {
2015                 $countblocks++;
2016             }
2017         }
2018         $this->assertCount(8, $children);
2019         $this->assertEquals(1, $countcats);
2020         $this->assertEquals(6, $countcourses);
2021         $this->assertEquals(1, $countblocks);
2023         $context = context_course::instance($testcourses[2]);
2024         $children = $context->get_child_contexts();
2025         $this->assertCount(7, $children); // Depends on number of default blocks.
2027         $context = context_module::instance($testpages[3]);
2028         $children = $context->get_child_contexts();
2029         $this->assertCount(1, $children);
2031         $context = context_block::instance($testblocks[1]);
2032         $children = $context->get_child_contexts();
2033         $this->assertCount(0, $children);
2035         unset($children);
2036         unset($countcats);
2037         unset($countcourses);
2038         unset($countblocks);
2041         // Test context_helper::reset_caches() method.
2043         context_helper::reset_caches();
2044         $this->assertEquals(0, context_inspection::test_context_cache_size());
2045         context_course::instance($SITE->id);
2046         $this->assertEquals(1, context_inspection::test_context_cache_size());
2049         // Test context preloading.
2051         context_helper::reset_caches();
2052         $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')."
2053                   FROM {context} c
2054                  WHERE c.contextlevel <> ".CONTEXT_SYSTEM;
2055         $records = $DB->get_records_sql($sql);
2056         $firstrecord = reset($records);
2057         $columns = context_helper::get_preload_record_columns('c');
2058         $firstrecord = (array)$firstrecord;
2059         $this->assertSame(array_keys($firstrecord), array_values($columns));
2060         context_helper::reset_caches();
2061         foreach ($records as $record) {
2062             context_helper::preload_from_record($record);
2063             $this->assertEquals(new stdClass(), $record);
2064         }
2065         $this->assertEquals(count($records), context_inspection::test_context_cache_size());
2066         unset($records);
2067         unset($columns);
2069         context_helper::reset_caches();
2070         context_helper::preload_course($SITE->id);
2071         $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
2072         $this->assertEquals(6 + $numfrontpagemodules, context_inspection::test_context_cache_size()); // Depends on number of default blocks.
2074         // Test assign_capability(), unassign_capability() functions.
2076         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2077         $this->assertFalse($rc);
2078         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id);
2079         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2080         $this->assertEquals(CAP_ALLOW, $rc->permission);
2081         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id);
2082         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2083         $this->assertEquals(CAP_ALLOW, $rc->permission);
2084         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true);
2085         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2086         $this->assertEquals(CAP_PREVENT, $rc->permission);
2088         assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext);
2089         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2090         $this->assertFalse($rc);
2091         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext);
2092         unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true);
2093         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2094         $this->assertFalse($rc);
2095         unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true);
2096         unset($rc);
2098         accesslib_clear_all_caches(false); // Must be done after assign_capability().
2101         // Test role_assign(), role_unassign(), role_unassign_all() functions.
2103         $context = context_course::instance($testcourses[1]);
2104         $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2105         role_assign($allroles['teacher'], $testusers[1], $context->id);
2106         role_assign($allroles['teacher'], $testusers[2], $context->id);
2107         role_assign($allroles['manager'], $testusers[1], $context->id);
2108         $this->assertEquals(3, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2109         role_unassign($allroles['teacher'], $testusers[1], $context->id);
2110         $this->assertEquals(2, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2111         role_unassign_all(array('contextid'=>$context->id));
2112         $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2113         unset($context);
2115         accesslib_clear_all_caches(false); // Just in case.
2118         // Test has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends functions.
2120         $adminid = get_admin()->id;
2121         $guestid = $CFG->siteguest;
2123         // Enrol some users into some courses.
2124         $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST);
2125         $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST);
2126         $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id');
2127         $cm1 = reset($cms);
2128         $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id');
2129         $block1 = reset($blocks);
2130         $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id));
2131         $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id));
2132         for ($i=0; $i<9; $i++) {
2133             $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']);
2134         }
2135         $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']);
2136         $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']);
2138         for ($i=10; $i<15; $i++) {
2139             $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']);
2140         }
2141         $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']);
2143         // Add tons of role assignments - the more the better.
2144         role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2]));
2145         role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1]));
2146         role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id));
2147         role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id));
2148         role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id));
2149         role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id));
2150         role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id));
2151         role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id));
2153         role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id));
2154         role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id));
2156         // Add tons of overrides - the more the better.
2157         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true);
2158         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true);
2159         assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true);
2160         assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true);
2161         assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true);
2163         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true);
2164         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true);
2165         assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true);
2166         assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true);
2167         assign_capability('moodle/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
2169         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true);
2170         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true);
2171         assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true);
2172         assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true);
2174         assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true);
2176         accesslib_clear_all_caches(false); // Must be done after assign_capability().
2178         // Extra tests for guests and not-logged-in users because they can not be verified by cross checking
2179         // with get_users_by_capability() where they are ignored.
2180         $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid));
2181         $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid));
2182         $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid));
2183         $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid));
2185         $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0));
2186         $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0));
2187         $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0));
2188         $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0));
2190         $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11]));
2191         $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11]));
2192         $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11]));
2193         $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11]));
2195         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9]));
2196         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9]));
2197         $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9]));
2199         // Test the list of enrolled users.
2200         $coursecontext = context_course::instance($course1->id);
2201         $enrolled = get_enrolled_users($coursecontext);
2202         $this->assertCount(10, $enrolled);
2203         for ($i=0; $i<10; $i++) {
2204             $this->assertTrue(isset($enrolled[$testusers[$i]]));
2205         }
2206         $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update');
2207         $this->assertCount(1, $enrolled);
2208         $this->assertTrue(isset($enrolled[$testusers[9]]));
2209         unset($enrolled);
2211         // Role switching.
2212         $userid = $testusers[9];
2213         $USER = $DB->get_record('user', array('id'=>$userid));
2214         load_all_capabilities();
2215         $coursecontext = context_course::instance($course1->id);
2216         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
2217         $this->assertFalse(is_role_switched($course1->id));
2218         role_switch($allroles['student'], $coursecontext);
2219         $this->assertTrue(is_role_switched($course1->id));
2220         $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
2221         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
2222         reload_all_capabilities();
2223         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
2224         role_switch(0, $coursecontext);
2225         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
2226         $userid = $adminid;
2227         $USER = $DB->get_record('user', array('id'=>$userid));
2228         load_all_capabilities();
2229         $coursecontext = context_course::instance($course1->id);
2230         $blockcontext = context_block::instance($block1->id);
2231         $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
2232         role_switch($allroles['student'], $coursecontext);
2233         $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
2234         $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
2235         reload_all_capabilities();
2236         $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
2237         load_all_capabilities();
2238         $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
2240         // Temp course role for enrol.
2241         $DB->delete_records('cache_flags', array()); // This prevents problem with dirty contexts immediately resetting the temp role - this is a known problem...
2242         $userid = $testusers[5];
2243         $roleid = $allroles['editingteacher'];
2244         $USER = $DB->get_record('user', array('id'=>$userid));
2245         load_all_capabilities();
2246         $coursecontext = context_course::instance($course1->id);
2247         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
2248         $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid]));
2249         load_temp_course_role($coursecontext, $roleid);
2250         $this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid);
2251         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
2252         remove_temp_course_roles($coursecontext);
2253         $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
2254         load_temp_course_role($coursecontext, $roleid);
2255         reload_all_capabilities();
2256         $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
2257         $USER = new stdClass();
2258         $USER->id = 0;
2260         // Now cross check has_capability() with get_users_by_capability(), each using different code paths,
2261         // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong,
2262         // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users).
2263         $contexts = $DB->get_records('context', array(), 'id');
2264         $contexts = array_values($contexts);
2265         $capabilities = $DB->get_records('capabilities', array(), 'id');
2266         $capabilities = array_values($capabilities);
2267         $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']);
2268         $userids = array_values($testusers);
2269         $userids[] = get_admin()->id;
2271         if (!PHPUNIT_LONGTEST) {
2272             $contexts = array_slice($contexts, 0, 10);
2273             $capabilities = array_slice($capabilities, 0, 5);
2274             $userids = array_slice($userids, 0, 5);
2275         }
2277         foreach ($userids as $userid) { // No guest or deleted.
2278             // Each user gets 0-10 random roles.
2279             $rcount = rand(0, 10);
2280             for ($j=0; $j<$rcount; $j++) {
2281                 $roleid = $roles[rand(0, count($roles)-1)];
2282                 $contextid = $contexts[rand(0, count($contexts)-1)]->id;
2283                 role_assign($roleid, $userid, $contextid);
2284             }
2285         }
2287         $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT);
2288         $maxoverrides = count($contexts)*10;
2289         for ($j=0; $j<$maxoverrides; $j++) {
2290             $roleid = $roles[rand(0, count($roles)-1)];
2291             $contextid = $contexts[rand(0, count($contexts)-1)]->id;
2292             $permission = $permissions[rand(0, count($permissions)-1)];
2293             $capname = $capabilities[rand(0, count($capabilities)-1)]->name;
2294             assign_capability($capname, $permission, $roleid, $contextid, true);
2295         }
2296         unset($permissions);
2297         unset($roles);
2299         accesslib_clear_all_caches(false); // Must be done after assign_capability().
2301         // Test time - let's set up some real user, just in case the logic for USER affects the others...
2302         $USER = $DB->get_record('user', array('id'=>$testusers[3]));
2303         load_all_capabilities();
2305         $userids[] = $CFG->siteguest;
2306         $userids[] = 0; // Not-logged-in user.
2307         $userids[] = -1; // Non-existent user.
2309         foreach ($contexts as $crecord) {
2310             $context = context::instance_by_id($crecord->id);
2311             if ($coursecontext = $context->get_course_context(false)) {
2312                 $enrolled = get_enrolled_users($context);
2313             } else {
2314                 $enrolled = array();
2315             }
2316             foreach ($capabilities as $cap) {
2317                 $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username');
2318                 if ($enrolled) {
2319                     $enrolledwithcap = get_enrolled_users($context, $cap->name);
2320                 } else {
2321                     $enrolledwithcap = array();
2322                 }
2323                 foreach ($userids as $userid) {
2324                     if ($userid == 0 or isguestuser($userid)) {
2325                         if ($userid == 0) {
2326                             $CFG->forcelogin = true;
2327                             $this->assertFalse(has_capability($cap->name, $context, $userid));
2328                             unset($CFG->forcelogin);
2329                         }
2330                         if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) {
2331                             $this->assertFalse(has_capability($cap->name, $context, $userid));
2332                         }
2333                         $this->assertFalse(isset($allowed[$userid]));
2334                     } else {
2335                         if (is_siteadmin($userid)) {
2336                             $this->assertTrue(has_capability($cap->name, $context, $userid, true));
2337                         }
2338                         $hascap = has_capability($cap->name, $context, $userid, false);
2339                         $this->assertSame($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
2340                         if (isset($enrolled[$userid])) {
2341                             $this->assertSame(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
2342                         }
2343                     }
2344                 }
2345             }
2346         }
2347         // Back to nobody.
2348         $USER = new stdClass();
2349         $USER->id = 0;
2350         unset($contexts);
2351         unset($userids);
2352         unset($capabilities);
2354         // Now let's do all the remaining tests that break our carefully prepared fake site.
2357         // Test $context->mark_dirty() method.
2359         $DB->delete_records('cache_flags', array());
2360         accesslib_clear_all_caches(false);
2361         $systemcontext->mark_dirty();
2362         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2363         $this->assertTrue(isset($dirty[$systemcontext->path]));
2364         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path]));
2367         // Test $context->reload_if_dirty() method.
2369         $DB->delete_records('cache_flags', array());
2370         accesslib_clear_all_caches(false);
2371         load_all_capabilities();
2372         $context = context_course::instance($testcourses[2]);
2373         $page = $DB->get_record('page', array('course'=>$testcourses[2]));
2374         $pagecm = get_coursemodule_from_instance('page', $page->id);
2375         $pagecontext = context_module::instance($pagecm->id);
2377         $context->mark_dirty();
2378         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
2379         $USER->access['test'] = true;
2380         $context->reload_if_dirty();
2381         $this->assertFalse(isset($USER->access['test']));
2383         $context->mark_dirty();
2384         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
2385         $USER->access['test'] = true;
2386         $pagecontext->reload_if_dirty();
2387         $this->assertFalse(isset($USER->access['test']));
2390         // Test context_helper::build_all_paths() method.
2392         $oldcontexts = $DB->get_records('context', array(), 'id');
2393         $DB->set_field_select('context', 'path', null, "contextlevel <> ".CONTEXT_SYSTEM);
2394         $DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM);
2395         context_helper::build_all_paths();
2396         $newcontexts = $DB->get_records('context', array(), 'id');
2397         $this->assertEquals($oldcontexts, $newcontexts);
2398         unset($oldcontexts);
2399         unset($newcontexts);
2402         // Test $context->reset_paths() method.
2404         $context = context_course::instance($testcourses[2]);
2405         $children = $context->get_child_contexts();
2406         $context->reset_paths(false);
2407         $this->assertNull($DB->get_field('context', 'path', array('id'=>$context->id)));
2408         $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$context->id)));
2409         foreach ($children as $child) {
2410             $this->assertNull($DB->get_field('context', 'path', array('id'=>$child->id)));
2411             $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$child->id)));
2412         }
2413         $this->assertEquals(count($children)+1, $DB->count_records('context', array('depth'=>0)));
2414         $this->assertEquals(count($children)+1, $DB->count_records('context', array('path'=>null)));
2416         $context = context_course::instance($testcourses[2]);
2417         $context->reset_paths(true);
2418         $context = context_course::instance($testcourses[2]);
2419         $this->assertSame($context->path, $DB->get_field('context', 'path', array('id'=>$context->id)));
2420         $this->assertSame($context->depth, $DB->get_field('context', 'depth', array('id'=>$context->id)));
2421         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2422         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2425         // Test $context->update_moved() method.
2427         accesslib_clear_all_caches(false);
2428         $DB->delete_records('cache_flags', array());
2429         $course = $DB->get_record('course', array('id'=>$testcourses[0]));
2430         $context = context_course::instance($course->id);
2431         $oldpath = $context->path;
2432         $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
2433         $categorycontext = context_coursecat::instance($miscid);
2434         $course->category = $miscid;
2435         $DB->update_record('course', $course);
2436         $context->update_moved($categorycontext);
2438         $context = context_course::instance($course->id);
2439         $this->assertEquals($categorycontext, $context->get_parent_context());
2440         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2441         $this->assertTrue(isset($dirty[$oldpath]));
2442         $this->assertTrue(isset($dirty[$context->path]));
2445         // Test $context->delete_content() method.
2447         context_helper::reset_caches();
2448         $context = context_module::instance($testpages[3]);
2449         $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
2450         $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2451         $context->delete_content();
2452         $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
2453         $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2456         // Test $context->delete() method.
2458         context_helper::reset_caches();
2459         $context = context_module::instance($testpages[4]);
2460         $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
2461         $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2462         $bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id));
2463         $bicontext = context_block::instance($bi->id);
2464         $DB->delete_records('cache_flags', array());
2465         $context->delete(); // Should delete also linked blocks.
2466         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2467         $this->assertTrue(isset($dirty[$context->path]));
2468         $this->assertFalse($DB->record_exists('context', array('id'=>$context->id)));
2469         $this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id)));
2470         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4])));
2471         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id)));
2472         $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2473         context_module::instance($testpages[4]);
2476         // Test context_helper::delete_instance() method.
2478         context_helper::reset_caches();
2479         $lastcourse = array_pop($testcourses);
2480         $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
2481         $coursecontext = context_course::instance($lastcourse);
2482         $this->assertEquals(1, context_inspection::test_context_cache_size());
2483         $this->assertNotEquals(CONTEXT_COURSE, $coursecontext->instanceid);
2484         $DB->delete_records('cache_flags', array());
2485         context_helper::delete_instance(CONTEXT_COURSE, $lastcourse);
2486         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2487         $this->assertTrue(isset($dirty[$coursecontext->path]));
2488         $this->assertEquals(0, context_inspection::test_context_cache_size());
2489         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
2490         context_course::instance($lastcourse);
2493         // Test context_helper::create_instances() method.
2495         $prevcount = $DB->count_records('context');
2496         $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
2497         context_helper::create_instances(null, true);
2498         $this->assertSame($DB->count_records('context'), $prevcount);
2499         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2500         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2502         $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
2503         $DB->delete_records('block_instances', array());
2504         $prevcount = $DB->count_records('context');
2505         $DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM);
2506         context_helper::create_instances(null, true);
2507         $this->assertSame($prevcount, $DB->count_records('context'));
2508         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2509         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2511         // Test context_helper::cleanup_instances() method.
2513         $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}");
2514         $DB->delete_records('course', array('id'=>$lastcourse));
2515         $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
2516         $DB->delete_records('course_categories', array('id'=>$lastcategory));
2517         $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0");
2518         $DB->delete_records('user', array('id'=>$lastuser));
2519         $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id));
2520         $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid));
2521         context_helper::cleanup_instances();
2522         $count = 1; // System.
2523         $count += $DB->count_records('user', array('deleted'=>0));
2524         $count += $DB->count_records('course_categories');
2525         $count += $DB->count_records('course');
2526         $count += $DB->count_records('course_modules');
2527         $count += $DB->count_records('block_instances');
2528         $this->assertEquals($count, $DB->count_records('context'));
2531         // Test context cache size restrictions.
2533         $testusers= array();
2534         for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
2535             $user = $generator->create_user();
2536             $testusers[$i] = $user->id;
2537         }
2538         context_helper::create_instances(null, true);
2539         context_helper::reset_caches();
2540         for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
2541             context_user::instance($testusers[$i]);
2542             if ($i == CONTEXT_CACHE_MAX_SIZE - 1) {
2543                 $this->assertEquals(CONTEXT_CACHE_MAX_SIZE, context_inspection::test_context_cache_size());
2544             } else if ($i == CONTEXT_CACHE_MAX_SIZE) {
2545                 // Once the limit is reached roughly 1/3 of records should be removed from cache.
2546                 $this->assertEquals((int)ceil(CONTEXT_CACHE_MAX_SIZE * (2/3) + 101), context_inspection::test_context_cache_size());
2547             }
2548         }
2549         // We keep the first 100 cached.
2550         $prevsize = context_inspection::test_context_cache_size();
2551         for ($i=0; $i<100; $i++) {
2552             context_user::instance($testusers[$i]);
2553             $this->assertEquals($prevsize, context_inspection::test_context_cache_size());
2554         }
2555         context_user::instance($testusers[102]);
2556         $this->assertEquals($prevsize+1, context_inspection::test_context_cache_size());
2557         unset($testusers);
2561         // Test basic test of legacy functions.
2562         // Note: watch out, the fake site might be pretty borked already.
2564         $this->assertEquals(get_system_context(), context_system::instance());
2566         foreach ($DB->get_records('context') as $contextid => $record) {
2567             $context = context::instance_by_id($contextid);
2568             $this->assertEquals($context, get_context_instance_by_id($contextid, IGNORE_MISSING));
2569             $this->assertEquals($context, get_context_instance($record->contextlevel, $record->instanceid));
2570             $this->assertEquals($context->get_parent_context_ids(), get_parent_contexts($context));
2571             if ($context->id == SYSCONTEXTID) {
2572                 $this->assertFalse(get_parent_contextid($context));
2573             } else {
2574                 $this->assertSame($context->get_parent_context()->id, get_parent_contextid($context));
2575             }
2576         }
2578         $children = get_child_contexts($systemcontext);
2579         // Using assertEquals here as assertSame fails for some reason...
2580         $this->assertEquals($children, $systemcontext->get_child_contexts());
2581         $this->assertEquals(count($children), $DB->count_records('context')-1);
2582         $this->resetDebugging();
2583         unset($children);
2585         // Make sure a debugging is thrown.
2586         get_context_instance($record->contextlevel, $record->instanceid);
2587         $this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER);
2588         get_context_instance_by_id($record->id);
2589         $this->assertDebuggingCalled('get_context_instance_by_id() is deprecated, please use context::instance_by_id($id) instead.', DEBUG_DEVELOPER);
2590         get_system_context();
2591         $this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER);
2592         get_parent_contexts($context);
2593         $this->assertDebuggingCalled('get_parent_contexts() is deprecated, please use $context->get_parent_context_ids() instead.', DEBUG_DEVELOPER);
2594         get_parent_contextid($context);
2595         $this->assertDebuggingCalled('get_parent_contextid() is deprecated, please use $context->get_parent_context() instead.', DEBUG_DEVELOPER);
2596         get_child_contexts($frontpagecontext);
2597         $this->assertDebuggingCalled('get_child_contexts() is deprecated, please use $context->get_child_contexts() instead.', DEBUG_DEVELOPER);
2599         $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
2600         create_contexts();
2601         $this->assertDebuggingCalled('create_contexts() is deprecated, please use context_helper::create_instances() instead.', DEBUG_DEVELOPER);
2602         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK)));
2604         $DB->set_field('context', 'depth', 0, array('contextlevel'=>CONTEXT_BLOCK));
2605         build_context_path();
2606         $this->assertDebuggingCalled('build_context_path() is deprecated, please use context_helper::build_all_paths() instead.', DEBUG_DEVELOPER);
2607         $this->assertFalse($DB->record_exists('context', array('depth'=>0)));
2609         $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}");
2610         $DB->delete_records('course', array('id'=>$lastcourse));
2611         $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
2612         $DB->delete_records('course_categories', array('id'=>$lastcategory));
2613         $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0");
2614         $DB->delete_records('user', array('id'=>$lastuser));
2615         $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id));
2616         $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid));
2617         cleanup_contexts();
2618         $this->assertDebuggingCalled('cleanup_contexts() is deprecated, please use context_helper::cleanup_instances() instead.', DEBUG_DEVELOPER);
2619         $count = 1; // System.
2620         $count += $DB->count_records('user', array('deleted'=>0));
2621         $count += $DB->count_records('course_categories');
2622         $count += $DB->count_records('course');
2623         $count += $DB->count_records('course_modules');
2624         $count += $DB->count_records('block_instances');
2625         $this->assertEquals($count, $DB->count_records('context'));
2627         // Test legacy rebuild_contexts().
2628         $context = context_course::instance($testcourses[2]);
2629         rebuild_contexts(array($context));
2630         $this->assertDebuggingCalled('rebuild_contexts() is deprecated, please use $context->reset_paths(true) instead.', DEBUG_DEVELOPER);
2631         $context = context_course::instance($testcourses[2]);
2632         $this->assertSame($context->path, $DB->get_field('context', 'path', array('id' => $context->id)));
2633         $this->assertSame($context->depth, $DB->get_field('context', 'depth', array('id' => $context->id)));
2634         $this->assertEquals(0, $DB->count_records('context', array('depth' => 0)));
2635         $this->assertEquals(0, $DB->count_records('context', array('path' => null)));
2637         context_helper::reset_caches();
2638         preload_course_contexts($SITE->id);
2639         $this->assertDebuggingCalled('preload_course_contexts() is deprecated, please use context_helper::preload_course() instead.', DEBUG_DEVELOPER);
2640         $this->assertEquals(1 + $DB->count_records('course_modules', array('course' => $SITE->id)),
2641                 context_inspection::test_context_cache_size());
2643         context_helper::reset_caches();
2644         list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx');
2645         $this->assertDebuggingCalled('context_instance_preload_sql() is deprecated, please use context_helper::get_preload_record_columns_sql() instead.', DEBUG_DEVELOPER);
2646         $this->assertEquals(', ' . context_helper::get_preload_record_columns_sql('ctx'), $select);
2647         $this->assertEquals('LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = ' . CONTEXT_COURSECAT . ')', $join);
2648         $sql = "SELECT c.id $select FROM {course_categories} c $join";
2649         $records = $DB->get_records_sql($sql);
2650         foreach ($records as $record) {
2651             context_instance_preload($record);
2652             $this->assertDebuggingCalled('context_instance_preload() is deprecated, please use context_helper::preload_from_record() instead.',
2653                     DEBUG_DEVELOPER);
2654             $record = (array)$record;
2655             $this->assertEquals(1, count($record)); // Only id left.
2656         }
2657         $this->assertEquals(count($records), context_inspection::test_context_cache_size());
2659         accesslib_clear_all_caches(true);
2660         $DB->delete_records('cache_flags', array());
2661         mark_context_dirty($systemcontext->path);
2662         $this->assertDebuggingCalled('mark_context_dirty() is deprecated, please use $context->mark_dirty() instead.', DEBUG_DEVELOPER);
2663         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2664         $this->assertTrue(isset($dirty[$systemcontext->path]));
2666         accesslib_clear_all_caches(false);
2667         $DB->delete_records('cache_flags', array());
2668         $course = $DB->get_record('course', array('id'=>$testcourses[2]));
2669         $context = context_course::instance($course->id);
2670         $oldpath = $context->path;
2671         $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
2672         $categorycontext = context_coursecat::instance($miscid);
2673         $course->category = $miscid;
2674         $DB->update_record('course', $course);
2675         context_moved($context, $categorycontext);
2676         $this->assertDebuggingCalled('context_moved() is deprecated, please use context::update_moved() instead.', DEBUG_DEVELOPER);
2677         $context = context_course::instance($course->id);
2678         $this->assertEquals($categorycontext, $context->get_parent_context());
2680         $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$testcourses[2])));
2681         delete_context(CONTEXT_COURSE, $testcourses[2]);
2682         $this->assertDebuggingCalled('delete_context() is deprecated, please use context_helper::delete_instance() instead.', DEBUG_DEVELOPER);
2683         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$testcourses[2])));
2684         delete_context(CONTEXT_COURSE, $testcourses[2], false);
2685         $this->assertDebuggingCalled('delete_context() is deprecated, please use $context->delete_content() instead.', DEBUG_DEVELOPER);
2687         $name = get_contextlevel_name(CONTEXT_COURSE);
2688         $this->assertDebuggingCalled('get_contextlevel_name() is deprecated, please use context_helper::get_level_name() instead.', DEBUG_DEVELOPER);
2689         $this->assertFalse(empty($name));
2691         $context = context_course::instance($testcourses[2]);
2692         $name = print_context_name($context);
2693         $this->assertDebuggingCalled('print_context_name() is deprecated, please use $context->get_context_name() instead.', DEBUG_DEVELOPER);
2694         $this->assertFalse(empty($name));
2696         $url1 = get_context_url($coursecontext);
2697         $this->assertDebuggingCalled('get_context_url() is deprecated, please use $context->get_url() instead.', DEBUG_DEVELOPER);
2698         $url2 = $coursecontext->get_url();
2699         $this->assertEquals($url1, $url2);
2700         $this->assertInstanceOf('moodle_url', $url2);
2702         $pagecm = get_coursemodule_from_instance('page', $testpages[7]);
2703         $context = context_module::instance($pagecm->id);
2704         $coursecontext1 = get_course_context($context);
2705         $this->assertDebuggingCalled('get_course_context() is deprecated, please use $context->get_course_context(true) instead.', DEBUG_DEVELOPER);
2706         $coursecontext2 = $context->get_course_context(true);
2707         $this->assertEquals($coursecontext1, $coursecontext2);
2708         $this->assertEquals(CONTEXT_COURSE, $coursecontext2->contextlevel);
2709         $this->assertEquals($pagecm->course, get_courseid_from_context($context));
2710         $this->assertDebuggingCalled('get_courseid_from_context() is deprecated, please use $context->get_course_context(false) instead.', DEBUG_DEVELOPER);
2712         $caps = fetch_context_capabilities($systemcontext);
2713         $this->assertDebuggingCalled('fetch_context_capabilities() is deprecated, please use $context->get_capabilities() instead.', DEBUG_DEVELOPER);
2714         $this->assertEquals($caps, $systemcontext->get_capabilities());
2715         unset($caps);
2716     }
2718     /**
2719      * Test updating of role capabilities during upgrade
2720      */
2721     public function test_update_capabilities() {
2722         global $DB, $SITE;
2724         $this->resetAfterTest(true);
2726         $froncontext = context_course::instance($SITE->id);
2727         $student = $DB->get_record('role', array('archetype'=>'student'));
2728         $teacher = $DB->get_record('role', array('archetype'=>'teacher'));
2730         $existingcaps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
2732         $this->assertFalse(isset($existingcaps['moodle/site:restore']));         // Moved to new 'moodle/restore:restorecourse'.
2733         $this->assertTrue(isset($existingcaps['moodle/restore:restorecourse'])); // New cap from 'moodle/site:restore'.
2734         $this->assertTrue(isset($existingcaps['moodle/site:sendmessage']));      // New capability.
2735         $this->assertTrue(isset($existingcaps['moodle/backup:backupcourse']));
2736         $this->assertTrue(isset($existingcaps['moodle/backup:backupsection']));  // Cloned from 'moodle/backup:backupcourse'.
2737         $this->assertTrue(isset($existingcaps['moodle/site:approvecourse']));    // Updated bitmask.
2738         $this->assertTrue(isset($existingcaps['moodle/course:manageactivities']));
2739         $this->assertTrue(isset($existingcaps['mod/page:addinstance']));         // Cloned from core 'moodle/course:manageactivities'.
2741         // Fake state before upgrade.
2742         $DB->set_field('capabilities', 'name', 'moodle/site:restore', array('name'=>'moodle/restore:restorecourse'));
2743         $DB->set_field('role_capabilities', 'capability', 'moodle/site:restore', array('capability'=>'moodle/restore:restorecourse'));
2744         assign_capability('moodle/site:restore', CAP_PROHIBIT, $teacher->id, $froncontext->id, true);
2745         $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/site:restore', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
2747         $DB->delete_records('role_capabilities', array('capability'=>'moodle/site:sendmessage'));
2748         $DB->delete_records('capabilities', array('name'=>'moodle/site:sendmessage'));
2750         $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupsection'));
2751         $DB->delete_records('capabilities', array('name'=>'moodle/backup:backupsection'));
2752         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $froncontext->id, true);
2753         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $teacher->id, $froncontext->id, true);
2755         $DB->set_field('capabilities', 'riskbitmask', 0, array('name'=>'moodle/site:approvecourse'));
2757         $DB->delete_records('role_capabilities', array('capability'=>'mod/page:addinstance'));
2758         $DB->delete_records('capabilities', array('name'=>'mod/page:addinstance'));
2759         assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $student->id, $froncontext->id, true);
2760         assign_capability('moodle/course:manageactivities', CAP_ALLOW, $teacher->id, $froncontext->id, true);
2762         // Execute core.
2763         update_capabilities('moodle');
2765         // Only core should be upgraded.
2766         $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
2768         $this->assertFalse(isset($existingcaps['moodle/site:restore']));
2769         $this->assertTrue(isset($caps['moodle/restore:restorecourse']));
2770         $this->assertEquals($existingcaps['moodle/restore:restorecourse'], $caps['moodle/restore:restorecourse']);
2771         $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/restore:restorecourse', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
2772         $this->assertEquals($perms1, $perms2);
2774         $this->assertTrue(isset($caps['moodle/site:sendmessage']));
2775         $this->assertEquals($existingcaps['moodle/site:sendmessage'], $caps['moodle/site:sendmessage']);
2777         $this->assertTrue(isset($caps['moodle/backup:backupsection']));
2778         $this->assertEquals($existingcaps['moodle/backup:backupsection'], $caps['moodle/backup:backupsection']);
2779         $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/backup:backupcourse', 'moodle/backup:backupsection'));
2780         foreach ($roles as $role) {
2781             $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2782             $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupsection', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2783             $this->assertEquals($perms1, $perms2);
2784         }
2786         $this->assertTrue(isset($caps['moodle/site:approvecourse']));
2787         $this->assertEquals($existingcaps['moodle/site:approvecourse'], $caps['moodle/site:approvecourse']);
2789         $this->assertFalse(isset($caps['mod/page:addinstance']));
2791         // Execute plugin.
2792         update_capabilities('mod_page');
2793         $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
2794         $this->assertTrue(isset($caps['mod/page:addinstance']));
2795         $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/course:manageactivities', 'mod/page:addinstance'));
2796         foreach ($roles as $role) {
2797             $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/course:manageactivities', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2798             $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'mod/page:addinstance', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2799         }
2800         $this->assertEquals($perms1, $perms2);
2801     }
2804 /**
2805  * Context caching fixture
2806  */
2807 class context_inspection extends context_helper {
2808     public static function test_context_cache_size() {
2809         return self::$cache_count;
2810     }