MDL-55423 accesslib: Prevent get_role_users() false debugging messages
[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_for_unit_testing();
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      * Check modifying capability record is not exposed to other code.
72      */
73     public function test_capabilities_mutation() {
74         $oldcap = get_capability_info('moodle/site:config');
75         $cap = get_capability_info('moodle/site:config');
76         unset($cap->name);
77         $newcap = get_capability_info('moodle/site:config');
79         $this->assertFalse(isset($cap->name));
80         $this->assertTrue(isset($newcap->name));
81         $this->assertTrue(isset($oldcap->name));
82     }
84     /**
85      * Test getting of role access
86      */
87     public function test_get_role_access() {
88         global $DB;
90         $roles = $DB->get_records('role');
91         foreach ($roles as $role) {
92             $access = get_role_access($role->id);
94             $this->assertTrue(is_array($access));
95             $this->assertTrue(is_array($access['ra']));
96             $this->assertTrue(is_array($access['rdef']));
97             $this->assertTrue(isset($access['rdef_count']));
98             $this->assertTrue(is_array($access['loaded']));
99             $this->assertTrue(isset($access['time']));
100             $this->assertTrue(is_array($access['rsw']));
101         }
103         // Note: the data is validated in the functional permission evaluation test at the end of this testcase.
104     }
106     /**
107      * Test getting of guest role.
108      */
109     public function test_get_guest_role() {
110         global $CFG;
112         $guest = get_guest_role();
113         $this->assertEquals('guest', $guest->archetype);
114         $this->assertEquals('guest', $guest->shortname);
116         $this->assertEquals($CFG->guestroleid, $guest->id);
117     }
119     /**
120      * Test if user is admin.
121      */
122     public function test_is_siteadmin() {
123         global $DB, $CFG;
125         $this->resetAfterTest();
127         $users = $DB->get_records('user');
129         foreach ($users as $user) {
130             $this->setUser(0);
131             if ($user->username === 'admin') {
132                 $this->assertTrue(is_siteadmin($user));
133                 $this->assertTrue(is_siteadmin($user->id));
134                 $this->setUser($user);
135                 $this->assertTrue(is_siteadmin());
136                 $this->assertTrue(is_siteadmin(null));
137             } else {
138                 $this->assertFalse(is_siteadmin($user));
139                 $this->assertFalse(is_siteadmin($user->id));
140                 $this->setUser($user);
141                 $this->assertFalse(is_siteadmin());
142                 $this->assertFalse(is_siteadmin(null));
143             }
144         }
146         // Change the site admin list and check that it still works with
147         // multiple admins. We do this with userids only (not real user
148         // accounts) because it makes the test simpler.
149         $before = $CFG->siteadmins;
150         set_config('siteadmins', '666,667,668');
151         $this->assertTrue(is_siteadmin(666));
152         $this->assertTrue(is_siteadmin(667));
153         $this->assertTrue(is_siteadmin(668));
154         $this->assertFalse(is_siteadmin(669));
155         set_config('siteadmins', '13');
156         $this->assertTrue(is_siteadmin(13));
157         $this->assertFalse(is_siteadmin(666));
158         set_config('siteadmins', $before);
159     }
161     /**
162      * Test if user is enrolled in a course
163      */
164     public function test_is_enrolled() {
165         global $DB;
167         $this->resetAfterTest();
169         // Generate data.
170         $user = $this->getDataGenerator()->create_user();
171         $course = $this->getDataGenerator()->create_course();
172         $coursecontext = context_course::instance($course->id);
173         $role = $DB->get_record('role', array('shortname'=>'student'));
175         // There should be a manual enrolment as part of the default install.
176         $plugin = enrol_get_plugin('manual');
177         $instance = $DB->get_record('enrol', array(
178             'courseid' => $course->id,
179             'enrol' => 'manual',
180         ));
181         $this->assertNotSame(false, $instance);
183         // Enrol the user in the course.
184         $plugin->enrol_user($instance, $user->id, $role->id);
186         // We'll test with the mod/assign:submit capability.
187         $capability= 'mod/assign:submit';
188         $this->assertTrue($DB->record_exists('capabilities', array('name' => $capability)));
190         // Switch to our user.
191         $this->setUser($user);
193         // Ensure that the user has the capability first.
194         $this->assertTrue(has_capability($capability, $coursecontext, $user->id));
196         // We first test whether the user is enrolled on the course as this
197         // seeds the cache, then we test for the capability.
198         $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
199         $this->assertTrue(is_enrolled($coursecontext, $user, $capability));
201         // Prevent the capability for this user role.
202         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
203         $coursecontext->mark_dirty();
204         $this->assertFalse(has_capability($capability, $coursecontext, $user->id));
206         // Again, we seed the cache first by checking initial enrolment,
207         // and then we test the actual capability.
208         $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
209         $this->assertFalse(is_enrolled($coursecontext, $user, $capability));
210     }
212     /**
213      * Test logged in test.
214      */
215     public function test_isloggedin() {
216         global $USER;
218         $this->resetAfterTest();
220         $USER->id = 0;
221         $this->assertFalse(isloggedin());
222         $USER->id = 1;
223         $this->assertTrue(isloggedin());
224     }
226     /**
227      * Test guest user test.
228      */
229     public function test_isguestuser() {
230         global $DB;
232         $this->resetAfterTest();
234         $guest = $DB->get_record('user', array('username'=>'guest'));
235         $this->setUser(0);
236         $this->assertFalse(isguestuser());
237         $this->setAdminUser();
238         $this->assertFalse(isguestuser());
239         $this->assertTrue(isguestuser($guest));
240         $this->assertTrue(isguestuser($guest->id));
241         $this->setUser($guest);
242         $this->assertTrue(isguestuser());
244         $users = $DB->get_records('user');
245         foreach ($users as $user) {
246             if ($user->username === 'guest') {
247                 continue;
248             }
249             $this->assertFalse(isguestuser($user));
250         }
251     }
253     /**
254      * Test capability riskiness.
255      */
256     public function test_is_safe_capability() {
257         global $DB;
258         // Note: there is not much to test, just make sure no notices are throw for the most dangerous cap.
259         $capability = $DB->get_record('capabilities', array('name'=>'moodle/site:config'), '*', MUST_EXIST);
260         $this->assertFalse(is_safe_capability($capability));
261     }
263     /**
264      * Test context fetching.
265      */
266     public function test_get_context_info_array() {
267         $this->resetAfterTest();
269         $syscontext = context_system::instance();
270         $user = $this->getDataGenerator()->create_user();
271         $usercontext = context_user::instance($user->id);
272         $course = $this->getDataGenerator()->create_course();
273         $catcontext = context_coursecat::instance($course->category);
274         $coursecontext = context_course::instance($course->id);
275         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
276         $modcontext = context_module::instance($page->cmid);
277         $cm = get_coursemodule_from_instance('page', $page->id);
278         $block1 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
279         $block1context = context_block::instance($block1->id);
280         $block2 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$modcontext->id));
281         $block2context = context_block::instance($block2->id);
283         $result = get_context_info_array($syscontext->id);
284         $this->assertCount(3, $result);
285         $this->assertEquals($syscontext, $result[0]);
286         $this->assertNull($result[1]);
287         $this->assertNull($result[2]);
289         $result = get_context_info_array($usercontext->id);
290         $this->assertCount(3, $result);
291         $this->assertEquals($usercontext, $result[0]);
292         $this->assertNull($result[1]);
293         $this->assertNull($result[2]);
295         $result = get_context_info_array($catcontext->id);
296         $this->assertCount(3, $result);
297         $this->assertEquals($catcontext, $result[0]);
298         $this->assertNull($result[1]);
299         $this->assertNull($result[2]);
301         $result = get_context_info_array($coursecontext->id);
302         $this->assertCount(3, $result);
303         $this->assertEquals($coursecontext, $result[0]);
304         $this->assertEquals($course->id, $result[1]->id);
305         $this->assertSame($course->shortname, $result[1]->shortname);
306         $this->assertNull($result[2]);
308         $result = get_context_info_array($block1context->id);
309         $this->assertCount(3, $result);
310         $this->assertEquals($block1context, $result[0]);
311         $this->assertEquals($course->id, $result[1]->id);
312         $this->assertEquals($course->shortname, $result[1]->shortname);
313         $this->assertNull($result[2]);
315         $result = get_context_info_array($modcontext->id);
316         $this->assertCount(3, $result);
317         $this->assertEquals($modcontext, $result[0]);
318         $this->assertEquals($course->id, $result[1]->id);
319         $this->assertSame($course->shortname, $result[1]->shortname);
320         $this->assertEquals($cm->id, $result[2]->id);
322         $result = get_context_info_array($block2context->id);
323         $this->assertCount(3, $result);
324         $this->assertEquals($block2context, $result[0]);
325         $this->assertEquals($course->id, $result[1]->id);
326         $this->assertSame($course->shortname, $result[1]->shortname);
327         $this->assertEquals($cm->id, $result[2]->id);
328     }
330     /**
331      * Test looking for course contacts.
332      */
333     public function test_has_coursecontact_role() {
334         global $DB, $CFG;
336         $this->resetAfterTest();
338         $users = $DB->get_records('user');
340         // Nobody is expected to have any course level roles.
341         $this->assertNotEmpty($CFG->coursecontact);
342         foreach ($users as $user) {
343             $this->assertFalse(has_coursecontact_role($user->id));
344         }
346         $user = $this->getDataGenerator()->create_user();
347         $course = $this->getDataGenerator()->create_course();
348         role_assign($CFG->coursecontact, $user->id, context_course::instance($course->id));
349         $this->assertTrue(has_coursecontact_role($user->id));
350     }
352     /**
353      * Test creation of roles.
354      */
355     public function test_create_role() {
356         global $DB;
358         $this->resetAfterTest();
360         $id = create_role('New student role', 'student2', 'New student description', 'student');
361         $role = $DB->get_record('role', array('id'=>$id));
363         $this->assertNotEmpty($role);
364         $this->assertSame('New student role', $role->name);
365         $this->assertSame('student2', $role->shortname);
366         $this->assertSame('New student description', $role->description);
367         $this->assertSame('student', $role->archetype);
368     }
370     /**
371      * Test adding of capabilities to roles.
372      */
373     public function test_assign_capability() {
374         global $DB;
376         $this->resetAfterTest();
378         $user = $this->getDataGenerator()->create_user();
379         $syscontext = context_system::instance();
380         $frontcontext = context_course::instance(SITEID);
381         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
382         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to student by default.
383         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
384         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
386         $this->setUser($user);
387         $result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $frontcontext->id);
388         $this->assertTrue($result);
389         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
390         $this->assertNotEmpty($permission);
391         $this->assertEquals(CAP_ALLOW, $permission->permission);
392         $this->assertEquals($user->id, $permission->modifierid);
394         $this->setUser(0);
395         $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, false);
396         $this->assertTrue($result);
397         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
398         $this->assertNotEmpty($permission);
399         $this->assertEquals(CAP_ALLOW, $permission->permission);
400         $this->assertEquals($user->id, $permission->modifierid);
402         $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, true);
403         $this->assertTrue($result);
404         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
405         $this->assertNotEmpty($permission);
406         $this->assertEquals(CAP_PROHIBIT, $permission->permission);
407         $this->assertEquals(0, $permission->modifierid);
409         $result = assign_capability('moodle/backup:backupcourse', CAP_INHERIT, $student->id, $frontcontext->id);
410         $this->assertTrue($result);
411         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
412         $this->assertEmpty($permission);
414         // Test event trigger.
415         $rolecapabilityevent = \core\event\role_capabilities_updated::create(array('context' => $syscontext,
416                                                                                   'objectid' => $student->id,
417                                                                                   'other' => array('name' => $student->shortname)
418                                                                                  ));
419         $expectedlegacylog = array(SITEID, 'role', 'view', 'admin/roles/define.php?action=view&roleid=' . $student->id,
420                             $student->shortname, '', $user->id);
421         $rolecapabilityevent->set_legacy_logdata($expectedlegacylog);
422         $rolecapabilityevent->add_record_snapshot('role', $student);
424         $sink = $this->redirectEvents();
425         $rolecapabilityevent->trigger();
426         $events = $sink->get_events();
427         $sink->close();
428         $event = array_pop($events);
430         $this->assertInstanceOf('\core\event\role_capabilities_updated', $event);
431         $expectedurl = new moodle_url('/admin/roles/define.php', array('action' => 'view', 'roleid' => $student->id));
432         $this->assertEquals($expectedurl, $event->get_url());
433         $this->assertEventLegacyLogData($expectedlegacylog, $event);
434         $this->assertEventContextNotUsed($event);
435     }
437     /**
438      * Test removing of capabilities from roles.
439      */
440     public function test_unassign_capability() {
441         global $DB;
443         $this->resetAfterTest();
445         $syscontext = context_system::instance();
446         $frontcontext = context_course::instance(SITEID);
447         $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
448         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to manager by default.
449         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
451         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
452         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
454         $result = unassign_capability('moodle/backup:backupcourse', $manager->id, $syscontext->id);
455         $this->assertTrue($result);
456         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
457         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
458         unassign_capability('moodle/backup:backupcourse', $manager->id, $frontcontext);
459         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
461         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
462         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
463         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
465         $result = unassign_capability('moodle/backup:backupcourse', $manager->id);
466         $this->assertTrue($result);
467         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
468         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
469     }
471     /**
472      * Test role assigning.
473      */
474     public function test_role_assign() {
475         global $DB, $USER;
477         $this->resetAfterTest();
479         $user = $this->getDataGenerator()->create_user();
480         $course = $this->getDataGenerator()->create_course();
481         $role = $DB->get_record('role', array('shortname'=>'student'));
483         $this->setUser(0);
484         $context = context_system::instance();
485         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
486         role_assign($role->id, $user->id, $context->id);
487         $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
488         $this->assertNotEmpty($ras);
489         $this->assertSame('', $ras->component);
490         $this->assertSame('0', $ras->itemid);
491         $this->assertEquals($USER->id, $ras->modifierid);
493         $this->setAdminUser();
494         $context = context_course::instance($course->id);
495         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
496         role_assign($role->id, $user->id, $context->id, 'enrol_self', 1, 666);
497         $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
498         $this->assertNotEmpty($ras);
499         $this->assertSame('enrol_self', $ras->component);
500         $this->assertSame('1', $ras->itemid);
501         $this->assertEquals($USER->id, $ras->modifierid);
502         $this->assertEquals(666, $ras->timemodified);
504         // Test event triggered.
506         $user2 = $this->getDataGenerator()->create_user();
507         $sink = $this->redirectEvents();
508         $raid = role_assign($role->id, $user2->id, $context->id);
509         $events = $sink->get_events();
510         $sink->close();
511         $this->assertCount(1, $events);
512         $event = $events[0];
513         $this->assertInstanceOf('\core\event\role_assigned', $event);
514         $this->assertSame('role', $event->target);
515         $this->assertSame('role', $event->objecttable);
516         $this->assertEquals($role->id, $event->objectid);
517         $this->assertEquals($context->id, $event->contextid);
518         $this->assertEquals($user2->id, $event->relateduserid);
519         $this->assertCount(3, $event->other);
520         $this->assertEquals($raid, $event->other['id']);
521         $this->assertSame('', $event->other['component']);
522         $this->assertEquals(0, $event->other['itemid']);
523         $this->assertInstanceOf('moodle_url', $event->get_url());
524         $this->assertSame('role_assigned', $event::get_legacy_eventname());
525         $roles = get_all_roles();
526         $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
527         $expectedlegacylog = array($course->id, 'role', 'assign',
528             'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
529         $this->assertEventLegacyLogData($expectedlegacylog, $event);
530     }
532     /**
533      * Test role unassigning.
534      */
535     public function test_role_unassign() {
536         global $DB, $USER;
538         $this->resetAfterTest();
540         $user = $this->getDataGenerator()->create_user();
541         $course = $this->getDataGenerator()->create_course();
542         $role = $DB->get_record('role', array('shortname'=>'student'));
544         $context = context_course::instance($course->id);
545         role_assign($role->id, $user->id, $context->id);
546         $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
547         role_unassign($role->id, $user->id, $context->id);
548         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
550         role_assign($role->id, $user->id, $context->id, 'enrol_self', 1);
551         $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
552         role_unassign($role->id, $user->id, $context->id, 'enrol_self', 1);
553         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
555         // Test event triggered.
557         role_assign($role->id, $user->id, $context->id);
558         $sink = $this->redirectEvents();
559         role_unassign($role->id, $user->id, $context->id);
560         $events = $sink->get_events();
561         $sink->close();
562         $this->assertCount(1, $events);
563         $event = $events[0];
564         $this->assertInstanceOf('\core\event\role_unassigned', $event);
565         $this->assertSame('role', $event->target);
566         $this->assertSame('role', $event->objecttable);
567         $this->assertEquals($role->id, $event->objectid);
568         $this->assertEquals($context->id, $event->contextid);
569         $this->assertEquals($user->id, $event->relateduserid);
570         $this->assertCount(3, $event->other);
571         $this->assertSame('', $event->other['component']);
572         $this->assertEquals(0, $event->other['itemid']);
573         $this->assertInstanceOf('moodle_url', $event->get_url());
574         $roles = get_all_roles();
575         $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
576         $expectedlegacylog = array($course->id, 'role', 'unassign',
577             'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
578         $this->assertEventLegacyLogData($expectedlegacylog, $event);
579     }
581     /**
582      * Test role unassigning.
583      */
584     public function test_role_unassign_all() {
585         global $DB;
587         $this->resetAfterTest();
589         $user = $this->getDataGenerator()->create_user();
590         $course = $this->getDataGenerator()->create_course();
591         $role = $DB->get_record('role', array('shortname'=>'student'));
592         $role2 = $DB->get_record('role', array('shortname'=>'teacher'));
593         $syscontext = context_system::instance();
594         $coursecontext = context_course::instance($course->id);
595         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
596         $modcontext = context_module::instance($page->cmid);
598         role_assign($role->id, $user->id, $syscontext->id);
599         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
600         $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
601         role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role->id));
602         $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
604         role_assign($role->id, $user->id, $syscontext->id);
605         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
606         role_assign($role->id, $user->id, $modcontext->id);
607         $this->assertEquals(3, $DB->count_records('role_assignments', array('userid'=>$user->id)));
608         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), false);
609         $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
610         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), true);
611         $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
612         role_unassign_all(array('userid'=>$user->id));
613         $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
615         role_assign($role->id, $user->id, $syscontext->id);
616         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
617         role_assign($role->id, $user->id, $coursecontext->id);
618         role_assign($role->id, $user->id, $modcontext->id);
619         $this->assertEquals(4, $DB->count_records('role_assignments', array('userid'=>$user->id)));
620         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id, 'component'=>'enrol_self'), true, true);
621         $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
623         // Test events triggered.
625         role_assign($role2->id, $user->id, $coursecontext->id);
626         role_assign($role2->id, $user->id, $modcontext->id);
627         $sink = $this->redirectEvents();
628         role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role2->id));
629         $events = $sink->get_events();
630         $sink->close();
631         $this->assertCount(2, $events);
632         $this->assertInstanceOf('\core\event\role_unassigned', $events[0]);
633         $this->assertInstanceOf('\core\event\role_unassigned', $events[1]);
634     }
636     /**
637      * Test role queries.
638      */
639     public function test_get_roles_with_capability() {
640         global $DB;
642         $this->resetAfterTest();
644         $syscontext = context_system::instance();
645         $frontcontext = context_course::instance(SITEID);
646         $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
647         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
649         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
650         $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse'));
652         $roles = get_roles_with_capability('moodle/backup:backupcourse');
653         $this->assertEquals(array(), $roles);
655         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
656         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $manager->id, $frontcontext->id);
657         assign_capability('moodle/backup:backupcourse', CAP_PREVENT, $teacher->id, $frontcontext->id);
659         $roles = get_roles_with_capability('moodle/backup:backupcourse');
660         $this->assertEquals(array($teacher->id, $manager->id), array_keys($roles), '', 0, 10, true);
662         $roles = get_roles_with_capability('moodle/backup:backupcourse', CAP_ALLOW);
663         $this->assertEquals(array($manager->id), array_keys($roles), '', 0, 10, true);
665         $roles = get_roles_with_capability('moodle/backup:backupcourse', null, $syscontext);
666         $this->assertEquals(array($manager->id), array_keys($roles), '', 0, 10, true);
667     }
669     /**
670      * Test deleting of roles.
671      */
672     public function test_delete_role() {
673         global $DB;
675         $this->resetAfterTest();
677         $role = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
678         $user = $this->getDataGenerator()->create_user();
679         role_assign($role->id, $user->id, context_system::instance());
680         $course = $this->getDataGenerator()->create_course();
681         $rolename = (object)array('roleid'=>$role->id, 'name'=>'Man', 'contextid'=>context_course::instance($course->id)->id);
682         $DB->insert_record('role_names', $rolename);
684         $this->assertTrue($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
685         $this->assertTrue($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
686         $this->assertTrue($DB->record_exists('role_names', array('roleid'=>$role->id)));
687         $this->assertTrue($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
688         $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
689         $this->assertTrue($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
690         $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
691         $this->assertTrue($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
693         // Delete role and get event.
694         $sink = $this->redirectEvents();
695         $result = delete_role($role->id);
696         $events = $sink->get_events();
697         $sink->close();
698         $event = array_pop($events);
700         $this->assertTrue($result);
701         $this->assertFalse($DB->record_exists('role', array('id'=>$role->id)));
702         $this->assertFalse($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
703         $this->assertFalse($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
704         $this->assertFalse($DB->record_exists('role_names', array('roleid'=>$role->id)));
705         $this->assertFalse($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
706         $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
707         $this->assertFalse($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
708         $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
709         $this->assertFalse($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
711         // Test triggered event.
712         $this->assertInstanceOf('\core\event\role_deleted', $event);
713         $this->assertSame('role', $event->target);
714         $this->assertSame('role', $event->objecttable);
715         $this->assertSame($role->id, $event->objectid);
716         $this->assertEquals(context_system::instance(), $event->get_context());
717         $this->assertSame($role->shortname, $event->other['shortname']);
718         $this->assertSame($role->description, $event->other['description']);
719         $this->assertSame($role->archetype, $event->other['archetype']);
721         $expectedlegacylog = array(SITEID, 'role', 'delete', 'admin/roles/manage.php?action=delete&roleid='.$role->id,
722                                    $role->shortname, '');
723         $this->assertEventLegacyLogData($expectedlegacylog, $event);
724     }
726     /**
727      * Test fetching of all roles.
728      */
729     public function test_get_all_roles() {
730         global $DB;
732         $this->resetAfterTest();
734         $allroles = get_all_roles();
735         $this->assertInternalType('array', $allroles);
736         $this->assertCount(8, $allroles); // There are 8 roles is standard install.
738         $role = reset($allroles);
739         $role = (array)$role;
741         $this->assertEquals(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype'), array_keys($role), '', 0, 10, true);
743         foreach ($allroles as $roleid => $role) {
744             $this->assertEquals($role->id, $roleid);
745         }
747         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
748         $course = $this->getDataGenerator()->create_course();
749         $coursecontext = context_course::instance($course->id);
750         $otherid = create_role('Other role', 'other', 'Some other role', '');
751         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
752         $DB->insert_record('role_names', $teacherename);
753         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
754         $DB->insert_record('role_names', $otherrename);
755         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
757         $allroles = get_all_roles($coursecontext);
758         $this->assertInternalType('array', $allroles);
759         $this->assertCount(9, $allroles);
760         $role = reset($allroles);
761         $role = (array)$role;
763         $this->assertEquals(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype', 'coursealias'), array_keys($role), '', 0, 10, true);
765         foreach ($allroles as $roleid => $role) {
766             $this->assertEquals($role->id, $roleid);
767             if (isset($renames[$roleid])) {
768                 $this->assertSame($renames[$roleid], $role->coursealias);
769             } else {
770                 $this->assertNull($role->coursealias);
771             }
772         }
773     }
775     /**
776      * Test getting of all archetypes.
777      */
778     public function test_get_role_archetypes() {
779         $archetypes = get_role_archetypes();
780         $this->assertCount(8, $archetypes); // There are 8 archetypes in standard install.
781         foreach ($archetypes as $k => $v) {
782             $this->assertSame($k, $v);
783         }
784     }
786     /**
787      * Test getting of roles with given archetype.
788      */
789     public function test_get_archetype_roles() {
790         $this->resetAfterTest();
792         // New install should have 1 role for each archetype.
793         $archetypes = get_role_archetypes();
794         foreach ($archetypes as $archetype) {
795             $roles = get_archetype_roles($archetype);
796             $this->assertCount(1, $roles);
797             $role = reset($roles);
798             $this->assertSame($archetype, $role->archetype);
799         }
801         create_role('New student role', 'student2', 'New student description', 'student');
802         $roles = get_archetype_roles('student');
803         $this->assertCount(2, $roles);
804     }
806     /**
807      * Test aliased role names.
808      */
809     public function test_role_get_name() {
810         global $DB;
812         $this->resetAfterTest();
814         $allroles = $DB->get_records('role');
815         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
816         $course = $this->getDataGenerator()->create_course();
817         $coursecontext = context_course::instance($course->id);
818         $otherid = create_role('Other role', 'other', 'Some other role', '');
819         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
820         $DB->insert_record('role_names', $teacherename);
821         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
822         $DB->insert_record('role_names', $otherrename);
823         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
825         foreach ($allroles as $role) {
826             // Get localised name from lang pack.
827             $this->assertSame('', $role->name);
828             $name = role_get_name($role, null, ROLENAME_ORIGINAL);
829             $this->assertNotEmpty($name);
830             $this->assertNotEquals($role->shortname, $name);
832             if (isset($renames[$role->id])) {
833                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext));
834                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS));
835                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
836                 $this->assertSame("{$renames[$role->id]} ($name)", role_get_name($role, $coursecontext, ROLENAME_BOTH));
837             } else {
838                 $this->assertSame($name, role_get_name($role, $coursecontext));
839                 $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ALIAS));
840                 $this->assertNull(role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
841                 $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_BOTH));
842             }
843             $this->assertSame($name, role_get_name($role));
844             $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ORIGINAL));
845             $this->assertSame($name, role_get_name($role, null, ROLENAME_ORIGINAL));
846             $this->assertSame($role->shortname, role_get_name($role, $coursecontext, ROLENAME_SHORT));
847             $this->assertSame($role->shortname, role_get_name($role, null, ROLENAME_SHORT));
848             $this->assertSame("$name ($role->shortname)", role_get_name($role, $coursecontext, ROLENAME_ORIGINALANDSHORT));
849             $this->assertSame("$name ($role->shortname)", role_get_name($role, null, ROLENAME_ORIGINALANDSHORT));
850             $this->assertNull(role_get_name($role, null, ROLENAME_ALIAS_RAW));
851         }
852     }
854     /**
855      * Test tweaking of role name arrays.
856      */
857     public function test_role_fix_names() {
858         global $DB;
860         $this->resetAfterTest();
862         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
863         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
864         $otherid = create_role('Other role', 'other', 'Some other role', '');
865         $anotherid = create_role('Another role', 'another', 'Yet another other role', '');
866         $allroles = $DB->get_records('role');
868         $syscontext = context_system::instance();
869         $frontcontext = context_course::instance(SITEID);
870         $course = $this->getDataGenerator()->create_course();
871         $coursecontext = context_course::instance($course->id);
872         $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
873         $categorycontext = context_coursecat::instance($category->id);
875         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
876         $DB->insert_record('role_names', $teacherename);
877         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
878         $DB->insert_record('role_names', $otherrename);
879         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
881         // Make sure all localname contain proper values for each ROLENAME_ constant,
882         // note role_get_name() on frontpage is used to get the original name for future compatibility.
883         $roles = $allroles;
884         unset($roles[$student->id]); // Remove one role to make sure no role is added or removed.
885         $rolenames = array();
886         foreach ($roles as $role) {
887             $rolenames[$role->id] = $role->name;
888         }
890         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
891         foreach ($alltypes as $type) {
892             $fixed = role_fix_names($roles, $coursecontext, $type);
893             $this->assertCount(count($roles), $fixed);
894             foreach ($fixed as $roleid => $rolename) {
895                 $this->assertInstanceOf('stdClass', $rolename);
896                 $role = $allroles[$roleid];
897                 $name = role_get_name($role, $coursecontext, $type);
898                 $this->assertSame($name, $rolename->localname);
899             }
900             $fixed = role_fix_names($rolenames, $coursecontext, $type);
901             $this->assertCount(count($rolenames), $fixed);
902             foreach ($fixed as $roleid => $rolename) {
903                 $role = $allroles[$roleid];
904                 $name = role_get_name($role, $coursecontext, $type);
905                 $this->assertSame($name, $rolename);
906             }
907         }
908     }
910     /**
911      * Test role default allows.
912      */
913     public function test_get_default_role_archetype_allows() {
914         $archetypes = get_role_archetypes();
915         foreach ($archetypes as $archetype) {
917             $result = get_default_role_archetype_allows('assign', $archetype);
918             $this->assertInternalType('array', $result);
920             $result = get_default_role_archetype_allows('override', $archetype);
921             $this->assertInternalType('array', $result);
923             $result = get_default_role_archetype_allows('switch', $archetype);
924             $this->assertInternalType('array', $result);
925         }
927         $result = get_default_role_archetype_allows('assign', '');
928         $this->assertSame(array(), $result);
930         $result = get_default_role_archetype_allows('override', '');
931         $this->assertSame(array(), $result);
933         $result = get_default_role_archetype_allows('switch', '');
934         $this->assertSame(array(), $result);
936         $result = get_default_role_archetype_allows('assign', 'wrongarchetype');
937         $this->assertSame(array(), $result);
938         $this->assertDebuggingCalled();
940         $result = get_default_role_archetype_allows('override', 'wrongarchetype');
941         $this->assertSame(array(), $result);
942         $this->assertDebuggingCalled();
944         $result = get_default_role_archetype_allows('switch', 'wrongarchetype');
945         $this->assertSame(array(), $result);
946         $this->assertDebuggingCalled();
947     }
949     /**
950      * Test allowing of role assignments.
951      */
952     public function test_allow_assign() {
953         global $DB, $CFG;
955         $this->resetAfterTest();
957         $otherid = create_role('Other role', 'other', 'Some other role', '');
958         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
960         $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
961         allow_assign($otherid, $student->id);
962         $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
964         // Test event trigger.
965         $allowroleassignevent = \core\event\role_allow_assign_updated::create(array('context' => context_system::instance()));
966         $sink = $this->redirectEvents();
967         $allowroleassignevent->trigger();
968         $events = $sink->get_events();
969         $sink->close();
970         $event = array_pop($events);
971         $this->assertInstanceOf('\core\event\role_allow_assign_updated', $event);
972         $mode = 'assign';
973         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
974         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
975         $this->assertEventLegacyLogData($expectedlegacylog, $event);
976     }
978     /**
979      * Test allowing of role overrides.
980      */
981     public function test_allow_override() {
982         global $DB, $CFG;
984         $this->resetAfterTest();
986         $otherid = create_role('Other role', 'other', 'Some other role', '');
987         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
989         $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
990         allow_override($otherid, $student->id);
991         $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
993         // Test event trigger.
994         $allowroleassignevent = \core\event\role_allow_override_updated::create(array('context' => context_system::instance()));
995         $sink = $this->redirectEvents();
996         $allowroleassignevent->trigger();
997         $events = $sink->get_events();
998         $sink->close();
999         $event = array_pop($events);
1000         $this->assertInstanceOf('\core\event\role_allow_override_updated', $event);
1001         $mode = 'override';
1002         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1003         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1004         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1005     }
1007     /**
1008      * Test allowing of role switching.
1009      */
1010     public function test_allow_switch() {
1011         global $DB, $CFG;
1013         $this->resetAfterTest();
1015         $otherid = create_role('Other role', 'other', 'Some other role', '');
1016         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1018         $this->assertFalse($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1019         allow_switch($otherid, $student->id);
1020         $this->assertTrue($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1022         // Test event trigger.
1023         $allowroleassignevent = \core\event\role_allow_switch_updated::create(array('context' => context_system::instance()));
1024         $sink = $this->redirectEvents();
1025         $allowroleassignevent->trigger();
1026         $events = $sink->get_events();
1027         $sink->close();
1028         $event = array_pop($events);
1029         $this->assertInstanceOf('\core\event\role_allow_switch_updated', $event);
1030         $mode = 'switch';
1031         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1032         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1033         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1034     }
1036     /**
1037      * Test returning of assignable roles in context.
1038      */
1039     public function test_get_assignable_roles() {
1040         global $DB;
1042         $this->resetAfterTest();
1044         $course = $this->getDataGenerator()->create_course();
1045         $coursecontext = context_course::instance($course->id);
1047         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1048         $teacher = $this->getDataGenerator()->create_user();
1049         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1050         $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1051         $DB->insert_record('role_names', $teacherename);
1053         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1054         $student = $this->getDataGenerator()->create_user();
1055         role_assign($studentrole->id, $student->id, $coursecontext);
1057         $contexts = $DB->get_records('context');
1058         $users = $DB->get_records('user');
1059         $allroles = $DB->get_records('role');
1061         // Evaluate all results for all users in all contexts.
1062         foreach ($users as $user) {
1063             $this->setUser($user);
1064             foreach ($contexts as $contextid => $unused) {
1065                 $context = context_helper::instance_by_id($contextid);
1066                 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1067                 foreach ($allroles as $roleid => $role) {
1068                     if (isset($roles[$roleid])) {
1069                         if (is_siteadmin()) {
1070                             $this->assertTrue($DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid)));
1071                         } else {
1072                             $this->assertTrue(user_can_assign($context, $roleid), "u:$user->id r:$roleid");
1073                         }
1074                         $this->assertEquals($role->shortname, $roles[$roleid]);
1075                     } else {
1076                         $allowed = $DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid));
1077                         if (is_siteadmin()) {
1078                             $this->assertFalse($allowed);
1079                         } else {
1080                             $this->assertFalse($allowed and user_can_assign($context, $roleid), "u:$user->id, r:{$allroles[$roleid]->name}, c:$context->contextlevel");
1081                         }
1082                     }
1083                 }
1084             }
1085         }
1087         // Not-logged-in user.
1088         $this->setUser(0);
1089         foreach ($contexts as $contextid => $unused) {
1090             $context = context_helper::instance_by_id($contextid);
1091             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1092             $this->assertSame(array(), $roles);
1093         }
1095         // Test current user.
1096         $this->setUser(0);
1097         $admin = $DB->get_record('user', array('username'=>'admin'), '*', MUST_EXIST);
1098         $roles1 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin);
1099         $roles2 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin->id);
1100         $this->setAdminUser();
1101         $roles3 = get_assignable_roles($coursecontext, ROLENAME_SHORT);
1102         $this->assertSame($roles1, $roles3);
1103         $this->assertSame($roles2, $roles3);
1105         // Test parameter defaults.
1106         $this->setAdminUser();
1107         $roles1 = get_assignable_roles($coursecontext);
1108         $roles2 = get_assignable_roles($coursecontext, ROLENAME_ALIAS, false, $admin);
1109         $this->assertEquals($roles2, $roles1);
1111         // Verify returned names - let's allow all roles everywhere to simplify this a bit.
1112         $alllevels = context_helper::get_all_levels();
1113         $alllevels = array_keys($alllevels);
1114         foreach ($allroles as $roleid => $role) {
1115             set_role_contextlevels($roleid, $alllevels);
1116         }
1117         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1118         foreach ($alltypes as $type) {
1119             $rolenames = role_fix_names($allroles, $coursecontext, $type);
1120             $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1121             foreach ($roles as $roleid => $rolename) {
1122                 $this->assertSame($rolenames[$roleid]->localname, $rolename);
1123             }
1124         }
1126         // Verify counts.
1127         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1128         foreach ($alltypes as $type) {
1129             $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1130             list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($coursecontext, $type, true, $admin);
1131             $this->assertEquals($roles, $rolenames);
1132             foreach ($rolenames as $roleid => $name) {
1133                 if ($roleid == $teacherrole->id or $roleid == $studentrole->id) {
1134                     $this->assertEquals(1, $rolecounts[$roleid]);
1135                 } else {
1136                     $this->assertEquals(0, $rolecounts[$roleid]);
1137                 }
1138                 $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1139             }
1140         }
1141     }
1143     /**
1144      * Test getting of all switchable roles.
1145      */
1146     public function test_get_switchable_roles() {
1147         global $DB;
1149         $this->resetAfterTest();
1151         $course = $this->getDataGenerator()->create_course();
1152         $coursecontext = context_course::instance($course->id);
1154         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1155         $teacher = $this->getDataGenerator()->create_user();
1156         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1157         $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1158         $DB->insert_record('role_names', $teacherename);
1160         $contexts = $DB->get_records('context');
1161         $users = $DB->get_records('user');
1162         $allroles = $DB->get_records('role');
1164         // Evaluate all results for all users in all contexts.
1165         foreach ($users as $user) {
1166             $this->setUser($user);
1167             foreach ($contexts as $contextid => $unused) {
1168                 $context = context_helper::instance_by_id($contextid);
1169                 $roles = get_switchable_roles($context);
1170                 foreach ($allroles as $roleid => $role) {
1171                     if (is_siteadmin()) {
1172                         $this->assertTrue(isset($roles[$roleid]));
1173                     } else {
1174                         $parents = $context->get_parent_context_ids(true);
1175                         $pcontexts = implode(',' , $parents);
1176                         $allowed = $DB->record_exists_sql(
1177                             "SELECT r.id
1178                                FROM {role} r
1179                                JOIN {role_allow_switch} ras ON ras.allowswitch = r.id
1180                                JOIN {role_assignments} ra ON ra.roleid = ras.roleid
1181                               WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1182                             ",
1183                             array('userid'=>$user->id, 'roleid'=>$roleid)
1184                         );
1185                         if (isset($roles[$roleid])) {
1186                             $this->assertTrue($allowed);
1187                         } else {
1188                             $this->assertFalse($allowed);
1189                         }
1190                     }
1192                     if (isset($roles[$roleid])) {
1193                         $coursecontext = $context->get_course_context(false);
1194                         $this->assertSame(role_get_name($role, $coursecontext), $roles[$roleid]);
1195                     }
1196                 }
1197             }
1198         }
1199     }
1201     /**
1202      * Test getting of all overridable roles.
1203      */
1204     public function test_get_overridable_roles() {
1205         global $DB;
1207         $this->resetAfterTest();
1209         $course = $this->getDataGenerator()->create_course();
1210         $coursecontext = context_course::instance($course->id);
1212         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1213         $teacher = $this->getDataGenerator()->create_user();
1214         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1215         $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1216         $DB->insert_record('role_names', $teacherename);
1217         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
1218         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id);
1220         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1221         $student = $this->getDataGenerator()->create_user();
1222         role_assign($studentrole->id, $student->id, $coursecontext);
1224         $contexts = $DB->get_records('context');
1225         $users = $DB->get_records('user');
1226         $allroles = $DB->get_records('role');
1228         // Evaluate all results for all users in all contexts.
1229         foreach ($users as $user) {
1230             $this->setUser($user);
1231             foreach ($contexts as $contextid => $unused) {
1232                 $context = context_helper::instance_by_id($contextid);
1233                 $roles = get_overridable_roles($context, ROLENAME_SHORT);
1234                 foreach ($allroles as $roleid => $role) {
1235                     $hascap = has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context);
1236                     if (is_siteadmin()) {
1237                         $this->assertTrue(isset($roles[$roleid]));
1238                     } else {
1239                         $parents = $context->get_parent_context_ids(true);
1240                         $pcontexts = implode(',' , $parents);
1241                         $allowed = $DB->record_exists_sql(
1242                             "SELECT r.id
1243                                FROM {role} r
1244                                JOIN {role_allow_override} rao ON r.id = rao.allowoverride
1245                                JOIN {role_assignments} ra ON rao.roleid = ra.roleid
1246                               WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1247                             ",
1248                             array('userid'=>$user->id, 'roleid'=>$roleid)
1249                         );
1250                         if (isset($roles[$roleid])) {
1251                             $this->assertTrue($hascap);
1252                             $this->assertTrue($allowed);
1253                         } else {
1254                             $this->assertFalse($hascap and $allowed);
1255                         }
1256                     }
1258                     if (isset($roles[$roleid])) {
1259                         $this->assertEquals($role->shortname, $roles[$roleid]);
1260                     }
1261                 }
1262             }
1263         }
1265         // Test parameter defaults.
1266         $this->setAdminUser();
1267         $roles1 = get_overridable_roles($coursecontext);
1268         $roles2 = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1269         $this->assertEquals($roles2, $roles1);
1271         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1272         foreach ($alltypes as $type) {
1273             $rolenames = role_fix_names($allroles, $coursecontext, $type);
1274             $roles = get_overridable_roles($coursecontext, $type, false);
1275             foreach ($roles as $roleid => $rolename) {
1276                 $this->assertSame($rolenames[$roleid]->localname, $rolename);
1277             }
1278         }
1280         // Verify counts.
1281         $roles = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1282         list($rolenames, $rolecounts, $nameswithcounts) = get_overridable_roles($coursecontext, ROLENAME_ALIAS, true);
1283         $this->assertEquals($roles, $rolenames);
1284         foreach ($rolenames as $roleid => $name) {
1285             if ($roleid == $teacherrole->id) {
1286                 $this->assertEquals(1, $rolecounts[$roleid]);
1287             } else {
1288                 $this->assertEquals(0, $rolecounts[$roleid]);
1289             }
1290             $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1291         }
1292     }
1294     /**
1295      * Test we have context level defaults.
1296      */
1297     public function test_get_default_contextlevels() {
1298         $archetypes = get_role_archetypes();
1299         $alllevels = context_helper::get_all_levels();
1300         foreach ($archetypes as $archetype) {
1301             $defaults = get_default_contextlevels($archetype);
1302             $this->assertInternalType('array', $defaults);
1303             foreach ($defaults as $level) {
1304                 $this->assertTrue(isset($alllevels[$level]));
1305             }
1306         }
1307     }
1309     /**
1310      * Test role context level setup.
1311      */
1312     public function test_set_role_contextlevels() {
1313         global $DB;
1315         $this->resetAfterTest();
1317         $roleid = create_role('New student role', 'student2', 'New student description', 'student');
1319         $this->assertFalse($DB->record_exists('role_context_levels', array('roleid' => $roleid)));
1321         set_role_contextlevels($roleid, array(CONTEXT_COURSE, CONTEXT_MODULE));
1322         $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1323         $this->assertCount(2, $levels);
1324         $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1325         $this->assertTrue(isset($levels[CONTEXT_MODULE]));
1327         set_role_contextlevels($roleid, array(CONTEXT_COURSE));
1328         $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1329         $this->assertCount(1, $levels);
1330         $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1331     }
1333     /**
1334      * Test getting of role context levels
1335      */
1336     public function test_get_roles_for_contextlevels() {
1337         global $DB;
1339         $allroles = get_all_roles();
1340         foreach (context_helper::get_all_levels() as $level => $unused) {
1341             $roles = get_roles_for_contextlevels($level);
1342             foreach ($allroles as $roleid => $unused) {
1343                 $exists = $DB->record_exists('role_context_levels', array('contextlevel'=>$level, 'roleid'=>$roleid));
1344                 if (in_array($roleid, $roles)) {
1345                     $this->assertTrue($exists);
1346                 } else {
1347                     $this->assertFalse($exists);
1348                 }
1349             }
1350         }
1351     }
1353     /**
1354      * Test default enrol roles.
1355      */
1356     public function test_get_default_enrol_roles() {
1357         $this->resetAfterTest();
1359         $course = $this->getDataGenerator()->create_course();
1360         $coursecontext = context_course::instance($course->id);
1362         $id2 = create_role('New student role', 'student2', 'New student description', 'student');
1363         set_role_contextlevels($id2, array(CONTEXT_COURSE));
1365         $allroles = get_all_roles();
1366         $expected = array($id2=>$allroles[$id2]);
1368         foreach (get_role_archetypes() as $archetype) {
1369             $defaults = get_default_contextlevels($archetype);
1370             if (in_array(CONTEXT_COURSE, $defaults)) {
1371                 $roles = get_archetype_roles($archetype);
1372                 foreach ($roles as $role) {
1373                     $expected[$role->id] = $role;
1374                 }
1375             }
1376         }
1378         $roles = get_default_enrol_roles($coursecontext);
1379         foreach ($allroles as $role) {
1380             $this->assertEquals(isset($expected[$role->id]), isset($roles[$role->id]));
1381             if (isset($roles[$role->id])) {
1382                 $this->assertSame(role_get_name($role, $coursecontext), $roles[$role->id]);
1383             }
1384         }
1385     }
1387     /**
1388      * Test getting of role users.
1389      */
1390     public function test_get_role_users() {
1391         global $DB;
1393         $this->resetAfterTest();
1395         $systemcontext = context_system::instance();
1396         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1397         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1398         $noeditteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
1399         $course = $this->getDataGenerator()->create_course();
1400         $coursecontext = context_course::instance($course->id);
1401         $otherid = create_role('Other role', 'other', 'Some other role', '');
1402         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1403         $DB->insert_record('role_names', $teacherrename);
1404         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1405         $DB->insert_record('role_names', $otherrename);
1407         $user1 = $this->getDataGenerator()->create_user(array('firstname'=>'John', 'lastname'=>'Smith'));
1408         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1409         $user2 = $this->getDataGenerator()->create_user(array('firstname'=>'Jan', 'lastname'=>'Kovar'));
1410         role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1411         $user3 = $this->getDataGenerator()->create_user();
1412         $this->getDataGenerator()->enrol_user($user3->id, $course->id, $teacherrole->id);
1413         $user4 = $this->getDataGenerator()->create_user();
1414         $this->getDataGenerator()->enrol_user($user4->id, $course->id, $studentrole->id);
1415         $this->getDataGenerator()->enrol_user($user4->id, $course->id, $noeditteacherrole->id);
1417         $group = $this->getDataGenerator()->create_group(array('courseid'=>$course->id));
1418         groups_add_member($group, $user3);
1420         $users = get_role_users($teacherrole->id, $coursecontext);
1421         $this->assertCount(2, $users);
1422         $this->assertArrayHasKey($user1->id, $users);
1423         $this->assertEquals($users[$user1->id]->id, $user1->id);
1424         $this->assertEquals($users[$user1->id]->roleid, $teacherrole->id);
1425         $this->assertEquals($users[$user1->id]->rolename, $teacherrole->name);
1426         $this->assertEquals($users[$user1->id]->roleshortname, $teacherrole->shortname);
1427         $this->assertEquals($users[$user1->id]->rolecoursealias, $teacherrename->name);
1428         $this->assertArrayHasKey($user3->id, $users);
1429         $this->assertEquals($users[$user3->id]->id, $user3->id);
1430         $this->assertEquals($users[$user3->id]->roleid, $teacherrole->id);
1431         $this->assertEquals($users[$user3->id]->rolename, $teacherrole->name);
1432         $this->assertEquals($users[$user3->id]->roleshortname, $teacherrole->shortname);
1433         $this->assertEquals($users[$user3->id]->rolecoursealias, $teacherrename->name);
1435         $users = get_role_users($teacherrole->id, $coursecontext, true);
1436         $this->assertCount(3, $users);
1438         $users = get_role_users($teacherrole->id, $coursecontext, true, '', null, null, '', 2, 1);
1439         $this->assertCount(1, $users);
1441         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber');
1442         $this->assertCount(2, $users);
1443         $this->assertArrayHasKey($user1->id, $users);
1444         $this->assertArrayHasKey($user3->id, $users);
1446         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email');
1447         $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1448         $this->assertCount(2, $users);
1449         $this->assertArrayHasKey($user1->id, $users);
1450         $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
1451         $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
1452         $this->assertArrayHasKey($user3->id, $users);
1453         $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
1454         $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
1456         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.*');
1457         $this->assertDebuggingNotCalled();
1458         $this->assertCount(2, $users);
1460         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id AS id_alias');
1461         $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1462         $this->assertCount(2, $users);
1463         $this->assertArrayHasKey($user1->id, $users);
1464         $this->assertObjectHasAttribute('id_alias', $users[$user1->id]);
1465         $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
1466         $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
1467         $this->assertArrayHasKey($user3->id, $users);
1468         $this->assertObjectHasAttribute('id_alias', $users[$user3->id]);
1469         $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
1470         $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
1472         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
1473         $this->assertCount(1, $users);
1474         $this->assertArrayHasKey($user3->id, $users);
1476         $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'));
1477         $this->assertCount(1, $users);
1478         $this->assertArrayHasKey($user1->id, $users);
1480         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.id', 'ra.id');
1481         $this->assertDebuggingNotCalled();
1482         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.userid', 'ra.userid');
1483         $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1484             'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1485         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false);
1486         $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1487             'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1488         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext,
1489             false, 'u.id, u.firstname', 'u.id, u.firstname');
1490         $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1491             'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1492     }
1494     /**
1495      * Test used role query.
1496      */
1497     public function test_get_roles_used_in_context() {
1498         global $DB;
1500         $this->resetAfterTest();
1502         $systemcontext = context_system::instance();
1503         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1504         $course = $this->getDataGenerator()->create_course();
1505         $coursecontext = context_course::instance($course->id);
1506         $otherid = create_role('Other role', 'other', 'Some other role', '');
1507         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1508         $DB->insert_record('role_names', $teacherrename);
1509         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1510         $DB->insert_record('role_names', $otherrename);
1512         $user1 = $this->getDataGenerator()->create_user();
1513         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1515         $roles = get_roles_used_in_context($coursecontext);
1516         $this->assertCount(1, $roles);
1517         $role = reset($roles);
1518         $roleid = key($roles);
1519         $this->assertEquals($roleid, $role->id);
1520         $this->assertEquals($teacherrole->id, $role->id);
1521         $this->assertSame($teacherrole->name, $role->name);
1522         $this->assertSame($teacherrole->shortname, $role->shortname);
1523         $this->assertEquals($teacherrole->sortorder, $role->sortorder);
1524         $this->assertSame($teacherrename->name, $role->coursealias);
1526         $user2 = $this->getDataGenerator()->create_user();
1527         role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1528         role_assign($otherid, $user2->id, $systemcontext->id);
1530         $roles = get_roles_used_in_context($systemcontext);
1531         $this->assertCount(2, $roles);
1532     }
1534     /**
1535      * Test roles used in course.
1536      */
1537     public function test_get_user_roles_in_course() {
1538         global $DB, $CFG;
1540         $this->resetAfterTest();
1542         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1543         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1544         $course = $this->getDataGenerator()->create_course();
1545         $coursecontext = context_course::instance($course->id);
1546         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1547         $DB->insert_record('role_names', $teacherrename);
1549         $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1550         $this->assertTrue(in_array($teacherrole->id, $roleids));
1551         $this->assertTrue(in_array($studentrole->id, $roleids));
1553         $user1 = $this->getDataGenerator()->create_user();
1554         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1555         role_assign($studentrole->id, $user1->id, $coursecontext->id);
1556         $user2 = $this->getDataGenerator()->create_user();
1557         role_assign($studentrole->id, $user2->id, $coursecontext->id);
1558         $user3 = $this->getDataGenerator()->create_user();
1560         $roles = get_user_roles_in_course($user1->id, $course->id);
1561         $this->assertEquals(1, preg_match_all('/,/', $roles, $matches));
1562         $this->assertTrue(strpos($roles, role_get_name($teacherrole, $coursecontext)) !== false);
1564         $roles = get_user_roles_in_course($user2->id, $course->id);
1565         $this->assertEquals(0, preg_match_all('/,/', $roles, $matches));
1566         $this->assertTrue(strpos($roles, role_get_name($studentrole, $coursecontext)) !== false);
1568         $roles = get_user_roles_in_course($user3->id, $course->id);
1569         $this->assertSame('', $roles);
1570     }
1572     /**
1573      * Test has_capability(), has_any_capability() and has_all_capabilities().
1574      */
1575     public function test_has_capability_and_friends() {
1576         global $DB;
1578         $this->resetAfterTest();
1580         $course = $this->getDataGenerator()->create_course();
1581         $coursecontext = context_course::instance($course->id);
1582         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1583         $teacher = $this->getDataGenerator()->create_user();
1584         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1585         $admin = $DB->get_record('user', array('username'=>'admin'));
1587         // Note: Here are used default capabilities, the full test is in permission evaluation bellow,
1588         // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
1590         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupsection')));
1591         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse')));
1592         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/site:approvecourse')));
1594         $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse', 'moodle/site:approvecourse');
1595         $sc = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
1597         $this->setUser(0);
1598         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext));
1599         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext));
1600         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1601         $this->assertFalse(has_any_capability($sca, $coursecontext));
1602         $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1604         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $teacher));
1605         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $teacher));
1606         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $teacher));
1607         $this->assertTrue(has_any_capability($sca, $coursecontext, $teacher));
1608         $this->assertTrue(has_all_capabilities($sc, $coursecontext, $teacher));
1609         $this->assertFalse(has_all_capabilities($sca, $coursecontext, $teacher));
1611         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $admin));
1612         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $admin));
1613         $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext, $admin));
1614         $this->assertTrue(has_any_capability($sca, $coursecontext, $admin));
1615         $this->assertTrue(has_all_capabilities($sc, $coursecontext, $admin));
1616         $this->assertTrue(has_all_capabilities($sca, $coursecontext, $admin));
1618         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, $admin, false));
1619         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, $admin, false));
1620         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $admin, false));
1621         $this->assertFalse(has_any_capability($sca, $coursecontext, $admin, false));
1622         $this->assertFalse(has_all_capabilities($sc, $coursecontext, $admin, false));
1623         $this->assertFalse(has_all_capabilities($sca, $coursecontext, $admin, false));
1625         $this->setUser($teacher);
1626         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1627         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1628         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1629         $this->assertTrue(has_any_capability($sca, $coursecontext));
1630         $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1631         $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1633         $this->setAdminUser();
1634         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1635         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1636         $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext));
1637         $this->assertTrue(has_any_capability($sca, $coursecontext));
1638         $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1639         $this->assertTrue(has_all_capabilities($sca, $coursecontext));
1641         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, 0));
1642         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, 0));
1643         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, 0));
1644         $this->assertFalse(has_any_capability($sca, $coursecontext, 0));
1645         $this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
1646     }
1648     /**
1649      * Test if course creator future capability lookup works.
1650      */
1651     public function test_guess_if_creator_will_have_course_capability() {
1652         global $DB, $CFG, $USER;
1654         $this->resetAfterTest();
1656         $category = $this->getDataGenerator()->create_category();
1657         $course = $this->getDataGenerator()->create_course(array('category'=>$category->id));
1659         $syscontext = context_system::instance();
1660         $categorycontext = context_coursecat::instance($category->id);
1661         $coursecontext = context_course::instance($course->id);
1662         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1663         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1664         $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST);
1665         $managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
1667         $this->assertEquals($teacherrole->id, $CFG->creatornewroleid);
1669         $creator = $this->getDataGenerator()->create_user();
1670         $manager = $this->getDataGenerator()->create_user();
1671         role_assign($managerrole->id, $manager->id, $categorycontext);
1673         $this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator));
1674         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
1675         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
1676         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
1677         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
1678         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
1680         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1681         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager));
1682         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager));
1683         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id));
1684         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id));
1686         $this->assertEquals(0, $USER->id);
1687         $this->assertFalse(has_capability('moodle/course:view', $categorycontext));
1688         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext));
1689         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext));
1690         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext));
1691         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
1692         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
1694         $this->setUser($manager);
1695         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
1696         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
1697         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
1698         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
1699         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
1701         $this->setAdminUser();
1702         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
1703         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
1704         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
1705         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
1706         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
1707         $this->setUser(0);
1709         role_assign($creatorrole->id, $creator->id, $categorycontext);
1711         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
1712         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
1713         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
1714         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
1715         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
1717         $this->setUser($creator);
1718         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, null));
1719         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, null));
1720         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, null));
1721         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, null));
1722         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, null));
1723         $this->setUser(0);
1725         set_config('creatornewroleid', $studentrole->id);
1727         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
1728         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
1729         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
1730         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
1732         set_config('creatornewroleid', $teacherrole->id);
1734         role_change_permission($managerrole->id, $categorycontext, 'moodle/course:visibility', CAP_PREVENT);
1735         role_assign($creatorrole->id, $manager->id, $categorycontext);
1737         $this->assertTrue(has_capability('moodle/course:view', $categorycontext, $manager));
1738         $this->assertTrue(has_capability('moodle/course:view', $coursecontext, $manager));
1739         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1740         $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
1741         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
1742         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
1743         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
1744         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
1746         role_change_permission($managerrole->id, $categorycontext, 'moodle/course:view', CAP_PREVENT);
1747         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1748         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
1749         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
1750         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
1751         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
1753         $this->getDataGenerator()->enrol_user($manager->id, $course->id, 0);
1755         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
1756         $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
1757         $this->assertTrue(is_enrolled($coursecontext, $manager));
1758         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
1759         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
1760         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
1761         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
1763         // Test problems.
1765         try {
1766             guess_if_creator_will_have_course_capability('moodle/course:visibility', $syscontext, $creator);
1767             $this->fail('Exception expected when non course/category context passed to guess_if_creator_will_have_course_capability()');
1768         } catch (moodle_exception $e) {
1769             $this->assertInstanceOf('coding_exception', $e);
1770         }
1771     }
1773     /**
1774      * Test require_capability() exceptions.
1775      */
1776     public function test_require_capability() {
1777         $this->resetAfterTest();
1779         $syscontext = context_system::instance();
1781         $this->setUser(0);
1782         $this->assertFalse(has_capability('moodle/site:config', $syscontext));
1783         try {
1784             require_capability('moodle/site:config', $syscontext);
1785             $this->fail('Exception expected from require_capability()');
1786         } catch (moodle_exception $e) {
1787             $this->assertInstanceOf('required_capability_exception', $e);
1788         }
1789         $this->setAdminUser();
1790         $this->assertFalse(has_capability('moodle/site:config', $syscontext, 0));
1791         try {
1792             require_capability('moodle/site:config', $syscontext, 0);
1793             $this->fail('Exception expected from require_capability()');
1794         } catch (moodle_exception $e) {
1795             $this->assertInstanceOf('required_capability_exception', $e);
1796         }
1797         $this->assertFalse(has_capability('moodle/site:config', $syscontext, null, false));
1798         try {
1799             require_capability('moodle/site:config', $syscontext, null, false);
1800             $this->fail('Exception expected from require_capability()');
1801         } catch (moodle_exception $e) {
1802             $this->assertInstanceOf('required_capability_exception', $e);
1803         }
1804     }
1806     /**
1807      * Test that enrolled users SQL does not return any values for users in
1808      * other courses.
1809      */
1810     public function test_get_enrolled_sql_different_course() {
1811         global $DB;
1813         $this->resetAfterTest();
1815         $course = $this->getDataGenerator()->create_course();
1816         $context = context_course::instance($course->id);
1817         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1818         $user = $this->getDataGenerator()->create_user();
1820         // This user should not appear anywhere, we're not interested in that context.
1821         $course2 = $this->getDataGenerator()->create_course();
1822         $this->getDataGenerator()->enrol_user($user->id, $course2->id, $student->id);
1824         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
1825         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
1826         $suspended  = get_suspended_userids($context);
1828         $this->assertFalse(isset($enrolled[$user->id]));
1829         $this->assertFalse(isset($active[$user->id]));
1830         $this->assertFalse(isset($suspended[$user->id]));
1831         $this->assertCount(0, $enrolled);
1832         $this->assertCount(0, $active);
1833         $this->assertCount(0, $suspended);
1834     }
1836     /**
1837      * Test that enrolled users SQL does not return any values for role
1838      * assignments without an enrolment.
1839      */
1840     public function test_get_enrolled_sql_role_only() {
1841         global $DB;
1843         $this->resetAfterTest();
1845         $course = $this->getDataGenerator()->create_course();
1846         $context = context_course::instance($course->id);
1847         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1848         $user = $this->getDataGenerator()->create_user();
1850         // Role assignment is not the same as course enrollment.
1851         role_assign($student->id, $user->id, $context->id);
1853         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
1854         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
1855         $suspended  = get_suspended_userids($context);
1857         $this->assertFalse(isset($enrolled[$user->id]));
1858         $this->assertFalse(isset($active[$user->id]));
1859         $this->assertFalse(isset($suspended[$user->id]));
1860         $this->assertCount(0, $enrolled);
1861         $this->assertCount(0, $active);
1862         $this->assertCount(0, $suspended);
1863     }
1865     /**
1866      * Test that multiple enrolments for the same user are counted correctly.
1867      */
1868     public function test_get_enrolled_sql_multiple_enrolments() {
1869         global $DB;
1871         $this->resetAfterTest();
1873         $course = $this->getDataGenerator()->create_course();
1874         $context = context_course::instance($course->id);
1875         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1876         $user = $this->getDataGenerator()->create_user();
1878         // Add a suspended enrol.
1879         $selfinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'self'));
1880         $selfplugin = enrol_get_plugin('self');
1881         $selfplugin->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
1882         $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'self', 0, 0, ENROL_USER_SUSPENDED);
1884         // Should be enrolled, but not active - user is suspended.
1885         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
1886         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
1887         $suspended  = get_suspended_userids($context);
1889         $this->assertTrue(isset($enrolled[$user->id]));
1890         $this->assertFalse(isset($active[$user->id]));
1891         $this->assertTrue(isset($suspended[$user->id]));
1892         $this->assertCount(1, $enrolled);
1893         $this->assertCount(0, $active);
1894         $this->assertCount(1, $suspended);
1896         // Add an active enrol for the user. Any active enrol makes them enrolled.
1897         $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id);
1899         // User should be active now.
1900         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
1901         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
1902         $suspended  = get_suspended_userids($context);
1904         $this->assertTrue(isset($enrolled[$user->id]));
1905         $this->assertTrue(isset($active[$user->id]));
1906         $this->assertFalse(isset($suspended[$user->id]));
1907         $this->assertCount(1, $enrolled);
1908         $this->assertCount(1, $active);
1909         $this->assertCount(0, $suspended);
1911     }
1913     public function get_enrolled_sql_provider() {
1914         return array(
1915             array(
1916                 // Two users who are enrolled.
1917                 'users' => array(
1918                     array(
1919                         'enrolled'  => true,
1920                         'active'    => true,
1921                     ),
1922                     array(
1923                         'enrolled'  => true,
1924                         'active'    => true,
1925                     ),
1926                 ),
1927                 'counts' => array(
1928                     'enrolled'      => 2,
1929                     'active'        => 2,
1930                     'suspended'     => 0,
1931                 ),
1932             ),
1933             array(
1934                 // A user who is suspended.
1935                 'users' => array(
1936                     array(
1937                         'status'    => ENROL_USER_SUSPENDED,
1938                         'enrolled'  => true,
1939                         'suspended' => true,
1940                     ),
1941                 ),
1942                 'counts' => array(
1943                     'enrolled'      => 1,
1944                     'active'        => 0,
1945                     'suspended'     => 1,
1946                 ),
1947             ),
1948             array(
1949                 // One of each.
1950                 'users' => array(
1951                     array(
1952                         'enrolled'  => true,
1953                         'active'    => true,
1954                     ),
1955                     array(
1956                         'status'    => ENROL_USER_SUSPENDED,
1957                         'enrolled'  => true,
1958                         'suspended' => true,
1959                     ),
1960                 ),
1961                 'counts' => array(
1962                     'enrolled'      => 2,
1963                     'active'        => 1,
1964                     'suspended'     => 1,
1965                 ),
1966             ),
1967             array(
1968                 // One user who is not yet enrolled.
1969                 'users' => array(
1970                     array(
1971                         'timestart' => DAYSECS,
1972                         'enrolled'  => true,
1973                         'active'    => false,
1974                         'suspended' => true,
1975                     ),
1976                 ),
1977                 'counts' => array(
1978                     'enrolled'      => 1,
1979                     'active'        => 0,
1980                     'suspended'     => 1,
1981                 ),
1982             ),
1983             array(
1984                 // One user who is no longer enrolled
1985                 'users' => array(
1986                     array(
1987                         'timeend'   => -DAYSECS,
1988                         'enrolled'  => true,
1989                         'active'    => false,
1990                         'suspended' => true,
1991                     ),
1992                 ),
1993                 'counts' => array(
1994                     'enrolled'      => 1,
1995                     'active'        => 0,
1996                     'suspended'     => 1,
1997                 ),
1998             ),
1999             array(
2000                 // One user who is not yet enrolled, and one who is no longer enrolled.
2001                 'users' => array(
2002                     array(
2003                         'timeend'   => -DAYSECS,
2004                         'enrolled'  => true,
2005                         'active'    => false,
2006                         'suspended' => true,
2007                     ),
2008                     array(
2009                         'timestart' => DAYSECS,
2010                         'enrolled'  => true,
2011                         'active'    => false,
2012                         'suspended' => true,
2013                     ),
2014                 ),
2015                 'counts' => array(
2016                     'enrolled'      => 2,
2017                     'active'        => 0,
2018                     'suspended'     => 2,
2019                 ),
2020             ),
2021         );
2022     }
2024     /**
2025      * @dataProvider get_enrolled_sql_provider
2026      */
2027     public function test_get_enrolled_sql_course($users, $counts) {
2028         global $DB;
2030         $this->resetAfterTest();
2032         $course = $this->getDataGenerator()->create_course();
2033         $context = context_course::instance($course->id);
2034         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2035         $createdusers = array();
2037         foreach ($users as &$userdata) {
2038             $user = $this->getDataGenerator()->create_user();
2039             $userdata['id'] = $user->id;
2041             $timestart  = 0;
2042             $timeend    = 0;
2043             $status     = null;
2044             if (isset($userdata['timestart'])) {
2045                 $timestart = time() + $userdata['timestart'];
2046             }
2047             if (isset($userdata['timeend'])) {
2048                 $timeend = time() + $userdata['timeend'];
2049             }
2050             if (isset($userdata['status'])) {
2051                 $status = $userdata['status'];
2052             }
2054             // Enrol the user in the course.
2055             $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'manual', $timestart, $timeend, $status);
2056         }
2058         // After all users have been enroled, check expectations.
2059         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2060         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2061         $suspended  = get_suspended_userids($context);
2063         foreach ($users as $userdata) {
2064             if (isset($userdata['enrolled']) && $userdata['enrolled']) {
2065                 $this->assertTrue(isset($enrolled[$userdata['id']]));
2066             } else {
2067                 $this->assertFalse(isset($enrolled[$userdata['id']]));
2068             }
2070             if (isset($userdata['active']) && $userdata['active']) {
2071                 $this->assertTrue(isset($active[$userdata['id']]));
2072             } else {
2073                 $this->assertFalse(isset($active[$userdata['id']]));
2074             }
2076             if (isset($userdata['suspended']) && $userdata['suspended']) {
2077                 $this->assertTrue(isset($suspended[$userdata['id']]));
2078             } else {
2079                 $this->assertFalse(isset($suspended[$userdata['id']]));
2080             }
2081         }
2083         $this->assertCount($counts['enrolled'],     $enrolled);
2084         $this->assertCount($counts['active'],       $active);
2085         $this->assertCount($counts['suspended'],    $suspended);
2086     }
2088     /**
2089      * A small functional test of permission evaluations.
2090      */
2091     public function test_permission_evaluation() {
2092         global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE;
2094         $this->resetAfterTest();
2096         $generator = $this->getDataGenerator();
2098         // Fill the site with some real data.
2099         $testcategories = array();
2100         $testcourses = array();
2101         $testpages = array();
2102         $testblocks = array();
2103         $allroles = $DB->get_records_menu('role', array(), 'id', 'archetype, id');
2105         $systemcontext = context_system::instance();
2106         $frontpagecontext = context_course::instance(SITEID);
2108         // Add block to system context.
2109         $bi = $generator->create_block('online_users');
2110         context_block::instance($bi->id);
2111         $testblocks[] = $bi->id;
2113         // Some users.
2114         $testusers = array();
2115         for ($i=0; $i<20; $i++) {
2116             $user = $generator->create_user();
2117             $testusers[$i] = $user->id;
2118             $usercontext = context_user::instance($user->id);
2120             // Add block to user profile.
2121             $bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id));
2122             $testblocks[] = $bi->id;
2123         }
2124         // Deleted user - should be ignored everywhere, can not have context.
2125         $generator->create_user(array('deleted'=>1));
2127         // Add block to frontpage.
2128         $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id));
2129         $frontpageblockcontext = context_block::instance($bi->id);
2130         $testblocks[] = $bi->id;
2132         // Add a resource to frontpage.
2133         $page = $generator->create_module('page', array('course'=>$SITE->id));
2134         $testpages[] = $page->cmid;
2135         $frontpagepagecontext = context_module::instance($page->cmid);
2137         // Add block to frontpage resource.
2138         $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id));
2139         $frontpagepageblockcontext = context_block::instance($bi->id);
2140         $testblocks[] = $bi->id;
2142         // Some nested course categories with courses.
2143         $manualenrol = enrol_get_plugin('manual');
2144         $parentcat = 0;
2145         for ($i=0; $i<5; $i++) {
2146             $cat = $generator->create_category(array('parent'=>$parentcat));
2147             $testcategories[] = $cat->id;
2148             $catcontext = context_coursecat::instance($cat->id);
2149             $parentcat = $cat->id;
2151             if ($i >= 4) {
2152                 continue;
2153             }
2155             // Add resource to each category.
2156             $bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id));
2157             context_block::instance($bi->id);
2159             // Add a few courses to each category.
2160             for ($j=0; $j<6; $j++) {
2161                 $course = $generator->create_course(array('category'=>$cat->id));
2162                 $testcourses[] = $course->id;
2163                 $coursecontext = context_course::instance($course->id);
2165                 if ($j >= 5) {
2166                     continue;
2167                 }
2168                 // Add manual enrol instance.
2169                 $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id)));
2171                 // Add block to each course.
2172                 $bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
2173                 $testblocks[] = $bi->id;
2175                 // Add a resource to each course.
2176                 $page = $generator->create_module('page', array('course'=>$course->id));
2177                 $testpages[] = $page->cmid;
2178                 $modcontext = context_module::instance($page->cmid);
2180                 // Add block to each module.
2181                 $bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id));
2182                 $testblocks[] = $bi->id;
2183             }
2184         }
2186         // Make sure all contexts were created properly.
2187         $count = 1; // System.
2188         $count += $DB->count_records('user', array('deleted'=>0));
2189         $count += $DB->count_records('course_categories');
2190         $count += $DB->count_records('course');
2191         $count += $DB->count_records('course_modules');
2192         $count += $DB->count_records('block_instances');
2193         $this->assertEquals($count, $DB->count_records('context'));
2194         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2195         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2198         // Test context_helper::get_level_name() method.
2200         $levels = context_helper::get_all_levels();
2201         foreach ($levels as $level => $classname) {
2202             $name = context_helper::get_level_name($level);
2203             $this->assertNotEmpty($name);
2204         }
2207         // Test context::instance_by_id(), context_xxx::instance() methods.
2209         $context = context::instance_by_id($frontpagecontext->id);
2210         $this->assertSame(CONTEXT_COURSE, $context->contextlevel);
2211         $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING));
2212         try {
2213             context::instance_by_id(-1);
2214             $this->fail('exception expected');
2215         } catch (moodle_exception $e) {
2216             $this->assertTrue(true);
2217         }
2218         $this->assertInstanceOf('context_system', context_system::instance());
2219         $this->assertInstanceOf('context_coursecat', context_coursecat::instance($testcategories[0]));
2220         $this->assertInstanceOf('context_course', context_course::instance($testcourses[0]));
2221         $this->assertInstanceOf('context_module', context_module::instance($testpages[0]));
2222         $this->assertInstanceOf('context_block', context_block::instance($testblocks[0]));
2224         $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING));
2225         $this->assertFalse(context_course::instance(-1, IGNORE_MISSING));
2226         $this->assertFalse(context_module::instance(-1, IGNORE_MISSING));
2227         $this->assertFalse(context_block::instance(-1, IGNORE_MISSING));
2228         try {
2229             context_coursecat::instance(-1);
2230             $this->fail('exception expected');
2231         } catch (moodle_exception $e) {
2232             $this->assertTrue(true);
2233         }
2234         try {
2235             context_course::instance(-1);
2236             $this->fail('exception expected');
2237         } catch (moodle_exception $e) {
2238             $this->assertTrue(true);
2239         }
2240         try {
2241             context_module::instance(-1);
2242             $this->fail('exception expected');
2243         } catch (moodle_exception $e) {
2244             $this->assertTrue(true);
2245         }
2246         try {
2247             context_block::instance(-1);
2248             $this->fail('exception expected');
2249         } catch (moodle_exception $e) {
2250             $this->assertTrue(true);
2251         }
2254         // Test $context->get_url(), $context->get_context_name(), $context->get_capabilities() methods.
2256         $testcontexts = array();
2257         $testcontexts[CONTEXT_SYSTEM]    = context_system::instance();
2258         $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]);
2259         $testcontexts[CONTEXT_COURSE]    = context_course::instance($testcourses[0]);
2260         $testcontexts[CONTEXT_MODULE]    = context_module::instance($testpages[0]);
2261         $testcontexts[CONTEXT_BLOCK]     = context_block::instance($testblocks[0]);
2263         foreach ($testcontexts as $context) {
2264             $name = $context->get_context_name(true, true);
2265             $this->assertNotEmpty($name);
2267             $this->assertInstanceOf('moodle_url', $context->get_url());
2269             $caps = $context->get_capabilities();
2270             $this->assertTrue(is_array($caps));
2271             foreach ($caps as $cap) {
2272                 $cap = (array)$cap;
2273                 $this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask'));
2274             }
2275         }
2276         unset($testcontexts);
2278         // Test $context->get_course_context() method.
2280         $this->assertFalse($systemcontext->get_course_context(false));
2281         try {
2282             $systemcontext->get_course_context();
2283             $this->fail('exception expected');
2284         } catch (moodle_exception $e) {
2285             $this->assertInstanceOf('coding_exception', $e);
2286         }
2287         $context = context_coursecat::instance($testcategories[0]);
2288         $this->assertFalse($context->get_course_context(false));
2289         try {
2290             $context->get_course_context();
2291             $this->fail('exception expected');
2292         } catch (moodle_exception $e) {
2293             $this->assertInstanceOf('coding_exception', $e);
2294         }
2295         $this->assertEquals($frontpagecontext, $frontpagecontext->get_course_context(true));
2296         $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_course_context(true));
2297         $this->assertEquals($frontpagecontext, $frontpagepageblockcontext->get_course_context(true));
2300         // Test $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() methods.
2302         $userid = reset($testusers);
2303         $usercontext = context_user::instance($userid);
2304         $this->assertEquals($systemcontext, $usercontext->get_parent_context());
2305         $this->assertEquals(array($systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts());
2306         $this->assertEquals(array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts(true));
2308         $this->assertEquals(array(), $systemcontext->get_parent_contexts());
2309         $this->assertEquals(array($systemcontext->id=>$systemcontext), $systemcontext->get_parent_contexts(true));
2310         $this->assertEquals(array(), $systemcontext->get_parent_context_ids());
2311         $this->assertEquals(array($systemcontext->id), $systemcontext->get_parent_context_ids(true));
2313         $this->assertEquals($systemcontext, $frontpagecontext->get_parent_context());
2314         $this->assertEquals(array($systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts());
2315         $this->assertEquals(array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts(true));
2316         $this->assertEquals(array($systemcontext->id), $frontpagecontext->get_parent_context_ids());
2317         $this->assertEquals(array($frontpagecontext->id, $systemcontext->id), $frontpagecontext->get_parent_context_ids(true));
2319         $this->assertFalse($systemcontext->get_parent_context());
2320         $frontpagecontext = context_course::instance($SITE->id);
2321         $parent = $systemcontext;
2322         foreach ($testcategories as $catid) {
2323             $catcontext = context_coursecat::instance($catid);
2324             $this->assertEquals($parent, $catcontext->get_parent_context());
2325             $parent = $catcontext;
2326         }
2327         $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_parent_context());
2328         $this->assertEquals($frontpagecontext, $frontpageblockcontext->get_parent_context());
2329         $this->assertEquals($frontpagepagecontext, $frontpagepageblockcontext->get_parent_context());
2332         // Test $context->get_child_contexts() method.
2334         $children = $systemcontext->get_child_contexts();
2335         $this->resetDebugging();
2336         $this->assertEquals(count($children)+1, $DB->count_records('context'));
2338         $context = context_coursecat::instance($testcategories[3]);
2339         $children = $context->get_child_contexts();
2340         $countcats    = 0;
2341         $countcourses = 0;
2342         $countblocks  = 0;
2343         foreach ($children as $child) {
2344             if ($child->contextlevel == CONTEXT_COURSECAT) {
2345                 $countcats++;
2346             }
2347             if ($child->contextlevel == CONTEXT_COURSE) {
2348                 $countcourses++;
2349             }
2350             if ($child->contextlevel == CONTEXT_BLOCK) {
2351                 $countblocks++;
2352             }
2353         }
2354         $this->assertCount(8, $children);
2355         $this->assertEquals(1, $countcats);
2356         $this->assertEquals(6, $countcourses);
2357         $this->assertEquals(1, $countblocks);
2359         $context = context_course::instance($testcourses[2]);
2360         $children = $context->get_child_contexts();
2361         $this->assertCount(7, $children); // Depends on number of default blocks.
2363         $context = context_module::instance($testpages[3]);
2364         $children = $context->get_child_contexts();
2365         $this->assertCount(1, $children);
2367         $context = context_block::instance($testblocks[1]);
2368         $children = $context->get_child_contexts();
2369         $this->assertCount(0, $children);
2371         unset($children);
2372         unset($countcats);
2373         unset($countcourses);
2374         unset($countblocks);
2377         // Test context_helper::reset_caches() method.
2379         context_helper::reset_caches();
2380         $this->assertEquals(0, context_inspection::test_context_cache_size());
2381         context_course::instance($SITE->id);
2382         $this->assertEquals(1, context_inspection::test_context_cache_size());
2385         // Test context preloading.
2387         context_helper::reset_caches();
2388         $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')."
2389                   FROM {context} c
2390                  WHERE c.contextlevel <> ".CONTEXT_SYSTEM;
2391         $records = $DB->get_records_sql($sql);
2392         $firstrecord = reset($records);
2393         $columns = context_helper::get_preload_record_columns('c');
2394         $firstrecord = (array)$firstrecord;
2395         $this->assertSame(array_keys($firstrecord), array_values($columns));
2396         context_helper::reset_caches();
2397         foreach ($records as $record) {
2398             context_helper::preload_from_record($record);
2399             $this->assertEquals(new stdClass(), $record);
2400         }
2401         $this->assertEquals(count($records), context_inspection::test_context_cache_size());
2402         unset($records);
2403         unset($columns);
2405         context_helper::reset_caches();
2406         context_helper::preload_course($SITE->id);
2407         $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
2408         $this->assertEquals(6 + $numfrontpagemodules, context_inspection::test_context_cache_size()); // Depends on number of default blocks.
2410         // Test assign_capability(), unassign_capability() functions.
2412         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2413         $this->assertFalse($rc);
2414         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id);
2415         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2416         $this->assertEquals(CAP_ALLOW, $rc->permission);
2417         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id);
2418         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2419         $this->assertEquals(CAP_ALLOW, $rc->permission);
2420         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true);
2421         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2422         $this->assertEquals(CAP_PREVENT, $rc->permission);
2424         assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext);
2425         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2426         $this->assertFalse($rc);
2427         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext);
2428         unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true);
2429         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
2430         $this->assertFalse($rc);
2431         unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true);
2432         unset($rc);
2434         accesslib_clear_all_caches_for_unit_testing(); // Must be done after assign_capability().
2437         // Test role_assign(), role_unassign(), role_unassign_all() functions.
2439         $context = context_course::instance($testcourses[1]);
2440         $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2441         role_assign($allroles['teacher'], $testusers[1], $context->id);
2442         role_assign($allroles['teacher'], $testusers[2], $context->id);
2443         role_assign($allroles['manager'], $testusers[1], $context->id);
2444         $this->assertEquals(3, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2445         role_unassign($allroles['teacher'], $testusers[1], $context->id);
2446         $this->assertEquals(2, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2447         role_unassign_all(array('contextid'=>$context->id));
2448         $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
2449         unset($context);
2451         accesslib_clear_all_caches_for_unit_testing(); // Just in case.
2454         // Test has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends functions.
2456         $adminid = get_admin()->id;
2457         $guestid = $CFG->siteguest;
2459         // Enrol some users into some courses.
2460         $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST);
2461         $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST);
2462         $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id');
2463         $cm1 = reset($cms);
2464         $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id');
2465         $block1 = reset($blocks);
2466         $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id));
2467         $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id));
2468         for ($i=0; $i<9; $i++) {
2469             $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']);
2470         }
2471         $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']);
2472         $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']);
2474         for ($i=10; $i<15; $i++) {
2475             $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']);
2476         }
2477         $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']);
2479         // Add tons of role assignments - the more the better.
2480         role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2]));
2481         role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1]));
2482         role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id));
2483         role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id));
2484         role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id));
2485         role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id));
2486         role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id));
2487         role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id));
2489         role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id));
2490         role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id));
2492         // Add tons of overrides - the more the better.
2493         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true);
2494         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true);
2495         assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true);
2496         assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true);
2497         assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true);
2499         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true);
2500         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true);
2501         assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true);
2502         assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true);
2503         assign_capability('moodle/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
2505         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true);
2506         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true);
2507         assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true);
2508         assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true);
2510         assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true);
2512         accesslib_clear_all_caches_for_unit_testing(); /// Must be done after assign_capability().
2514         // Extra tests for guests and not-logged-in users because they can not be verified by cross checking
2515         // with get_users_by_capability() where they are ignored.
2516         $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid));
2517         $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid));
2518         $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid));
2519         $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid));
2521         $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0));
2522         $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0));
2523         $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0));
2524         $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0));
2526         $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11]));
2527         $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11]));
2528         $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11]));
2529         $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11]));
2531         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9]));
2532         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9]));
2533         $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9]));
2535         // Test the list of enrolled users.
2536         $coursecontext = context_course::instance($course1->id);
2537         $enrolled = get_enrolled_users($coursecontext);
2538         $this->assertCount(10, $enrolled);
2539         for ($i=0; $i<10; $i++) {
2540             $this->assertTrue(isset($enrolled[$testusers[$i]]));
2541         }
2542         $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update');
2543         $this->assertCount(1, $enrolled);
2544         $this->assertTrue(isset($enrolled[$testusers[9]]));
2545         unset($enrolled);
2547         // Role switching.
2548         $userid = $testusers[9];
2549         $USER = $DB->get_record('user', array('id'=>$userid));
2550         load_all_capabilities();
2551         $coursecontext = context_course::instance($course1->id);
2552         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
2553         $this->assertFalse(is_role_switched($course1->id));
2554         role_switch($allroles['student'], $coursecontext);
2555         $this->assertTrue(is_role_switched($course1->id));
2556         $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
2557         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
2558         reload_all_capabilities();
2559         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
2560         role_switch(0, $coursecontext);
2561         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
2562         $userid = $adminid;
2563         $USER = $DB->get_record('user', array('id'=>$userid));
2564         load_all_capabilities();
2565         $coursecontext = context_course::instance($course1->id);
2566         $blockcontext = context_block::instance($block1->id);
2567         $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
2568         role_switch($allroles['student'], $coursecontext);
2569         $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
2570         $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
2571         reload_all_capabilities();
2572         $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
2573         load_all_capabilities();
2574         $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
2576         // Temp course role for enrol.
2577         $DB->delete_records('cache_flags', array()); // This prevents problem with dirty contexts immediately resetting the temp role - this is a known problem...
2578         $userid = $testusers[5];
2579         $roleid = $allroles['editingteacher'];
2580         $USER = $DB->get_record('user', array('id'=>$userid));
2581         load_all_capabilities();
2582         $coursecontext = context_course::instance($course1->id);
2583         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
2584         $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid]));
2585         load_temp_course_role($coursecontext, $roleid);
2586         $this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid);
2587         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
2588         remove_temp_course_roles($coursecontext);
2589         $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
2590         load_temp_course_role($coursecontext, $roleid);
2591         reload_all_capabilities();
2592         $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
2593         $USER = new stdClass();
2594         $USER->id = 0;
2596         // Now cross check has_capability() with get_users_by_capability(), each using different code paths,
2597         // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong,
2598         // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users).
2599         $contexts = $DB->get_records('context', array(), 'id');
2600         $contexts = array_values($contexts);
2601         $capabilities = $DB->get_records('capabilities', array(), 'id');
2602         $capabilities = array_values($capabilities);
2603         $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']);
2604         $userids = array_values($testusers);
2605         $userids[] = get_admin()->id;
2607         if (!PHPUNIT_LONGTEST) {
2608             $contexts = array_slice($contexts, 0, 10);
2609             $capabilities = array_slice($capabilities, 0, 5);
2610             $userids = array_slice($userids, 0, 5);
2611         }
2613         foreach ($userids as $userid) { // No guest or deleted.
2614             // Each user gets 0-10 random roles.
2615             $rcount = rand(0, 10);
2616             for ($j=0; $j<$rcount; $j++) {
2617                 $roleid = $roles[rand(0, count($roles)-1)];
2618                 $contextid = $contexts[rand(0, count($contexts)-1)]->id;
2619                 role_assign($roleid, $userid, $contextid);
2620             }
2621         }
2623         $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT);
2624         $maxoverrides = count($contexts)*10;
2625         for ($j=0; $j<$maxoverrides; $j++) {
2626             $roleid = $roles[rand(0, count($roles)-1)];
2627             $contextid = $contexts[rand(0, count($contexts)-1)]->id;
2628             $permission = $permissions[rand(0, count($permissions)-1)];
2629             $capname = $capabilities[rand(0, count($capabilities)-1)]->name;
2630             assign_capability($capname, $permission, $roleid, $contextid, true);
2631         }
2632         unset($permissions);
2633         unset($roles);
2635         accesslib_clear_all_caches_for_unit_testing(); // must be done after assign_capability().
2637         // Test time - let's set up some real user, just in case the logic for USER affects the others...
2638         $USER = $DB->get_record('user', array('id'=>$testusers[3]));
2639         load_all_capabilities();
2641         $userids[] = $CFG->siteguest;
2642         $userids[] = 0; // Not-logged-in user.
2643         $userids[] = -1; // Non-existent user.
2645         foreach ($contexts as $crecord) {
2646             $context = context::instance_by_id($crecord->id);
2647             if ($coursecontext = $context->get_course_context(false)) {
2648                 $enrolled = get_enrolled_users($context);
2649             } else {
2650                 $enrolled = array();
2651             }
2652             foreach ($capabilities as $cap) {
2653                 $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username');
2654                 if ($enrolled) {
2655                     $enrolledwithcap = get_enrolled_users($context, $cap->name);
2656                 } else {
2657                     $enrolledwithcap = array();
2658                 }
2659                 foreach ($userids as $userid) {
2660                     if ($userid == 0 or isguestuser($userid)) {
2661                         if ($userid == 0) {
2662                             $CFG->forcelogin = true;
2663                             $this->assertFalse(has_capability($cap->name, $context, $userid));
2664                             unset($CFG->forcelogin);
2665                         }
2666                         if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) {
2667                             $this->assertFalse(has_capability($cap->name, $context, $userid));
2668                         }
2669                         $this->assertFalse(isset($allowed[$userid]));
2670                     } else {
2671                         if (is_siteadmin($userid)) {
2672                             $this->assertTrue(has_capability($cap->name, $context, $userid, true));
2673                         }
2674                         $hascap = has_capability($cap->name, $context, $userid, false);
2675                         $this->assertSame($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
2676                         if (isset($enrolled[$userid])) {
2677                             $this->assertSame(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
2678                         }
2679                     }
2680                 }
2681             }
2682         }
2683         // Back to nobody.
2684         $USER = new stdClass();
2685         $USER->id = 0;
2686         unset($contexts);
2687         unset($userids);
2688         unset($capabilities);
2690         // Now let's do all the remaining tests that break our carefully prepared fake site.
2693         // Test $context->mark_dirty() method.
2695         $DB->delete_records('cache_flags', array());
2696         accesslib_clear_all_caches(false);
2697         $systemcontext->mark_dirty();
2698         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2699         $this->assertTrue(isset($dirty[$systemcontext->path]));
2700         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path]));
2703         // Test $context->reload_if_dirty() method.
2705         $DB->delete_records('cache_flags', array());
2706         accesslib_clear_all_caches(false);
2707         load_all_capabilities();
2708         $context = context_course::instance($testcourses[2]);
2709         $page = $DB->get_record('page', array('course'=>$testcourses[2]));
2710         $pagecm = get_coursemodule_from_instance('page', $page->id);
2711         $pagecontext = context_module::instance($pagecm->id);
2713         $context->mark_dirty();
2714         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
2715         $USER->access['test'] = true;
2716         $context->reload_if_dirty();
2717         $this->assertFalse(isset($USER->access['test']));
2719         $context->mark_dirty();
2720         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
2721         $USER->access['test'] = true;
2722         $pagecontext->reload_if_dirty();
2723         $this->assertFalse(isset($USER->access['test']));
2726         // Test context_helper::build_all_paths() method.
2728         $oldcontexts = $DB->get_records('context', array(), 'id');
2729         $DB->set_field_select('context', 'path', null, "contextlevel <> ".CONTEXT_SYSTEM);
2730         $DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM);
2731         context_helper::build_all_paths();
2732         $newcontexts = $DB->get_records('context', array(), 'id');
2733         $this->assertEquals($oldcontexts, $newcontexts);
2734         unset($oldcontexts);
2735         unset($newcontexts);
2738         // Test $context->reset_paths() method.
2740         $context = context_course::instance($testcourses[2]);
2741         $children = $context->get_child_contexts();
2742         $context->reset_paths(false);
2743         $this->assertNull($DB->get_field('context', 'path', array('id'=>$context->id)));
2744         $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$context->id)));
2745         foreach ($children as $child) {
2746             $this->assertNull($DB->get_field('context', 'path', array('id'=>$child->id)));
2747             $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$child->id)));
2748         }
2749         $this->assertEquals(count($children)+1, $DB->count_records('context', array('depth'=>0)));
2750         $this->assertEquals(count($children)+1, $DB->count_records('context', array('path'=>null)));
2752         $context = context_course::instance($testcourses[2]);
2753         $context->reset_paths(true);
2754         $context = context_course::instance($testcourses[2]);
2755         $this->assertSame($context->path, $DB->get_field('context', 'path', array('id'=>$context->id)));
2756         $this->assertSame($context->depth, $DB->get_field('context', 'depth', array('id'=>$context->id)));
2757         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2758         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2761         // Test $context->update_moved() method.
2763         accesslib_clear_all_caches(false);
2764         $DB->delete_records('cache_flags', array());
2765         $course = $DB->get_record('course', array('id'=>$testcourses[0]));
2766         $context = context_course::instance($course->id);
2767         $oldpath = $context->path;
2768         $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
2769         $categorycontext = context_coursecat::instance($miscid);
2770         $course->category = $miscid;
2771         $DB->update_record('course', $course);
2772         $context->update_moved($categorycontext);
2774         $context = context_course::instance($course->id);
2775         $this->assertEquals($categorycontext, $context->get_parent_context());
2776         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2777         $this->assertTrue(isset($dirty[$oldpath]));
2778         $this->assertTrue(isset($dirty[$context->path]));
2781         // Test $context->delete_content() method.
2783         context_helper::reset_caches();
2784         $context = context_module::instance($testpages[3]);
2785         $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
2786         $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2787         $context->delete_content();
2788         $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
2789         $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2792         // Test $context->delete() method.
2794         context_helper::reset_caches();
2795         $context = context_module::instance($testpages[4]);
2796         $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
2797         $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2798         $bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id));
2799         $bicontext = context_block::instance($bi->id);
2800         $DB->delete_records('cache_flags', array());
2801         $context->delete(); // Should delete also linked blocks.
2802         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2803         $this->assertTrue(isset($dirty[$context->path]));
2804         $this->assertFalse($DB->record_exists('context', array('id'=>$context->id)));
2805         $this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id)));
2806         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4])));
2807         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id)));
2808         $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
2809         context_module::instance($testpages[4]);
2812         // Test context_helper::delete_instance() method.
2814         context_helper::reset_caches();
2815         $lastcourse = array_pop($testcourses);
2816         $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
2817         $coursecontext = context_course::instance($lastcourse);
2818         $this->assertEquals(1, context_inspection::test_context_cache_size());
2819         $this->assertNotEquals(CONTEXT_COURSE, $coursecontext->instanceid);
2820         $DB->delete_records('cache_flags', array());
2821         context_helper::delete_instance(CONTEXT_COURSE, $lastcourse);
2822         $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
2823         $this->assertTrue(isset($dirty[$coursecontext->path]));
2824         $this->assertEquals(0, context_inspection::test_context_cache_size());
2825         $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
2826         context_course::instance($lastcourse);
2829         // Test context_helper::create_instances() method.
2831         $prevcount = $DB->count_records('context');
2832         $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
2833         context_helper::create_instances(null, true);
2834         $this->assertSame($DB->count_records('context'), $prevcount);
2835         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2836         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2838         $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
2839         $DB->delete_records('block_instances', array());
2840         $prevcount = $DB->count_records('context');
2841         $DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM);
2842         context_helper::create_instances(null, true);
2843         $this->assertSame($prevcount, $DB->count_records('context'));
2844         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2845         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2847         // Test context_helper::cleanup_instances() method.
2849         $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}");
2850         $DB->delete_records('course', array('id'=>$lastcourse));
2851         $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
2852         $DB->delete_records('course_categories', array('id'=>$lastcategory));
2853         $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0");
2854         $DB->delete_records('user', array('id'=>$lastuser));
2855         $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id));
2856         $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid));
2857         context_helper::cleanup_instances();
2858         $count = 1; // System.
2859         $count += $DB->count_records('user', array('deleted'=>0));
2860         $count += $DB->count_records('course_categories');
2861         $count += $DB->count_records('course');
2862         $count += $DB->count_records('course_modules');
2863         $count += $DB->count_records('block_instances');
2864         $this->assertEquals($count, $DB->count_records('context'));
2867         // Test context cache size restrictions.
2869         $testusers= array();
2870         for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
2871             $user = $generator->create_user();
2872             $testusers[$i] = $user->id;
2873         }
2874         context_helper::create_instances(null, true);
2875         context_helper::reset_caches();
2876         for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
2877             context_user::instance($testusers[$i]);
2878             if ($i == CONTEXT_CACHE_MAX_SIZE - 1) {
2879                 $this->assertEquals(CONTEXT_CACHE_MAX_SIZE, context_inspection::test_context_cache_size());
2880             } else if ($i == CONTEXT_CACHE_MAX_SIZE) {
2881                 // Once the limit is reached roughly 1/3 of records should be removed from cache.
2882                 $this->assertEquals((int)ceil(CONTEXT_CACHE_MAX_SIZE * (2/3) + 101), context_inspection::test_context_cache_size());
2883             }
2884         }
2885         // We keep the first 100 cached.
2886         $prevsize = context_inspection::test_context_cache_size();
2887         for ($i=0; $i<100; $i++) {
2888             context_user::instance($testusers[$i]);
2889             $this->assertEquals($prevsize, context_inspection::test_context_cache_size());
2890         }
2891         context_user::instance($testusers[102]);
2892         $this->assertEquals($prevsize+1, context_inspection::test_context_cache_size());
2893         unset($testusers);
2897         // Test basic test of legacy functions.
2898         // Note: watch out, the fake site might be pretty borked already.
2900         $this->assertEquals(get_system_context(), context_system::instance());
2901         $this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER);
2903         foreach ($DB->get_records('context') as $contextid => $record) {
2904             $context = context::instance_by_id($contextid);
2905             $this->assertEquals($context, get_context_instance($record->contextlevel, $record->instanceid));
2906             $this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER);
2907         }
2909         // Make sure a debugging is thrown.
2910         get_context_instance($record->contextlevel, $record->instanceid);
2911         $this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER);
2912         get_system_context();
2913         $this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER);
2914     }
2916     /**
2917      * Test updating of role capabilities during upgrade
2918      */
2919     public function test_update_capabilities() {
2920         global $DB, $SITE;
2922         $this->resetAfterTest(true);
2924         $froncontext = context_course::instance($SITE->id);
2925         $student = $DB->get_record('role', array('archetype'=>'student'));
2926         $teacher = $DB->get_record('role', array('archetype'=>'teacher'));
2928         $existingcaps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
2930         $this->assertFalse(isset($existingcaps['moodle/site:restore']));         // Moved to new 'moodle/restore:restorecourse'.
2931         $this->assertTrue(isset($existingcaps['moodle/restore:restorecourse'])); // New cap from 'moodle/site:restore'.
2932         $this->assertTrue(isset($existingcaps['moodle/site:sendmessage']));      // New capability.
2933         $this->assertTrue(isset($existingcaps['moodle/backup:backupcourse']));
2934         $this->assertTrue(isset($existingcaps['moodle/backup:backupsection']));  // Cloned from 'moodle/backup:backupcourse'.
2935         $this->assertTrue(isset($existingcaps['moodle/site:approvecourse']));    // Updated bitmask.
2936         $this->assertTrue(isset($existingcaps['moodle/course:manageactivities']));
2937         $this->assertTrue(isset($existingcaps['mod/page:addinstance']));         // Cloned from core 'moodle/course:manageactivities'.
2939         // Fake state before upgrade.
2940         $DB->set_field('capabilities', 'name', 'moodle/site:restore', array('name'=>'moodle/restore:restorecourse'));
2941         $DB->set_field('role_capabilities', 'capability', 'moodle/site:restore', array('capability'=>'moodle/restore:restorecourse'));
2942         assign_capability('moodle/site:restore', CAP_PROHIBIT, $teacher->id, $froncontext->id, true);
2943         $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/site:restore', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
2945         $DB->delete_records('role_capabilities', array('capability'=>'moodle/site:sendmessage'));
2946         $DB->delete_records('capabilities', array('name'=>'moodle/site:sendmessage'));
2948         $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupsection'));
2949         $DB->delete_records('capabilities', array('name'=>'moodle/backup:backupsection'));
2950         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $froncontext->id, true);
2951         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $teacher->id, $froncontext->id, true);
2953         $DB->set_field('capabilities', 'riskbitmask', 0, array('name'=>'moodle/site:approvecourse'));
2955         $DB->delete_records('role_capabilities', array('capability'=>'mod/page:addinstance'));
2956         $DB->delete_records('capabilities', array('name'=>'mod/page:addinstance'));
2957         assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $student->id, $froncontext->id, true);
2958         assign_capability('moodle/course:manageactivities', CAP_ALLOW, $teacher->id, $froncontext->id, true);
2960         // Execute core.
2961         update_capabilities('moodle');
2963         // Only core should be upgraded.
2964         $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
2966         $this->assertFalse(isset($existingcaps['moodle/site:restore']));
2967         $this->assertTrue(isset($caps['moodle/restore:restorecourse']));
2968         $this->assertEquals($existingcaps['moodle/restore:restorecourse'], $caps['moodle/restore:restorecourse']);
2969         $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/restore:restorecourse', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
2970         $this->assertEquals($perms1, $perms2);
2972         $this->assertTrue(isset($caps['moodle/site:sendmessage']));
2973         $this->assertEquals($existingcaps['moodle/site:sendmessage'], $caps['moodle/site:sendmessage']);
2975         $this->assertTrue(isset($caps['moodle/backup:backupsection']));
2976         $this->assertEquals($existingcaps['moodle/backup:backupsection'], $caps['moodle/backup:backupsection']);
2977         $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/backup:backupcourse', 'moodle/backup:backupsection'));
2978         foreach ($roles as $role) {
2979             $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2980             $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupsection', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2981             $this->assertEquals($perms1, $perms2);
2982         }
2984         $this->assertTrue(isset($caps['moodle/site:approvecourse']));
2985         $this->assertEquals($existingcaps['moodle/site:approvecourse'], $caps['moodle/site:approvecourse']);
2987         $this->assertFalse(isset($caps['mod/page:addinstance']));
2989         // Execute plugin.
2990         update_capabilities('mod_page');
2991         $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
2992         $this->assertTrue(isset($caps['mod/page:addinstance']));
2993         $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/course:manageactivities', 'mod/page:addinstance'));
2994         foreach ($roles as $role) {
2995             $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/course:manageactivities', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2996             $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'mod/page:addinstance', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
2997         }
2998         $this->assertEquals($perms1, $perms2);
2999     }
3001     /**
3002      * Tests reset_role_capabilities function.
3003      */
3004     public function test_reset_role_capabilities() {
3005         global $DB;
3006         $this->resetAfterTest(true);
3007         $generator = $this->getDataGenerator();
3009         // Create test course and user, enrol one in the other.
3010         $course = $generator->create_course();
3011         $user = $generator->create_user();
3012         $roleid = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST);
3013         $generator->enrol_user($user->id, $course->id, $roleid);
3015         // Change student role so it DOES have 'mod/forum:addinstance'.
3016         $systemcontext = context_system::instance();
3017         assign_capability('mod/forum:addinstance', CAP_ALLOW, $roleid, $systemcontext->id);
3019         // Override course so it does NOT allow students 'mod/forum:viewdiscussion'.
3020         $coursecontext = context_course::instance($course->id);
3021         assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $roleid, $coursecontext->id);
3023         // Check expected capabilities so far.
3024         $this->assertTrue(has_capability('mod/forum:addinstance', $coursecontext, $user));
3025         $this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user));
3027         // Oops, allowing student to add forums was a mistake, let's reset the role.
3028         reset_role_capabilities($roleid);
3030         // Check new expected capabilities - role capabilities should have been reset,
3031         // while the override at course level should remain.
3032         $this->assertFalse(has_capability('mod/forum:addinstance', $coursecontext, $user));
3033         $this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user));
3034     }
3036     /**
3037      * Tests count_role_users function.
3038      */
3039     public function test_count_role_users() {
3040         global $DB;
3041         $this->resetAfterTest(true);
3042         $generator = self::getDataGenerator();
3043         // Create a course in a category, and some users.
3044         $category = $generator->create_category();
3045         $course = $generator->create_course(array('category' => $category->id));
3046         $user1 = $generator->create_user();
3047         $user2 = $generator->create_user();
3048         $user3 = $generator->create_user();
3049         $user4 = $generator->create_user();
3050         $user5 = $generator->create_user();
3051         $roleid1 = $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST);
3052         $roleid2 = $DB->get_field('role', 'id', array('shortname' => 'coursecreator'), MUST_EXIST);
3053         // Enrol two users as managers onto the course, and 1 onto the category.
3054         $generator->enrol_user($user1->id, $course->id, $roleid1);
3055         $generator->enrol_user($user2->id, $course->id, $roleid1);
3056         $generator->role_assign($roleid1, $user3->id, context_coursecat::instance($category->id));
3057         // Enrol 1 user as a coursecreator onto the course, and another onto the category.
3058         // This is to ensure we do not count users with roles that are not specified.
3059         $generator->enrol_user($user4->id, $course->id, $roleid2);
3060         $generator->role_assign($roleid2, $user5->id, context_coursecat::instance($category->id));
3061         // Check that the correct users are found on the course.
3062         $this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
3063         $this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
3064         // Check for the category.
3065         $this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), false));
3066         $this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), true));
3067         // Have a user with the same role at both the category and course level.
3068         $generator->role_assign($roleid1, $user1->id, context_coursecat::instance($category->id));
3069         // The course level checks should remain the same.
3070         $this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
3071         $this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
3072     }
3075 /**
3076  * Context caching fixture
3077  */
3078 class context_inspection extends context_helper {
3079     public static function test_context_cache_size() {
3080         return self::$cache_count;
3081     }