f5abbd102232504757ffa12bd5536c6917f83e62
[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->accessdatabyuser);
61         accesslib_clear_all_caches_for_unit_testing();
62         $this->assertEmpty($ACCESSLIB_PRIVATE->dirtycontexts);
63         $this->assertEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
64     }
66     /**
67      * Check modifying capability record is not exposed to other code.
68      */
69     public function test_capabilities_mutation() {
70         $oldcap = get_capability_info('moodle/site:config');
71         $cap = get_capability_info('moodle/site:config');
72         unset($cap->name);
73         $newcap = get_capability_info('moodle/site:config');
75         $this->assertFalse(isset($cap->name));
76         $this->assertTrue(isset($newcap->name));
77         $this->assertTrue(isset($oldcap->name));
78     }
80     /**
81      * Test getting of role access
82      */
83     public function test_get_role_access() {
84         global $DB;
86         $roles = $DB->get_records('role');
87         foreach ($roles as $role) {
88             $access = get_role_access($role->id);
90             $this->assertTrue(is_array($access));
91             $this->assertTrue(is_array($access['ra']));
92             $this->assertFalse(isset($access['rdef']));
93             $this->assertFalse(isset($access['rdef_count']));
94             $this->assertFalse(isset($access['loaded']));
95             $this->assertTrue(isset($access['time']));
96             $this->assertTrue(is_array($access['rsw']));
97         }
99         // Note: the data is validated in the functional permission evaluation test at the end of this testcase.
100     }
102     /**
103      * Test getting of guest role.
104      */
105     public function test_get_guest_role() {
106         global $CFG;
108         $guest = get_guest_role();
109         $this->assertEquals('guest', $guest->archetype);
110         $this->assertEquals('guest', $guest->shortname);
112         $this->assertEquals($CFG->guestroleid, $guest->id);
113     }
115     /**
116      * Test if user is admin.
117      */
118     public function test_is_siteadmin() {
119         global $DB, $CFG;
121         $this->resetAfterTest();
123         $users = $DB->get_records('user');
125         foreach ($users as $user) {
126             $this->setUser(0);
127             if ($user->username === 'admin') {
128                 $this->assertTrue(is_siteadmin($user));
129                 $this->assertTrue(is_siteadmin($user->id));
130                 $this->setUser($user);
131                 $this->assertTrue(is_siteadmin());
132                 $this->assertTrue(is_siteadmin(null));
133             } else {
134                 $this->assertFalse(is_siteadmin($user));
135                 $this->assertFalse(is_siteadmin($user->id));
136                 $this->setUser($user);
137                 $this->assertFalse(is_siteadmin());
138                 $this->assertFalse(is_siteadmin(null));
139             }
140         }
142         // Change the site admin list and check that it still works with
143         // multiple admins. We do this with userids only (not real user
144         // accounts) because it makes the test simpler.
145         $before = $CFG->siteadmins;
146         set_config('siteadmins', '666,667,668');
147         $this->assertTrue(is_siteadmin(666));
148         $this->assertTrue(is_siteadmin(667));
149         $this->assertTrue(is_siteadmin(668));
150         $this->assertFalse(is_siteadmin(669));
151         set_config('siteadmins', '13');
152         $this->assertTrue(is_siteadmin(13));
153         $this->assertFalse(is_siteadmin(666));
154         set_config('siteadmins', $before);
155     }
157     /**
158      * Test if user is enrolled in a course
159      */
160     public function test_is_enrolled() {
161         global $DB;
163         $this->resetAfterTest();
165         // Generate data.
166         $user = $this->getDataGenerator()->create_user();
167         $course = $this->getDataGenerator()->create_course();
168         $coursecontext = context_course::instance($course->id);
169         $role = $DB->get_record('role', array('shortname'=>'student'));
171         // There should be a manual enrolment as part of the default install.
172         $plugin = enrol_get_plugin('manual');
173         $instance = $DB->get_record('enrol', array(
174             'courseid' => $course->id,
175             'enrol' => 'manual',
176         ));
177         $this->assertNotSame(false, $instance);
179         // Enrol the user in the course.
180         $plugin->enrol_user($instance, $user->id, $role->id);
182         // We'll test with the mod/assign:submit capability.
183         $capability= 'mod/assign:submit';
184         $this->assertTrue($DB->record_exists('capabilities', array('name' => $capability)));
186         // Switch to our user.
187         $this->setUser($user);
189         // Ensure that the user has the capability first.
190         $this->assertTrue(has_capability($capability, $coursecontext, $user->id));
192         // We first test whether the user is enrolled on the course as this
193         // seeds the cache, then we test for the capability.
194         $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
195         $this->assertTrue(is_enrolled($coursecontext, $user, $capability));
197         // Prevent the capability for this user role.
198         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
199         $this->assertFalse(has_capability($capability, $coursecontext, $user->id));
201         // Again, we seed the cache first by checking initial enrolment,
202         // and then we test the actual capability.
203         $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
204         $this->assertFalse(is_enrolled($coursecontext, $user, $capability));
205     }
207     /**
208      * Test logged in test.
209      */
210     public function test_isloggedin() {
211         global $USER;
213         $this->resetAfterTest();
215         $USER->id = 0;
216         $this->assertFalse(isloggedin());
217         $USER->id = 1;
218         $this->assertTrue(isloggedin());
219     }
221     /**
222      * Test guest user test.
223      */
224     public function test_isguestuser() {
225         global $DB;
227         $this->resetAfterTest();
229         $guest = $DB->get_record('user', array('username'=>'guest'));
230         $this->setUser(0);
231         $this->assertFalse(isguestuser());
232         $this->setAdminUser();
233         $this->assertFalse(isguestuser());
234         $this->assertTrue(isguestuser($guest));
235         $this->assertTrue(isguestuser($guest->id));
236         $this->setUser($guest);
237         $this->assertTrue(isguestuser());
239         $users = $DB->get_records('user');
240         foreach ($users as $user) {
241             if ($user->username === 'guest') {
242                 continue;
243             }
244             $this->assertFalse(isguestuser($user));
245         }
246     }
248     /**
249      * Test capability riskiness.
250      */
251     public function test_is_safe_capability() {
252         global $DB;
253         // Note: there is not much to test, just make sure no notices are throw for the most dangerous cap.
254         $capability = $DB->get_record('capabilities', array('name'=>'moodle/site:config'), '*', MUST_EXIST);
255         $this->assertFalse(is_safe_capability($capability));
256     }
258     /**
259      * Test context fetching.
260      */
261     public function test_get_context_info_array() {
262         $this->resetAfterTest();
264         $syscontext = context_system::instance();
265         $user = $this->getDataGenerator()->create_user();
266         $usercontext = context_user::instance($user->id);
267         $course = $this->getDataGenerator()->create_course();
268         $catcontext = context_coursecat::instance($course->category);
269         $coursecontext = context_course::instance($course->id);
270         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
271         $modcontext = context_module::instance($page->cmid);
272         $cm = get_coursemodule_from_instance('page', $page->id);
273         $block1 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
274         $block1context = context_block::instance($block1->id);
275         $block2 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$modcontext->id));
276         $block2context = context_block::instance($block2->id);
278         $result = get_context_info_array($syscontext->id);
279         $this->assertCount(3, $result);
280         $this->assertEquals($syscontext, $result[0]);
281         $this->assertNull($result[1]);
282         $this->assertNull($result[2]);
284         $result = get_context_info_array($usercontext->id);
285         $this->assertCount(3, $result);
286         $this->assertEquals($usercontext, $result[0]);
287         $this->assertNull($result[1]);
288         $this->assertNull($result[2]);
290         $result = get_context_info_array($catcontext->id);
291         $this->assertCount(3, $result);
292         $this->assertEquals($catcontext, $result[0]);
293         $this->assertNull($result[1]);
294         $this->assertNull($result[2]);
296         $result = get_context_info_array($coursecontext->id);
297         $this->assertCount(3, $result);
298         $this->assertEquals($coursecontext, $result[0]);
299         $this->assertEquals($course->id, $result[1]->id);
300         $this->assertSame($course->shortname, $result[1]->shortname);
301         $this->assertNull($result[2]);
303         $result = get_context_info_array($block1context->id);
304         $this->assertCount(3, $result);
305         $this->assertEquals($block1context, $result[0]);
306         $this->assertEquals($course->id, $result[1]->id);
307         $this->assertEquals($course->shortname, $result[1]->shortname);
308         $this->assertNull($result[2]);
310         $result = get_context_info_array($modcontext->id);
311         $this->assertCount(3, $result);
312         $this->assertEquals($modcontext, $result[0]);
313         $this->assertEquals($course->id, $result[1]->id);
314         $this->assertSame($course->shortname, $result[1]->shortname);
315         $this->assertEquals($cm->id, $result[2]->id);
317         $result = get_context_info_array($block2context->id);
318         $this->assertCount(3, $result);
319         $this->assertEquals($block2context, $result[0]);
320         $this->assertEquals($course->id, $result[1]->id);
321         $this->assertSame($course->shortname, $result[1]->shortname);
322         $this->assertEquals($cm->id, $result[2]->id);
323     }
325     /**
326      * Test looking for course contacts.
327      */
328     public function test_has_coursecontact_role() {
329         global $DB, $CFG;
331         $this->resetAfterTest();
333         $users = $DB->get_records('user');
335         // Nobody is expected to have any course level roles.
336         $this->assertNotEmpty($CFG->coursecontact);
337         foreach ($users as $user) {
338             $this->assertFalse(has_coursecontact_role($user->id));
339         }
341         $user = $this->getDataGenerator()->create_user();
342         $course = $this->getDataGenerator()->create_course();
343         $contactroles = preg_split('/,/', $CFG->coursecontact);
344         $roleid = reset($contactroles);
345         role_assign($roleid, $user->id, context_course::instance($course->id));
346         $this->assertTrue(has_coursecontact_role($user->id));
347     }
349     /**
350      * Test creation of roles.
351      */
352     public function test_create_role() {
353         global $DB;
355         $this->resetAfterTest();
357         $id = create_role('New student role', 'student2', 'New student description', 'student');
358         $role = $DB->get_record('role', array('id'=>$id));
360         $this->assertNotEmpty($role);
361         $this->assertSame('New student role', $role->name);
362         $this->assertSame('student2', $role->shortname);
363         $this->assertSame('New student description', $role->description);
364         $this->assertSame('student', $role->archetype);
365     }
367     /**
368      * Test adding of capabilities to roles.
369      */
370     public function test_assign_capability() {
371         global $DB, $USER;
373         $this->resetAfterTest();
375         $user = $this->getDataGenerator()->create_user();
376         $syscontext = context_system::instance();
377         $frontcontext = context_course::instance(SITEID);
378         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
379         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to student by default.
380         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
381         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
383         $this->setUser($user);
384         $result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $frontcontext->id);
385         $this->assertTrue($result);
386         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
387         $this->assertNotEmpty($permission);
388         $this->assertEquals(CAP_ALLOW, $permission->permission);
389         $this->assertEquals($user->id, $permission->modifierid);
391         $this->setUser(0);
392         $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, false);
393         $this->assertTrue($result);
394         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
395         $this->assertNotEmpty($permission);
396         $this->assertEquals(CAP_ALLOW, $permission->permission);
397         $this->assertEquals($user->id, $permission->modifierid);
399         $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, true);
400         $this->assertTrue($result);
401         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
402         $this->assertNotEmpty($permission);
403         $this->assertEquals(CAP_PROHIBIT, $permission->permission);
404         $this->assertEquals(0, $permission->modifierid);
406         $result = assign_capability('moodle/backup:backupcourse', CAP_INHERIT, $student->id, $frontcontext->id);
407         $this->assertTrue($result);
408         $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
409         $this->assertEmpty($permission);
411         // Test event triggered.
412         $sink = $this->redirectEvents();
413         $capability = 'moodle/backup:backupcourse';
414         assign_capability($capability, CAP_ALLOW, $student->id, $syscontext);
415         $events = $sink->get_events();
416         $sink->close();
417         $this->assertCount(1, $events);
418         $event = $events[0];
419         $this->assertInstanceOf('\core\event\capability_assigned', $event);
420         $this->assertSame('role_capabilities', $event->objecttable);
421         $this->assertEquals($student->id, $event->objectid);
422         $this->assertEquals($syscontext->id, $event->contextid);
423         $other = ['capability' => $capability, 'oldpermission' => CAP_INHERIT, 'permission' => CAP_ALLOW];
424         $this->assertEquals($other, $event->other);
425         $description = "The user id '$USER->id' assigned the '$capability' capability for " .
426             "role '$student->id' with 'Allow' permission";
427         $this->assertEquals($description, $event->get_description());
429         // Test if the event has different description when updating the capability permission.
430         $sink = $this->redirectEvents();
431         assign_capability($capability, CAP_PROHIBIT, $student->id, $syscontext, true);
432         $events = $sink->get_events();
433         $sink->close();
434         $event = $events[0];
435         $description = "The user id '$USER->id' changed the '$capability' capability permission for " .
436             "role '$student->id' from 'Allow' to 'Prohibit'";
437         $this->assertEquals($description, $event->get_description());
438     }
440     /**
441      * Test removing of capabilities from roles.
442      */
443     public function test_unassign_capability() {
444         global $DB, $USER;
446         $this->resetAfterTest();
448         $syscontext = context_system::instance();
449         $frontcontext = context_course::instance(SITEID);
450         $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
451         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to manager by default.
452         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
454         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
455         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
457         $result = unassign_capability('moodle/backup:backupcourse', $manager->id, $syscontext->id);
458         $this->assertTrue($result);
459         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
460         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
461         unassign_capability('moodle/backup:backupcourse', $manager->id, $frontcontext);
462         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
464         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
465         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
466         $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
468         $result = unassign_capability('moodle/backup:backupcourse', $manager->id);
469         $this->assertTrue($result);
470         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
471         $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
473         // Test event triggered.
474         $sink = $this->redirectEvents();
475         $capability = 'moodle/backup:backupcourse';
476         unassign_capability($capability, CAP_ALLOW, $manager->id);
477         $events = $sink->get_events();
478         $sink->close();
479         $this->assertCount(1, $events);
480         $event = $events[0];
481         $this->assertInstanceOf('\core\event\capability_unassigned', $event);
482         $this->assertSame('role_capabilities', $event->objecttable);
483         $this->assertEquals($manager->id, $event->objectid);
484         $this->assertEquals($syscontext->id, $event->contextid);
485         $this->assertEquals($capability, $event->other['capability']);
486         $description = "The user id id '$USER->id' has unassigned the '$capability' capability for role '$manager->id'";
487         $this->assertEquals($description, $event->get_description());
488     }
490     /**
491      * Test role assigning.
492      */
493     public function test_role_assign() {
494         global $DB, $USER;
496         $this->resetAfterTest();
498         $user = $this->getDataGenerator()->create_user();
499         $course = $this->getDataGenerator()->create_course();
500         $role = $DB->get_record('role', array('shortname'=>'student'));
502         $this->setUser(0);
503         $context = context_system::instance();
504         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
505         role_assign($role->id, $user->id, $context->id);
506         $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
507         $this->assertNotEmpty($ras);
508         $this->assertSame('', $ras->component);
509         $this->assertSame('0', $ras->itemid);
510         $this->assertEquals($USER->id, $ras->modifierid);
512         $this->setAdminUser();
513         $context = context_course::instance($course->id);
514         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
515         role_assign($role->id, $user->id, $context->id, 'enrol_self', 1, 666);
516         $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
517         $this->assertNotEmpty($ras);
518         $this->assertSame('enrol_self', $ras->component);
519         $this->assertSame('1', $ras->itemid);
520         $this->assertEquals($USER->id, $ras->modifierid);
521         $this->assertEquals(666, $ras->timemodified);
523         // Test event triggered.
525         $user2 = $this->getDataGenerator()->create_user();
526         $sink = $this->redirectEvents();
527         $raid = role_assign($role->id, $user2->id, $context->id);
528         $events = $sink->get_events();
529         $sink->close();
530         $this->assertCount(1, $events);
531         $event = $events[0];
532         $this->assertInstanceOf('\core\event\role_assigned', $event);
533         $this->assertSame('role', $event->target);
534         $this->assertSame('role', $event->objecttable);
535         $this->assertEquals($role->id, $event->objectid);
536         $this->assertEquals($context->id, $event->contextid);
537         $this->assertEquals($user2->id, $event->relateduserid);
538         $this->assertCount(3, $event->other);
539         $this->assertEquals($raid, $event->other['id']);
540         $this->assertSame('', $event->other['component']);
541         $this->assertEquals(0, $event->other['itemid']);
542         $this->assertInstanceOf('moodle_url', $event->get_url());
543         $this->assertSame('role_assigned', $event::get_legacy_eventname());
544         $roles = get_all_roles();
545         $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
546         $expectedlegacylog = array($course->id, 'role', 'assign',
547             'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
548         $this->assertEventLegacyLogData($expectedlegacylog, $event);
549     }
551     /**
552      * Test role unassigning.
553      */
554     public function test_role_unassign() {
555         global $DB, $USER;
557         $this->resetAfterTest();
559         $user = $this->getDataGenerator()->create_user();
560         $course = $this->getDataGenerator()->create_course();
561         $role = $DB->get_record('role', array('shortname'=>'student'));
563         $context = context_course::instance($course->id);
564         role_assign($role->id, $user->id, $context->id);
565         $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
566         role_unassign($role->id, $user->id, $context->id);
567         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
569         role_assign($role->id, $user->id, $context->id, 'enrol_self', 1);
570         $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
571         role_unassign($role->id, $user->id, $context->id, 'enrol_self', 1);
572         $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
574         // Test event triggered.
576         role_assign($role->id, $user->id, $context->id);
577         $sink = $this->redirectEvents();
578         role_unassign($role->id, $user->id, $context->id);
579         $events = $sink->get_events();
580         $sink->close();
581         $this->assertCount(1, $events);
582         $event = $events[0];
583         $this->assertInstanceOf('\core\event\role_unassigned', $event);
584         $this->assertSame('role', $event->target);
585         $this->assertSame('role', $event->objecttable);
586         $this->assertEquals($role->id, $event->objectid);
587         $this->assertEquals($context->id, $event->contextid);
588         $this->assertEquals($user->id, $event->relateduserid);
589         $this->assertCount(3, $event->other);
590         $this->assertSame('', $event->other['component']);
591         $this->assertEquals(0, $event->other['itemid']);
592         $this->assertInstanceOf('moodle_url', $event->get_url());
593         $roles = get_all_roles();
594         $rolenames = role_fix_names($roles, $context, ROLENAME_ORIGINAL, true);
595         $expectedlegacylog = array($course->id, 'role', 'unassign',
596             'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$role->id, $rolenames[$role->id], '', $USER->id);
597         $this->assertEventLegacyLogData($expectedlegacylog, $event);
598     }
600     /**
601      * Test role unassigning.
602      */
603     public function test_role_unassign_all() {
604         global $DB;
606         $this->resetAfterTest();
608         $user = $this->getDataGenerator()->create_user();
609         $course = $this->getDataGenerator()->create_course();
610         $role = $DB->get_record('role', array('shortname'=>'student'));
611         $role2 = $DB->get_record('role', array('shortname'=>'teacher'));
612         $syscontext = context_system::instance();
613         $coursecontext = context_course::instance($course->id);
614         $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
615         $modcontext = context_module::instance($page->cmid);
617         role_assign($role->id, $user->id, $syscontext->id);
618         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
619         $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
620         role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role->id));
621         $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
623         role_assign($role->id, $user->id, $syscontext->id);
624         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
625         role_assign($role->id, $user->id, $modcontext->id);
626         $this->assertEquals(3, $DB->count_records('role_assignments', array('userid'=>$user->id)));
627         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), false);
628         $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
629         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), true);
630         $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
631         role_unassign_all(array('userid'=>$user->id));
632         $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
634         role_assign($role->id, $user->id, $syscontext->id);
635         role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
636         role_assign($role->id, $user->id, $coursecontext->id);
637         role_assign($role->id, $user->id, $modcontext->id);
638         $this->assertEquals(4, $DB->count_records('role_assignments', array('userid'=>$user->id)));
639         role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id, 'component'=>'enrol_self'), true, true);
640         $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
642         // Test events triggered.
644         role_assign($role2->id, $user->id, $coursecontext->id);
645         role_assign($role2->id, $user->id, $modcontext->id);
646         $sink = $this->redirectEvents();
647         role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role2->id));
648         $events = $sink->get_events();
649         $sink->close();
650         $this->assertCount(2, $events);
651         $this->assertInstanceOf('\core\event\role_unassigned', $events[0]);
652         $this->assertInstanceOf('\core\event\role_unassigned', $events[1]);
653     }
655     /**
656      * Test role queries.
657      */
658     public function test_get_roles_with_capability() {
659         global $DB;
661         $this->resetAfterTest();
663         $syscontext = context_system::instance();
664         $frontcontext = context_course::instance(SITEID);
665         $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
666         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
668         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
669         $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse'));
671         $roles = get_roles_with_capability('moodle/backup:backupcourse');
672         $this->assertEquals(array(), $roles);
674         assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
675         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $manager->id, $frontcontext->id);
676         assign_capability('moodle/backup:backupcourse', CAP_PREVENT, $teacher->id, $frontcontext->id);
678         $roles = get_roles_with_capability('moodle/backup:backupcourse');
679         $this->assertEquals(array($teacher->id, $manager->id), array_keys($roles), '', 0, 10, true);
681         $roles = get_roles_with_capability('moodle/backup:backupcourse', CAP_ALLOW);
682         $this->assertEquals(array($manager->id), array_keys($roles), '', 0, 10, true);
684         $roles = get_roles_with_capability('moodle/backup:backupcourse', null, $syscontext);
685         $this->assertEquals(array($manager->id), array_keys($roles), '', 0, 10, true);
686     }
688     /**
689      * Test deleting of roles.
690      */
691     public function test_delete_role() {
692         global $DB;
694         $this->resetAfterTest();
696         $role = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
697         $user = $this->getDataGenerator()->create_user();
698         role_assign($role->id, $user->id, context_system::instance());
699         $course = $this->getDataGenerator()->create_course();
700         $rolename = (object)array('roleid'=>$role->id, 'name'=>'Man', 'contextid'=>context_course::instance($course->id)->id);
701         $DB->insert_record('role_names', $rolename);
703         $this->assertTrue($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
704         $this->assertTrue($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
705         $this->assertTrue($DB->record_exists('role_names', array('roleid'=>$role->id)));
706         $this->assertTrue($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
707         $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
708         $this->assertTrue($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
709         $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
710         $this->assertTrue($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
712         // Delete role and get event.
713         $sink = $this->redirectEvents();
714         $result = delete_role($role->id);
715         $events = $sink->get_events();
716         $sink->close();
717         $event = array_pop($events);
719         $this->assertTrue($result);
720         $this->assertFalse($DB->record_exists('role', array('id'=>$role->id)));
721         $this->assertFalse($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
722         $this->assertFalse($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
723         $this->assertFalse($DB->record_exists('role_names', array('roleid'=>$role->id)));
724         $this->assertFalse($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
725         $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
726         $this->assertFalse($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
727         $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
728         $this->assertFalse($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
730         // Test triggered event.
731         $this->assertInstanceOf('\core\event\role_deleted', $event);
732         $this->assertSame('role', $event->target);
733         $this->assertSame('role', $event->objecttable);
734         $this->assertSame($role->id, $event->objectid);
735         $this->assertEquals(context_system::instance(), $event->get_context());
736         $this->assertSame($role->shortname, $event->other['shortname']);
737         $this->assertSame($role->description, $event->other['description']);
738         $this->assertSame($role->archetype, $event->other['archetype']);
740         $expectedlegacylog = array(SITEID, 'role', 'delete', 'admin/roles/manage.php?action=delete&roleid='.$role->id,
741                                    $role->shortname, '');
742         $this->assertEventLegacyLogData($expectedlegacylog, $event);
743     }
745     /**
746      * Test fetching of all roles.
747      */
748     public function test_get_all_roles() {
749         global $DB;
751         $this->resetAfterTest();
753         $allroles = get_all_roles();
754         $this->assertIsArray($allroles);
755         $initialrolescount = count($allroles);
756         $this->assertTrue($initialrolescount >= 8); // There are 8 roles is standard install.
757         $rolenames = array_column($allroles, 'shortname');
758         foreach (get_role_archetypes() as $archetype) {
759             $this->assertContains($archetype, $rolenames);
760         }
762         $role = reset($allroles);
763         $role = (array)$role;
765         $this->assertEquals(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype'), array_keys($role), '', 0, 10, true);
767         foreach ($allroles as $roleid => $role) {
768             $this->assertEquals($role->id, $roleid);
769         }
771         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
772         $course = $this->getDataGenerator()->create_course();
773         $coursecontext = context_course::instance($course->id);
774         $otherid = create_role('Other role', 'other', 'Some other role', '');
775         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
776         $DB->insert_record('role_names', $teacherename);
777         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
778         $DB->insert_record('role_names', $otherrename);
779         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
781         $allroles = get_all_roles($coursecontext);
782         $this->assertIsArray($allroles);
783         $this->assertCount($initialrolescount + 1, $allroles);
784         $role = reset($allroles);
785         $role = (array)$role;
787         $this->assertEquals(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype', 'coursealias'), array_keys($role), '', 0, 10, true);
789         foreach ($allroles as $roleid => $role) {
790             $this->assertEquals($role->id, $roleid);
791             if (isset($renames[$roleid])) {
792                 $this->assertSame($renames[$roleid], $role->coursealias);
793             } else {
794                 $this->assertNull($role->coursealias);
795             }
796         }
797     }
799     /**
800      * Test getting of all archetypes.
801      */
802     public function test_get_role_archetypes() {
803         $archetypes = get_role_archetypes();
804         $this->assertCount(8, $archetypes); // There are 8 archetypes in standard install.
805         foreach ($archetypes as $k => $v) {
806             $this->assertSame($k, $v);
807         }
808     }
810     /**
811      * Test getting of roles with given archetype.
812      */
813     public function test_get_archetype_roles() {
814         $this->resetAfterTest();
816         // New install should have at least 1 role for each archetype.
817         $archetypes = get_role_archetypes();
818         foreach ($archetypes as $archetype) {
819             $roles = get_archetype_roles($archetype);
820             $this->assertGreaterThanOrEqual(1, count($roles));
821             $role = reset($roles);
822             $this->assertSame($archetype, $role->archetype);
823         }
825         create_role('New student role', 'student2', 'New student description', 'student');
826         $roles = get_archetype_roles('student');
827         $this->assertGreaterThanOrEqual(2, count($roles));
828     }
830     /**
831      * Test aliased role names.
832      */
833     public function test_role_get_name() {
834         global $DB;
836         $this->resetAfterTest();
838         $allroles = $DB->get_records('role');
839         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
840         $course = $this->getDataGenerator()->create_course();
841         $coursecontext = context_course::instance($course->id);
842         $otherid = create_role('Other role', 'other', 'Some other role', '');
843         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
844         $DB->insert_record('role_names', $teacherename);
845         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
846         $DB->insert_record('role_names', $otherrename);
847         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
849         foreach ($allroles as $role) {
850             if (in_array($role->shortname, get_role_archetypes())) {
851                 // Standard roles do not have a set name.
852                 $this->assertSame('', $role->name);
853             }
854             // Get localised name from lang pack.
855             $name = role_get_name($role, null, ROLENAME_ORIGINAL);
856             $this->assertNotEmpty($name);
857             $this->assertNotEquals($role->shortname, $name);
859             if (isset($renames[$role->id])) {
860                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext));
861                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS));
862                 $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
863                 $this->assertSame("{$renames[$role->id]} ($name)", role_get_name($role, $coursecontext, ROLENAME_BOTH));
864             } else {
865                 $this->assertSame($name, role_get_name($role, $coursecontext));
866                 $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ALIAS));
867                 $this->assertNull(role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
868                 $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_BOTH));
869             }
870             $this->assertSame($name, role_get_name($role));
871             $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ORIGINAL));
872             $this->assertSame($name, role_get_name($role, null, ROLENAME_ORIGINAL));
873             $this->assertSame($role->shortname, role_get_name($role, $coursecontext, ROLENAME_SHORT));
874             $this->assertSame($role->shortname, role_get_name($role, null, ROLENAME_SHORT));
875             $this->assertSame("$name ($role->shortname)", role_get_name($role, $coursecontext, ROLENAME_ORIGINALANDSHORT));
876             $this->assertSame("$name ($role->shortname)", role_get_name($role, null, ROLENAME_ORIGINALANDSHORT));
877             $this->assertNull(role_get_name($role, null, ROLENAME_ALIAS_RAW));
878         }
879     }
881     /**
882      * Test tweaking of role name arrays.
883      */
884     public function test_role_fix_names() {
885         global $DB;
887         $this->resetAfterTest();
889         $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
890         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
891         $otherid = create_role('Other role', 'other', 'Some other role', '');
892         $anotherid = create_role('Another role', 'another', 'Yet another other role', '');
893         $allroles = $DB->get_records('role');
895         $syscontext = context_system::instance();
896         $frontcontext = context_course::instance(SITEID);
897         $course = $this->getDataGenerator()->create_course();
898         $coursecontext = context_course::instance($course->id);
899         $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
900         $categorycontext = context_coursecat::instance($category->id);
902         $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
903         $DB->insert_record('role_names', $teacherename);
904         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
905         $DB->insert_record('role_names', $otherrename);
906         $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
908         // Make sure all localname contain proper values for each ROLENAME_ constant,
909         // note role_get_name() on frontpage is used to get the original name for future compatibility.
910         $roles = $allroles;
911         unset($roles[$student->id]); // Remove one role to make sure no role is added or removed.
912         $rolenames = array();
913         foreach ($roles as $role) {
914             $rolenames[$role->id] = $role->name;
915         }
917         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
918         foreach ($alltypes as $type) {
919             $fixed = role_fix_names($roles, $coursecontext, $type);
920             $this->assertCount(count($roles), $fixed);
921             foreach ($fixed as $roleid => $rolename) {
922                 $this->assertInstanceOf('stdClass', $rolename);
923                 $role = $allroles[$roleid];
924                 $name = role_get_name($role, $coursecontext, $type);
925                 $this->assertSame($name, $rolename->localname);
926             }
927             $fixed = role_fix_names($rolenames, $coursecontext, $type);
928             $this->assertCount(count($rolenames), $fixed);
929             foreach ($fixed as $roleid => $rolename) {
930                 $role = $allroles[$roleid];
931                 $name = role_get_name($role, $coursecontext, $type);
932                 $this->assertSame($name, $rolename);
933             }
934         }
935     }
937     /**
938      * Test role default allows.
939      */
940     public function test_get_default_role_archetype_allows() {
941         $archetypes = get_role_archetypes();
942         foreach ($archetypes as $archetype) {
944             $result = get_default_role_archetype_allows('assign', $archetype);
945             $this->assertInternalType('array', $result);
947             $result = get_default_role_archetype_allows('override', $archetype);
948             $this->assertInternalType('array', $result);
950             $result = get_default_role_archetype_allows('switch', $archetype);
951             $this->assertInternalType('array', $result);
953             $result = get_default_role_archetype_allows('view', $archetype);
954             $this->assertInternalType('array', $result);
955         }
957         $result = get_default_role_archetype_allows('assign', '');
958         $this->assertSame(array(), $result);
960         $result = get_default_role_archetype_allows('override', '');
961         $this->assertSame(array(), $result);
963         $result = get_default_role_archetype_allows('switch', '');
964         $this->assertSame(array(), $result);
966         $result = get_default_role_archetype_allows('view', '');
967         $this->assertSame(array(), $result);
969         $result = get_default_role_archetype_allows('assign', 'wrongarchetype');
970         $this->assertSame(array(), $result);
971         $this->assertDebuggingCalled();
973         $result = get_default_role_archetype_allows('override', 'wrongarchetype');
974         $this->assertSame(array(), $result);
975         $this->assertDebuggingCalled();
977         $result = get_default_role_archetype_allows('switch', 'wrongarchetype');
978         $this->assertSame(array(), $result);
979         $this->assertDebuggingCalled();
981         $result = get_default_role_archetype_allows('view', 'wrongarchetype');
982         $this->assertSame(array(), $result);
983         $this->assertDebuggingCalled();
984     }
986     /**
987      * Test allowing of role assignments.
988      */
989     public function test_core_role_set_assign_allowed() {
990         global $DB, $CFG;
992         $this->resetAfterTest();
994         $otherid = create_role('Other role', 'other', 'Some other role', '');
995         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
997         $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
998         core_role_set_assign_allowed($otherid, $student->id);
999         $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
1001         // Test event trigger.
1002         $allowroleassignevent = \core\event\role_allow_assign_updated::create([
1003             'context' => context_system::instance(),
1004             'objectid' => $otherid,
1005             'other' => ['targetroleid' => $student->id]
1006         ]);
1007         $sink = $this->redirectEvents();
1008         $allowroleassignevent->trigger();
1009         $events = $sink->get_events();
1010         $sink->close();
1011         $event = array_pop($events);
1012         $this->assertInstanceOf('\core\event\role_allow_assign_updated', $event);
1013         $mode = 'assign';
1014         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1015         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1016         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1017     }
1019     /**
1020      * Test allowing of role overrides.
1021      */
1022     public function test_core_role_set_override_allowed() {
1023         global $DB, $CFG;
1025         $this->resetAfterTest();
1027         $otherid = create_role('Other role', 'other', 'Some other role', '');
1028         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1030         $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
1031         core_role_set_override_allowed($otherid, $student->id);
1032         $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
1034         // Test event trigger.
1035         $allowroleassignevent = \core\event\role_allow_override_updated::create([
1036             'context' => context_system::instance(),
1037             'objectid' => $otherid,
1038             'other' => ['targetroleid' => $student->id]
1039         ]);
1040         $sink = $this->redirectEvents();
1041         $allowroleassignevent->trigger();
1042         $events = $sink->get_events();
1043         $sink->close();
1044         $event = array_pop($events);
1045         $this->assertInstanceOf('\core\event\role_allow_override_updated', $event);
1046         $mode = 'override';
1047         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1048         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1049         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1050     }
1052     /**
1053      * Test allowing of role switching.
1054      */
1055     public function test_core_role_set_switch_allowed() {
1056         global $DB, $CFG;
1058         $this->resetAfterTest();
1060         $otherid = create_role('Other role', 'other', 'Some other role', '');
1061         $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1063         $this->assertFalse($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1064         core_role_set_switch_allowed($otherid, $student->id);
1065         $this->assertTrue($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1067         // Test event trigger.
1068         $allowroleassignevent = \core\event\role_allow_switch_updated::create([
1069             'context' => context_system::instance(),
1070             'objectid' => $otherid,
1071             'other' => ['targetroleid' => $student->id]
1072         ]);
1073         $sink = $this->redirectEvents();
1074         $allowroleassignevent->trigger();
1075         $events = $sink->get_events();
1076         $sink->close();
1077         $event = array_pop($events);
1078         $this->assertInstanceOf('\core\event\role_allow_switch_updated', $event);
1079         $mode = 'switch';
1080         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1081         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1082         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1083     }
1085     /**
1086      * Test allowing of role switching.
1087      */
1088     public function test_core_role_set_view_allowed() {
1089         global $DB, $CFG;
1091         $this->resetAfterTest();
1093         $otherid = create_role('Other role', 'other', 'Some other role', '');
1094         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1096         $this->assertFalse($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
1097         core_role_set_view_allowed($otherid, $student->id);
1098         $this->assertTrue($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
1100         // Test event trigger.
1101         $allowroleassignevent = \core\event\role_allow_view_updated::create([
1102             'context' => context_system::instance(),
1103             'objectid' => $otherid,
1104             'other' => ['targetroleid' => $student->id]
1105         ]);
1106         $sink = $this->redirectEvents();
1107         $allowroleassignevent->trigger();
1108         $events = $sink->get_events();
1109         $sink->close();
1110         $event = array_pop($events);
1111         $this->assertInstanceOf('\core\event\role_allow_view_updated', $event);
1112         $mode = 'view';
1113         $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1114         $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1115         $this->assertEventLegacyLogData($expectedlegacylog, $event);
1116     }
1118     /**
1119      * Test returning of assignable roles in context.
1120      */
1121     public function test_get_assignable_roles() {
1122         global $DB;
1124         $this->resetAfterTest();
1126         $course = $this->getDataGenerator()->create_course();
1127         $coursecontext = context_course::instance($course->id);
1129         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1130         $teacher = $this->getDataGenerator()->create_user();
1131         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1132         $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1133         $DB->insert_record('role_names', $teacherename);
1135         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1136         $student = $this->getDataGenerator()->create_user();
1137         role_assign($studentrole->id, $student->id, $coursecontext);
1139         $contexts = $DB->get_records('context');
1140         $users = $DB->get_records('user');
1141         $allroles = $DB->get_records('role');
1143         // Evaluate all results for all users in all contexts.
1144         foreach ($users as $user) {
1145             $this->setUser($user);
1146             foreach ($contexts as $contextid => $unused) {
1147                 $context = context_helper::instance_by_id($contextid);
1148                 $roles = get_assignable_roles($context, ROLENAME_SHORT);
1149                 foreach ($allroles as $roleid => $role) {
1150                     if (isset($roles[$roleid])) {
1151                         if (is_siteadmin()) {
1152                             $this->assertTrue($DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid)));
1153                         } else {
1154                             $this->assertTrue(user_can_assign($context, $roleid), "u:$user->id r:$roleid");
1155                         }
1156                         $this->assertEquals($role->shortname, $roles[$roleid]);
1157                     } else {
1158                         $allowed = $DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid));
1159                         if (is_siteadmin()) {
1160                             $this->assertFalse($allowed);
1161                         } else {
1162                             $this->assertFalse($allowed and user_can_assign($context, $roleid), "u:$user->id, r:{$allroles[$roleid]->name}, c:$context->contextlevel");
1163                         }
1164                     }
1165                 }
1166             }
1167         }
1169         // Not-logged-in user.
1170         $this->setUser(0);
1171         foreach ($contexts as $contextid => $unused) {
1172             $context = context_helper::instance_by_id($contextid);
1173             $roles = get_assignable_roles($context, ROLENAME_SHORT);
1174             $this->assertSame(array(), $roles);
1175         }
1177         // Test current user.
1178         $this->setUser(0);
1179         $admin = $DB->get_record('user', array('username'=>'admin'), '*', MUST_EXIST);
1180         $roles1 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin);
1181         $roles2 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin->id);
1182         $this->setAdminUser();
1183         $roles3 = get_assignable_roles($coursecontext, ROLENAME_SHORT);
1184         $this->assertSame($roles1, $roles3);
1185         $this->assertSame($roles2, $roles3);
1187         // Test parameter defaults.
1188         $this->setAdminUser();
1189         $roles1 = get_assignable_roles($coursecontext);
1190         $roles2 = get_assignable_roles($coursecontext, ROLENAME_ALIAS, false, $admin);
1191         $this->assertEquals($roles2, $roles1);
1193         // Verify returned names - let's allow all roles everywhere to simplify this a bit.
1194         $alllevels = context_helper::get_all_levels();
1195         $alllevels = array_keys($alllevels);
1196         foreach ($allroles as $roleid => $role) {
1197             set_role_contextlevels($roleid, $alllevels);
1198         }
1199         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1200         foreach ($alltypes as $type) {
1201             $rolenames = role_fix_names($allroles, $coursecontext, $type);
1202             $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1203             foreach ($roles as $roleid => $rolename) {
1204                 $this->assertSame($rolenames[$roleid]->localname, $rolename);
1205             }
1206         }
1208         // Verify counts.
1209         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1210         foreach ($alltypes as $type) {
1211             $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1212             list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($coursecontext, $type, true, $admin);
1213             $this->assertEquals($roles, $rolenames);
1214             foreach ($rolenames as $roleid => $name) {
1215                 if ($roleid == $teacherrole->id or $roleid == $studentrole->id) {
1216                     $this->assertEquals(1, $rolecounts[$roleid]);
1217                 } else {
1218                     $this->assertEquals(0, $rolecounts[$roleid]);
1219                 }
1220                 $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1221             }
1222         }
1223     }
1225     /**
1226      * Test user count of assignable roles in context where users are assigned the role via different components.
1227      */
1228     public function test_get_assignable_roles_distinct_usercount() {
1229         global $DB;
1231         $this->resetAfterTest(true);
1233         $this->setAdminUser();
1235         $course = $this->getDataGenerator()->create_course();
1236         $context = \context_course::instance($course->id);
1238         $user1 = $this->getDataGenerator()->create_user();
1239         $user2 = $this->getDataGenerator()->create_user();
1241         $studentrole = $DB->get_record('role', ['shortname' => 'student']);
1243         // Assign each user the student role in course.
1244         role_assign($studentrole->id, $user1->id, $context->id);
1245         role_assign($studentrole->id, $user2->id, $context->id);
1247         list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
1248         $this->assertEquals(2, $rolecounts[$studentrole->id]);
1250         // Assign first user the student role in course again (this time via 'enrol_self' component).
1251         role_assign($studentrole->id, $user1->id, $context->id, 'enrol_self', 1);
1253         // There are still only two distinct users.
1254         list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
1255         $this->assertEquals(2, $rolecounts[$studentrole->id]);
1256     }
1258     /**
1259      * Test getting of all switchable roles.
1260      */
1261     public function test_get_switchable_roles() {
1262         global $DB;
1264         $this->resetAfterTest();
1266         $course = $this->getDataGenerator()->create_course();
1267         $coursecontext = context_course::instance($course->id);
1269         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1270         $teacher = $this->getDataGenerator()->create_user();
1271         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1272         $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1273         $DB->insert_record('role_names', $teacherename);
1275         $contexts = $DB->get_records('context');
1276         $users = $DB->get_records('user');
1277         $allroles = $DB->get_records('role');
1279         // Evaluate all results for all users in all contexts.
1280         foreach ($users as $user) {
1281             $this->setUser($user);
1282             foreach ($contexts as $contextid => $unused) {
1283                 $context = context_helper::instance_by_id($contextid);
1284                 $roles = get_switchable_roles($context);
1285                 foreach ($allroles as $roleid => $role) {
1286                     if (is_siteadmin()) {
1287                         $this->assertTrue(isset($roles[$roleid]));
1288                     } else {
1289                         $parents = $context->get_parent_context_ids(true);
1290                         $pcontexts = implode(',' , $parents);
1291                         $allowed = $DB->record_exists_sql(
1292                             "SELECT r.id
1293                                FROM {role} r
1294                                JOIN {role_allow_switch} ras ON ras.allowswitch = r.id
1295                                JOIN {role_assignments} ra ON ra.roleid = ras.roleid
1296                               WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1297                             ",
1298                             array('userid'=>$user->id, 'roleid'=>$roleid)
1299                         );
1300                         if (isset($roles[$roleid])) {
1301                             $this->assertTrue($allowed);
1302                         } else {
1303                             $this->assertFalse($allowed);
1304                         }
1305                     }
1307                     if (isset($roles[$roleid])) {
1308                         $coursecontext = $context->get_course_context(false);
1309                         $this->assertSame(role_get_name($role, $coursecontext), $roles[$roleid]);
1310                     }
1311                 }
1312             }
1313         }
1314     }
1316     /**
1317      * Test getting of all overridable roles.
1318      */
1319     public function test_get_overridable_roles() {
1320         global $DB;
1322         $this->resetAfterTest();
1324         $course = $this->getDataGenerator()->create_course();
1325         $coursecontext = context_course::instance($course->id);
1327         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1328         $teacher = $this->getDataGenerator()->create_user();
1329         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1330         $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1331         $DB->insert_record('role_names', $teacherename);
1332         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
1333         assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id);
1335         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1336         $student = $this->getDataGenerator()->create_user();
1337         role_assign($studentrole->id, $student->id, $coursecontext);
1339         $contexts = $DB->get_records('context');
1340         $users = $DB->get_records('user');
1341         $allroles = $DB->get_records('role');
1343         // Evaluate all results for all users in all contexts.
1344         foreach ($users as $user) {
1345             $this->setUser($user);
1346             foreach ($contexts as $contextid => $unused) {
1347                 $context = context_helper::instance_by_id($contextid);
1348                 $roles = get_overridable_roles($context, ROLENAME_SHORT);
1349                 foreach ($allroles as $roleid => $role) {
1350                     $hascap = has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context);
1351                     if (is_siteadmin()) {
1352                         $this->assertTrue(isset($roles[$roleid]));
1353                     } else {
1354                         $parents = $context->get_parent_context_ids(true);
1355                         $pcontexts = implode(',' , $parents);
1356                         $allowed = $DB->record_exists_sql(
1357                             "SELECT r.id
1358                                FROM {role} r
1359                                JOIN {role_allow_override} rao ON r.id = rao.allowoverride
1360                                JOIN {role_assignments} ra ON rao.roleid = ra.roleid
1361                               WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1362                             ",
1363                             array('userid'=>$user->id, 'roleid'=>$roleid)
1364                         );
1365                         if (isset($roles[$roleid])) {
1366                             $this->assertTrue($hascap);
1367                             $this->assertTrue($allowed);
1368                         } else {
1369                             $this->assertFalse($hascap and $allowed);
1370                         }
1371                     }
1373                     if (isset($roles[$roleid])) {
1374                         $this->assertEquals($role->shortname, $roles[$roleid]);
1375                     }
1376                 }
1377             }
1378         }
1380         // Test parameter defaults.
1381         $this->setAdminUser();
1382         $roles1 = get_overridable_roles($coursecontext);
1383         $roles2 = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1384         $this->assertEquals($roles2, $roles1);
1386         $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1387         foreach ($alltypes as $type) {
1388             $rolenames = role_fix_names($allroles, $coursecontext, $type);
1389             $roles = get_overridable_roles($coursecontext, $type, false);
1390             foreach ($roles as $roleid => $rolename) {
1391                 $this->assertSame($rolenames[$roleid]->localname, $rolename);
1392             }
1393         }
1395         // Verify counts.
1396         $roles = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1397         list($rolenames, $rolecounts, $nameswithcounts) = get_overridable_roles($coursecontext, ROLENAME_ALIAS, true);
1398         $this->assertEquals($roles, $rolenames);
1399         foreach ($rolenames as $roleid => $name) {
1400             if ($roleid == $teacherrole->id) {
1401                 $this->assertEquals(1, $rolecounts[$roleid]);
1402             } else {
1403                 $this->assertEquals(0, $rolecounts[$roleid]);
1404             }
1405             $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1406         }
1407     }
1409     /**
1410      * Test getting of all overridable roles.
1411      */
1412     public function test_get_viewable_roles_course() {
1413         global $DB;
1415         $this->resetAfterTest();
1417         $course = $this->getDataGenerator()->create_course();
1418         $coursecontext = context_course::instance($course->id);
1420         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1421         $teacher = $this->getDataGenerator()->create_user();
1422         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1424         $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1425         $studentrolerename = (object) array('roleid' => $studentrole->id, 'name' => 'Učitel', 'contextid' => $coursecontext->id);
1426         $DB->insert_record('role_names', $studentrolerename);
1428         // By default teacher can see student.
1429         $this->setUser($teacher);
1430         $viewableroles = get_viewable_roles($coursecontext);
1431         $this->assertContains($studentrolerename->name, array_values($viewableroles));
1432         // Remove view permission.
1433         $DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
1434         $viewableroles = get_viewable_roles($coursecontext);
1435         // Teacher can no longer see student role.
1436         $this->assertNotContains($studentrolerename->name, array_values($viewableroles));
1437         // Allow again teacher to view student.
1438         core_role_set_view_allowed($teacherrole->id, $studentrole->id);
1439         // Teacher can now see student role.
1440         $viewableroles = get_viewable_roles($coursecontext);
1441         $this->assertContains($studentrolerename->name, array_values($viewableroles));
1442     }
1444     /**
1445      * Test getting of all overridable roles.
1446      */
1447     public function test_get_viewable_roles_system() {
1448         global $DB;
1450         $this->resetAfterTest();
1452         $context = context_system::instance();
1454         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1455         $teacher = $this->getDataGenerator()->create_user();
1456         role_assign($teacherrole->id, $teacher->id, $context);
1458         $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1459         $studentrolename = role_get_name($studentrole, $context);
1461         // By default teacher can see student.
1462         $this->setUser($teacher);
1463         $viewableroles = get_viewable_roles($context);
1464         $this->assertContains($studentrolename, array_values($viewableroles));
1465         // Remove view permission.
1466         $DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
1467         $viewableroles = get_viewable_roles($context);
1468         // Teacher can no longer see student role.
1469         $this->assertNotContains($studentrolename, array_values($viewableroles));
1470         // Allow again teacher to view student.
1471         core_role_set_view_allowed($teacherrole->id, $studentrole->id);
1472         // Teacher can now see student role.
1473         $viewableroles = get_viewable_roles($context);
1474         $this->assertContains($studentrolename, array_values($viewableroles));
1475     }
1477     /**
1478      * Test we have context level defaults.
1479      */
1480     public function test_get_default_contextlevels() {
1481         $archetypes = get_role_archetypes();
1482         $alllevels = context_helper::get_all_levels();
1483         foreach ($archetypes as $archetype) {
1484             $defaults = get_default_contextlevels($archetype);
1485             $this->assertInternalType('array', $defaults);
1486             foreach ($defaults as $level) {
1487                 $this->assertTrue(isset($alllevels[$level]));
1488             }
1489         }
1490     }
1492     /**
1493      * Test role context level setup.
1494      */
1495     public function test_set_role_contextlevels() {
1496         global $DB;
1498         $this->resetAfterTest();
1500         $roleid = create_role('New student role', 'student2', 'New student description', 'student');
1502         $this->assertFalse($DB->record_exists('role_context_levels', array('roleid' => $roleid)));
1504         set_role_contextlevels($roleid, array(CONTEXT_COURSE, CONTEXT_MODULE));
1505         $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1506         $this->assertCount(2, $levels);
1507         $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1508         $this->assertTrue(isset($levels[CONTEXT_MODULE]));
1510         set_role_contextlevels($roleid, array(CONTEXT_COURSE));
1511         $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1512         $this->assertCount(1, $levels);
1513         $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1514     }
1516     /**
1517      * Test getting of role context levels
1518      */
1519     public function test_get_roles_for_contextlevels() {
1520         global $DB;
1522         $allroles = get_all_roles();
1523         foreach (context_helper::get_all_levels() as $level => $unused) {
1524             $roles = get_roles_for_contextlevels($level);
1525             foreach ($allroles as $roleid => $unused) {
1526                 $exists = $DB->record_exists('role_context_levels', array('contextlevel'=>$level, 'roleid'=>$roleid));
1527                 if (in_array($roleid, $roles)) {
1528                     $this->assertTrue($exists);
1529                 } else {
1530                     $this->assertFalse($exists);
1531                 }
1532             }
1533         }
1534     }
1536     /**
1537      * Test default enrol roles.
1538      */
1539     public function test_get_default_enrol_roles() {
1540         $this->resetAfterTest();
1542         $course = $this->getDataGenerator()->create_course();
1543         $coursecontext = context_course::instance($course->id);
1545         $id2 = create_role('New student role', 'student2', 'New student description', 'student');
1546         set_role_contextlevels($id2, array(CONTEXT_COURSE));
1548         $allroles = get_all_roles();
1549         $expected = array($id2=>$allroles[$id2]);
1551         foreach (get_roles_for_contextlevels(CONTEXT_COURSE) as $roleid) {
1552             $expected[$roleid] = $roleid;
1553         }
1555         $roles = get_default_enrol_roles($coursecontext);
1556         foreach ($allroles as $role) {
1557             $this->assertEquals(isset($expected[$role->id]), isset($roles[$role->id]));
1558             if (isset($roles[$role->id])) {
1559                 $this->assertSame(role_get_name($role, $coursecontext), $roles[$role->id]);
1560             }
1561         }
1562     }
1564     /**
1565      * Test getting of role users.
1566      */
1567     public function test_get_role_users() {
1568         global $DB;
1570         $this->resetAfterTest();
1572         $systemcontext = context_system::instance();
1573         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1574         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1575         $noeditteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
1576         $course = $this->getDataGenerator()->create_course();
1577         $coursecontext = context_course::instance($course->id);
1578         $otherid = create_role('Other role', 'other', 'Some other role', '');
1579         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1580         $DB->insert_record('role_names', $teacherrename);
1581         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1582         $DB->insert_record('role_names', $otherrename);
1584         $user1 = $this->getDataGenerator()->create_user(array('firstname'=>'John', 'lastname'=>'Smith'));
1585         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1586         $user2 = $this->getDataGenerator()->create_user(array('firstname'=>'Jan', 'lastname'=>'Kovar'));
1587         role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1588         $user3 = $this->getDataGenerator()->create_user();
1589         $this->getDataGenerator()->enrol_user($user3->id, $course->id, $teacherrole->id);
1590         $user4 = $this->getDataGenerator()->create_user();
1591         $this->getDataGenerator()->enrol_user($user4->id, $course->id, $studentrole->id);
1592         $this->getDataGenerator()->enrol_user($user4->id, $course->id, $noeditteacherrole->id);
1594         $group = $this->getDataGenerator()->create_group(array('courseid'=>$course->id));
1595         groups_add_member($group, $user3);
1597         $users = get_role_users($teacherrole->id, $coursecontext);
1598         $this->assertCount(2, $users);
1599         $this->assertArrayHasKey($user1->id, $users);
1600         $this->assertEquals($users[$user1->id]->id, $user1->id);
1601         $this->assertEquals($users[$user1->id]->roleid, $teacherrole->id);
1602         $this->assertEquals($users[$user1->id]->rolename, $teacherrole->name);
1603         $this->assertEquals($users[$user1->id]->roleshortname, $teacherrole->shortname);
1604         $this->assertEquals($users[$user1->id]->rolecoursealias, $teacherrename->name);
1605         $this->assertArrayHasKey($user3->id, $users);
1606         $this->assertEquals($users[$user3->id]->id, $user3->id);
1607         $this->assertEquals($users[$user3->id]->roleid, $teacherrole->id);
1608         $this->assertEquals($users[$user3->id]->rolename, $teacherrole->name);
1609         $this->assertEquals($users[$user3->id]->roleshortname, $teacherrole->shortname);
1610         $this->assertEquals($users[$user3->id]->rolecoursealias, $teacherrename->name);
1612         $users = get_role_users($teacherrole->id, $coursecontext, true);
1613         $this->assertCount(3, $users);
1615         $users = get_role_users($teacherrole->id, $coursecontext, true, '', null, null, '', 2, 1);
1616         $this->assertCount(1, $users);
1618         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber');
1619         $this->assertCount(2, $users);
1620         $this->assertArrayHasKey($user1->id, $users);
1621         $this->assertArrayHasKey($user3->id, $users);
1623         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email');
1624         $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1625         $this->assertCount(2, $users);
1626         $this->assertArrayHasKey($user1->id, $users);
1627         $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
1628         $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
1629         $this->assertArrayHasKey($user3->id, $users);
1630         $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
1631         $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
1633         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id AS id_alias');
1634         $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1635         $this->assertCount(2, $users);
1636         $this->assertArrayHasKey($user1->id, $users);
1637         $this->assertObjectHasAttribute('id_alias', $users[$user1->id]);
1638         $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
1639         $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
1640         $this->assertArrayHasKey($user3->id, $users);
1641         $this->assertObjectHasAttribute('id_alias', $users[$user3->id]);
1642         $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
1643         $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
1645         $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
1646         $this->assertCount(1, $users);
1647         $this->assertArrayHasKey($user3->id, $users);
1649         $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'));
1650         $this->assertCount(1, $users);
1651         $this->assertArrayHasKey($user1->id, $users);
1653         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.id', 'ra.id');
1654         $this->assertDebuggingNotCalled();
1655         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.userid', 'ra.userid');
1656         $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1657             'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1658         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false);
1659         $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1660             'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1661         $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext,
1662             false, 'u.id, u.firstname', 'u.id, u.firstname');
1663         $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1664             'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1665     }
1667     /**
1668      * Test used role query.
1669      */
1670     public function test_get_roles_used_in_context() {
1671         global $DB;
1673         $this->resetAfterTest();
1675         $systemcontext = context_system::instance();
1676         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1677         $course = $this->getDataGenerator()->create_course();
1678         $coursecontext = context_course::instance($course->id);
1679         $otherid = create_role('Other role', 'other', 'Some other role', '');
1680         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1681         $DB->insert_record('role_names', $teacherrename);
1682         $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1683         $DB->insert_record('role_names', $otherrename);
1685         $user1 = $this->getDataGenerator()->create_user();
1686         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1688         $roles = get_roles_used_in_context($coursecontext);
1689         $this->assertCount(1, $roles);
1690         $role = reset($roles);
1691         $roleid = key($roles);
1692         $this->assertEquals($roleid, $role->id);
1693         $this->assertEquals($teacherrole->id, $role->id);
1694         $this->assertSame($teacherrole->name, $role->name);
1695         $this->assertSame($teacherrole->shortname, $role->shortname);
1696         $this->assertEquals($teacherrole->sortorder, $role->sortorder);
1697         $this->assertSame($teacherrename->name, $role->coursealias);
1699         $user2 = $this->getDataGenerator()->create_user();
1700         role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1701         role_assign($otherid, $user2->id, $systemcontext->id);
1703         $roles = get_roles_used_in_context($systemcontext);
1704         $this->assertCount(2, $roles);
1705     }
1707     /**
1708      * Test roles used in course.
1709      */
1710     public function test_get_user_roles_in_course() {
1711         global $DB, $CFG;
1713         $this->resetAfterTest();
1715         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1716         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1717         $managerrole = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
1718         $course = $this->getDataGenerator()->create_course();
1719         $coursecontext = context_course::instance($course->id);
1720         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1721         $DB->insert_record('role_names', $teacherrename);
1723         $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1724         $this->assertTrue(in_array($teacherrole->id, $roleids));
1725         $this->assertTrue(in_array($studentrole->id, $roleids));
1726         $this->assertFalse(in_array($managerrole->id, $roleids));
1728         $user1 = $this->getDataGenerator()->create_user();
1729         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1730         role_assign($studentrole->id, $user1->id, $coursecontext->id);
1731         $user2 = $this->getDataGenerator()->create_user();
1732         role_assign($studentrole->id, $user2->id, $coursecontext->id);
1733         $user3 = $this->getDataGenerator()->create_user();
1734         $user4 = $this->getDataGenerator()->create_user();
1735         role_assign($managerrole->id, $user4->id, $coursecontext->id);
1737         $this->setAdminUser();
1739         $roles = get_user_roles_in_course($user1->id, $course->id);
1740         $this->assertEquals(1, preg_match_all('/,/', $roles, $matches));
1741         $this->assertTrue(strpos($roles, role_get_name($teacherrole, $coursecontext)) !== false);
1743         $roles = get_user_roles_in_course($user2->id, $course->id);
1744         $this->assertEquals(0, preg_match_all('/,/', $roles, $matches));
1745         $this->assertTrue(strpos($roles, role_get_name($studentrole, $coursecontext)) !== false);
1747         $roles = get_user_roles_in_course($user3->id, $course->id);
1748         $this->assertSame('', $roles);
1750         // Managers should be able to see a link to their own role type, given they can assign it in the context.
1751         $this->setUser($user4);
1752         $roles = get_user_roles_in_course($user4->id, $course->id);
1753         $this->assertNotEmpty($roles);
1754         $this->assertEquals(1, count(explode(',', $roles)));
1755         $this->assertTrue(strpos($roles, role_get_name($managerrole, $coursecontext)) !== false);
1757         // Managers should see 2 roles if viewing a user who has been enrolled as a student and a teacher in the course.
1758         $roles = get_user_roles_in_course($user1->id, $course->id);
1759         $this->assertEquals(2, count(explode(',', $roles)));
1760         $this->assertTrue(strpos($roles, role_get_name($studentrole, $coursecontext)) !== false);
1761         $this->assertTrue(strpos($roles, role_get_name($teacherrole, $coursecontext)) !== false);
1763         // Students should not see the manager role if viewing a manager's profile.
1764         $this->setUser($user2);
1765         $roles = get_user_roles_in_course($user4->id, $course->id);
1766         $this->assertEmpty($roles); // Should see 0 roles on the manager's profile.
1767         $this->assertFalse(strpos($roles, role_get_name($managerrole, $coursecontext)) !== false);
1768     }
1770     /**
1771      * Test get_user_roles and get_users_roles
1772      */
1773     public function test_get_user_roles() {
1774         global $DB, $CFG;
1776         $this->resetAfterTest();
1778         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1779         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1780         $course = $this->getDataGenerator()->create_course();
1781         $coursecontext = context_course::instance($course->id);
1782         $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1783         $DB->insert_record('role_names', $teacherrename);
1785         $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1787         $user1 = $this->getDataGenerator()->create_user();
1788         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1789         role_assign($studentrole->id, $user1->id, $coursecontext->id);
1790         $user2 = $this->getDataGenerator()->create_user();
1791         role_assign($studentrole->id, $user2->id, $coursecontext->id);
1792         $user3 = $this->getDataGenerator()->create_user();
1794         $u1roles = get_user_roles($coursecontext, $user1->id);
1796         $u2roles = get_user_roles($coursecontext, $user2->id);
1798         $allroles = get_users_roles($coursecontext, [], false);
1799         $specificuserroles = get_users_roles($coursecontext, [$user1->id, $user2->id]);
1800         $this->assertEquals($u1roles, $allroles[$user1->id]);
1801         $this->assertEquals($u1roles, $specificuserroles[$user1->id]);
1802         $this->assertEquals($u2roles, $allroles[$user2->id]);
1803         $this->assertEquals($u2roles, $specificuserroles[$user2->id]);
1804     }
1806     /**
1807      * Test has_capability(), has_any_capability() and has_all_capabilities().
1808      */
1809     public function test_has_capability_and_friends() {
1810         global $DB;
1812         $this->resetAfterTest();
1814         $course = $this->getDataGenerator()->create_course();
1815         $coursecontext = context_course::instance($course->id);
1816         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1817         $teacher = $this->getDataGenerator()->create_user();
1818         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1819         $admin = $DB->get_record('user', array('username'=>'admin'));
1821         // Note: Here are used default capabilities, the full test is in permission evaluation bellow,
1822         // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
1824         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupsection')));
1825         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse')));
1826         $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/site:approvecourse')));
1828         $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse', 'moodle/site:approvecourse');
1829         $sc = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
1831         $this->setUser(0);
1832         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext));
1833         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext));
1834         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1835         $this->assertFalse(has_any_capability($sca, $coursecontext));
1836         $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1838         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $teacher));
1839         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $teacher));
1840         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $teacher));
1841         $this->assertTrue(has_any_capability($sca, $coursecontext, $teacher));
1842         $this->assertTrue(has_all_capabilities($sc, $coursecontext, $teacher));
1843         $this->assertFalse(has_all_capabilities($sca, $coursecontext, $teacher));
1845         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $admin));
1846         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $admin));
1847         $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext, $admin));
1848         $this->assertTrue(has_any_capability($sca, $coursecontext, $admin));
1849         $this->assertTrue(has_all_capabilities($sc, $coursecontext, $admin));
1850         $this->assertTrue(has_all_capabilities($sca, $coursecontext, $admin));
1852         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, $admin, false));
1853         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, $admin, false));
1854         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $admin, false));
1855         $this->assertFalse(has_any_capability($sca, $coursecontext, $admin, false));
1856         $this->assertFalse(has_all_capabilities($sc, $coursecontext, $admin, false));
1857         $this->assertFalse(has_all_capabilities($sca, $coursecontext, $admin, false));
1859         $this->setUser($teacher);
1860         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1861         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1862         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1863         $this->assertTrue(has_any_capability($sca, $coursecontext));
1864         $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1865         $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1867         $this->setAdminUser();
1868         $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1869         $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1870         $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext));
1871         $this->assertTrue(has_any_capability($sca, $coursecontext));
1872         $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1873         $this->assertTrue(has_all_capabilities($sca, $coursecontext));
1875         $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, 0));
1876         $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, 0));
1877         $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, 0));
1878         $this->assertFalse(has_any_capability($sca, $coursecontext, 0));
1879         $this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
1880     }
1882     /**
1883      * Test that assigning a fake cap does not return.
1884      */
1885     public function test_fake_capability() {
1886         global $DB;
1888         $this->resetAfterTest();
1890         $course = $this->getDataGenerator()->create_course();
1891         $coursecontext = context_course::instance($course->id);
1892         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1893         $teacher = $this->getDataGenerator()->create_user();
1895         $fakecapname = 'moodle/fake:capability';
1897         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1898         $admin = $DB->get_record('user', array('username' => 'admin'));
1900         // Test a capability which does not exist.
1901         // Note: Do not use assign_capability because it will not allow fake caps.
1902         $DB->insert_record('role_capabilities', (object) [
1903             'contextid' => $coursecontext->id,
1904             'roleid' => $teacherrole->id,
1905             'capability' => $fakecapname,
1906             'permission' => CAP_ALLOW,
1907             'timemodified' => time(),
1908             'modifierid' => 0,
1909         ]);
1911         // Check `has_capability`.
1912         $this->assertFalse(has_capability($fakecapname, $coursecontext, $teacher));
1913         $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
1914         $this->assertFalse(has_capability($fakecapname, $coursecontext, $admin));
1915         $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
1917         // Check `get_with_capability_sql` (with uses `get_with_capability_join`).
1918         list($sql, $params) = get_with_capability_sql($coursecontext, $fakecapname);
1919         $users = $DB->get_records_sql($sql, $params);
1921         $this->assertFalse(array_key_exists($teacher->id, $users));
1922         $this->assertFalse(array_key_exists($admin->id, $users));
1924         // Check `get_users_by_capability`.
1925         $users = get_users_by_capability($coursecontext, $fakecapname);
1927         $this->assertFalse(array_key_exists($teacher->id, $users));
1928         $this->assertFalse(array_key_exists($admin->id, $users));
1929     }
1931     /**
1932      * Test that assigning a fake cap does not return.
1933      */
1934     public function test_fake_capability_assign() {
1935         global $DB;
1937         $this->resetAfterTest();
1939         $course = $this->getDataGenerator()->create_course();
1940         $coursecontext = context_course::instance($course->id);
1941         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1942         $teacher = $this->getDataGenerator()->create_user();
1944         $capability = 'moodle/fake:capability';
1946         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1947         $admin = $DB->get_record('user', array('username' => 'admin'));
1949         $this->expectException('coding_exception');
1950         $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
1951         assign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
1952     }
1954     /**
1955      * Test that assigning a fake cap does not return.
1956      */
1957     public function test_fake_capability_unassign() {
1958         global $DB;
1960         $this->resetAfterTest();
1962         $course = $this->getDataGenerator()->create_course();
1963         $coursecontext = context_course::instance($course->id);
1964         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1965         $teacher = $this->getDataGenerator()->create_user();
1967         $capability = 'moodle/fake:capability';
1969         role_assign($teacherrole->id, $teacher->id, $coursecontext);
1970         $admin = $DB->get_record('user', array('username' => 'admin'));
1972         $this->expectException('coding_exception');
1973         $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
1974         unassign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
1975     }
1977     /**
1978      * Test that the caching in get_role_definitions() and get_role_definitions_uncached()
1979      * works as intended.
1980      */
1981     public function test_role_definition_caching() {
1982         global $DB;
1984         $this->resetAfterTest();
1986         // Get some role ids.
1987         $authenticatedrole = $DB->get_record('role', array('shortname' => 'user'), '*', MUST_EXIST);
1988         $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1989         $emptyroleid = create_role('No capabilities', 'empty', 'A role with no capabilties');
1990         $course = $this->getDataGenerator()->create_course();
1991         $coursecontext = context_course::instance($course->id);
1993         // Instantiate the cache instance, since that does DB queries (get_config)
1994         // and we don't care about those.
1995         cache::make('core', 'roledefs');
1997         // One database query is not necessarily one database read, it seems. Find out how many.
1998         $startdbreads = $DB->perf_get_reads();
1999         $rs = $DB->get_recordset('user');
2000         $rs->close();
2001         $readsperquery = $DB->perf_get_reads() - $startdbreads;
2003         // Now load some role definitions, and check when it queries the database.
2005         // Load the capabilities for two roles. Should be one query.
2006         $startdbreads = $DB->perf_get_reads();
2007         get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2008         $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2010         // Load the capabilities for same two roles. Should not query the DB.
2011         $startdbreads = $DB->perf_get_reads();
2012         get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2013         $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2015         // Include a third role. Should do one DB query.
2016         $startdbreads = $DB->perf_get_reads();
2017         get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2018         $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2020         // Repeat call. No DB queries.
2021         $startdbreads = $DB->perf_get_reads();
2022         get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2023         $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2025         // Alter a role.
2026         role_change_permission($studentrole->id, $coursecontext, 'moodle/course:tag', CAP_ALLOW);
2028         // Should now know to do one query.
2029         $startdbreads = $DB->perf_get_reads();
2030         get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2031         $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2033         // Now clear the in-memory cache, and verify that it does not query the DB.
2034         // Cannot use accesslib_clear_all_caches_for_unit_testing since that also
2035         // clears the MUC cache.
2036         global $ACCESSLIB_PRIVATE;
2037         $ACCESSLIB_PRIVATE->cacheroledefs = array();
2039         // Get all roles. Should not need the DB.
2040         $startdbreads = $DB->perf_get_reads();
2041         get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2042         $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2043     }
2045     /**
2046      * Tests get_user_capability_course() which checks a capability across all courses.
2047      */
2048     public function test_get_user_capability_course() {
2049         global $CFG, $USER;
2051         $this->resetAfterTest();
2053         $generator = $this->getDataGenerator();
2054         $cap = 'moodle/course:view';
2056         // The structure being created here is this:
2057         //
2058         // All tests work with the single capability 'moodle/course:view'.
2059         //
2060         //             ROLE DEF/OVERRIDE                        ROLE ASSIGNS
2061         //    Role:  Allow    Prohib    Empty   Def user      u1  u2  u3  u4   u5  u6  u7  u8
2062         // System    ALLOW    PROHIBIT                            A   E   A+E
2063         //   cat1                       ALLOW
2064         //     C1                               (ALLOW)                            P
2065         //     C2             ALLOW                                                    E   P
2066         //     cat2                     PREVENT
2067         //       C3                     ALLOW                                      E
2068         //       C4
2069         //   Misc.                                                             A
2070         //     C5    PREVENT                                                       A
2071         //     C6                       PROHIBIT
2072         //
2073         // Front-page and guest role stuff from the end of this test not included in the diagram.
2075         // Create a role which allows course:view and one that prohibits it, and one neither.
2076         $allowroleid = $generator->create_role();
2077         $prohibitroleid = $generator->create_role();
2078         $emptyroleid = $generator->create_role();
2079         $systemcontext = context_system::instance();
2080         assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
2081         assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
2083         // Create two categories (nested).
2084         $cat1 = $generator->create_category();
2085         $cat2 = $generator->create_category(['parent' => $cat1->id]);
2087         // Create six courses - two in cat1, two in cat2, and two in default category.
2088         // Shortnames are used for a sorting test. Otherwise they are not significant.
2089         $c1 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Z']);
2090         $c2 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Y']);
2091         $c3 = $generator->create_course(['category' => $cat2->id, 'shortname' => 'X']);
2092         $c4 = $generator->create_course(['category' => $cat2->id]);
2093         $c5 = $generator->create_course();
2094         $c6 = $generator->create_course();
2096         // Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
2097         assign_capability($cap, CAP_ALLOW, $emptyroleid,
2098                 context_coursecat::instance($cat1->id)->id);
2099         assign_capability($cap, CAP_PREVENT, $emptyroleid,
2100                 context_coursecat::instance($cat2->id)->id);
2102         // Course overrides: in C5, allow role is prevented; in C6, empty role is prohibited; in
2103         // C3, empty role is allowed.
2104         assign_capability($cap, CAP_PREVENT, $allowroleid,
2105                 context_course::instance($c5->id)->id);
2106         assign_capability($cap, CAP_PROHIBIT, $emptyroleid,
2107                 context_course::instance($c6->id)->id);
2108         assign_capability($cap, CAP_ALLOW, $emptyroleid,
2109                 context_course::instance($c3->id)->id);
2110         assign_capability($cap, CAP_ALLOW, $prohibitroleid,
2111                 context_course::instance($c2->id)->id);
2113         // User 1 has no roles except default user role.
2114         $u1 = $generator->create_user();
2116         // It returns false (annoyingly) if there are no courses.
2117         $this->assertFalse(get_user_capability_course($cap, $u1->id, true, '', 'id'));
2119         // Final override: in C1, default user role is allowed.
2120         assign_capability($cap, CAP_ALLOW, $CFG->defaultuserroleid,
2121                 context_course::instance($c1->id)->id);
2123         // Should now get C1 only.
2124         $courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
2125         $this->assert_course_ids([$c1->id], $courses);
2127         // User 2 has allow role (system wide).
2128         $u2 = $generator->create_user();
2129         role_assign($allowroleid, $u2->id, $systemcontext->id);
2131         // Should get everything except C5.
2132         $courses = get_user_capability_course($cap, $u2->id, true, '', 'id');
2133         $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c6->id], $courses);
2135         // User 3 has empty role (system wide).
2136         $u3 = $generator->create_user();
2137         role_assign($emptyroleid, $u3->id, $systemcontext->id);
2139         // Should get cat 1 courses but not cat2, except C3.
2140         $courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
2141         $this->assert_course_ids([$c1->id, $c2->id, $c3->id], $courses);
2143         // User 4 has allow and empty role (system wide).
2144         $u4 = $generator->create_user();
2145         role_assign($allowroleid, $u4->id, $systemcontext->id);
2146         role_assign($emptyroleid, $u4->id, $systemcontext->id);
2148         // Should get everything except C5 and C6.
2149         $courses = get_user_capability_course($cap, $u4->id, true, '', 'id');
2150         $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id], $courses);
2152         // User 5 has allow role in default category only.
2153         $u5 = $generator->create_user();
2154         role_assign($allowroleid, $u5->id, context_coursecat::instance($c5->category)->id);
2156         // Should get C1 and the default category courses but not C5.
2157         $courses = get_user_capability_course($cap, $u5->id, true, '', 'id');
2158         $this->assert_course_ids([$c1->id, $c6->id], $courses);
2160         // User 6 has a bunch of course roles: prohibit role in C1, empty role in C3, allow role in
2161         // C6.
2162         $u6 = $generator->create_user();
2163         role_assign($prohibitroleid, $u6->id, context_course::instance($c1->id)->id);
2164         role_assign($emptyroleid, $u6->id, context_course::instance($c3->id)->id);
2165         role_assign($allowroleid, $u6->id, context_course::instance($c5->id)->id);
2167         // Should get C3 only because the allow role is prevented in C5.
2168         $courses = get_user_capability_course($cap, $u6->id, true, '', 'id');
2169         $this->assert_course_ids([$c3->id], $courses);
2171         // User 7 has empty role in C2.
2172         $u7 = $generator->create_user();
2173         role_assign($emptyroleid, $u7->id, context_course::instance($c2->id)->id);
2175         // Should get C1 by the default user role override, and C2 by the cat1 level override.
2176         $courses = get_user_capability_course($cap, $u7->id, true, '', 'id');
2177         $this->assert_course_ids([$c1->id, $c2->id], $courses);
2179         // User 8 has prohibit role as system context, to verify that prohibits can't be overridden.
2180         $u8 = $generator->create_user();
2181         role_assign($prohibitroleid, $u8->id, context_course::instance($c2->id)->id);
2183         // Should get C1 by the default user role override, no other courses because the prohibit cannot be overridden.
2184         $courses = get_user_capability_course($cap, $u8->id, true, '', 'id');
2185         $this->assert_course_ids([$c1->id], $courses);
2187         // Admin user gets everything....
2188         $courses = get_user_capability_course($cap, get_admin()->id, true, '', 'id');
2189         $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c5->id, $c6->id],
2190                 $courses);
2192         // Unless you turn off doanything, when it only has the things a user with no role does.
2193         $courses = get_user_capability_course($cap, get_admin()->id, false, '', 'id');
2194         $this->assert_course_ids([$c1->id], $courses);
2196         // Using u3 as an example, test the limit feature.
2197         $courses = get_user_capability_course($cap, $u3->id, true, '', 'id', 2);
2198         $this->assert_course_ids([$c1->id, $c2->id], $courses);
2200         // Check sorting.
2201         $courses = get_user_capability_course($cap, $u3->id, true, '', 'shortname');
2202         $this->assert_course_ids([$c3->id, $c2->id, $c1->id], $courses);
2204         // Check returned fields - default.
2205         $courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
2206         $this->assertEquals((object)['id' => $c1->id], $courses[0]);
2208         // Add a selection of fields, including the context ones with special handling.
2209         $courses = get_user_capability_course($cap, $u3->id, true, 'shortname, ctxlevel, ctxdepth, ctxinstance', 'id');
2210         $this->assertEquals((object)['id' => $c1->id, 'shortname' => 'Z', 'ctxlevel' => 50,
2211                 'ctxdepth' => 3, 'ctxinstance' => $c1->id], $courses[0]);
2213         // Test front page role - user 1 has no roles, but if we change the front page role
2214         // definition so that it has our capability, then they should see the front page course.
2215         // as well as C1.
2216         assign_capability($cap, CAP_ALLOW, $CFG->defaultfrontpageroleid, $systemcontext->id);
2217         $courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
2218         $this->assert_course_ids([SITEID, $c1->id], $courses);
2220         // Check that temporary guest access (in this case, given on course 2 for user 1)
2221         // also is included, if it has this capability.
2222         assign_capability($cap, CAP_ALLOW, $CFG->guestroleid, $systemcontext->id);
2223         $this->setUser($u1);
2224         load_temp_course_role(context_course::instance($c2->id), $CFG->guestroleid);
2225         $courses = get_user_capability_course($cap, $USER->id, true, '', 'id');
2226         $this->assert_course_ids([SITEID, $c1->id, $c2->id], $courses);
2227     }
2229     /**
2230      * Extracts an array of course ids to make the above test script shorter.
2231      *
2232      * @param int[] $expected Array of expected course ids
2233      * @param stdClass[] $courses Array of course objects
2234      */
2235     protected function assert_course_ids(array $expected, array $courses) {
2236         $courseids = array_map(function($c) {
2237             return $c->id;
2238         }, $courses);
2239         $this->assertEquals($expected, $courseids);
2240     }
2242     /**
2243      * Test if course creator future capability lookup works.
2244      */
2245     public function test_guess_if_creator_will_have_course_capability() {
2246         global $DB, $CFG, $USER;
2248         $this->resetAfterTest();
2250         $category = $this->getDataGenerator()->create_category();
2251         $course = $this->getDataGenerator()->create_course(array('category'=>$category->id));
2253         $syscontext = context_system::instance();
2254         $categorycontext = context_coursecat::instance($category->id);
2255         $coursecontext = context_course::instance($course->id);
2256         $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
2257         $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
2258         $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST);
2259         $managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
2261         $this->assertEquals($teacherrole->id, $CFG->creatornewroleid);
2263         $creator = $this->getDataGenerator()->create_user();
2264         $manager = $this->getDataGenerator()->create_user();
2265         role_assign($managerrole->id, $manager->id, $categorycontext);
2267         $this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator));
2268         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
2269         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2270         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2271         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2272         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2274         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2275         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager));
2276         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager));
2277         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id));
2278         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id));
2280         $this->assertEquals(0, $USER->id);
2281         $this->assertFalse(has_capability('moodle/course:view', $categorycontext));
2282         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext));
2283         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext));
2284         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext));
2285         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2286         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2288         $this->setUser($manager);
2289         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
2290         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
2291         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
2292         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2293         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2295         $this->setAdminUser();
2296         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
2297         $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
2298         $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
2299         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2300         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2301         $this->setUser(0);
2303         role_assign($creatorrole->id, $creator->id, $categorycontext);
2305         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
2306         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2307         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2308         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2309         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2311         $this->setUser($creator);
2312         $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, null));
2313         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, null));
2314         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, null));
2315         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, null));
2316         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, null));
2317         $this->setUser(0);
2319         set_config('creatornewroleid', $studentrole->id);
2321         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2322         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2323         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2324         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2326         set_config('creatornewroleid', $teacherrole->id);
2328         role_change_permission($managerrole->id, $categorycontext, 'moodle/course:visibility', CAP_PREVENT);
2329         role_assign($creatorrole->id, $manager->id, $categorycontext);
2331         $this->assertTrue(has_capability('moodle/course:view', $categorycontext, $manager));
2332         $this->assertTrue(has_capability('moodle/course:view', $coursecontext, $manager));
2333         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2334         $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
2335         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
2336         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
2337         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
2338         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
2340         role_change_permission($managerrole->id, $categorycontext, 'moodle/course:view', CAP_PREVENT);
2341         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2342         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
2343         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
2344         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
2345         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
2347         $this->getDataGenerator()->enrol_user($manager->id, $course->id, 0);
2349         $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2350         $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
2351         $this->assertTrue(is_enrolled($coursecontext, $manager));
2352         $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
2353         $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
2354         $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
2355         $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
2357         // Test problems.
2359         try {
2360             guess_if_creator_will_have_course_capability('moodle/course:visibility', $syscontext, $creator);
2361             $this->fail('Exception expected when non course/category context passed to guess_if_creator_will_have_course_capability()');
2362         } catch (moodle_exception $e) {
2363             $this->assertInstanceOf('coding_exception', $e);
2364         }
2365     }
2367     /**
2368      * Test require_capability() exceptions.
2369      */
2370     public function test_require_capability() {
2371         $this->resetAfterTest();
2373         $syscontext = context_system::instance();
2375         $this->setUser(0);
2376         $this->assertFalse(has_capability('moodle/site:config', $syscontext));
2377         try {
2378             require_capability('moodle/site:config', $syscontext);
2379             $this->fail('Exception expected from require_capability()');
2380         } catch (moodle_exception $e) {
2381             $this->assertInstanceOf('required_capability_exception', $e);
2382         }
2383         $this->setAdminUser();
2384         $this->assertFalse(has_capability('moodle/site:config', $syscontext, 0));
2385         try {
2386             require_capability('moodle/site:config', $syscontext, 0);
2387             $this->fail('Exception expected from require_capability()');
2388         } catch (moodle_exception $e) {
2389             $this->assertInstanceOf('required_capability_exception', $e);
2390         }
2391         $this->assertFalse(has_capability('moodle/site:config', $syscontext, null, false));
2392         try {
2393             require_capability('moodle/site:config', $syscontext, null, false);
2394             $this->fail('Exception expected from require_capability()');
2395         } catch (moodle_exception $e) {
2396             $this->assertInstanceOf('required_capability_exception', $e);
2397         }
2398     }
2400     /**
2401      * Test that enrolled users SQL does not return any values for users in
2402      * other courses.
2403      */
2404     public function test_get_enrolled_sql_different_course() {
2405         global $DB;
2407         $this->resetAfterTest();
2409         $course = $this->getDataGenerator()->create_course();
2410         $context = context_course::instance($course->id);
2411         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2412         $user = $this->getDataGenerator()->create_user();
2414         // This user should not appear anywhere, we're not interested in that context.
2415         $course2 = $this->getDataGenerator()->create_course();
2416         $this->getDataGenerator()->enrol_user($user->id, $course2->id, $student->id);
2418         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2419         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2420         $suspended  = get_suspended_userids($context);
2422         $this->assertFalse(isset($enrolled[$user->id]));
2423         $this->assertFalse(isset($active[$user->id]));
2424         $this->assertFalse(isset($suspended[$user->id]));
2425         $this->assertCount(0, $enrolled);
2426         $this->assertCount(0, $active);
2427         $this->assertCount(0, $suspended);
2428     }
2430     /**
2431      * Test that enrolled users SQL does not return any values for role
2432      * assignments without an enrolment.
2433      */
2434     public function test_get_enrolled_sql_role_only() {
2435         global $DB;
2437         $this->resetAfterTest();
2439         $course = $this->getDataGenerator()->create_course();
2440         $context = context_course::instance($course->id);
2441         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2442         $user = $this->getDataGenerator()->create_user();
2444         // Role assignment is not the same as course enrollment.
2445         role_assign($student->id, $user->id, $context->id);
2447         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2448         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2449         $suspended  = get_suspended_userids($context);
2451         $this->assertFalse(isset($enrolled[$user->id]));
2452         $this->assertFalse(isset($active[$user->id]));
2453         $this->assertFalse(isset($suspended[$user->id]));
2454         $this->assertCount(0, $enrolled);
2455         $this->assertCount(0, $active);
2456         $this->assertCount(0, $suspended);
2457     }
2459     /**
2460      * Test that multiple enrolments for the same user are counted correctly.
2461      */
2462     public function test_get_enrolled_sql_multiple_enrolments() {
2463         global $DB;
2465         $this->resetAfterTest();
2467         $course = $this->getDataGenerator()->create_course();
2468         $context = context_course::instance($course->id);
2469         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2470         $user = $this->getDataGenerator()->create_user();
2472         // Add a suspended enrol.
2473         $selfinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'self'));
2474         $selfplugin = enrol_get_plugin('self');
2475         $selfplugin->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
2476         $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'self', 0, 0, ENROL_USER_SUSPENDED);
2478         // Should be enrolled, but not active - user is suspended.
2479         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2480         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2481         $suspended  = get_suspended_userids($context);
2483         $this->assertTrue(isset($enrolled[$user->id]));
2484         $this->assertFalse(isset($active[$user->id]));
2485         $this->assertTrue(isset($suspended[$user->id]));
2486         $this->assertCount(1, $enrolled);
2487         $this->assertCount(0, $active);
2488         $this->assertCount(1, $suspended);
2490         // Add an active enrol for the user. Any active enrol makes them enrolled.
2491         $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id);
2493         // User should be active now.
2494         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2495         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2496         $suspended  = get_suspended_userids($context);
2498         $this->assertTrue(isset($enrolled[$user->id]));
2499         $this->assertTrue(isset($active[$user->id]));
2500         $this->assertFalse(isset($suspended[$user->id]));
2501         $this->assertCount(1, $enrolled);
2502         $this->assertCount(1, $active);
2503         $this->assertCount(0, $suspended);
2505     }
2507     /**
2508      * Test that enrolled users SQL does not return any values for users
2509      * without a group when $context is not a valid course context.
2510      */
2511     public function test_get_enrolled_sql_userswithoutgroup() {
2512         global $DB;
2514         $this->resetAfterTest();
2516         $systemcontext = context_system::instance();
2517         $course = $this->getDataGenerator()->create_course();
2518         $coursecontext = context_course::instance($course->id);
2519         $user1 = $this->getDataGenerator()->create_user();
2520         $user2 = $this->getDataGenerator()->create_user();
2522         $this->getDataGenerator()->enrol_user($user1->id, $course->id);
2523         $this->getDataGenerator()->enrol_user($user2->id, $course->id);
2525         $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2526         groups_add_member($group, $user1);
2528         $enrolled   = get_enrolled_users($coursecontext);
2529         $this->assertCount(2, $enrolled);
2531         // Get users without any group on the course context.
2532         $enrolledwithoutgroup = get_enrolled_users($coursecontext, '', USERSWITHOUTGROUP);
2533         $this->assertCount(1, $enrolledwithoutgroup);
2534         $this->assertFalse(isset($enrolledwithoutgroup[$user1->id]));
2536         // Get users without any group on the system context (it should throw an exception).
2537         $this->expectException('coding_exception');
2538         get_enrolled_users($systemcontext, '', USERSWITHOUTGROUP);
2539     }
2541     public function get_enrolled_sql_provider() {
2542         return array(
2543             array(
2544                 // Two users who are enrolled.
2545                 'users' => array(
2546                     array(
2547                         'enrolled'  => true,
2548                         'active'    => true,
2549                     ),
2550                     array(
2551                         'enrolled'  => true,
2552                         'active'    => true,
2553                     ),
2554                 ),
2555                 'counts' => array(
2556                     'enrolled'      => 2,
2557                     'active'        => 2,
2558                     'suspended'     => 0,
2559                 ),
2560             ),
2561             array(
2562                 // A user who is suspended.
2563                 'users' => array(
2564                     array(
2565                         'status'    => ENROL_USER_SUSPENDED,
2566                         'enrolled'  => true,
2567                         'suspended' => true,
2568                     ),
2569                 ),
2570                 'counts' => array(
2571                     'enrolled'      => 1,
2572                     'active'        => 0,
2573                     'suspended'     => 1,
2574                 ),
2575             ),
2576             array(
2577                 // One of each.
2578                 'users' => array(
2579                     array(
2580                         'enrolled'  => true,
2581                         'active'    => true,
2582                     ),
2583                     array(
2584                         'status'    => ENROL_USER_SUSPENDED,
2585                         'enrolled'  => true,
2586                         'suspended' => true,
2587                     ),
2588                 ),
2589                 'counts' => array(
2590                     'enrolled'      => 2,
2591                     'active'        => 1,
2592                     'suspended'     => 1,
2593                 ),
2594             ),
2595             array(
2596                 // One user who is not yet enrolled.
2597                 'users' => array(
2598                     array(
2599                         'timestart' => DAYSECS,
2600                         'enrolled'  => true,
2601                         'active'    => false,
2602                         'suspended' => true,
2603                     ),
2604                 ),
2605                 'counts' => array(
2606                     'enrolled'      => 1,
2607                     'active'        => 0,
2608                     'suspended'     => 1,
2609                 ),
2610             ),
2611             array(
2612                 // One user who is no longer enrolled
2613                 'users' => array(
2614                     array(
2615                         'timeend'   => -DAYSECS,
2616                         'enrolled'  => true,
2617                         'active'    => false,
2618                         'suspended' => true,
2619                     ),
2620                 ),
2621                 'counts' => array(
2622                     'enrolled'      => 1,
2623                     'active'        => 0,
2624                     'suspended'     => 1,
2625                 ),
2626             ),
2627             array(
2628                 // One user who is not yet enrolled, and one who is no longer enrolled.
2629                 'users' => array(
2630                     array(
2631                         'timeend'   => -DAYSECS,
2632                         'enrolled'  => true,
2633                         'active'    => false,
2634                         'suspended' => true,
2635                     ),
2636                     array(
2637                         'timestart' => DAYSECS,
2638                         'enrolled'  => true,
2639                         'active'    => false,
2640                         'suspended' => true,
2641                     ),
2642                 ),
2643                 'counts' => array(
2644                     'enrolled'      => 2,
2645                     'active'        => 0,
2646                     'suspended'     => 2,
2647                 ),
2648             ),
2649         );
2650     }
2652     /**
2653      * @dataProvider get_enrolled_sql_provider
2654      */
2655     public function test_get_enrolled_sql_course($users, $counts) {
2656         global $DB;
2658         $this->resetAfterTest();
2660         $course = $this->getDataGenerator()->create_course();
2661         $context = context_course::instance($course->id);
2662         $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2663         $createdusers = array();
2665         foreach ($users as &$userdata) {
2666             $user = $this->getDataGenerator()->create_user();
2667             $userdata['id'] = $user->id;
2669             $timestart  = 0;
2670             $timeend    = 0;
2671             $status     = null;
2672             if (isset($userdata['timestart'])) {
2673                 $timestart = time() + $userdata['timestart'];
2674             }
2675             if (isset($userdata['timeend'])) {
2676                 $timeend = time() + $userdata['timeend'];
2677             }
2678             if (isset($userdata['status'])) {
2679                 $status = $userdata['status'];
2680             }
2682             // Enrol the user in the course.
2683             $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'manual', $timestart, $timeend, $status);
2684         }
2686         // After all users have been enroled, check expectations.
2687         $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2688         $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2689         $suspended  = get_suspended_userids($context);
2691         foreach ($users as $userdata) {
2692             if (isset($userdata['enrolled']) && $userdata['enrolled']) {
2693                 $this->assertTrue(isset($enrolled[$userdata['id']]));
2694             } else {
2695                 $this->assertFalse(isset($enrolled[$userdata['id']]));
2696             }
2698             if (isset($userdata['active']) && $userdata['active']) {
2699                 $this->assertTrue(isset($active[$userdata['id']]));
2700             } else {
2701                 $this->assertFalse(isset($active[$userdata['id']]));
2702             }
2704             if (isset($userdata['suspended']) && $userdata['suspended']) {
2705                 $this->assertTrue(isset($suspended[$userdata['id']]));
2706             } else {
2707                 $this->assertFalse(isset($suspended[$userdata['id']]));
2708             }
2709         }
2711         $this->assertCount($counts['enrolled'],     $enrolled);
2712         $this->assertCount($counts['active'],       $active);
2713         $this->assertCount($counts['suspended'],    $suspended);
2714     }
2716     /**
2717      * A small functional test of permission evaluations.
2718      */
2719     public function test_permission_evaluation() {
2720         global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE;
2722         $this->resetAfterTest();
2724         $generator = $this->getDataGenerator();
2726         // Fill the site with some real data.
2727         $testcategories = array();
2728         $testcourses = array();
2729         $testpages = array();
2730         $testblocks = array();
2731         $allroles = $DB->get_records_menu('role', array(), 'id', 'shortname, id');
2733         $systemcontext = context_system::instance();
2734         $frontpagecontext = context_course::instance(SITEID);
2736         // Add block to system context.
2737         $bi = $generator->create_block('online_users');
2738         context_block::instance($bi->id);
2739         $testblocks[] = $bi->id;
2741         // Some users.
2742         $testusers = array();
2743         for ($i=0; $i<20; $i++) {
2744             $user = $generator->create_user();
2745             $testusers[$i] = $user->id;
2746             $usercontext = context_user::instance($user->id);
2748             // Add block to user profile.
2749             $bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id));
2750             $testblocks[] = $bi->id;
2751         }
2753         // Add block to frontpage.
2754         $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id));
2755         $frontpageblockcontext = context_block::instance($bi->id);
2756         $testblocks[] = $bi->id;
2758         // Add a resource to frontpage.
2759         $page = $generator->create_module('page', array('course'=>$SITE->id));
2760         $testpages[] = $page->cmid;
2761         $frontpagepagecontext = context_module::instance($page->cmid);
2763         // Add block to frontpage resource.
2764         $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id));
2765         $frontpagepageblockcontext = context_block::instance($bi->id);
2766         $testblocks[] = $bi->id;
2768         // Some nested course categories with courses.
2769         $manualenrol = enrol_get_plugin('manual');
2770         $parentcat = 0;
2771         for ($i=0; $i<5; $i++) {
2772             $cat = $generator->create_category(array('parent'=>$parentcat));
2773             $testcategories[] = $cat->id;
2774             $catcontext = context_coursecat::instance($cat->id);
2775             $parentcat = $cat->id;
2777             if ($i >= 4) {
2778                 continue;
2779             }
2781             // Add resource to each category.
2782             $bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id));
2783             context_block::instance($bi->id);
2785             // Add a few courses to each category.
2786             for ($j=0; $j<6; $j++) {
2787                 $course = $generator->create_course(array('category'=>$cat->id));
2788                 $testcourses[] = $course->id;
2789                 $coursecontext = context_course::instance($course->id);
2791                 if ($j >= 5) {
2792                     continue;
2793                 }
2794                 // Add manual enrol instance.
2795                 $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id)));
2797                 // Add block to each course.
2798                 $bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
2799                 $testblocks[] = $bi->id;
2801                 // Add a resource to each course.
2802                 $page = $generator->create_module('page', array('course'=>$course->id));
2803                 $testpages[] = $page->cmid;
2804                 $modcontext = context_module::instance($page->cmid);
2806                 // Add block to each module.
2807                 $bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id));
2808                 $testblocks[] = $bi->id;
2809             }
2810         }
2812         // Make sure all contexts were created properly.
2813         $count = 1; // System.
2814         $count += $DB->count_records('user', array('deleted'=>0));
2815         $count += $DB->count_records('course_categories');
2816         $count += $DB->count_records('course');
2817         $count += $DB->count_records('course_modules');
2818         $count += $DB->count_records('block_instances');
2819         $this->assertEquals($count, $DB->count_records('context'));
2820         $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
2821         $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
2824         // Test context_helper::get_level_name() method.
2826         $levels = context_helper::get_all_levels();
2827         foreach ($levels as $level => $classname) {
2828             $name = context_helper::get_level_name($level);
2829             $this->assertNotEmpty($name);
2830         }
2833         // Test context::instance_by_id(), context_xxx::instance() methods.
2835         $context = context::instance_by_id($frontpagecontext->id);
2836         $this->assertSame(CONTEXT_COURSE, $context->contextlevel);
2837         $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING));
2838         try {
2839             context::instance_by_id(-1);
2840             $this->fail('exception expected');
2841         } catch (moodle_exception $e) {
2842             $this->assertTrue(true);
2843         }
2844         $this->assertInstanceOf('context_system', context_system::instance());
2845         $this->assertInstanceOf('context_coursecat', context_coursecat::instance($testcategories[0]));
2846         $this->assertInstanceOf('context_course', context_course::instance($testcourses[0]));
2847         $this->assertInstanceOf('context_module', context_module::instance($testpages[0]));
2848         $this->assertInstanceOf('context_block', context_block::instance($testblocks[0]));
2850         $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING));
2851         $this->assertFalse(context_course::instance(-1, IGNORE_MISSING));
2852         $this->assertFalse(context_module::instance(-1, IGNORE_MISSING));
2853         $this->assertFalse(context_block::instance(-1, IGNORE_MISSING));
2854         try {
2855             context_coursecat::instance(-1);
2856             $this->fail('exception expected');
2857         } catch (moodle_exception $e) {
2858             $this->assertTrue(true);
2859         }
2860         try {
2861             context_course::instance(-1);
2862             $this->fail('exception expected');
2863         } catch (moodle_exception $e) {
2864             $this->assertTrue(true);
2865         }
2866         try {
2867             context_module::instance(-1);
2868             $this->fail('exception expected');
2869         } catch (moodle_exception $e) {
2870             $this->assertTrue(true);
2871         }
2872         try {
2873             context_block::instance(-1);
2874             $this->fail('exception expected');
2875         } catch (moodle_exception $e) {
2876             $this->assertTrue(true);
2877         }
2880         // Test $context->get_url(), $context->get_context_name(), $context->get_capabilities() methods.
2882         $testcontexts = array();
2883         $testcontexts[CONTEXT_SYSTEM]    = context_system::instance();
2884         $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]);
2885         $testcontexts[CONTEXT_COURSE]    = context_course::instance($testcourses[0]);
2886         $testcontexts[CONTEXT_MODULE]    = context_module::instance($testpages[0]);
2887         $testcontexts[CONTEXT_BLOCK]     = context_block::instance($testblocks[0]);
2889         foreach ($testcontexts as $context) {
2890             $name = $context->get_context_name(true, true);
2891             $this->assertNotEmpty($name);
2893             $this->assertInstanceOf('moodle_url', $context->get_url());
2895             $caps = $context->get_capabilities();
2896             $this->assertTrue(is_array($caps));
2897             foreach ($caps as $cap) {
2898                 $cap = (array)$cap;
2899                 $this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask'));
2900             }
2901         }
2902         unset($testcontexts);
2904         // Test $context->get_course_context() method.
2906         $this->assertFalse($systemcontext->get_course_context(false));
2907         try {
2908             $systemcontext->get_course_context();
2909             $this->fail('exception expected');
2910         } catch (moodle_exception $e) {
2911             $this->assertInstanceOf('coding_exception', $e);
2912         }
2913         $context = context_coursecat::instance($testcategories[0]);
2914         $this->assertFalse($context->get_course_context(false));
2915         try {
2916             $context->get_course_context();
2917             $this->fail('exception expected');
2918         } catch (moodle_exception $e) {
2919             $this->assertInstanceOf('coding_exception', $e);
2920         }
2921         $this->assertEquals($frontpagecontext, $frontpagecontext->get_course_context(true));
2922         $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_course_context(true));
2923         $this->assertEquals($frontpagecontext, $frontpagepageblockcontext->get_course_context(true));
2926         // Test $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() methods.
2928         $userid = reset($testusers);
2929         $usercontext = context_user::instance($userid);
2930         $this->assertEquals($systemcontext, $usercontext->get_parent_context());
2931         $this->assertEquals(array($systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts());
2932         $this->assertEquals(array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts(true));
2934         $this->assertEquals(array(), $systemcontext->get_parent_contexts());
2935         $this->assertEquals(array($systemcontext->id=>$systemcontext), $systemcontext->get_parent_contexts(true));
2936         $this->assertEquals(array(), $systemcontext->get_parent_context_ids());
2937         $this->assertEquals(array($systemcontext->id), $systemcontext->get_parent_context_ids(true));
2938         $this->assertEquals(array(), $systemcontext->get_parent_context_paths());
2939         $this->assertEquals(array($systemcontext->id => $systemcontext->path), $systemcontext->get_parent_context_paths(true));
2941         $this->assertEquals($systemcontext, $frontpagecontext->get_parent_context());
2942         $this->assertEquals(array($systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts());
2943         $this->assertEquals(array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts(true));
2944         $this->assertEquals(array($systemcontext->id), $frontpagecontext->get_parent_context_ids());
2945         $this->assertEquals(array($frontpagecontext->id, $systemcontext->id), $frontpagecontext->get_parent_context_ids(true));
2946         $this->assertEquals(array($systemcontext->id => $systemcontext->path), $frontpagecontext->get_parent_context_paths());
2947         $expected = array($systemcontext->id => $systemcontext->path, $frontpagecontext->id => $frontpagecontext->path);
2948         $this->assertEquals($expected, $frontpagecontext->get_parent_context_paths(true));
2950         $this->assertFalse($systemcontext->get_parent_context());
2951         $frontpagecontext = context_course::instance($SITE->id);
2952         $parent = $systemcontext;
2953         foreach ($testcategories as $catid) {
2954             $catcontext = context_coursecat::instance($catid);
2955             $this->assertEquals($parent, $catcontext->get_parent_context());
2956             $parent = $catcontext;
2957         }
2958         $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_parent_context());
2959         $this->assertEquals($frontpagecontext, $frontpageblockcontext->get_parent_context());
2960         $this->assertEquals($frontpagepagecontext, $frontpagepageblockcontext->get_parent_context());
2963         // Test $context->get_child_contexts() method.
2965         $children = $systemcontext->get_child_contexts();
2966         $this->resetDebugging();
2967         $this->assertEquals(count($children)+1, $DB->count_records('context'));
2969         $context = context_coursecat::instance($testcategories[3]);
2970         $children = $context->get_child_contexts();
2971         $countcats    = 0;
2972         $countcourses = 0;
2973         $countblocks  = 0;
2974         foreach ($children as $child) {
2975             if ($child->contextlevel == CONTEXT_COURSECAT) {
2976                 $countcats++;
2977             }
2978             if ($child->contextlevel == CONTEXT_COURSE) {
2979                 $countcourses++;
2980             }
2981             if ($child->contextlevel == CONTEXT_BLOCK) {
2982                 $countblocks++;
2983             }
2984         }
2985         $this->assertCount(8, $children);
2986         $this->assertEquals(1, $countcats);
2987         $this->assertEquals(6, $countcourses);
2988         $this->assertEquals(1, $countblocks);
2990         $context = context_course::instance($testcourses[2]);
2991         $children = $context->get_child_contexts();
2993         $context = context_module::instance($testpages[3]);
2994         $children = $context->get_child_contexts();
2995         $this->assertCount(1, $children);
2997         $context = context_block::instance($testblocks[1]);
2998         $children = $context->get_child_contexts();
2999         $this->assertCount(0, $children);
3001         unset($children);
3002         unset($countcats);
3003         unset($countcourses);
3004         unset($countblocks);
3007         // Test context_helper::reset_caches() method.
3009         context_helper::reset_caches();
3010         $this->assertEquals(0, context_inspection::test_context_cache_size());
3011         context_course::instance($SITE->id);
3012         $this->assertEquals(1, context_inspection::test_context_cache_size());
3015         // Test context preloading.
3017         context_helper::reset_caches();
3018         $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')."
3019                   FROM {context} c
3020                  WHERE c.contextlevel <> ".CONTEXT_SYSTEM;
3021         $records = $DB->get_records_sql($sql);
3022         $firstrecord = reset($records);
3023         $columns = context_helper::get_preload_record_columns('c');
3024         $firstrecord = (array)$firstrecord;
3025         $this->assertSame(array_keys($firstrecord), array_values($columns));
3026         context_helper::reset_caches();
3027         foreach ($records as $record) {
3028             context_helper::preload_from_record($record);
3029             $this->assertEquals(new stdClass(), $record);
3030         }
3031         $this->assertEquals(count($records), context_inspection::test_context_cache_size());
3032         unset($records);
3033         unset($columns);
3035         context_helper::reset_caches();
3036         context_helper::preload_course($SITE->id);
3037         $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
3038         $this->assertEquals(3 + $numfrontpagemodules, context_inspection::test_context_cache_size()); // Depends on number of default blocks.
3040         // Test assign_capability(), unassign_capability() functions.
3042         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3043         $this->assertFalse($rc);
3044         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id);
3045         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3046         $this->assertEquals(CAP_ALLOW, $rc->permission);
3047         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id);
3048         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3049         $this->assertEquals(CAP_ALLOW, $rc->permission);
3050         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true);
3051         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3052         $this->assertEquals(CAP_PREVENT, $rc->permission);
3054         assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext);
3055         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3056         $this->assertFalse($rc);
3057         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext);
3058         unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true);
3059         $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3060         $this->assertFalse($rc);
3061         unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true);
3062         unset($rc);
3064         accesslib_clear_all_caches_for_unit_testing(); // Must be done after assign_capability().
3067         // Test role_assign(), role_unassign(), role_unassign_all() functions.
3069         $context = context_course::instance($testcourses[1]);
3070         $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3071         role_assign($allroles['teacher'], $testusers[1], $context->id);
3072         role_assign($allroles['teacher'], $testusers[2], $context->id);
3073         role_assign($allroles['manager'], $testusers[1], $context->id);
3074         $this->assertEquals(3, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3075         role_unassign($allroles['teacher'], $testusers[1], $context->id);
3076         $this->assertEquals(2, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3077         role_unassign_all(array('contextid'=>$context->id));
3078         $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3079         unset($context);
3081         accesslib_clear_all_caches_for_unit_testing(); // Just in case.
3084         // Test has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends functions.
3086         $adminid = get_admin()->id;
3087         $guestid = $CFG->siteguest;
3089         // Enrol some users into some courses.
3090         $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST);
3091         $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST);
3092         $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id');
3093         $cm1 = reset($cms);
3094         $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id');
3095         $block1 = reset($blocks);
3096         $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id));
3097         $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id));
3098         for ($i=0; $i<9; $i++) {
3099             $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']);
3100         }
3101         $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']);
3102         $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']);
3104         for ($i=10; $i<15; $i++) {
3105             $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']);
3106         }
3107         $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']);
3109         // Add tons of role assignments - the more the better.
3110         role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2]));
3111         role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1]));
3112         role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id));
3113         role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id));
3114         role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id));
3115         role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id));
3116         role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id));
3117         role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id));
3119         role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id));
3120         role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id));
3122         // Add tons of overrides - the more the better.
3123         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true);
3124         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true);
3125         assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true);
3126         assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true);
3127         assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true);
3129         assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true);
3130         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true);
3131         assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true);
3132         assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true);
3133         assign_capability('mod/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
3135         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true);
3136         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true);
3137         assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true);
3138         assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true);
3140         assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true);
3142         // Prepare for prohibit test.
3143         role_assign($allroles['editingteacher'], $testusers[19], context_system::instance());
3144         role_assign($allroles['teacher'], $testusers[19], context_course::instance($testcourses[17]));
3145         role_assign($allroles['editingteacher'], $testusers[19], context_course::instance($testcourses[17]));
3146         assign_capability('moodle/course:update', CAP_PROHIBIT, $allroles['teacher'], context_course::instance($testcourses[17]), true);
3148         accesslib_clear_all_caches_for_unit_testing(); /// Must be done after assign_capability().
3150         // Extra tests for guests and not-logged-in users because they can not be verified by cross checking
3151         // with get_users_by_capability() where they are ignored.
3152         $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid));
3153         $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid));
3154         $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid));
3155         $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid));
3157         $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0));
3158         $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0));
3159         $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0));
3160         $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0));
3162         $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11]));
3163         $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11]));
3164         $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11]));
3165         $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11]));
3167         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9]));
3168         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9]));
3169         $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9]));
3171         // Test prohibits.
3172         $this->assertTrue(has_capability('moodle/course:update', context_system::instance(), $testusers[19]));
3173         $ids = get_users_by_capability(context_system::instance(), 'moodle/course:update', 'u.id');
3174         $this->assertArrayHasKey($testusers[19], $ids);
3175         $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[17]), $testusers[19]));
3176         $ids = get_users_by_capability(context_course::instance($testcourses[17]), 'moodle/course:update', 'u.id');
3177         $this->assertArrayNotHasKey($testusers[19], $ids);
3179         // Test the list of enrolled users.
3180         $coursecontext = context_course::instance($course1->id);
3181         $enrolled = get_enrolled_users($coursecontext);
3182         $this->assertCount(10, $enrolled);
3183         for ($i=0; $i<10; $i++) {
3184             $this->assertTrue(isset($enrolled[$testusers[$i]]));
3185         }
3186         $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update');
3187         $this->assertCount(1, $enrolled);
3188         $this->assertTrue(isset($enrolled[$testusers[9]]));
3189         unset($enrolled);
3191         // Role switching.
3192         $userid = $testusers[9];
3193         $USER = $DB->get_record('user', array('id'=>$userid));
3194         load_all_capabilities();
3195         $coursecontext = context_course::instance($course1->id);
3196         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
3197         $this->assertFalse(is_role_switched($course1->id));
3198         role_switch($allroles['student'], $coursecontext);
3199         $this->assertTrue(is_role_switched($course1->id));
3200         $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
3201         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
3202         reload_all_capabilities();
3203         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
3204         role_switch(0, $coursecontext);
3205         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
3206         $userid = $adminid;
3207         $USER = $DB->get_record('user', array('id'=>$userid));
3208         load_all_capabilities();
3209         $coursecontext = context_course::instance($course1->id);
3210         $blockcontext = context_block::instance($block1->id);
3211         $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
3212         role_switch($allroles['student'], $coursecontext);
3213         $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
3214         $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
3215         reload_all_capabilities();
3216         $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
3217         load_all_capabilities();
3218         $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
3220         // Temp course role for enrol.
3221         $DB->delete_records('cache_flags', array()); // This prevents problem with dirty contexts immediately resetting the temp role - this is a known problem...
3222         $userid = $testusers[5];
3223         $roleid = $allroles['editingteacher'];
3224         $USER = $DB->get_record('user', array('id'=>$userid));
3225         load_all_capabilities();
3226         $coursecontext = context_course::instance($course1->id);
3227         $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
3228         $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid]));
3229         load_temp_course_role($coursecontext, $roleid);
3230         $this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid);
3231         $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
3232         remove_temp_course_roles($coursecontext);
3233         $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
3234         load_temp_course_role($coursecontext, $roleid);
3235         reload_all_capabilities();
3236         $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
3237         $USER = new stdClass();
3238         $USER->id = 0;
3240         // Now cross check has_capability() with get_users_by_capability(), each using different code paths,
3241         // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong,
3242         // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users).
3243         $contexts = $DB->get_records('context', array(), 'id');
3244         $contexts = array_values($contexts);
3245         $capabilities = $DB->get_records('capabilities', array(), 'id');
3246         $capabilities = array_values($capabilities);
3247         $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']);
3248         $userids = array_values($testusers);
3249         $userids[] = get_admin()->id;
3251         if (!PHPUNIT_LONGTEST) {
3252             $contexts = array_slice($contexts, 0, 10);
3253             $capabilities = array_slice($capabilities, 0, 5);
3254             $userids = array_slice($userids, 0, 5);
3255         }
3257      &nb