Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * 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
 */
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
/**
29
 * Functional test for accesslib.php
30
 *
31
 * Note: execution may take many minutes especially on slower servers.
32
 */
1441 ariadna 33
final class accesslib_test extends advanced_testcase {
1 efrain 34
 
35
    /**
36
     * Setup.
37
     */
38
    protected function setUp(): void {
39
        parent::setUp();
40
        $this->resetAfterTest();
41
        // Turn off the course welcome message, so we can easily test other messages.
42
        set_config('sendcoursewelcomemessage', 0, 'enrol_manual');
43
    }
44
 
45
    /**
46
     * Verify comparison of context instances in phpunit asserts.
47
     */
11 efrain 48
    public function test_context_comparisons(): void {
1 efrain 49
        $frontpagecontext1 = context_course::instance(SITEID);
50
        context_helper::reset_caches();
51
        $frontpagecontext2 = context_course::instance(SITEID);
52
        $this->assertEquals($frontpagecontext1, $frontpagecontext2);
53
 
54
        $user1 = context_user::instance(1);
55
        $user2 = context_user::instance(2);
56
        $this->assertNotEquals($user1, $user2);
57
    }
58
 
59
    /**
60
     * Test resetting works.
61
     *
62
     * @covers ::accesslib_clear_all_caches_for_unit_testing
63
     */
11 efrain 64
    public function test_accesslib_clear_all_caches(): void {
1 efrain 65
        global $ACCESSLIB_PRIVATE;
66
 
67
        $this->resetAfterTest();
68
 
69
        $this->setAdminUser();
70
        load_all_capabilities();
71
 
72
        $this->assertNotEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
73
        accesslib_clear_all_caches_for_unit_testing();
74
        $this->assertEmpty($ACCESSLIB_PRIVATE->dirtycontexts);
75
        $this->assertEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
76
    }
77
 
78
    /**
79
     * Check modifying capability record is not exposed to other code.
80
     */
11 efrain 81
    public function test_capabilities_mutation(): void {
1 efrain 82
        $oldcap = get_capability_info('moodle/site:config');
83
        $cap = get_capability_info('moodle/site:config');
84
        unset($cap->name);
85
        $newcap = get_capability_info('moodle/site:config');
86
 
87
        $this->assertFalse(isset($cap->name));
88
        $this->assertTrue(isset($newcap->name));
89
        $this->assertTrue(isset($oldcap->name));
90
    }
91
 
92
    /**
93
     * Test getting of role access
94
     *
95
     * @covers ::get_role_access
96
     */
11 efrain 97
    public function test_get_role_access(): void {
1 efrain 98
        global $DB;
99
 
100
        $roles = $DB->get_records('role');
101
        foreach ($roles as $role) {
102
            $access = get_role_access($role->id);
103
 
104
            $this->assertTrue(is_array($access));
105
            $this->assertTrue(is_array($access['ra']));
106
            $this->assertFalse(isset($access['rdef']));
107
            $this->assertFalse(isset($access['rdef_count']));
108
            $this->assertFalse(isset($access['loaded']));
109
            $this->assertTrue(isset($access['time']));
110
            $this->assertTrue(is_array($access['rsw']));
111
        }
112
 
113
        // Note: the data is validated in the functional permission evaluation test at the end of this testcase.
114
    }
115
 
116
    /**
117
     * Test getting of guest role.
118
     *
119
     * @covers ::get_guest_role
120
     */
11 efrain 121
    public function test_get_guest_role(): void {
1 efrain 122
        global $CFG;
123
 
124
        $guest = get_guest_role();
125
        $this->assertEquals('guest', $guest->archetype);
126
        $this->assertEquals('guest', $guest->shortname);
127
 
128
        $this->assertEquals($CFG->guestroleid, $guest->id);
129
    }
130
 
131
    /**
132
     * Test if user is admin.
133
     *
134
     * @covers ::is_siteadmin
135
     */
11 efrain 136
    public function test_is_siteadmin(): void {
1 efrain 137
        global $DB, $CFG;
138
 
139
        $this->resetAfterTest();
140
 
141
        $users = $DB->get_records('user');
142
 
143
        foreach ($users as $user) {
144
            $this->setUser(0);
145
            if ($user->username === 'admin') {
146
                $this->assertTrue(is_siteadmin($user));
147
                $this->assertTrue(is_siteadmin($user->id));
148
                $this->setUser($user);
149
                $this->assertTrue(is_siteadmin());
150
                $this->assertTrue(is_siteadmin(null));
151
            } else {
152
                $this->assertFalse(is_siteadmin($user));
153
                $this->assertFalse(is_siteadmin($user->id));
154
                $this->setUser($user);
155
                $this->assertFalse(is_siteadmin());
156
                $this->assertFalse(is_siteadmin(null));
157
            }
158
        }
159
 
160
        // Change the site admin list and check that it still works with
161
        // multiple admins. We do this with userids only (not real user
162
        // accounts) because it makes the test simpler.
163
        $before = $CFG->siteadmins;
164
        set_config('siteadmins', '666,667,668');
165
        $this->assertTrue(is_siteadmin(666));
166
        $this->assertTrue(is_siteadmin(667));
167
        $this->assertTrue(is_siteadmin(668));
168
        $this->assertFalse(is_siteadmin(669));
169
        set_config('siteadmins', '13');
170
        $this->assertTrue(is_siteadmin(13));
171
        $this->assertFalse(is_siteadmin(666));
172
        set_config('siteadmins', $before);
173
    }
174
 
175
    /**
176
     * Test if user is enrolled in a course
177
     *
178
     * @covers ::is_enrolled
179
     */
11 efrain 180
    public function test_is_enrolled(): void {
1 efrain 181
        global $DB;
182
 
183
        $this->resetAfterTest();
184
 
185
        // Generate data.
186
        $user = $this->getDataGenerator()->create_user();
187
        $course = $this->getDataGenerator()->create_course();
188
        $coursecontext = context_course::instance($course->id);
189
        $role = $DB->get_record('role', array('shortname'=>'student'));
190
 
191
        // There should be a manual enrolment as part of the default install.
192
        $plugin = enrol_get_plugin('manual');
193
        $instance = $DB->get_record('enrol', array(
194
            'courseid' => $course->id,
195
            'enrol' => 'manual',
196
        ));
197
        $this->assertNotSame(false, $instance);
198
 
199
        // Enrol the user in the course.
200
        $plugin->enrol_user($instance, $user->id, $role->id);
201
 
202
        // We'll test with the mod/assign:submit capability.
203
        $capability= 'mod/assign:submit';
204
        $this->assertTrue($DB->record_exists('capabilities', array('name' => $capability)));
205
 
206
        // Switch to our user.
207
        $this->setUser($user);
208
 
209
        // Ensure that the user has the capability first.
210
        $this->assertTrue(has_capability($capability, $coursecontext, $user->id));
211
 
212
        // We first test whether the user is enrolled on the course as this
213
        // seeds the cache, then we test for the capability.
214
        $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
215
        $this->assertTrue(is_enrolled($coursecontext, $user, $capability));
216
 
217
        // Prevent the capability for this user role.
218
        assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
219
        $this->assertFalse(has_capability($capability, $coursecontext, $user->id));
220
 
221
        // Again, we seed the cache first by checking initial enrolment,
222
        // and then we test the actual capability.
223
        $this->assertTrue(is_enrolled($coursecontext, $user, '', true));
224
        $this->assertFalse(is_enrolled($coursecontext, $user, $capability));
225
    }
226
 
227
    /**
228
     * Test logged in test.
229
     *
230
     * @covers ::isloggedin
231
     */
11 efrain 232
    public function test_isloggedin(): void {
1 efrain 233
        global $USER;
234
 
235
        $this->resetAfterTest();
236
 
237
        $USER->id = 0;
238
        $this->assertFalse(isloggedin());
239
        $USER->id = 1;
240
        $this->assertTrue(isloggedin());
241
    }
242
 
243
    /**
244
     * Test guest user test.
245
     *
246
     * @covers ::isguestuser
247
     */
11 efrain 248
    public function test_isguestuser(): void {
1 efrain 249
        global $DB;
250
 
251
        $this->resetAfterTest();
252
 
253
        $guest = $DB->get_record('user', array('username'=>'guest'));
254
        $this->setUser(0);
255
        $this->assertFalse(isguestuser());
256
        $this->setAdminUser();
257
        $this->assertFalse(isguestuser());
258
        $this->assertTrue(isguestuser($guest));
259
        $this->assertTrue(isguestuser($guest->id));
260
        $this->setUser($guest);
261
        $this->assertTrue(isguestuser());
262
 
263
        $users = $DB->get_records('user');
264
        foreach ($users as $user) {
265
            if ($user->username === 'guest') {
266
                continue;
267
            }
268
            $this->assertFalse(isguestuser($user));
269
        }
270
    }
271
 
272
    /**
273
     * Test capability riskiness.
274
     *
275
     * @covers ::is_safe_capability
276
     */
11 efrain 277
    public function test_is_safe_capability(): void {
1 efrain 278
        global $DB;
279
        // Note: there is not much to test, just make sure no notices are throw for the most dangerous cap.
280
        $capability = $DB->get_record('capabilities', array('name'=>'moodle/site:config'), '*', MUST_EXIST);
281
        $this->assertFalse(is_safe_capability($capability));
282
    }
283
 
284
    /**
285
     * Test context fetching.
286
     *
287
     * @covers ::get_context_info_array
288
     */
11 efrain 289
    public function test_get_context_info_array(): void {
1 efrain 290
        $this->resetAfterTest();
291
 
292
        $syscontext = context_system::instance();
293
        $user = $this->getDataGenerator()->create_user();
294
        $usercontext = context_user::instance($user->id);
295
        $course = $this->getDataGenerator()->create_course();
296
        $catcontext = context_coursecat::instance($course->category);
297
        $coursecontext = context_course::instance($course->id);
298
        $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
299
        $modcontext = context_module::instance($page->cmid);
300
        $cm = get_coursemodule_from_instance('page', $page->id);
301
        $block1 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
302
        $block1context = context_block::instance($block1->id);
303
        $block2 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$modcontext->id));
304
        $block2context = context_block::instance($block2->id);
305
 
306
        $result = get_context_info_array($syscontext->id);
307
        $this->assertCount(3, $result);
308
        $this->assertEquals($syscontext, $result[0]);
309
        $this->assertNull($result[1]);
310
        $this->assertNull($result[2]);
311
 
312
        $result = get_context_info_array($usercontext->id);
313
        $this->assertCount(3, $result);
314
        $this->assertEquals($usercontext, $result[0]);
315
        $this->assertNull($result[1]);
316
        $this->assertNull($result[2]);
317
 
318
        $result = get_context_info_array($catcontext->id);
319
        $this->assertCount(3, $result);
320
        $this->assertEquals($catcontext, $result[0]);
321
        $this->assertNull($result[1]);
322
        $this->assertNull($result[2]);
323
 
324
        $result = get_context_info_array($coursecontext->id);
325
        $this->assertCount(3, $result);
326
        $this->assertEquals($coursecontext, $result[0]);
327
        $this->assertEquals($course->id, $result[1]->id);
328
        $this->assertSame($course->shortname, $result[1]->shortname);
329
        $this->assertNull($result[2]);
330
 
331
        $result = get_context_info_array($block1context->id);
332
        $this->assertCount(3, $result);
333
        $this->assertEquals($block1context, $result[0]);
334
        $this->assertEquals($course->id, $result[1]->id);
335
        $this->assertEquals($course->shortname, $result[1]->shortname);
336
        $this->assertNull($result[2]);
337
 
338
        $result = get_context_info_array($modcontext->id);
339
        $this->assertCount(3, $result);
340
        $this->assertEquals($modcontext, $result[0]);
341
        $this->assertEquals($course->id, $result[1]->id);
342
        $this->assertSame($course->shortname, $result[1]->shortname);
343
        $this->assertEquals($cm->id, $result[2]->id);
344
 
345
        $result = get_context_info_array($block2context->id);
346
        $this->assertCount(3, $result);
347
        $this->assertEquals($block2context, $result[0]);
348
        $this->assertEquals($course->id, $result[1]->id);
349
        $this->assertSame($course->shortname, $result[1]->shortname);
350
        $this->assertEquals($cm->id, $result[2]->id);
351
    }
352
 
353
    /**
354
     * Test looking for course contacts.
355
     *
356
     * @covers ::has_coursecontact_role
357
     */
11 efrain 358
    public function test_has_coursecontact_role(): void {
1 efrain 359
        global $DB, $CFG;
360
 
361
        $this->resetAfterTest();
362
 
363
        $users = $DB->get_records('user');
364
 
365
        // Nobody is expected to have any course level roles.
366
        $this->assertNotEmpty($CFG->coursecontact);
367
        foreach ($users as $user) {
368
            $this->assertFalse(has_coursecontact_role($user->id));
369
        }
370
 
371
        $user = $this->getDataGenerator()->create_user();
372
        $course = $this->getDataGenerator()->create_course();
373
        $contactroles = preg_split('/,/', $CFG->coursecontact);
374
        $roleid = reset($contactroles);
375
        role_assign($roleid, $user->id, context_course::instance($course->id));
376
        $this->assertTrue(has_coursecontact_role($user->id));
377
    }
378
 
379
    /**
380
     * Test creation of roles.
381
     *
382
     * @covers ::create_role
383
     */
11 efrain 384
    public function test_create_role(): void {
1 efrain 385
        global $DB;
386
 
387
        $this->resetAfterTest();
388
 
389
        // Create role and get event.
390
        $sink = $this->redirectEvents();
391
        $id = create_role('New student role', 'student2', 'New student description', 'student');
392
        $events = $sink->get_events();
393
        $sink->close();
394
        $event = array_pop($events);
395
        $role = $DB->get_record('role', ['id' => $id]);
396
 
397
        $this->assertNotEmpty($role);
398
        $this->assertSame('New student role', $role->name);
399
        $this->assertSame('student2', $role->shortname);
400
        $this->assertSame('New student description', $role->description);
401
        $this->assertSame('student', $role->archetype);
402
 
403
        // Test triggered event.
404
        $this->assertInstanceOf('\core\event\role_created', $event);
405
        $this->assertSame('role', $event->target);
406
        $this->assertSame('role', $event->objecttable);
407
        $this->assertSame((int)$role->id, $event->objectid);
408
        $this->assertEquals(context_system::instance(), $event->get_context());
409
        $this->assertSame($role->shortname, $event->other['shortname']);
410
        $this->assertSame($role->archetype, $event->other['archetype']);
411
    }
412
 
413
    /**
414
     * Test adding of capabilities to roles.
415
     *
416
     * @covers ::assign_capability
417
     */
11 efrain 418
    public function test_assign_capability(): void {
1 efrain 419
        global $DB, $USER;
420
 
421
        $this->resetAfterTest();
422
 
423
        $user = $this->getDataGenerator()->create_user();
424
        $syscontext = context_system::instance();
425
        $frontcontext = context_course::instance(SITEID);
426
        $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
427
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to student by default.
428
        $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
429
        $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
430
 
431
        $this->setUser($user);
432
        $result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $frontcontext->id);
433
        $this->assertTrue($result);
434
        $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
435
        $this->assertNotEmpty($permission);
436
        $this->assertEquals(CAP_ALLOW, $permission->permission);
437
        $this->assertEquals($user->id, $permission->modifierid);
438
 
439
        $this->setUser(0);
440
        $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, false);
441
        $this->assertTrue($result);
442
        $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
443
        $this->assertNotEmpty($permission);
444
        $this->assertEquals(CAP_ALLOW, $permission->permission);
445
        $this->assertEquals($user->id, $permission->modifierid);
446
 
447
        $result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, true);
448
        $this->assertTrue($result);
449
        $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
450
        $this->assertNotEmpty($permission);
451
        $this->assertEquals(CAP_PROHIBIT, $permission->permission);
452
        $this->assertEquals(0, $permission->modifierid);
453
 
454
        $result = assign_capability('moodle/backup:backupcourse', CAP_INHERIT, $student->id, $frontcontext->id);
455
        $this->assertTrue($result);
456
        $permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
457
        $this->assertEmpty($permission);
458
 
459
        // Test event triggered.
460
        $sink = $this->redirectEvents();
461
        $capability = 'moodle/backup:backupcourse';
462
        assign_capability($capability, CAP_ALLOW, $student->id, $syscontext);
463
        $events = $sink->get_events();
464
        $sink->close();
465
        $this->assertCount(1, $events);
466
        $event = $events[0];
467
        $this->assertInstanceOf('\core\event\capability_assigned', $event);
468
        $this->assertSame('role_capabilities', $event->objecttable);
469
        $this->assertEquals($student->id, $event->objectid);
470
        $this->assertEquals($syscontext->id, $event->contextid);
471
        $other = ['capability' => $capability, 'oldpermission' => CAP_INHERIT, 'permission' => CAP_ALLOW];
472
        $this->assertEquals($other, $event->other);
473
        $description = "The user id '$USER->id' assigned the '$capability' capability for " .
474
            "role '$student->id' with 'Allow' permission";
475
        $this->assertEquals($description, $event->get_description());
476
 
477
        // Test if the event has different description when updating the capability permission.
478
        $sink = $this->redirectEvents();
479
        assign_capability($capability, CAP_PROHIBIT, $student->id, $syscontext, true);
480
        $events = $sink->get_events();
481
        $sink->close();
482
        $event = $events[0];
483
        $description = "The user id '$USER->id' changed the '$capability' capability permission for " .
484
            "role '$student->id' from 'Allow' to 'Prohibit'";
485
        $this->assertEquals($description, $event->get_description());
486
    }
487
 
488
    /**
489
     * Test removing of capabilities from roles.
490
     *
491
     * @covers ::unassign_capability
492
     */
11 efrain 493
    public function test_unassign_capability(): void {
1 efrain 494
        global $DB, $USER;
495
 
496
        $this->resetAfterTest();
497
 
498
        $syscontext = context_system::instance();
499
        $frontcontext = context_course::instance(SITEID);
500
        $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
501
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to manager by default.
502
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
503
 
504
        $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
505
        $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
506
 
507
        $result = unassign_capability('moodle/backup:backupcourse', $manager->id, $syscontext->id);
508
        $this->assertTrue($result);
509
        $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
510
        $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
511
        unassign_capability('moodle/backup:backupcourse', $manager->id, $frontcontext);
512
        $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
513
 
514
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
515
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
516
        $this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
517
 
518
        $result = unassign_capability('moodle/backup:backupcourse', $manager->id);
519
        $this->assertTrue($result);
520
        $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
521
        $this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
522
 
523
        // Test event triggered.
524
        $sink = $this->redirectEvents();
525
        $capability = 'moodle/backup:backupcourse';
526
        unassign_capability($capability, CAP_ALLOW, $manager->id);
527
        $events = $sink->get_events();
528
        $sink->close();
529
        $this->assertCount(1, $events);
530
        $event = $events[0];
531
        $this->assertInstanceOf('\core\event\capability_unassigned', $event);
532
        $this->assertSame('role_capabilities', $event->objecttable);
533
        $this->assertEquals($manager->id, $event->objectid);
534
        $this->assertEquals($syscontext->id, $event->contextid);
535
        $this->assertEquals($capability, $event->other['capability']);
536
        $description = "The user id id '$USER->id' has unassigned the '$capability' capability for role '$manager->id'";
537
        $this->assertEquals($description, $event->get_description());
538
    }
539
 
540
    /**
541
     * Test role assigning.
542
     *
543
     * @covers ::role_assign
544
     */
11 efrain 545
    public function test_role_assign(): void {
1 efrain 546
        global $DB, $USER;
547
 
548
        $this->resetAfterTest();
549
 
550
        $user = $this->getDataGenerator()->create_user();
551
        $course = $this->getDataGenerator()->create_course();
552
        $role = $DB->get_record('role', array('shortname'=>'student'));
553
 
554
        $this->setUser(0);
555
        $context = context_system::instance();
556
        $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
557
        role_assign($role->id, $user->id, $context->id);
558
        $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
559
        $this->assertNotEmpty($ras);
560
        $this->assertSame('', $ras->component);
561
        $this->assertSame('0', $ras->itemid);
562
        $this->assertEquals($USER->id, $ras->modifierid);
563
 
564
        $this->setAdminUser();
565
        $context = context_course::instance($course->id);
566
        $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
567
        role_assign($role->id, $user->id, $context->id, 'enrol_self', 1, 666);
568
        $ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
569
        $this->assertNotEmpty($ras);
570
        $this->assertSame('enrol_self', $ras->component);
571
        $this->assertSame('1', $ras->itemid);
572
        $this->assertEquals($USER->id, $ras->modifierid);
573
        $this->assertEquals(666, $ras->timemodified);
574
 
575
        // Test event triggered.
576
 
577
        $user2 = $this->getDataGenerator()->create_user();
578
        $sink = $this->redirectEvents();
579
        $raid = role_assign($role->id, $user2->id, $context->id);
580
        $events = $sink->get_events();
581
        $sink->close();
582
        $this->assertCount(1, $events);
583
        $event = $events[0];
584
        $this->assertInstanceOf('\core\event\role_assigned', $event);
585
        $this->assertSame('role', $event->target);
586
        $this->assertSame('role', $event->objecttable);
587
        $this->assertEquals($role->id, $event->objectid);
588
        $this->assertEquals($context->id, $event->contextid);
589
        $this->assertEquals($user2->id, $event->relateduserid);
590
        $this->assertCount(3, $event->other);
591
        $this->assertEquals($raid, $event->other['id']);
592
        $this->assertSame('', $event->other['component']);
593
        $this->assertEquals(0, $event->other['itemid']);
594
        $this->assertInstanceOf('moodle_url', $event->get_url());
595
    }
596
 
597
    /**
598
     * Test role unassigning.
599
     *
600
     * @covers ::role_unassign
601
     */
11 efrain 602
    public function test_role_unassign(): void {
1 efrain 603
        global $DB, $USER;
604
 
605
        $this->resetAfterTest();
606
 
607
        $user = $this->getDataGenerator()->create_user();
608
        $course = $this->getDataGenerator()->create_course();
609
        $role = $DB->get_record('role', array('shortname'=>'student'));
610
 
611
        $context = context_course::instance($course->id);
612
        role_assign($role->id, $user->id, $context->id);
613
        $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
614
        role_unassign($role->id, $user->id, $context->id);
615
        $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
616
 
617
        role_assign($role->id, $user->id, $context->id, 'enrol_self', 1);
618
        $this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
619
        role_unassign($role->id, $user->id, $context->id, 'enrol_self', 1);
620
        $this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
621
 
622
        // Test event triggered.
623
 
624
        role_assign($role->id, $user->id, $context->id);
625
        $sink = $this->redirectEvents();
626
        role_unassign($role->id, $user->id, $context->id);
627
        $events = $sink->get_events();
628
        $sink->close();
629
        $this->assertCount(1, $events);
630
        $event = $events[0];
631
        $this->assertInstanceOf('\core\event\role_unassigned', $event);
632
        $this->assertSame('role', $event->target);
633
        $this->assertSame('role', $event->objecttable);
634
        $this->assertEquals($role->id, $event->objectid);
635
        $this->assertEquals($context->id, $event->contextid);
636
        $this->assertEquals($user->id, $event->relateduserid);
637
        $this->assertCount(3, $event->other);
638
        $this->assertSame('', $event->other['component']);
639
        $this->assertEquals(0, $event->other['itemid']);
640
        $this->assertInstanceOf('moodle_url', $event->get_url());
641
    }
642
 
643
    /**
644
     * Test role unassigning.
645
     *
646
     * @covers ::role_unassign_all
647
     */
11 efrain 648
    public function test_role_unassign_all(): void {
1 efrain 649
        global $DB;
650
 
651
        $this->resetAfterTest();
652
 
653
        $user = $this->getDataGenerator()->create_user();
654
        $course = $this->getDataGenerator()->create_course();
655
        $role = $DB->get_record('role', array('shortname'=>'student'));
656
        $role2 = $DB->get_record('role', array('shortname'=>'teacher'));
657
        $syscontext = context_system::instance();
658
        $coursecontext = context_course::instance($course->id);
659
        $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
660
        $modcontext = context_module::instance($page->cmid);
661
 
662
        role_assign($role->id, $user->id, $syscontext->id);
663
        role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
664
        $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
665
        role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role->id));
666
        $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
667
 
668
        role_assign($role->id, $user->id, $syscontext->id);
669
        role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
670
        role_assign($role->id, $user->id, $modcontext->id);
671
        $this->assertEquals(3, $DB->count_records('role_assignments', array('userid'=>$user->id)));
672
        role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), false);
673
        $this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
674
        role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), true);
675
        $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
676
        role_unassign_all(array('userid'=>$user->id));
677
        $this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
678
 
679
        role_assign($role->id, $user->id, $syscontext->id);
680
        role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
681
        role_assign($role->id, $user->id, $coursecontext->id);
682
        role_assign($role->id, $user->id, $modcontext->id);
683
        $this->assertEquals(4, $DB->count_records('role_assignments', array('userid'=>$user->id)));
684
        role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id, 'component'=>'enrol_self'), true, true);
685
        $this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
686
 
687
        // Test events triggered.
688
 
689
        role_assign($role2->id, $user->id, $coursecontext->id);
690
        role_assign($role2->id, $user->id, $modcontext->id);
691
        $sink = $this->redirectEvents();
692
        role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role2->id));
693
        $events = $sink->get_events();
694
        $sink->close();
695
        $this->assertCount(2, $events);
696
        $this->assertInstanceOf('\core\event\role_unassigned', $events[0]);
697
        $this->assertInstanceOf('\core\event\role_unassigned', $events[1]);
698
    }
699
 
700
    /**
701
     * Test role queries.
702
     *
703
     * @covers ::get_roles_with_capability
704
     */
11 efrain 705
    public function test_get_roles_with_capability(): void {
1 efrain 706
        global $DB;
707
 
708
        $this->resetAfterTest();
709
 
710
        $syscontext = context_system::instance();
711
        $frontcontext = context_course::instance(SITEID);
712
        $manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
713
        $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
714
 
715
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
716
        $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse'));
717
 
718
        $roles = get_roles_with_capability('moodle/backup:backupcourse');
719
        $this->assertEquals(array(), $roles);
720
 
721
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
722
        assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $manager->id, $frontcontext->id);
723
        assign_capability('moodle/backup:backupcourse', CAP_PREVENT, $teacher->id, $frontcontext->id);
724
 
725
        $roles = get_roles_with_capability('moodle/backup:backupcourse');
726
        $this->assertEqualsCanonicalizing(array($teacher->id, $manager->id), array_keys($roles), true);
727
 
728
        $roles = get_roles_with_capability('moodle/backup:backupcourse', CAP_ALLOW);
729
        $this->assertEqualsCanonicalizing(array($manager->id), array_keys($roles), true);
730
 
731
        $roles = get_roles_with_capability('moodle/backup:backupcourse', null, $syscontext);
732
        $this->assertEqualsCanonicalizing(array($manager->id), array_keys($roles), true);
733
    }
734
 
735
    /**
736
     * Test deleting of roles.
737
     *
738
     * @covers ::delete_role
739
     */
11 efrain 740
    public function test_delete_role(): void {
1 efrain 741
        global $DB;
742
 
743
        $this->resetAfterTest();
744
 
745
        $role = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
746
        $user = $this->getDataGenerator()->create_user();
747
        role_assign($role->id, $user->id, context_system::instance());
748
        $course = $this->getDataGenerator()->create_course();
749
        $rolename = (object)array('roleid'=>$role->id, 'name'=>'Man', 'contextid'=>context_course::instance($course->id)->id);
750
        $DB->insert_record('role_names', $rolename);
751
 
752
        $this->assertTrue($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
753
        $this->assertTrue($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
754
        $this->assertTrue($DB->record_exists('role_names', array('roleid'=>$role->id)));
755
        $this->assertTrue($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
756
        $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
757
        $this->assertTrue($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
758
        $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
759
        $this->assertTrue($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
760
 
761
        // Delete role and get event.
762
        $sink = $this->redirectEvents();
763
        $result = delete_role($role->id);
764
        $events = $sink->get_events();
765
        $sink->close();
766
        $event = array_pop($events);
767
 
768
        $this->assertTrue($result);
769
        $this->assertFalse($DB->record_exists('role', array('id'=>$role->id)));
770
        $this->assertFalse($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
771
        $this->assertFalse($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
772
        $this->assertFalse($DB->record_exists('role_names', array('roleid'=>$role->id)));
773
        $this->assertFalse($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
774
        $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
775
        $this->assertFalse($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
776
        $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
777
        $this->assertFalse($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
778
 
779
        // Test triggered event.
780
        $this->assertInstanceOf('\core\event\role_deleted', $event);
781
        $this->assertSame('role', $event->target);
782
        $this->assertSame('role', $event->objecttable);
783
        $this->assertSame($role->id, $event->objectid);
784
        $this->assertEquals(context_system::instance(), $event->get_context());
785
        $this->assertSame($role->shortname, $event->other['shortname']);
786
        $this->assertSame($role->description, $event->other['description']);
787
        $this->assertSame($role->archetype, $event->other['archetype']);
788
    }
789
 
790
    /**
791
     * Test fetching of all roles.
792
     *
793
     * @covers ::get_all_roles
794
     */
11 efrain 795
    public function test_get_all_roles(): void {
1 efrain 796
        global $DB;
797
 
798
        $this->resetAfterTest();
799
 
800
        $allroles = get_all_roles();
801
        $this->assertIsArray($allroles);
802
        $initialrolescount = count($allroles);
803
        $this->assertTrue($initialrolescount >= 8); // There are 8 roles is standard install.
804
        $rolenames = array_column($allroles, 'shortname');
805
        foreach (get_role_archetypes() as $archetype) {
806
            $this->assertContains($archetype, $rolenames);
807
        }
808
 
809
        $role = reset($allroles);
810
        $role = (array)$role;
811
 
812
        $this->assertEqualsCanonicalizing(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype'),
813
            array_keys($role));
814
 
815
        foreach ($allroles as $roleid => $role) {
816
            $this->assertEquals($role->id, $roleid);
817
        }
818
 
819
        $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
820
        $course = $this->getDataGenerator()->create_course();
821
        $coursecontext = context_course::instance($course->id);
822
        $otherid = create_role('Other role', 'other', 'Some other role', '');
823
        $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
824
        $DB->insert_record('role_names', $teacherename);
825
        $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
826
        $DB->insert_record('role_names', $otherrename);
827
        $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
828
 
829
        $allroles = get_all_roles($coursecontext);
830
        $this->assertIsArray($allroles);
831
        $this->assertCount($initialrolescount + 1, $allroles);
832
        $role = reset($allroles);
833
        $role = (array)$role;
834
 
835
        $this->assertEqualsCanonicalizing(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype', 'coursealias'), array_keys($role));
836
 
837
        foreach ($allroles as $roleid => $role) {
838
            $this->assertEquals($role->id, $roleid);
839
            if (isset($renames[$roleid])) {
840
                $this->assertSame($renames[$roleid], $role->coursealias);
841
            } else {
842
                $this->assertNull($role->coursealias);
843
            }
844
        }
845
    }
846
 
847
    /**
848
     * Test getting of all archetypes.
849
     *
850
     * @covers ::get_role_archetypes
851
     */
11 efrain 852
    public function test_get_role_archetypes(): void {
1 efrain 853
        $archetypes = get_role_archetypes();
854
        $this->assertCount(8, $archetypes); // There are 8 archetypes in standard install.
855
        foreach ($archetypes as $k => $v) {
856
            $this->assertSame($k, $v);
857
        }
858
    }
859
 
860
    /**
861
     * Test getting of roles with given archetype.
862
     *
863
     * @covers ::get_archetype_roles
864
     */
11 efrain 865
    public function test_get_archetype_roles(): void {
1 efrain 866
        $this->resetAfterTest();
867
 
868
        // New install should have at least 1 role for each archetype.
869
        $archetypes = get_role_archetypes();
870
        foreach ($archetypes as $archetype) {
871
            $roles = get_archetype_roles($archetype);
872
            $this->assertGreaterThanOrEqual(1, count($roles));
873
            $role = reset($roles);
874
            $this->assertSame($archetype, $role->archetype);
875
        }
876
 
877
        create_role('New student role', 'student2', 'New student description', 'student');
878
        $roles = get_archetype_roles('student');
879
        $this->assertGreaterThanOrEqual(2, count($roles));
880
    }
881
 
882
    /**
883
     * Test aliased role names.
884
     *
885
     * @covers ::role_get_name
886
     */
11 efrain 887
    public function test_role_get_name(): void {
1 efrain 888
        global $DB;
889
 
890
        $this->resetAfterTest();
891
 
892
        $allroles = $DB->get_records('role');
893
        $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
894
        $course = $this->getDataGenerator()->create_course();
895
        $coursecontext = context_course::instance($course->id);
896
        $otherid = create_role('Other role', 'other', 'Some other role', '');
897
        $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
898
        $DB->insert_record('role_names', $teacherename);
899
        $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
900
        $DB->insert_record('role_names', $otherrename);
901
        $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
902
 
903
        foreach ($allroles as $role) {
904
            if (in_array($role->shortname, get_role_archetypes())) {
905
                // Standard roles do not have a set name.
906
                $this->assertSame('', $role->name);
907
            }
908
            // Get localised name from lang pack.
909
            $name = role_get_name($role, null, ROLENAME_ORIGINAL);
910
            $this->assertNotEmpty($name);
911
            $this->assertNotEquals($role->shortname, $name);
912
 
913
            if (isset($renames[$role->id])) {
914
                $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext));
915
                $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS));
916
                $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
917
                $this->assertSame("{$renames[$role->id]} ($name)", role_get_name($role, $coursecontext, ROLENAME_BOTH));
918
            } else {
919
                $this->assertSame($name, role_get_name($role, $coursecontext));
920
                $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ALIAS));
921
                $this->assertNull(role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
922
                $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_BOTH));
923
            }
924
            $this->assertSame($name, role_get_name($role));
925
            $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ORIGINAL));
926
            $this->assertSame($name, role_get_name($role, null, ROLENAME_ORIGINAL));
927
            $this->assertSame($role->shortname, role_get_name($role, $coursecontext, ROLENAME_SHORT));
928
            $this->assertSame($role->shortname, role_get_name($role, null, ROLENAME_SHORT));
929
            $this->assertSame("$name ($role->shortname)", role_get_name($role, $coursecontext, ROLENAME_ORIGINALANDSHORT));
930
            $this->assertSame("$name ($role->shortname)", role_get_name($role, null, ROLENAME_ORIGINALANDSHORT));
931
            $this->assertNull(role_get_name($role, null, ROLENAME_ALIAS_RAW));
932
        }
933
    }
934
 
935
    /**
936
     * Test tweaking of role name arrays.
937
     *
938
     * @covers ::role_fix_names
939
     */
11 efrain 940
    public function test_role_fix_names(): void {
1 efrain 941
        global $DB;
942
 
943
        $this->resetAfterTest();
944
 
945
        $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
946
        $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
947
        $otherid = create_role('Other role', 'other', 'Some other role', '');
948
        $anotherid = create_role('Another role', 'another', 'Yet another other role', '');
949
        $allroles = $DB->get_records('role');
950
 
951
        $syscontext = context_system::instance();
952
        $frontcontext = context_course::instance(SITEID);
953
        $course = $this->getDataGenerator()->create_course();
954
        $coursecontext = context_course::instance($course->id);
955
        $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
956
        $categorycontext = context_coursecat::instance($category->id);
957
 
958
        $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
959
        $DB->insert_record('role_names', $teacherename);
960
        $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
961
        $DB->insert_record('role_names', $otherrename);
962
        $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
963
 
964
        // Make sure all localname contain proper values for each ROLENAME_ constant,
965
        // note role_get_name() on frontpage is used to get the original name for future compatibility.
966
        $roles = $allroles;
967
        unset($roles[$student->id]); // Remove one role to make sure no role is added or removed.
968
        $rolenames = array();
969
        foreach ($roles as $role) {
970
            $rolenames[$role->id] = $role->name;
971
        }
972
 
973
        $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
974
        foreach ($alltypes as $type) {
975
            $fixed = role_fix_names($roles, $coursecontext, $type);
976
            $this->assertCount(count($roles), $fixed);
977
            foreach ($fixed as $roleid => $rolename) {
978
                $this->assertInstanceOf('stdClass', $rolename);
979
                $role = $allroles[$roleid];
980
                $name = role_get_name($role, $coursecontext, $type);
981
                $this->assertSame($name, $rolename->localname);
982
            }
983
            $fixed = role_fix_names($rolenames, $coursecontext, $type);
984
            $this->assertCount(count($rolenames), $fixed);
985
            foreach ($fixed as $roleid => $rolename) {
986
                $role = $allroles[$roleid];
987
                $name = role_get_name($role, $coursecontext, $type);
988
                $this->assertSame($name, $rolename);
989
            }
990
        }
991
    }
992
 
993
    /**
994
     * Test role default allows.
995
     *
996
     * @covers ::get_default_role_archetype_allows
997
     */
11 efrain 998
    public function test_get_default_role_archetype_allows(): void {
1 efrain 999
        $archetypes = get_role_archetypes();
1000
        foreach ($archetypes as $archetype) {
1001
 
1002
            $result = get_default_role_archetype_allows('assign', $archetype);
1003
            $this->assertIsArray($result);
1004
 
1005
            $result = get_default_role_archetype_allows('override', $archetype);
1006
            $this->assertIsArray($result);
1007
 
1008
            $result = get_default_role_archetype_allows('switch', $archetype);
1009
            $this->assertIsArray($result);
1010
 
1011
            $result = get_default_role_archetype_allows('view', $archetype);
1012
            $this->assertIsArray($result);
1013
        }
1014
 
1015
        $result = get_default_role_archetype_allows('assign', '');
1016
        $this->assertSame(array(), $result);
1017
 
1018
        $result = get_default_role_archetype_allows('override', '');
1019
        $this->assertSame(array(), $result);
1020
 
1021
        $result = get_default_role_archetype_allows('switch', '');
1022
        $this->assertSame(array(), $result);
1023
 
1024
        $result = get_default_role_archetype_allows('view', '');
1025
        $this->assertSame(array(), $result);
1026
 
1027
        $result = get_default_role_archetype_allows('assign', 'wrongarchetype');
1028
        $this->assertSame(array(), $result);
1029
        $this->assertDebuggingCalled();
1030
 
1031
        $result = get_default_role_archetype_allows('override', 'wrongarchetype');
1032
        $this->assertSame(array(), $result);
1033
        $this->assertDebuggingCalled();
1034
 
1035
        $result = get_default_role_archetype_allows('switch', 'wrongarchetype');
1036
        $this->assertSame(array(), $result);
1037
        $this->assertDebuggingCalled();
1038
 
1039
        $result = get_default_role_archetype_allows('view', 'wrongarchetype');
1040
        $this->assertSame(array(), $result);
1041
        $this->assertDebuggingCalled();
1042
    }
1043
 
1044
    /**
1045
     * Test allowing of role assignments.
1046
     *
1047
     * @covers ::core_role_set_assign_allowed
1048
     */
11 efrain 1049
    public function test_core_role_set_assign_allowed(): void {
1 efrain 1050
        global $DB, $CFG;
1051
 
1052
        $this->resetAfterTest();
1053
 
1054
        $otherid = create_role('Other role', 'other', 'Some other role', '');
1055
        $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1056
 
1057
        $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
1058
        core_role_set_assign_allowed($otherid, $student->id);
1059
        $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
1060
 
1061
        // Test event trigger.
1062
        $allowroleassignevent = \core\event\role_allow_assign_updated::create([
1063
            'context' => context_system::instance(),
1064
            'objectid' => $otherid,
1065
            'other' => ['targetroleid' => $student->id]
1066
        ]);
1067
        $sink = $this->redirectEvents();
1068
        $allowroleassignevent->trigger();
1069
        $events = $sink->get_events();
1070
        $sink->close();
1071
        $event = array_pop($events);
1072
        $this->assertInstanceOf('\core\event\role_allow_assign_updated', $event);
1073
    }
1074
 
1075
    /**
1076
     * Test allowing of role overrides.
1077
     *
1078
     * @covers ::core_role_set_override_allowed
1079
     */
11 efrain 1080
    public function test_core_role_set_override_allowed(): void {
1 efrain 1081
        global $DB, $CFG;
1082
 
1083
        $this->resetAfterTest();
1084
 
1085
        $otherid = create_role('Other role', 'other', 'Some other role', '');
1086
        $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1087
 
1088
        $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
1089
        core_role_set_override_allowed($otherid, $student->id);
1090
        $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
1091
 
1092
        // Test event trigger.
1093
        $allowroleassignevent = \core\event\role_allow_override_updated::create([
1094
            'context' => context_system::instance(),
1095
            'objectid' => $otherid,
1096
            'other' => ['targetroleid' => $student->id]
1097
        ]);
1098
        $sink = $this->redirectEvents();
1099
        $allowroleassignevent->trigger();
1100
        $events = $sink->get_events();
1101
        $sink->close();
1102
        $event = array_pop($events);
1103
        $this->assertInstanceOf('\core\event\role_allow_override_updated', $event);
1104
    }
1105
 
1106
    /**
1107
     * Test allowing of role switching.
1108
     *
1109
     * @covers ::core_role_set_switch_allowed
1110
     */
11 efrain 1111
    public function test_core_role_set_switch_allowed(): void {
1 efrain 1112
        global $DB, $CFG;
1113
 
1114
        $this->resetAfterTest();
1115
 
1116
        $otherid = create_role('Other role', 'other', 'Some other role', '');
1117
        $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1118
 
1119
        $this->assertFalse($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1120
        core_role_set_switch_allowed($otherid, $student->id);
1121
        $this->assertTrue($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1122
 
1123
        // Test event trigger.
1124
        $allowroleassignevent = \core\event\role_allow_switch_updated::create([
1125
            'context' => context_system::instance(),
1126
            'objectid' => $otherid,
1127
            'other' => ['targetroleid' => $student->id]
1128
        ]);
1129
        $sink = $this->redirectEvents();
1130
        $allowroleassignevent->trigger();
1131
        $events = $sink->get_events();
1132
        $sink->close();
1133
        $event = array_pop($events);
1134
        $this->assertInstanceOf('\core\event\role_allow_switch_updated', $event);
1135
    }
1136
 
1137
    /**
1138
     * Test allowing of role switching.
1139
     *
1140
     * @covers ::core_role_set_view_allowed
1141
     */
11 efrain 1142
    public function test_core_role_set_view_allowed(): void {
1 efrain 1143
        global $DB, $CFG;
1144
 
1145
        $this->resetAfterTest();
1146
 
1147
        $otherid = create_role('Other role', 'other', 'Some other role', '');
1148
        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1149
 
1150
        $this->assertFalse($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
1151
        core_role_set_view_allowed($otherid, $student->id);
1152
        $this->assertTrue($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
1153
 
1154
        // Test event trigger.
1155
        $allowroleassignevent = \core\event\role_allow_view_updated::create([
1156
            'context' => context_system::instance(),
1157
            'objectid' => $otherid,
1158
            'other' => ['targetroleid' => $student->id]
1159
        ]);
1160
        $sink = $this->redirectEvents();
1161
        $allowroleassignevent->trigger();
1162
        $events = $sink->get_events();
1163
        $sink->close();
1164
        $event = array_pop($events);
1165
        $this->assertInstanceOf('\core\event\role_allow_view_updated', $event);
1166
    }
1167
 
1168
    /**
1169
     * Test returning of assignable roles in context.
1170
     *
1171
     * @covers ::get_assignable_roles
1172
     */
11 efrain 1173
    public function test_get_assignable_roles(): void {
1 efrain 1174
        global $DB;
1175
 
1176
        $this->resetAfterTest();
1177
 
1178
        $course = $this->getDataGenerator()->create_course();
1179
        $coursecontext = context_course::instance($course->id);
1180
 
1181
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1182
        $teacher = $this->getDataGenerator()->create_user();
1183
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
1184
        $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1185
        $DB->insert_record('role_names', $teacherename);
1186
 
1187
        $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1188
        $student = $this->getDataGenerator()->create_user();
1189
        role_assign($studentrole->id, $student->id, $coursecontext);
1190
 
1191
        $contexts = $DB->get_records('context');
1192
        $users = $DB->get_records('user');
1193
        $allroles = $DB->get_records('role');
1194
 
1195
        // Evaluate all results for all users in all contexts.
1196
        foreach ($users as $user) {
1197
            $this->setUser($user);
1198
            foreach ($contexts as $contextid => $unused) {
1199
                $context = context_helper::instance_by_id($contextid);
1200
                $roles = get_assignable_roles($context, ROLENAME_SHORT);
1201
                foreach ($allroles as $roleid => $role) {
1202
                    if (isset($roles[$roleid])) {
1203
                        if (is_siteadmin()) {
1204
                            $this->assertTrue($DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid)));
1205
                        } else {
1206
                            $this->assertTrue(user_can_assign($context, $roleid), "u:$user->id r:$roleid");
1207
                        }
1208
                        $this->assertEquals($role->shortname, $roles[$roleid]);
1209
                    } else {
1210
                        $allowed = $DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid));
1211
                        if (is_siteadmin()) {
1212
                            $this->assertFalse($allowed);
1213
                        } else {
1214
                            $this->assertFalse($allowed and user_can_assign($context, $roleid), "u:$user->id, r:{$allroles[$roleid]->name}, c:$context->contextlevel");
1215
                        }
1216
                    }
1217
                }
1218
            }
1219
        }
1220
 
1221
        // Not-logged-in user.
1222
        $this->setUser(0);
1223
        foreach ($contexts as $contextid => $unused) {
1224
            $context = context_helper::instance_by_id($contextid);
1225
            $roles = get_assignable_roles($context, ROLENAME_SHORT);
1226
            $this->assertSame(array(), $roles);
1227
        }
1228
 
1229
        // Test current user.
1230
        $this->setUser(0);
1231
        $admin = $DB->get_record('user', array('username'=>'admin'), '*', MUST_EXIST);
1232
        $roles1 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin);
1233
        $roles2 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin->id);
1234
        $this->setAdminUser();
1235
        $roles3 = get_assignable_roles($coursecontext, ROLENAME_SHORT);
1236
        $this->assertSame($roles1, $roles3);
1237
        $this->assertSame($roles2, $roles3);
1238
 
1239
        // Test parameter defaults.
1240
        $this->setAdminUser();
1241
        $roles1 = get_assignable_roles($coursecontext);
1242
        $roles2 = get_assignable_roles($coursecontext, ROLENAME_ALIAS, false, $admin);
1243
        $this->assertEquals($roles2, $roles1);
1244
 
1245
        // Verify returned names - let's allow all roles everywhere to simplify this a bit.
1246
        $alllevels = context_helper::get_all_levels();
1247
        $alllevels = array_keys($alllevels);
1248
        foreach ($allroles as $roleid => $role) {
1249
            set_role_contextlevels($roleid, $alllevels);
1250
        }
1251
        $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1252
        foreach ($alltypes as $type) {
1253
            $rolenames = role_fix_names($allroles, $coursecontext, $type);
1254
            $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1255
            foreach ($roles as $roleid => $rolename) {
1256
                $this->assertSame($rolenames[$roleid]->localname, $rolename);
1257
            }
1258
        }
1259
 
1260
        // Verify counts.
1261
        $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1262
        foreach ($alltypes as $type) {
1263
            $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1264
            list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($coursecontext, $type, true, $admin);
1265
            $this->assertEquals($roles, $rolenames);
1266
            foreach ($rolenames as $roleid => $name) {
1267
                if ($roleid == $teacherrole->id or $roleid == $studentrole->id) {
1268
                    $this->assertEquals(1, $rolecounts[$roleid]);
1269
                } else {
1270
                    $this->assertEquals(0, $rolecounts[$roleid]);
1271
                }
1272
                $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1273
            }
1274
        }
1275
    }
1276
 
1277
    /**
1278
     * Test user count of assignable roles in context where users are assigned the role via different components.
1279
     *
1280
     * @covers ::get_assignable_roles
1281
     */
11 efrain 1282
    public function test_get_assignable_roles_distinct_usercount(): void {
1 efrain 1283
        global $DB;
1284
 
1285
        $this->resetAfterTest(true);
1286
 
1287
        $this->setAdminUser();
1288
 
1289
        $course = $this->getDataGenerator()->create_course();
1290
        $context = \context_course::instance($course->id);
1291
 
1292
        $user1 = $this->getDataGenerator()->create_user();
1293
        $user2 = $this->getDataGenerator()->create_user();
1294
 
1295
        $studentrole = $DB->get_record('role', ['shortname' => 'student']);
1296
 
1297
        // Assign each user the student role in course.
1298
        role_assign($studentrole->id, $user1->id, $context->id);
1299
        role_assign($studentrole->id, $user2->id, $context->id);
1300
 
1301
        list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
1302
        $this->assertEquals(2, $rolecounts[$studentrole->id]);
1303
 
1304
        // Assign first user the student role in course again (this time via 'enrol_self' component).
1305
        role_assign($studentrole->id, $user1->id, $context->id, 'enrol_self', 1);
1306
 
1307
        // There are still only two distinct users.
1308
        list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
1309
        $this->assertEquals(2, $rolecounts[$studentrole->id]);
1310
    }
1311
 
1312
    /**
1313
     * Test getting of all switchable roles.
1314
     *
1315
     * @covers ::get_switchable_roles
1316
     */
11 efrain 1317
    public function test_get_switchable_roles(): void {
1 efrain 1318
        global $DB;
1319
 
1320
        $this->resetAfterTest();
1321
 
1322
        $course = $this->getDataGenerator()->create_course();
1323
        $coursecontext = context_course::instance($course->id);
1324
 
1325
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1326
        $teacher = $this->getDataGenerator()->create_user();
1327
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
1328
        $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1329
        $DB->insert_record('role_names', $teacherename);
1330
 
1331
        $contexts = $DB->get_records('context');
1332
        $users = $DB->get_records('user');
1333
        $allroles = $DB->get_records('role');
1334
 
1335
        // Evaluate all results for all users in all contexts.
1336
        foreach ($users as $user) {
1337
            $this->setUser($user);
1338
            foreach ($contexts as $contextid => $unused) {
1339
                $context = context_helper::instance_by_id($contextid);
1340
                $roles = get_switchable_roles($context);
1341
                foreach ($allroles as $roleid => $role) {
1342
                    if (is_siteadmin()) {
1343
                        $this->assertTrue(isset($roles[$roleid]));
1344
                    } else {
1345
                        $parents = $context->get_parent_context_ids(true);
1346
                        $pcontexts = implode(',' , $parents);
1347
                        $allowed = $DB->record_exists_sql(
1348
                            "SELECT r.id
1349
                               FROM {role} r
1350
                               JOIN {role_allow_switch} ras ON ras.allowswitch = r.id
1351
                               JOIN {role_assignments} ra ON ra.roleid = ras.roleid
1352
                              WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1353
                            ",
1354
                            array('userid'=>$user->id, 'roleid'=>$roleid)
1355
                        );
1356
                        if (isset($roles[$roleid])) {
1357
                            $this->assertTrue($allowed);
1358
                        } else {
1359
                            $this->assertFalse($allowed);
1360
                        }
1361
                    }
1362
 
1363
                    if (isset($roles[$roleid])) {
1364
                        $coursecontext = $context->get_course_context(false);
1365
                        $this->assertSame(role_get_name($role, $coursecontext), $roles[$roleid]);
1366
                    }
1367
                }
1368
            }
1369
        }
1370
    }
1371
 
1372
    /**
1373
     * Test getting of all overridable roles.
1374
     *
1375
     * @covers ::get_overridable_roles
1376
     */
11 efrain 1377
    public function test_get_overridable_roles(): void {
1 efrain 1378
        global $DB;
1379
 
1380
        $this->resetAfterTest();
1381
 
1382
        $course = $this->getDataGenerator()->create_course();
1383
        $coursecontext = context_course::instance($course->id);
1384
 
1385
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1386
        $teacher = $this->getDataGenerator()->create_user();
1387
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
1388
        $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1389
        $DB->insert_record('role_names', $teacherename);
1390
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
1391
        assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id);
1392
 
1393
        $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1394
        $student = $this->getDataGenerator()->create_user();
1395
        role_assign($studentrole->id, $student->id, $coursecontext);
1396
 
1397
        $contexts = $DB->get_records('context');
1398
        $users = $DB->get_records('user');
1399
        $allroles = $DB->get_records('role');
1400
 
1401
        // Evaluate all results for all users in all contexts.
1402
        foreach ($users as $user) {
1403
            $this->setUser($user);
1404
            foreach ($contexts as $contextid => $unused) {
1405
                $context = context_helper::instance_by_id($contextid);
1406
                $roles = get_overridable_roles($context, ROLENAME_SHORT);
1407
                foreach ($allroles as $roleid => $role) {
1408
                    $hascap = has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context);
1409
                    if (is_siteadmin()) {
1410
                        $this->assertTrue(isset($roles[$roleid]));
1411
                    } else {
1412
                        $parents = $context->get_parent_context_ids(true);
1413
                        $pcontexts = implode(',' , $parents);
1414
                        $allowed = $DB->record_exists_sql(
1415
                            "SELECT r.id
1416
                               FROM {role} r
1417
                               JOIN {role_allow_override} rao ON r.id = rao.allowoverride
1418
                               JOIN {role_assignments} ra ON rao.roleid = ra.roleid
1419
                              WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1420
                            ",
1421
                            array('userid'=>$user->id, 'roleid'=>$roleid)
1422
                        );
1423
                        if (isset($roles[$roleid])) {
1424
                            $this->assertTrue($hascap);
1425
                            $this->assertTrue($allowed);
1426
                        } else {
1427
                            $this->assertFalse($hascap and $allowed);
1428
                        }
1429
                    }
1430
 
1431
                    if (isset($roles[$roleid])) {
1432
                        $this->assertEquals($role->shortname, $roles[$roleid]);
1433
                    }
1434
                }
1435
            }
1436
        }
1437
 
1438
        // Test parameter defaults.
1439
        $this->setAdminUser();
1440
        $roles1 = get_overridable_roles($coursecontext);
1441
        $roles2 = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1442
        $this->assertEquals($roles2, $roles1);
1443
 
1444
        $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1445
        foreach ($alltypes as $type) {
1446
            $rolenames = role_fix_names($allroles, $coursecontext, $type);
1447
            $roles = get_overridable_roles($coursecontext, $type, false);
1448
            foreach ($roles as $roleid => $rolename) {
1449
                $this->assertSame($rolenames[$roleid]->localname, $rolename);
1450
            }
1451
        }
1452
 
1453
        // Verify counts.
1454
        $roles = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1455
        list($rolenames, $rolecounts, $nameswithcounts) = get_overridable_roles($coursecontext, ROLENAME_ALIAS, true);
1456
        $this->assertEquals($roles, $rolenames);
1457
        foreach ($rolenames as $roleid => $name) {
1458
            if ($roleid == $teacherrole->id) {
1459
                $this->assertEquals(1, $rolecounts[$roleid]);
1460
            } else {
1461
                $this->assertEquals(0, $rolecounts[$roleid]);
1462
            }
1463
            $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1464
        }
1465
    }
1466
 
1467
    /**
1468
     * Test getting of all overridable roles.
1469
     *
1470
     * @covers ::get_viewable_roles
1471
     */
11 efrain 1472
    public function test_get_viewable_roles_course(): void {
1 efrain 1473
        global $DB;
1474
 
1475
        $this->resetAfterTest();
1476
 
1477
        $course = $this->getDataGenerator()->create_course();
1478
        $coursecontext = context_course::instance($course->id);
1479
 
1480
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1481
        $teacher = $this->getDataGenerator()->create_user();
1482
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
1483
 
1484
        $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1485
        $studentrolerename = (object) array('roleid' => $studentrole->id, 'name' => 'Učitel', 'contextid' => $coursecontext->id);
1486
        $DB->insert_record('role_names', $studentrolerename);
1487
 
1488
        // By default teacher can see student.
1489
        $this->setUser($teacher);
1490
        $viewableroles = get_viewable_roles($coursecontext);
1491
        $this->assertContains($studentrolerename->name, array_values($viewableroles));
1492
        // Remove view permission.
1493
        $DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
1494
        $viewableroles = get_viewable_roles($coursecontext);
1495
        // Teacher can no longer see student role.
1496
        $this->assertNotContains($studentrolerename->name, array_values($viewableroles));
1497
        // Allow again teacher to view student.
1498
        core_role_set_view_allowed($teacherrole->id, $studentrole->id);
1499
        // Teacher can now see student role.
1500
        $viewableroles = get_viewable_roles($coursecontext);
1501
        $this->assertContains($studentrolerename->name, array_values($viewableroles));
1502
    }
1503
 
1504
    /**
1505
     * Test getting of all overridable roles.
1506
     *
1507
     * @covers ::get_viewable_roles
1508
     */
11 efrain 1509
    public function test_get_viewable_roles_system(): void {
1 efrain 1510
        global $DB;
1511
 
1512
        $this->resetAfterTest();
1513
 
1514
        $context = context_system::instance();
1515
 
1516
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1517
        $teacher = $this->getDataGenerator()->create_user();
1518
        role_assign($teacherrole->id, $teacher->id, $context);
1519
 
1520
        $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1521
        $studentrolename = role_get_name($studentrole, $context);
1522
 
1523
        // By default teacher can see student.
1524
        $this->setUser($teacher);
1525
        $viewableroles = get_viewable_roles($context);
1526
        $this->assertContains($studentrolename, array_values($viewableroles));
1527
        // Remove view permission.
1528
        $DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
1529
        $viewableroles = get_viewable_roles($context);
1530
        // Teacher can no longer see student role.
1531
        $this->assertNotContains($studentrolename, array_values($viewableroles));
1532
        // Allow again teacher to view student.
1533
        core_role_set_view_allowed($teacherrole->id, $studentrole->id);
1534
        // Teacher can now see student role.
1535
        $viewableroles = get_viewable_roles($context);
1536
        $this->assertContains($studentrolename, array_values($viewableroles));
1537
    }
1538
 
1539
    /**
1540
     * Test we have context level defaults.
1541
     *
1542
     * @covers ::get_default_contextlevels
1543
     */
11 efrain 1544
    public function test_get_default_contextlevels(): void {
1 efrain 1545
        $archetypes = get_role_archetypes();
1546
        $alllevels = context_helper::get_all_levels();
1547
        foreach ($archetypes as $archetype) {
1548
            $defaults = get_default_contextlevels($archetype);
1549
            $this->assertIsArray($defaults);
1550
            foreach ($defaults as $level) {
1551
                $this->assertTrue(isset($alllevels[$level]));
1552
            }
1553
        }
1554
    }
1555
 
1556
    /**
1557
     * Test role context level setup.
1558
     *
1559
     * @covers ::set_role_contextlevels
1560
     */
11 efrain 1561
    public function test_set_role_contextlevels(): void {
1 efrain 1562
        global $DB;
1563
 
1564
        $this->resetAfterTest();
1565
 
1566
        $roleid = create_role('New student role', 'student2', 'New student description', 'student');
1567
 
1568
        $this->assertFalse($DB->record_exists('role_context_levels', array('roleid' => $roleid)));
1569
 
1570
        set_role_contextlevels($roleid, array(CONTEXT_COURSE, CONTEXT_MODULE));
1571
        $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1572
        $this->assertCount(2, $levels);
1573
        $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1574
        $this->assertTrue(isset($levels[CONTEXT_MODULE]));
1575
 
1576
        set_role_contextlevels($roleid, array(CONTEXT_COURSE));
1577
        $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1578
        $this->assertCount(1, $levels);
1579
        $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1580
    }
1581
 
1582
    /**
1583
     * Test getting of role context levels
1584
     *
1585
     * @covers ::get_roles_for_contextlevels
1586
     */
11 efrain 1587
    public function test_get_roles_for_contextlevels(): void {
1 efrain 1588
        global $DB;
1589
 
1590
        $allroles = get_all_roles();
1591
        foreach (context_helper::get_all_levels() as $level => $unused) {
1592
            $roles = get_roles_for_contextlevels($level);
1593
            foreach ($allroles as $roleid => $unused) {
1594
                $exists = $DB->record_exists('role_context_levels', array('contextlevel'=>$level, 'roleid'=>$roleid));
1595
                if (in_array($roleid, $roles)) {
1596
                    $this->assertTrue($exists);
1597
                } else {
1598
                    $this->assertFalse($exists);
1599
                }
1600
            }
1601
        }
1602
    }
1603
 
1604
    /**
1605
     * Test default enrol roles.
1606
     *
1607
     * @covers ::get_default_enrol_roles
1608
     */
11 efrain 1609
    public function test_get_default_enrol_roles(): void {
1 efrain 1610
        $this->resetAfterTest();
1611
 
1612
        $course = $this->getDataGenerator()->create_course();
1613
        $coursecontext = context_course::instance($course->id);
1614
 
1615
        $id2 = create_role('New student role', 'student2', 'New student description', 'student');
1616
        set_role_contextlevels($id2, array(CONTEXT_COURSE));
1617
 
1618
        $allroles = get_all_roles();
1619
        $expected = array($id2=>$allroles[$id2]);
1620
 
1621
        foreach (get_roles_for_contextlevels(CONTEXT_COURSE) as $roleid) {
1622
            $expected[$roleid] = $roleid;
1623
        }
1624
 
1625
        $roles = get_default_enrol_roles($coursecontext);
1626
        foreach ($allroles as $role) {
1627
            $this->assertEquals(isset($expected[$role->id]), isset($roles[$role->id]));
1628
            if (isset($roles[$role->id])) {
1629
                $this->assertSame(role_get_name($role, $coursecontext), $roles[$role->id]);
1630
            }
1631
        }
1632
    }
1633
 
1634
    /**
1635
     * Test getting of role users.
1636
     *
1637
     * @covers ::get_role_users
1638
     */
11 efrain 1639
    public function test_get_role_users(): void {
1 efrain 1640
        global $DB;
1641
 
1642
        $this->resetAfterTest();
1643
 
1644
        $systemcontext = context_system::instance();
1645
        $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1646
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1647
        $noeditteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
1648
        $course = $this->getDataGenerator()->create_course();
1649
        $coursecontext = context_course::instance($course->id);
1650
        $otherid = create_role('Other role', 'other', 'Some other role', '');
1651
        $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1652
        $DB->insert_record('role_names', $teacherrename);
1653
        $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1654
        $DB->insert_record('role_names', $otherrename);
1655
 
1656
        $user1 = $this->getDataGenerator()->create_user(array('firstname'=>'John', 'lastname'=>'Smith'));
1657
        role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1658
        $user2 = $this->getDataGenerator()->create_user(array('firstname'=>'Jan', 'lastname'=>'Kovar'));
1659
        role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1660
        $user3 = $this->getDataGenerator()->create_user();
1661
        $this->getDataGenerator()->enrol_user($user3->id, $course->id, $teacherrole->id);
1662
        $user4 = $this->getDataGenerator()->create_user();
1663
        $this->getDataGenerator()->enrol_user($user4->id, $course->id, $studentrole->id);
1664
        $this->getDataGenerator()->enrol_user($user4->id, $course->id, $noeditteacherrole->id);
1665
 
1666
        $group = $this->getDataGenerator()->create_group(array('courseid'=>$course->id));
1667
        groups_add_member($group, $user3);
1668
 
1669
        $users = get_role_users($teacherrole->id, $coursecontext);
1670
        $this->assertCount(2, $users);
1671
        $this->assertArrayHasKey($user1->id, $users);
1672
        $this->assertEquals($users[$user1->id]->id, $user1->id);
1673
        $this->assertEquals($users[$user1->id]->roleid, $teacherrole->id);
1674
        $this->assertEquals($users[$user1->id]->rolename, $teacherrole->name);
1675
        $this->assertEquals($users[$user1->id]->roleshortname, $teacherrole->shortname);
1676
        $this->assertEquals($users[$user1->id]->rolecoursealias, $teacherrename->name);
1677
        $this->assertArrayHasKey($user3->id, $users);
1678
        $this->assertEquals($users[$user3->id]->id, $user3->id);
1679
        $this->assertEquals($users[$user3->id]->roleid, $teacherrole->id);
1680
        $this->assertEquals($users[$user3->id]->rolename, $teacherrole->name);
1681
        $this->assertEquals($users[$user3->id]->roleshortname, $teacherrole->shortname);
1682
        $this->assertEquals($users[$user3->id]->rolecoursealias, $teacherrename->name);
1683
 
1684
        $users = get_role_users($teacherrole->id, $coursecontext, true);
1685
        $this->assertCount(3, $users);
1686
 
1687
        $users = get_role_users($teacherrole->id, $coursecontext, true, '', null, null, '', 2, 1);
1688
        $this->assertCount(1, $users);
1689
 
1690
        $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber');
1691
        $this->assertCount(2, $users);
1692
        $this->assertArrayHasKey($user1->id, $users);
1693
        $this->assertArrayHasKey($user3->id, $users);
1694
 
1695
        $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email');
1696
        $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1697
        $this->assertCount(2, $users);
1698
        $this->assertArrayHasKey($user1->id, $users);
1699
        $this->assertObjectHasProperty('lastname', $users[$user1->id]);
1700
        $this->assertObjectHasProperty('firstname', $users[$user1->id]);
1701
        $this->assertArrayHasKey($user3->id, $users);
1702
        $this->assertObjectHasProperty('lastname', $users[$user3->id]);
1703
        $this->assertObjectHasProperty('firstname', $users[$user3->id]);
1704
 
1705
        $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id AS id_alias');
1706
        $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1707
        $this->assertCount(2, $users);
1708
        $this->assertArrayHasKey($user1->id, $users);
1709
        $this->assertObjectHasProperty('id_alias', $users[$user1->id]);
1710
        $this->assertObjectHasProperty('lastname', $users[$user1->id]);
1711
        $this->assertObjectHasProperty('firstname', $users[$user1->id]);
1712
        $this->assertArrayHasKey($user3->id, $users);
1713
        $this->assertObjectHasProperty('id_alias', $users[$user3->id]);
1714
        $this->assertObjectHasProperty('lastname', $users[$user3->id]);
1715
        $this->assertObjectHasProperty('firstname', $users[$user3->id]);
1716
 
1717
        $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
1718
        $this->assertCount(1, $users);
1719
        $this->assertArrayHasKey($user3->id, $users);
1720
 
1721
        $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'));
1722
        $this->assertCount(1, $users);
1723
        $this->assertArrayHasKey($user1->id, $users);
1724
 
1725
        $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.id', 'ra.id');
1726
        $this->assertDebuggingNotCalled();
1727
        $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.userid', 'ra.userid');
1728
        $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1729
            'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1730
        $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false);
1731
        $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1732
            'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1733
        $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext,
1734
            false, 'u.id, u.firstname', 'u.id, u.firstname');
1735
        $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1736
            'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1737
    }
1738
 
1739
    /**
1740
     * Test used role query.
1741
     *
1742
     * @covers ::get_roles_used_in_context
1743
     */
11 efrain 1744
    public function test_get_roles_used_in_context(): void {
1 efrain 1745
        global $DB;
1746
 
1747
        $this->resetAfterTest();
1748
 
1749
        $systemcontext = context_system::instance();
1750
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1751
        $course = $this->getDataGenerator()->create_course();
1752
        $coursecontext = context_course::instance($course->id);
1753
        $otherid = create_role('Other role', 'other', 'Some other role', '');
1754
        $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1755
        $DB->insert_record('role_names', $teacherrename);
1756
        $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1757
        $DB->insert_record('role_names', $otherrename);
1758
 
1759
        $user1 = $this->getDataGenerator()->create_user();
1760
        role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1761
 
1762
        $roles = get_roles_used_in_context($coursecontext);
1763
        $this->assertCount(1, $roles);
1764
        $role = reset($roles);
1765
        $roleid = key($roles);
1766
        $this->assertEquals($roleid, $role->id);
1767
        $this->assertEquals($teacherrole->id, $role->id);
1768
        $this->assertSame($teacherrole->name, $role->name);
1769
        $this->assertSame($teacherrole->shortname, $role->shortname);
1770
        $this->assertEquals($teacherrole->sortorder, $role->sortorder);
1771
        $this->assertSame($teacherrename->name, $role->coursealias);
1772
 
1773
        $user2 = $this->getDataGenerator()->create_user();
1774
        role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1775
        role_assign($otherid, $user2->id, $systemcontext->id);
1776
 
1777
        $roles = get_roles_used_in_context($systemcontext);
1778
        $this->assertCount(2, $roles);
1779
    }
1780
 
1781
    /**
1782
     * Test roles used in course.
1783
     *
1784
     * @covers ::get_user_roles_in_course
1785
     */
11 efrain 1786
    public function test_get_user_roles_in_course(): void {
1 efrain 1787
        global $DB, $CFG;
1788
 
1789
        $this->resetAfterTest();
1790
 
1791
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1792
        $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1793
        $managerrole = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
1794
        $course = $this->getDataGenerator()->create_course();
1795
        $coursecontext = context_course::instance($course->id);
1796
        $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1797
        $DB->insert_record('role_names', $teacherrename);
1798
 
1799
        $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1800
        $this->assertTrue(in_array($teacherrole->id, $roleids));
1801
        $this->assertTrue(in_array($studentrole->id, $roleids));
1802
        $this->assertFalse(in_array($managerrole->id, $roleids));
1803
 
1804
        $user1 = $this->getDataGenerator()->create_user();
1805
        role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1806
        role_assign($studentrole->id, $user1->id, $coursecontext->id);
1807
        $user2 = $this->getDataGenerator()->create_user();
1808
        role_assign($studentrole->id, $user2->id, $coursecontext->id);
1809
        $user3 = $this->getDataGenerator()->create_user();
1810
        $user4 = $this->getDataGenerator()->create_user();
1811
        role_assign($managerrole->id, $user4->id, $coursecontext->id);
1812
 
1813
        $this->setAdminUser();
1814
 
1815
        $roles = get_user_roles_in_course($user1->id, $course->id);
1816
        $this->assertEquals([
1817
            role_get_name($teacherrole, $coursecontext),
1818
            role_get_name($studentrole, $coursecontext),
1819
        ], array_map('strip_tags', explode(', ', $roles)));
1820
 
1821
        $roles = get_user_roles_in_course($user2->id, $course->id);
1822
        $this->assertEquals([
1823
            role_get_name($studentrole, $coursecontext),
1824
        ], array_map('strip_tags', explode(', ', $roles)));
1825
 
1826
        $roles = get_user_roles_in_course($user3->id, $course->id);
1827
        $this->assertEmpty($roles);
1828
 
1829
        // Managers should be able to see a link to their own role type, given they can assign it in the context.
1830
        $this->setUser($user4);
1831
        $roles = get_user_roles_in_course($user4->id, $course->id);
1832
        $this->assertEquals([
1833
            role_get_name($managerrole, $coursecontext),
1834
        ], array_map('strip_tags', explode(', ', $roles)));
1835
 
1836
        // Managers should see 2 roles if viewing a user who has been enrolled as a student and a teacher in the course.
1837
        $roles = get_user_roles_in_course($user1->id, $course->id);
1838
        $this->assertEquals([
1839
            role_get_name($teacherrole, $coursecontext),
1840
            role_get_name($studentrole, $coursecontext),
1841
        ], array_map('strip_tags', explode(', ', $roles)));
1842
 
1843
        // Students should not see the manager role if viewing a manager's profile.
1844
        $this->setUser($user2);
1845
        $roles = get_user_roles_in_course($user4->id, $course->id);
1846
        $this->assertEmpty($roles); // Should see 0 roles on the manager's profile.
1847
    }
1848
 
1849
    /**
1850
     * Test get_user_roles and get_users_roles
1851
     *
1852
     * @covers ::get_user_roles
1853
     */
11 efrain 1854
    public function test_get_user_roles(): void {
1 efrain 1855
        global $DB, $CFG;
1856
 
1857
        $this->resetAfterTest();
1858
 
1859
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1860
        $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1861
        $course = $this->getDataGenerator()->create_course();
1862
        $coursecontext = context_course::instance($course->id);
1863
        $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1864
        $DB->insert_record('role_names', $teacherrename);
1865
 
1866
        $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1867
 
1868
        $user1 = $this->getDataGenerator()->create_user();
1869
        role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1870
        role_assign($studentrole->id, $user1->id, $coursecontext->id);
1871
        $user2 = $this->getDataGenerator()->create_user();
1872
        role_assign($studentrole->id, $user2->id, $coursecontext->id);
1873
        $user3 = $this->getDataGenerator()->create_user();
1874
 
1875
        $u1roles = get_user_roles($coursecontext, $user1->id);
1876
 
1877
        $u2roles = get_user_roles($coursecontext, $user2->id);
1878
 
1879
        $allroles = get_users_roles($coursecontext, [], false);
1880
        $specificuserroles = get_users_roles($coursecontext, [$user1->id, $user2->id]);
1881
        $this->assertEquals($u1roles, $allroles[$user1->id]);
1882
        $this->assertEquals($u1roles, $specificuserroles[$user1->id]);
1883
        $this->assertEquals($u2roles, $allroles[$user2->id]);
1884
        $this->assertEquals($u2roles, $specificuserroles[$user2->id]);
1885
    }
1886
 
1887
    /**
1888
     * Test has_capability(), has_any_capability() and has_all_capabilities().
1889
     *
1890
     * @covers ::has_capability
1891
     * @covers ::has_any_capability
1892
     * @covers ::has_all_capabilities
1893
     */
11 efrain 1894
    public function test_has_capability_and_friends(): void {
1 efrain 1895
        global $DB;
1896
 
1897
        $this->resetAfterTest();
1898
 
1899
        $course = $this->getDataGenerator()->create_course();
1900
        $coursecontext = context_course::instance($course->id);
1901
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1902
        $teacher = $this->getDataGenerator()->create_user();
1903
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
1904
        $admin = $DB->get_record('user', array('username'=>'admin'));
1905
 
1906
        // Note: Here are used default capabilities, the full test is in permission evaluation bellow,
1907
        // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
1908
 
1909
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupsection')));
1910
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse')));
1911
        $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/site:approvecourse')));
1912
 
1913
        $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse', 'moodle/site:approvecourse');
1914
        $sc = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
1915
 
1916
        $this->setUser(0);
1917
        $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext));
1918
        $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext));
1919
        $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1920
        $this->assertFalse(has_any_capability($sca, $coursecontext));
1921
        $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1922
 
1923
        $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $teacher));
1924
        $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $teacher));
1925
        $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $teacher));
1926
        $this->assertTrue(has_any_capability($sca, $coursecontext, $teacher));
1927
        $this->assertTrue(has_all_capabilities($sc, $coursecontext, $teacher));
1928
        $this->assertFalse(has_all_capabilities($sca, $coursecontext, $teacher));
1929
 
1930
        $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $admin));
1931
        $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $admin));
1932
        $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext, $admin));
1933
        $this->assertTrue(has_any_capability($sca, $coursecontext, $admin));
1934
        $this->assertTrue(has_all_capabilities($sc, $coursecontext, $admin));
1935
        $this->assertTrue(has_all_capabilities($sca, $coursecontext, $admin));
1936
 
1937
        $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, $admin, false));
1938
        $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, $admin, false));
1939
        $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $admin, false));
1940
        $this->assertFalse(has_any_capability($sca, $coursecontext, $admin, false));
1941
        $this->assertFalse(has_all_capabilities($sc, $coursecontext, $admin, false));
1942
        $this->assertFalse(has_all_capabilities($sca, $coursecontext, $admin, false));
1943
 
1944
        $this->setUser($teacher);
1945
        $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1946
        $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1947
        $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1948
        $this->assertTrue(has_any_capability($sca, $coursecontext));
1949
        $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1950
        $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1951
 
1952
        $this->setAdminUser();
1953
        $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1954
        $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1955
        $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext));
1956
        $this->assertTrue(has_any_capability($sca, $coursecontext));
1957
        $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1958
        $this->assertTrue(has_all_capabilities($sca, $coursecontext));
1959
 
1960
        $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, 0));
1961
        $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, 0));
1962
        $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, 0));
1963
        $this->assertFalse(has_any_capability($sca, $coursecontext, 0));
1964
        $this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
1965
    }
1966
 
1967
    /**
1968
     * Utility method to fake a plugin
1969
     *
1970
     * @param string $pluginname plugin name
1971
     * @return void
1972
     */
1973
    protected function setup_fake_plugin($pluginname) {
1974
        global $CFG;
1975
        // Here we have to hack the component loader so we can insert our fake plugin and test that
1976
        // the access.php works.
1977
        $mockedcomponent = new ReflectionClass(core_component::class);
1978
        $mockedplugins = $mockedcomponent->getProperty('plugins');
1979
        $plugins = $mockedplugins->getValue();
1980
        $plugins['fake'] = [$pluginname => "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins/$pluginname"];
1981
        $mockedplugins->setValue(null, $plugins);
1982
        update_capabilities('fake_access');
1983
        $this->resetDebugging(); // We have debugging messages here that we need to get rid of.
1984
        // End of the component loader mock.
1985
    }
1986
 
1987
    /**
1988
     * Test get_deprecated_capability_info()
1989
     *
1990
     * @covers ::get_deprecated_capability_info
1991
     */
11 efrain 1992
    public function test_get_deprecated_capability_info(): void {
1 efrain 1993
        $this->resetAfterTest();
1994
        $course = $this->getDataGenerator()->create_course();
1995
        $coursecontext = context_course::instance($course->id);
1996
        $user = $this->getDataGenerator()->create_and_enrol($course);
1997
        $this->setup_fake_plugin('access');
1998
 
1999
        // For now we have deprecated fake/access:fakecapability.
2000
        $capinfo = get_deprecated_capability_info('fake/access:fakecapability');
1441 ariadna 2001
        $this->assertNotNull(get_capability_info('fake/access:existingcapability'));
1 efrain 2002
        $this->assertNotEmpty($capinfo);
2003
        $this->assertEquals("The capability 'fake/access:fakecapability' is"
2004
            . " deprecated.This capability should not be used anymore.", $capinfo['fullmessage']);
2005
    }
2006
 
2007
    /**
2008
     * Test get_deprecated_capability_info() through has_capability
2009
     *
2010
     * @covers ::get_deprecated_capability_info
2011
     */
11 efrain 2012
    public function test_get_deprecated_capability_info_through_has_capability(): void {
1 efrain 2013
        $this->resetAfterTest();
2014
        $course = $this->getDataGenerator()->create_course();
2015
        $coursecontext = context_course::instance($course->id);
2016
        $user = $this->getDataGenerator()->create_and_enrol($course);
2017
        $this->setup_fake_plugin('access');
2018
 
2019
        // For now we have deprecated fake/access:fakecapability.
2020
        $hascap = has_capability('fake/access:fakecapability', $coursecontext, $user);
2021
        $this->assertTrue($hascap);
2022
        $this->assertDebuggingCalled("The capability 'fake/access:fakecapability' is deprecated."
2023
            . "This capability should not be used anymore.");
2024
    }
2025
 
2026
    /**
2027
     * Test get_deprecated_capability_info() through get_user_capability_contexts()
2028
     *
2029
     * @covers ::get_deprecated_capability_info
2030
     */
11 efrain 2031
    public function test_get_deprecated_capability_info_through_get_user_capability_contexts(): void {
1 efrain 2032
        $this->resetAfterTest();
2033
        $category = $this->getDataGenerator()->create_category();
2034
        $course = $this->getDataGenerator()->create_course(['categoryid' => $category->id]);
2035
        $user = $this->getDataGenerator()->create_and_enrol($course);
2036
        $this->setup_fake_plugin('access');
2037
 
2038
        // For now we have deprecated fake/access:fakecapability.
2039
        list($categories, $courses) = get_user_capability_contexts('fake/access:fakecapability', false, $user->id);
2040
        $this->assertNotEmpty($courses);
2041
        $this->assertDebuggingCalled("The capability 'fake/access:fakecapability' is deprecated."
2042
                . "This capability should not be used anymore.");
2043
    }
2044
 
2045
    /**
2046
     * Test get_deprecated_capability_info with a capability that does not exist
2047
     *
2048
     * @param string $capability the capability name
2049
     * @param array $debugmessages the debug messsages we expect
2050
     * @param bool $expectedexisting does the capability exist
2051
     * @covers ::get_deprecated_capability_info
2052
     * @dataProvider deprecated_capabilities_use_cases
2053
     */
2054
    public function test_get_deprecated_capability_specific_cases(string $capability, array $debugmessages,
11 efrain 2055
        bool $expectedexisting): void {
1 efrain 2056
        $this->resetAfterTest();
2057
        $course = $this->getDataGenerator()->create_course();
2058
        $coursecontext = context_course::instance($course->id);
2059
        $user = $this->getDataGenerator()->create_and_enrol($course);
2060
        $this->setup_fake_plugin('access');
2061
 
2062
        // For now we have deprecated fake/access:fakecapability.
2063
        $this->resetDebugging();
2064
        $hascap = has_capability($capability, $coursecontext, $user);
2065
        $this->assertEquals($expectedexisting, $hascap);
2066
        $this->assertDebuggingCalledCount(count($debugmessages), $debugmessages);
2067
    }
2068
 
2069
    /**
2070
     * Specific use case for deprecated capabilities
2071
     *
2072
     * @return array
2073
     */
1441 ariadna 2074
    public static function deprecated_capabilities_use_cases(): array {
1 efrain 2075
        return [
2076
            'capability missing' => [
2077
                'fake/access:missingcapability',
2078
                [
2079
                    "Capability \"fake/access:missingcapability\" was not found! This has to be fixed in code."
2080
                ],
2081
                false
2082
            ],
2083
            'replacement no info' => [
2084
                'fake/access:replacementnoinfo',
2085
                [
2086
                    "The capability 'fake/access:replacementnoinfo' is deprecated.",
2087
                ],
2088
                true
2089
            ],
2090
            'replacement missing' => [
2091
                'fake/access:replacementmissing',
2092
                [
2093
                    "The capability 'fake/access:replacementmissing' is deprecated.This capability should not be used anymore.",
2094
                ],
2095
                true
2096
            ],
2097
            'replacement with non existing cap' => [
2098
                'fake/access:replacementwithwrongcapability',
2099
                [
2100
                    "Capability 'fake/access:replacementwithwrongcapability' was supposed to be replaced with"
2101
                    . " 'fake/access:nonexistingcapabilty', which does not exist !",
2102
                    "The capability 'fake/access:replacementwithwrongcapability' is deprecated."
2103
                    . "This capability should not be used anymore.It will be replaced by 'fake/access:nonexistingcapabilty'."
2104
                ],
2105
                true
2106
            ],
2107
            'replacement with existing' => [
2108
                'fake/access:replacementwithexisting', // Existing capability buf for a different role.
2109
                [
2110
                    "The capability 'fake/access:replacementwithexisting' is deprecated.This capability should not be used anymore."
2111
                    . "It will be replaced by 'fake/access:existingcapability'.",
2112
                ],
2113
                false // As the capability is applied to managers, we should not have this capability for this simple user.
2114
            ],
2115
        ];
2116
    }
2117
 
2118
    /**
1441 ariadna 2119
     * Test get_deprecated_capability_info() with an invalid component.
2120
     *
2121
     * @covers get_deprecated_capability_info
2122
     */
2123
    public function test_get_deprecated_capability_info_invalid_component(): void {
2124
        global $DB;
2125
 
2126
        $this->resetAfterTest();
2127
 
2128
        // Set up a fake plugin.
2129
        $this->setup_fake_plugin('access');
2130
 
2131
        // Add a plugin for an unrelated fake component.
2132
        $DB->insert_record('capabilities', [
2133
            'name' => 'mod/fake:addinstance',
2134
            'captype' => 'write',
2135
            'contextlevel' => CONTEXT_COURSE,
2136
            'component' => 'mod_fake',
2137
            'riskbitmask' => 4,
2138
        ]);
2139
 
2140
        // Purge the cache.
2141
        cache::make('core', 'capabilities')->purge();
2142
 
2143
        // For now we have deprecated fake/access:fakecapability.
2144
        $this->assertNotEmpty($DB->get_record('capabilities', ['component' => 'mod_fake']));
2145
        $info = get_deprecated_capability_info('fake/access:fakecapability');
2146
        $this->assertIsArray($info);
2147
        $this->assertDebuggingNotCalled();
2148
    }
2149
 
2150
    /**
1 efrain 2151
     * Test that assigning a fake cap does not return.
2152
     *
2153
     * @covers ::get_users_by_capability
2154
     * @covers ::get_with_capability_join
2155
     * @covers ::get_with_capability_sql
2156
     * @covers ::has_capability
2157
     */
11 efrain 2158
    public function test_fake_capability(): void {
1 efrain 2159
        global $DB;
2160
 
2161
        $this->resetAfterTest();
2162
 
2163
        $course = $this->getDataGenerator()->create_course();
2164
        $coursecontext = context_course::instance($course->id);
2165
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
2166
        $teacher = $this->getDataGenerator()->create_user();
2167
 
2168
        $fakecapname = 'moodle/fake:capability';
2169
 
2170
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
2171
        $admin = $DB->get_record('user', array('username' => 'admin'));
2172
 
2173
        // Test a capability which does not exist.
2174
        // Note: Do not use assign_capability because it will not allow fake caps.
2175
        $DB->insert_record('role_capabilities', (object) [
2176
            'contextid' => $coursecontext->id,
2177
            'roleid' => $teacherrole->id,
2178
            'capability' => $fakecapname,
2179
            'permission' => CAP_ALLOW,
2180
            'timemodified' => time(),
2181
            'modifierid' => 0,
2182
        ]);
2183
 
2184
        // Check `has_capability`.
2185
        $this->assertFalse(has_capability($fakecapname, $coursecontext, $teacher));
2186
        $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
2187
        $this->assertFalse(has_capability($fakecapname, $coursecontext, $admin));
2188
        $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
2189
 
2190
        // Check `get_with_capability_sql` (with uses `get_with_capability_join`).
2191
        list($sql, $params) = get_with_capability_sql($coursecontext, $fakecapname);
2192
        $users = $DB->get_records_sql($sql, $params);
2193
 
2194
        $this->assertFalse(array_key_exists($teacher->id, $users));
2195
        $this->assertFalse(array_key_exists($admin->id, $users));
2196
 
2197
        // Check `get_users_by_capability`.
2198
        $users = get_users_by_capability($coursecontext, $fakecapname);
2199
 
2200
        $this->assertFalse(array_key_exists($teacher->id, $users));
2201
        $this->assertFalse(array_key_exists($admin->id, $users));
2202
    }
2203
 
2204
    /**
2205
     * Test that assigning a fake cap does not return.
2206
     *
2207
     * @covers ::assign_capability
2208
     */
11 efrain 2209
    public function test_fake_capability_assign(): void {
1 efrain 2210
        global $DB;
2211
 
2212
        $this->resetAfterTest();
2213
 
2214
        $course = $this->getDataGenerator()->create_course();
2215
        $coursecontext = context_course::instance($course->id);
2216
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
2217
        $teacher = $this->getDataGenerator()->create_user();
2218
 
2219
        $capability = 'moodle/fake:capability';
2220
 
2221
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
2222
        $admin = $DB->get_record('user', array('username' => 'admin'));
2223
 
2224
        $this->expectException('coding_exception');
2225
        $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
2226
        assign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
2227
    }
2228
 
2229
    /**
2230
     * Test that assigning a fake cap does not return.
2231
     *
2232
     * @covers ::unassign_capability
2233
     */
11 efrain 2234
    public function test_fake_capability_unassign(): void {
1 efrain 2235
        global $DB;
2236
 
2237
        $this->resetAfterTest();
2238
 
2239
        $course = $this->getDataGenerator()->create_course();
2240
        $coursecontext = context_course::instance($course->id);
2241
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
2242
        $teacher = $this->getDataGenerator()->create_user();
2243
 
2244
        $capability = 'moodle/fake:capability';
2245
 
2246
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
2247
        $admin = $DB->get_record('user', array('username' => 'admin'));
2248
 
2249
        $this->expectException('coding_exception');
2250
        $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
1441 ariadna 2251
        unassign_capability($capability, $teacherrole->id, $coursecontext);
1 efrain 2252
    }
2253
 
2254
    /**
2255
     * Test that the caching in get_role_definitions() and get_role_definitions_uncached()
2256
     * works as intended.
2257
     *
2258
     * @covers ::get_role_definitions
2259
     * @covers ::role_change_permission
2260
     */
11 efrain 2261
    public function test_role_definition_caching(): void {
1 efrain 2262
        global $DB;
2263
 
2264
        $this->resetAfterTest();
2265
 
2266
        // Get some role ids.
2267
        $authenticatedrole = $DB->get_record('role', array('shortname' => 'user'), '*', MUST_EXIST);
2268
        $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2269
        $emptyroleid = create_role('No capabilities', 'empty', 'A role with no capabilties');
2270
        $course = $this->getDataGenerator()->create_course();
2271
        $coursecontext = context_course::instance($course->id);
2272
 
2273
        // Instantiate the cache instance, since that does DB queries (get_config)
2274
        // and we don't care about those.
2275
        cache::make('core', 'roledefs');
2276
 
2277
        // One database query is not necessarily one database read, it seems. Find out how many.
2278
        $startdbreads = $DB->perf_get_reads();
2279
        $rs = $DB->get_recordset('user');
2280
        $rs->close();
2281
        $readsperquery = $DB->perf_get_reads() - $startdbreads;
2282
 
2283
        // Now load some role definitions, and check when it queries the database.
2284
 
2285
        // Load the capabilities for two roles. Should be one query.
2286
        $startdbreads = $DB->perf_get_reads();
2287
        get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2288
        $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2289
 
2290
        // Load the capabilities for same two roles. Should not query the DB.
2291
        $startdbreads = $DB->perf_get_reads();
2292
        get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2293
        $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2294
 
2295
        // Include a third role. Should do one DB query.
2296
        $startdbreads = $DB->perf_get_reads();
2297
        get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2298
        $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2299
 
2300
        // Repeat call. No DB queries.
2301
        $startdbreads = $DB->perf_get_reads();
2302
        get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2303
        $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2304
 
2305
        // Alter a role.
2306
        role_change_permission($studentrole->id, $coursecontext, 'moodle/course:tag', CAP_ALLOW);
2307
 
2308
        // Should now know to do one query.
2309
        $startdbreads = $DB->perf_get_reads();
2310
        get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2311
        $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2312
 
2313
        // Now clear the in-memory cache, and verify that it does not query the DB.
2314
        // Cannot use accesslib_clear_all_caches_for_unit_testing since that also
2315
        // clears the MUC cache.
2316
        global $ACCESSLIB_PRIVATE;
2317
        $ACCESSLIB_PRIVATE->cacheroledefs = array();
2318
 
2319
        // Get all roles. Should not need the DB.
2320
        $startdbreads = $DB->perf_get_reads();
2321
        get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2322
        $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2323
    }
2324
 
2325
    /**
2326
     * Tests get_user_capability_course() which checks a capability across all courses.
2327
     *
2328
     * @covers ::get_user_capability_course
2329
     */
11 efrain 2330
    public function test_get_user_capability_course(): void {
1 efrain 2331
        global $CFG, $USER;
2332
 
2333
        $this->resetAfterTest();
2334
 
2335
        $generator = $this->getDataGenerator();
2336
        $cap = 'moodle/course:view';
2337
 
2338
        // The structure being created here is this:
2339
        //
2340
        // All tests work with the single capability 'moodle/course:view'.
2341
        //
2342
        //             ROLE DEF/OVERRIDE                        ROLE ASSIGNS
2343
        //    Role:  Allow    Prohib    Empty   Def user      u1  u2  u3  u4   u5  u6  u7  u8
2344
        // System    ALLOW    PROHIBIT                            A   E   A+E
2345
        //   cat1                       ALLOW
2346
        //     C1                               (ALLOW)                            P
2347
        //     C2             ALLOW                                                    E   P
2348
        //     cat2                     PREVENT
2349
        //       C3                     ALLOW                                      E
2350
        //       C4
2351
        //   Misc.                                                             A
2352
        //     C5    PREVENT                                                       A
2353
        //     C6                       PROHIBIT
2354
        //
2355
        // Front-page and guest role stuff from the end of this test not included in the diagram.
2356
 
2357
        // Create a role which allows course:view and one that prohibits it, and one neither.
2358
        $allowroleid = $generator->create_role();
2359
        $prohibitroleid = $generator->create_role();
2360
        $emptyroleid = $generator->create_role();
2361
        $systemcontext = context_system::instance();
2362
        assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
2363
        assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
2364
 
2365
        // Create two categories (nested).
2366
        $cat1 = $generator->create_category();
2367
        $cat2 = $generator->create_category(['parent' => $cat1->id]);
2368
 
2369
        // Create six courses - two in cat1, two in cat2, and two in default category.
2370
        // Shortnames are used for a sorting test. Otherwise they are not significant.
2371
        $c1 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Z']);
2372
        $c2 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Y']);
2373
        $c3 = $generator->create_course(['category' => $cat2->id, 'shortname' => 'X']);
2374
        $c4 = $generator->create_course(['category' => $cat2->id]);
2375
        $c5 = $generator->create_course();
2376
        $c6 = $generator->create_course();
2377
 
2378
        // Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
2379
        assign_capability($cap, CAP_ALLOW, $emptyroleid,
2380
                context_coursecat::instance($cat1->id)->id);
2381
        assign_capability($cap, CAP_PREVENT, $emptyroleid,
2382
                context_coursecat::instance($cat2->id)->id);
2383
 
2384
        // Course overrides: in C5, allow role is prevented; in C6, empty role is prohibited; in
2385
        // C3, empty role is allowed.
2386
        assign_capability($cap, CAP_PREVENT, $allowroleid,
2387
                context_course::instance($c5->id)->id);
2388
        assign_capability($cap, CAP_PROHIBIT, $emptyroleid,
2389
                context_course::instance($c6->id)->id);
2390
        assign_capability($cap, CAP_ALLOW, $emptyroleid,
2391
                context_course::instance($c3->id)->id);
2392
        assign_capability($cap, CAP_ALLOW, $prohibitroleid,
2393
                context_course::instance($c2->id)->id);
2394
 
2395
        // User 1 has no roles except default user role.
2396
        $u1 = $generator->create_user();
2397
 
2398
        // It returns false (annoyingly) if there are no courses.
2399
        $this->assertFalse(get_user_capability_course($cap, $u1->id, true, '', 'id'));
2400
 
2401
        // Final override: in C1, default user role is allowed.
2402
        assign_capability($cap, CAP_ALLOW, $CFG->defaultuserroleid,
2403
                context_course::instance($c1->id)->id);
2404
 
2405
        // Should now get C1 only.
2406
        $courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
2407
        $this->assert_course_ids([$c1->id], $courses);
2408
 
2409
        // User 2 has allow role (system wide).
2410
        $u2 = $generator->create_user();
2411
        role_assign($allowroleid, $u2->id, $systemcontext->id);
2412
 
2413
        // Should get everything except C5.
2414
        $courses = get_user_capability_course($cap, $u2->id, true, '', 'id');
2415
        $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c6->id], $courses);
2416
 
2417
        // User 3 has empty role (system wide).
2418
        $u3 = $generator->create_user();
2419
        role_assign($emptyroleid, $u3->id, $systemcontext->id);
2420
 
2421
        // Should get cat 1 courses but not cat2, except C3.
2422
        $courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
2423
        $this->assert_course_ids([$c1->id, $c2->id, $c3->id], $courses);
2424
 
2425
        // User 4 has allow and empty role (system wide).
2426
        $u4 = $generator->create_user();
2427
        role_assign($allowroleid, $u4->id, $systemcontext->id);
2428
        role_assign($emptyroleid, $u4->id, $systemcontext->id);
2429
 
2430
        // Should get everything except C5 and C6.
2431
        $courses = get_user_capability_course($cap, $u4->id, true, '', 'id');
2432
        $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id], $courses);
2433
 
2434
        // User 5 has allow role in default category only.
2435
        $u5 = $generator->create_user();
2436
        role_assign($allowroleid, $u5->id, context_coursecat::instance($c5->category)->id);
2437
 
2438
        // Should get C1 and the default category courses but not C5.
2439
        $courses = get_user_capability_course($cap, $u5->id, true, '', 'id');
2440
        $this->assert_course_ids([$c1->id, $c6->id], $courses);
2441
 
2442
        // User 6 has a bunch of course roles: prohibit role in C1, empty role in C3, allow role in
2443
        // C6.
2444
        $u6 = $generator->create_user();
2445
        role_assign($prohibitroleid, $u6->id, context_course::instance($c1->id)->id);
2446
        role_assign($emptyroleid, $u6->id, context_course::instance($c3->id)->id);
2447
        role_assign($allowroleid, $u6->id, context_course::instance($c5->id)->id);
2448
 
2449
        // Should get C3 only because the allow role is prevented in C5.
2450
        $courses = get_user_capability_course($cap, $u6->id, true, '', 'id');
2451
        $this->assert_course_ids([$c3->id], $courses);
2452
 
2453
        // User 7 has empty role in C2.
2454
        $u7 = $generator->create_user();
2455
        role_assign($emptyroleid, $u7->id, context_course::instance($c2->id)->id);
2456
 
2457
        // Should get C1 by the default user role override, and C2 by the cat1 level override.
2458
        $courses = get_user_capability_course($cap, $u7->id, true, '', 'id');
2459
        $this->assert_course_ids([$c1->id, $c2->id], $courses);
2460
 
2461
        // User 8 has prohibit role as system context, to verify that prohibits can't be overridden.
2462
        $u8 = $generator->create_user();
2463
        role_assign($prohibitroleid, $u8->id, context_course::instance($c2->id)->id);
2464
 
2465
        // Should get C1 by the default user role override, no other courses because the prohibit cannot be overridden.
2466
        $courses = get_user_capability_course($cap, $u8->id, true, '', 'id');
2467
        $this->assert_course_ids([$c1->id], $courses);
2468
 
2469
        // Admin user gets everything....
2470
        $courses = get_user_capability_course($cap, get_admin()->id, true, '', 'id');
2471
        $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c5->id, $c6->id],
2472
                $courses);
2473
 
2474
        // Unless you turn off doanything, when it only has the things a user with no role does.
2475
        $courses = get_user_capability_course($cap, get_admin()->id, false, '', 'id');
2476
        $this->assert_course_ids([$c1->id], $courses);
2477
 
2478
        // Using u3 as an example, test the limit feature.
2479
        $courses = get_user_capability_course($cap, $u3->id, true, '', 'id', 2);
2480
        $this->assert_course_ids([$c1->id, $c2->id], $courses);
2481
 
2482
        // Check sorting.
2483
        $courses = get_user_capability_course($cap, $u3->id, true, '', 'shortname');
2484
        $this->assert_course_ids([$c3->id, $c2->id, $c1->id], $courses);
2485
 
2486
        // Check returned fields - default.
2487
        $courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
2488
        $this->assertEquals((object)['id' => $c1->id], $courses[0]);
2489
 
2490
        // Add a selection of fields, including the context ones with special handling.
2491
        $courses = get_user_capability_course($cap, $u3->id, true, 'shortname, ctxlevel, ctxdepth, ctxinstance', 'id');
2492
        $this->assertEquals((object)['id' => $c1->id, 'shortname' => 'Z', 'ctxlevel' => 50,
2493
                'ctxdepth' => 3, 'ctxinstance' => $c1->id], $courses[0]);
2494
 
2495
        // Test front page role - user 1 has no roles, but if we change the front page role
2496
        // definition so that it has our capability, then they should see the front page course.
2497
        // as well as C1.
2498
        assign_capability($cap, CAP_ALLOW, $CFG->defaultfrontpageroleid, $systemcontext->id);
2499
        $courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
2500
        $this->assert_course_ids([SITEID, $c1->id], $courses);
2501
 
2502
        // Check that temporary guest access (in this case, given on course 2 for user 1)
2503
        // also is included, if it has this capability.
2504
        assign_capability($cap, CAP_ALLOW, $CFG->guestroleid, $systemcontext->id);
2505
        $this->setUser($u1);
2506
        load_temp_course_role(context_course::instance($c2->id), $CFG->guestroleid);
2507
        $courses = get_user_capability_course($cap, $USER->id, true, '', 'id');
2508
        $this->assert_course_ids([SITEID, $c1->id, $c2->id], $courses);
2509
    }
2510
 
2511
    /**
2512
     * Tests get_user_capability_contexts() which checks a capability across all courses and categories.
2513
     * Testing for categories only because courses results are covered by test_get_user_capability_course.
2514
     *
2515
     * @covers ::get_user_capability_contexts
2516
     */
11 efrain 2517
    public function test_get_user_capability_contexts(): void {
1 efrain 2518
        $this->resetAfterTest();
2519
 
2520
        $generator = $this->getDataGenerator();
2521
        $cap = 'moodle/contentbank:access';
2522
        $defaultcategoryid = 1;
2523
 
2524
//         The structure being created here is this:
2525
//
2526
//         All tests work with the single capability 'moodle/contentbank:access'.
2527
//         ROLE DEF/OVERRIDE                                                    .
2528
//         Role:                Allow       Prohibit        Empty               .
2529
//                  System      ALLOW       PROHIBIT                            .
2530
//                  cat1        PREVENT     ALLOW           ALLOW               .
2531
//                      cat3    ALLOW       PROHIBIT                            .
2532
//                 cat2        PROHIBIT    PROHIBIT        PROHIBIT             .
2533
 
2534
        // Create a role which allows contentbank:access and one that prohibits it, and one neither.
2535
        $allowroleid = $generator->create_role();
2536
        $prohibitroleid = $generator->create_role();
2537
        $emptyroleid = $generator->create_role();
2538
        $systemcontext = context_system::instance();
2539
        assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
2540
        assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
2541
 
2542
        // Create three categories (two of them nested).
2543
        $cat1 = $generator->create_category(['name' => 'Aardvarks']);
2544
        $cat2 = $generator->create_category(['name' => 'Badgers']);
2545
        $cat3 = $generator->create_category(['parent' => $cat1->id, 'name' => 'Cheetahs']);
2546
 
2547
        // Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
2548
        assign_capability($cap, CAP_ALLOW, $emptyroleid,
2549
            context_coursecat::instance($cat1->id)->id);
2550
        assign_capability($cap, CAP_PREVENT, $emptyroleid,
2551
            context_coursecat::instance($cat2->id)->id);
2552
 
2553
        // Course category overrides: in cat1, allow role is prevented and prohibit role is allowed;
2554
        // in Cat2, allow role is prohibited.
2555
        assign_capability($cap, CAP_PREVENT, $allowroleid,
2556
            context_coursecat::instance($cat1->id)->id);
2557
        assign_capability($cap, CAP_ALLOW, $prohibitroleid,
2558
            context_coursecat::instance($cat1->id)->id);
2559
        assign_capability($cap, CAP_PROHIBIT, $allowroleid,
2560
            context_coursecat::instance($cat2->id)->id);
2561
 
2562
        // User 1 has no roles except default user role.
2563
        $u1 = $generator->create_user();
2564
 
2565
        // It returns false (annoyingly) if there are no course categories.
2566
        list($categories, $courses) = get_user_capability_contexts($cap, true, $u1->id);
2567
        $this->assertFalse($categories);
2568
 
2569
        // User 2 has allow role (system wide).
2570
        $u2 = $generator->create_user();
2571
        role_assign($allowroleid, $u2->id, $systemcontext->id);
2572
 
2573
        // Should get $defaultcategory only. cat2 is prohibited; cat1 is prevented, so cat3 is not allowed.
2574
        list($categories, $courses) = get_user_capability_contexts($cap, true, $u2->id);
2575
        // Using same assert_course_ids helper even when we are checking course category ids.
2576
        $this->assert_course_ids([$defaultcategoryid], $categories);
2577
 
2578
        // User 3 has empty role (system wide).
2579
        $u3 = $generator->create_user();
2580
        role_assign($emptyroleid, $u3->id, $systemcontext->id);
2581
 
2582
        // Should get cat1 and cat3. cat2 is prohibited; no access to system level. Sorted by category name.
2583
        list($categories, $courses) = get_user_capability_contexts($cap, true, $u3->id, true, '', '', '', 'name');
2584
        $this->assert_course_ids([$cat1->id, $cat3->id], $categories);
2585
 
2586
        // User 4 has prohibit role (system wide).
2587
        $u4 = $generator->create_user();
2588
        role_assign($prohibitroleid, $u4->id, $systemcontext->id);
2589
 
2590
        // Should not get any, because all of them are prohibited at system level.
2591
        // Even if we try to allow an specific category.
2592
        list($categories, $courses) = get_user_capability_contexts($cap, true, $u4->id);
2593
        $this->assertFalse($categories);
2594
    }
2595
 
2596
    /**
2597
     * Extracts an array of course ids to make the above test script shorter.
2598
     *
2599
     * @param int[] $expected Array of expected course ids
2600
     * @param stdClass[] $courses Array of course objects
2601
     */
2602
    protected function assert_course_ids(array $expected, array $courses) {
2603
        $courseids = array_map(function($c) {
2604
            return $c->id;
2605
        }, $courses);
2606
        $this->assertEquals($expected, $courseids);
2607
    }
2608
 
2609
    /**
2610
     * Test if course creator future capability lookup works.
2611
     *
2612
     * @covers ::guess_if_creator_will_have_course_capability
2613
     * @covers ::has_capability
2614
     */
11 efrain 2615
    public function test_guess_if_creator_will_have_course_capability(): void {
1 efrain 2616
        global $DB, $CFG, $USER;
2617
 
2618
        $this->resetAfterTest();
2619
 
2620
        $category = $this->getDataGenerator()->create_category();
2621
        $course = $this->getDataGenerator()->create_course(array('category'=>$category->id));
2622
 
2623
        $syscontext = context_system::instance();
2624
        $categorycontext = context_coursecat::instance($category->id);
2625
        $coursecontext = context_course::instance($course->id);
2626
        $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
2627
        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
2628
        $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST);
2629
        $managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
2630
 
2631
        $this->assertEquals($teacherrole->id, $CFG->creatornewroleid);
2632
 
2633
        $creator = $this->getDataGenerator()->create_user();
2634
        $manager = $this->getDataGenerator()->create_user();
2635
        role_assign($managerrole->id, $manager->id, $categorycontext);
2636
 
2637
        $this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator));
2638
        $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
2639
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2640
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2641
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2642
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2643
 
2644
        $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2645
        $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager));
2646
        $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager));
2647
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id));
2648
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id));
2649
 
2650
        $this->assertEquals(0, $USER->id);
2651
        $this->assertFalse(has_capability('moodle/course:view', $categorycontext));
2652
        $this->assertFalse(has_capability('moodle/role:assign', $categorycontext));
2653
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext));
2654
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext));
2655
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2656
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2657
 
2658
        $this->setUser($manager);
2659
        $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
2660
        $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
2661
        $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
2662
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2663
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2664
 
2665
        $this->setAdminUser();
2666
        $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
2667
        $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
2668
        $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
2669
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2670
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2671
        $this->setUser(0);
2672
 
2673
        role_assign($creatorrole->id, $creator->id, $categorycontext);
2674
 
2675
        $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
2676
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2677
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2678
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2679
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2680
 
2681
        $this->setUser($creator);
2682
        $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, null));
2683
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, null));
2684
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, null));
2685
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, null));
2686
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, null));
2687
        $this->setUser(0);
2688
 
2689
        set_config('creatornewroleid', $studentrole->id);
2690
 
2691
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2692
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2693
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2694
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2695
 
2696
        set_config('creatornewroleid', $teacherrole->id);
2697
 
2698
        role_change_permission($managerrole->id, $categorycontext, 'moodle/course:visibility', CAP_PREVENT);
2699
        role_assign($creatorrole->id, $manager->id, $categorycontext);
2700
 
2701
        $this->assertTrue(has_capability('moodle/course:view', $categorycontext, $manager));
2702
        $this->assertTrue(has_capability('moodle/course:view', $coursecontext, $manager));
2703
        $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2704
        $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
2705
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
2706
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
2707
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
2708
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
2709
 
2710
        role_change_permission($managerrole->id, $categorycontext, 'moodle/course:view', CAP_PREVENT);
2711
        $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2712
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
2713
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
2714
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
2715
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
2716
 
2717
        $this->getDataGenerator()->enrol_user($manager->id, $course->id, 0);
2718
 
2719
        $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2720
        $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
2721
        $this->assertTrue(is_enrolled($coursecontext, $manager));
2722
        $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
2723
        $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
2724
        $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
2725
        $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
2726
 
2727
        // Test problems.
2728
 
2729
        try {
2730
            guess_if_creator_will_have_course_capability('moodle/course:visibility', $syscontext, $creator);
2731
            $this->fail('Exception expected when non course/category context passed to guess_if_creator_will_have_course_capability()');
2732
        } catch (moodle_exception $e) {
2733
            $this->assertInstanceOf('coding_exception', $e);
2734
        }
2735
    }
2736
 
2737
    /**
2738
     * Test require_capability() exceptions.
2739
     *
2740
     * @covers ::require_capability
2741
     */
11 efrain 2742
    public function test_require_capability(): void {
1 efrain 2743
        $this->resetAfterTest();
2744
 
2745
        $syscontext = context_system::instance();
2746
 
2747
        $this->setUser(0);
2748
        $this->assertFalse(has_capability('moodle/site:config', $syscontext));
2749
        try {
2750
            require_capability('moodle/site:config', $syscontext);
2751
            $this->fail('Exception expected from require_capability()');
2752
        } catch (moodle_exception $e) {
2753
            $this->assertInstanceOf('required_capability_exception', $e);
2754
        }
2755
        $this->setAdminUser();
2756
        $this->assertFalse(has_capability('moodle/site:config', $syscontext, 0));
2757
        try {
2758
            require_capability('moodle/site:config', $syscontext, 0);
2759
            $this->fail('Exception expected from require_capability()');
2760
        } catch (moodle_exception $e) {
2761
            $this->assertInstanceOf('required_capability_exception', $e);
2762
        }
2763
        $this->assertFalse(has_capability('moodle/site:config', $syscontext, null, false));
2764
        try {
2765
            require_capability('moodle/site:config', $syscontext, null, false);
2766
            $this->fail('Exception expected from require_capability()');
2767
        } catch (moodle_exception $e) {
2768
            $this->assertInstanceOf('required_capability_exception', $e);
2769
        }
2770
    }
2771
 
2772
    /**
2773
     * Test that enrolled users SQL does not return any values for users in
2774
     * other courses.
2775
     *
2776
     *
2777
     * @covers ::get_enrolled_users
2778
     * @covers ::get_enrolled_sql
2779
     * @covers ::get_enrolled_with_capabilities_join
2780
     * @covers ::get_enrolled_join
2781
     * @covers ::get_with_capability_join
2782
     * @covers ::groups_get_members_join
2783
     * @covers ::get_suspended_userids
2784
     */
11 efrain 2785
    public function test_get_enrolled_sql_different_course(): void {
1 efrain 2786
        global $DB;
2787
 
2788
        $this->resetAfterTest();
2789
 
2790
        $course = $this->getDataGenerator()->create_course();
2791
        $context = context_course::instance($course->id);
2792
        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2793
        $user = $this->getDataGenerator()->create_user();
2794
 
2795
        // This user should not appear anywhere, we're not interested in that context.
2796
        $course2 = $this->getDataGenerator()->create_course();
2797
        $this->getDataGenerator()->enrol_user($user->id, $course2->id, $student->id);
2798
 
2799
        $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2800
        $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2801
        $suspended  = get_suspended_userids($context);
2802
 
2803
        $this->assertFalse(isset($enrolled[$user->id]));
2804
        $this->assertFalse(isset($active[$user->id]));
2805
        $this->assertFalse(isset($suspended[$user->id]));
2806
        $this->assertCount(0, $enrolled);
2807
        $this->assertCount(0, $active);
2808
        $this->assertCount(0, $suspended);
2809
    }
2810
 
2811
    /**
2812
     * Test that enrolled users SQL does not return any values for role
2813
     * assignments without an enrolment.
2814
     *
2815
     *
2816
     * @covers ::get_enrolled_users
2817
     * @covers ::get_enrolled_sql
2818
     * @covers ::get_enrolled_with_capabilities_join
2819
     * @covers ::get_enrolled_join
2820
     * @covers ::get_with_capability_join
2821
     * @covers ::groups_get_members_join
2822
     * @covers ::get_suspended_userids
2823
     */
11 efrain 2824
    public function test_get_enrolled_sql_role_only(): void {
1 efrain 2825
        global $DB;
2826
 
2827
        $this->resetAfterTest();
2828
 
2829
        $course = $this->getDataGenerator()->create_course();
2830
        $context = context_course::instance($course->id);
2831
        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2832
        $user = $this->getDataGenerator()->create_user();
2833
 
2834
        // Role assignment is not the same as course enrollment.
2835
        role_assign($student->id, $user->id, $context->id);
2836
 
2837
        $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2838
        $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2839
        $suspended  = get_suspended_userids($context);
2840
 
2841
        $this->assertFalse(isset($enrolled[$user->id]));
2842
        $this->assertFalse(isset($active[$user->id]));
2843
        $this->assertFalse(isset($suspended[$user->id]));
2844
        $this->assertCount(0, $enrolled);
2845
        $this->assertCount(0, $active);
2846
        $this->assertCount(0, $suspended);
2847
    }
2848
 
2849
    /**
2850
     * Test that multiple enrolments for the same user are counted correctly.
2851
     *
2852
     * @covers ::get_enrolled_users
2853
     * @covers ::get_enrolled_sql
2854
     * @covers ::get_enrolled_with_capabilities_join
2855
     * @covers ::get_enrolled_join
2856
     * @covers ::get_with_capability_join
2857
     * @covers ::groups_get_members_join
2858
     * @covers ::get_suspended_userids
2859
     */
11 efrain 2860
    public function test_get_enrolled_sql_multiple_enrolments(): void {
1 efrain 2861
        global $DB;
2862
 
2863
        $this->resetAfterTest();
2864
 
2865
        $course = $this->getDataGenerator()->create_course();
2866
        $context = context_course::instance($course->id);
2867
        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
2868
        $user = $this->getDataGenerator()->create_user();
2869
 
2870
        // Add a suspended enrol.
2871
        $selfinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'self'));
2872
        $selfplugin = enrol_get_plugin('self');
2873
        $selfplugin->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
2874
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'self', 0, 0, ENROL_USER_SUSPENDED);
2875
 
2876
        // Should be enrolled, but not active - user is suspended.
2877
        $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2878
        $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2879
        $suspended  = get_suspended_userids($context);
2880
 
2881
        $this->assertTrue(isset($enrolled[$user->id]));
2882
        $this->assertFalse(isset($active[$user->id]));
2883
        $this->assertTrue(isset($suspended[$user->id]));
2884
        $this->assertCount(1, $enrolled);
2885
        $this->assertCount(0, $active);
2886
        $this->assertCount(1, $suspended);
2887
 
2888
        // Add an active enrol for the user. Any active enrol makes them enrolled.
2889
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id);
2890
 
2891
        // User should be active now.
2892
        $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
2893
        $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
2894
        $suspended  = get_suspended_userids($context);
2895
 
2896
        $this->assertTrue(isset($enrolled[$user->id]));
2897
        $this->assertTrue(isset($active[$user->id]));
2898
        $this->assertFalse(isset($suspended[$user->id]));
2899
        $this->assertCount(1, $enrolled);
2900
        $this->assertCount(1, $active);
2901
        $this->assertCount(0, $suspended);
2902
 
2903
    }
2904
 
2905
    /**
2906
     * Test that enrolled users returns only users in those groups that are
2907
     * specified.
2908
     *
2909
     * @covers ::get_enrolled_users
2910
     * @covers ::get_enrolled_sql
2911
     * @covers ::get_enrolled_with_capabilities_join
2912
     * @covers ::get_enrolled_join
2913
     * @covers ::get_with_capability_join
2914
     * @covers ::groups_get_members_join
2915
     * @covers ::get_suspended_userids
2916
     */
11 efrain 2917
    public function test_get_enrolled_sql_userswithgroups(): void {
1 efrain 2918
        $this->resetAfterTest();
2919
 
2920
        $systemcontext = context_system::instance();
2921
        $course = $this->getDataGenerator()->create_course();
2922
        $coursecontext = context_course::instance($course->id);
2923
        $user1 = $this->getDataGenerator()->create_user();
2924
        $user2 = $this->getDataGenerator()->create_user();
2925
 
2926
        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
2927
        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
2928
 
2929
        $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2930
        groups_add_member($group1, $user1);
2931
        $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2932
        groups_add_member($group2, $user2);
2933
 
2934
        // Get user from group 1.
2935
        $group1users   = get_enrolled_users($coursecontext, '', $group1->id);
2936
        $this->assertCount(1, $group1users);
2937
        $this->assertArrayHasKey($user1->id, $group1users);
2938
        $this->assertEquals(1, count_enrolled_users($coursecontext, '', $group1->id));
2939
 
2940
        // Get user from group 2.
2941
        $group2users   = get_enrolled_users($coursecontext, '', $group2->id);
2942
        $this->assertCount(1, $group2users);
2943
        $this->assertArrayHasKey($user2->id, $group2users);
2944
        $this->assertEquals(1, count_enrolled_users($coursecontext, '', $group2->id));
2945
 
2946
        // Get users from multiple groups.
2947
        $groupusers   = get_enrolled_users($coursecontext, '', [$group1->id, $group2->id]);
2948
        $this->assertCount(2, $groupusers);
2949
        $this->assertArrayHasKey($user1->id, $groupusers);
2950
        $this->assertArrayHasKey($user2->id, $groupusers);
2951
        $this->assertEquals(2, count_enrolled_users($coursecontext, '', [$group1->id, $group2->id]));
2952
    }
2953
 
2954
    /**
2955
     * Test that enrolled users SQL does not return any values for users
2956
     * without a group when $context is not a valid course context.
2957
     *
2958
     * @covers ::get_enrolled_users
2959
     * @covers ::get_enrolled_sql
2960
     * @covers ::get_enrolled_with_capabilities_join
2961
     * @covers ::get_enrolled_join
2962
     * @covers ::get_with_capability_join
2963
     * @covers ::groups_get_members_join
2964
     * @covers ::get_suspended_userids
2965
     */
11 efrain 2966
    public function test_get_enrolled_sql_userswithoutgroup(): void {
1 efrain 2967
        global $DB;
2968
 
2969
        $this->resetAfterTest();
2970
 
2971
        $systemcontext = context_system::instance();
2972
        $course = $this->getDataGenerator()->create_course();
2973
        $coursecontext = context_course::instance($course->id);
2974
        $user1 = $this->getDataGenerator()->create_user();
2975
        $user2 = $this->getDataGenerator()->create_user();
2976
 
2977
        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
2978
        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
2979
 
2980
        $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
2981
        groups_add_member($group, $user1);
2982
 
2983
        $enrolled = get_enrolled_users($coursecontext);
2984
        $this->assertCount(2, $enrolled);
2985
 
2986
        // Get users without any group on the course context.
2987
        $enrolledwithoutgroup = get_enrolled_users($coursecontext, '', USERSWITHOUTGROUP);
2988
        $this->assertCount(1, $enrolledwithoutgroup);
2989
        $this->assertFalse(isset($enrolledwithoutgroup[$user1->id]));
2990
 
2991
        // Get users without any group on the system context (it should throw an exception).
2992
        $this->expectException('coding_exception');
2993
        get_enrolled_users($systemcontext, '', USERSWITHOUTGROUP);
2994
    }
2995
 
2996
    /**
2997
     * Test that enrolled users returns only users in those groups that are
2998
     * specified, and they are allowed to see members of.
2999
     *
3000
     * @covers ::get_enrolled_users
3001
     * @covers ::get_enrolled_sql
3002
     * @covers ::get_enrolled_with_capabilities_join
3003
     * @covers ::get_enrolled_join
3004
     * @covers ::get_with_capability_join
3005
     * @covers ::groups_get_members_join
3006
     * @covers ::get_suspended_userids
3007
     */
11 efrain 3008
    public function test_get_enrolled_sql_userswithhiddengroups(): void {
1 efrain 3009
        $this->resetAfterTest();
3010
 
3011
        $course = $this->getDataGenerator()->create_course();
3012
        $coursecontext = context_course::instance($course->id);
3013
        $user1 = $this->getDataGenerator()->create_user();
3014
        $user2 = $this->getDataGenerator()->create_user();
3015
        $user3 = $this->getDataGenerator()->create_user();
3016
        $user4 = $this->getDataGenerator()->create_user();
3017
        $user5 = $this->getDataGenerator()->create_user();
3018
        $user6 = $this->getDataGenerator()->create_user();
3019
 
3020
        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
3021
        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
3022
        $this->getDataGenerator()->enrol_user($user3->id, $course->id);
3023
        $this->getDataGenerator()->enrol_user($user4->id, $course->id);
3024
        $this->getDataGenerator()->enrol_user($user5->id, $course->id);
3025
        $this->getDataGenerator()->enrol_user($user6->id, $course->id);
3026
 
3027
        $group1 = $this->getDataGenerator()->create_group([
3028
                'courseid' => $course->id,
3029
                'visibility' => GROUPS_VISIBILITY_ALL,
3030
        ]);
3031
        groups_add_member($group1, $user1);
3032
        $group2 = $this->getDataGenerator()->create_group([
3033
                'courseid' => $course->id,
3034
                'visibility' => GROUPS_VISIBILITY_MEMBERS,
3035
        ]);
3036
        groups_add_member($group2, $user2);
3037
        groups_add_member($group2, $user5);
3038
        $group3 = $this->getDataGenerator()->create_group([
3039
                'courseid' => $course->id,
3040
                'visibility' => GROUPS_VISIBILITY_OWN,
3041
        ]);
3042
        groups_add_member($group3, $user3);
3043
        groups_add_member($group3, $user6);
3044
        $group4 = $this->getDataGenerator()->create_group([
3045
                'courseid' => $course->id,
3046
                'visibility' => GROUPS_VISIBILITY_NONE,
3047
        ]);
3048
        groups_add_member($group4, $user4);
3049
 
3050
        $groupids = [$group1->id, $group2->id, $group3->id, $group4->id];
3051
        // User 1 can only see members of Group 1.
3052
        $this->setUser($user1);
3053
        $user1groupusers = get_enrolled_users($coursecontext, '', $groupids);
3054
        $this->assertCount(1, $user1groupusers);
3055
        $this->assertArrayHasKey($user1->id, $user1groupusers);
3056
        $this->assertEquals(1, count_enrolled_users($coursecontext, '', $groupids));
3057
        // User 2 can see all members of Group 1 and Group 2.
3058
        $this->setUser($user2);
3059
        $user2groupusers = get_enrolled_users($coursecontext, '', $groupids);
3060
        $this->assertCount(3, $user2groupusers);
3061
        $this->assertArrayHasKey($user1->id, $user2groupusers);
3062
        $this->assertArrayHasKey($user2->id, $user2groupusers);
3063
        $this->assertArrayHasKey($user5->id, $user2groupusers);
3064
        $this->assertEquals(3, count_enrolled_users($coursecontext, '', $groupids));
3065
        // User 3 can see members of Group 1, and themselves in Group 3 but not other members.
3066
        $this->setUser($user3);
3067
        $user3groupusers = get_enrolled_users($coursecontext, '', $groupids);
3068
        $this->assertCount(2, $user3groupusers);
3069
        $this->assertArrayHasKey($user1->id, $user3groupusers);
3070
        $this->assertArrayHasKey($user3->id, $user3groupusers);
3071
        $this->assertEquals(2, count_enrolled_users($coursecontext, '', $groupids));
3072
        // User 4 can only see members of Group 1.
3073
        $this->setUser($user4);
3074
        $user4groupusers = get_enrolled_users($coursecontext, '', $groupids);
3075
        $this->assertCount(1, $user4groupusers);
3076
        $this->assertArrayHasKey($user1->id, $user4groupusers);
3077
        $this->assertEquals(1, count_enrolled_users($coursecontext, '', $groupids));
3078
    }
3079
 
1441 ariadna 3080
    public static function get_enrolled_sql_provider(): array {
1 efrain 3081
        return array(
3082
            array(
3083
                // Two users who are enrolled.
3084
                'users' => array(
3085
                    array(
3086
                        'enrolled'  => true,
3087
                        'active'    => true,
3088
                    ),
3089
                    array(
3090
                        'enrolled'  => true,
3091
                        'active'    => true,
3092
                    ),
3093
                ),
3094
                'counts' => array(
3095
                    'enrolled'      => 2,
3096
                    'active'        => 2,
3097
                    'suspended'     => 0,
3098
                ),
3099
            ),
3100
            array(
3101
                // A user who is suspended.
3102
                'users' => array(
3103
                    array(
3104
                        'status'    => ENROL_USER_SUSPENDED,
3105
                        'enrolled'  => true,
3106
                        'suspended' => true,
3107
                    ),
3108
                ),
3109
                'counts' => array(
3110
                    'enrolled'      => 1,
3111
                    'active'        => 0,
3112
                    'suspended'     => 1,
3113
                ),
3114
            ),
3115
            array(
3116
                // One of each.
3117
                'users' => array(
3118
                    array(
3119
                        'enrolled'  => true,
3120
                        'active'    => true,
3121
                    ),
3122
                    array(
3123
                        'status'    => ENROL_USER_SUSPENDED,
3124
                        'enrolled'  => true,
3125
                        'suspended' => true,
3126
                    ),
3127
                ),
3128
                'counts' => array(
3129
                    'enrolled'      => 2,
3130
                    'active'        => 1,
3131
                    'suspended'     => 1,
3132
                ),
3133
            ),
3134
            array(
3135
                // One user who is not yet enrolled.
3136
                'users' => array(
3137
                    array(
3138
                        'timestart' => DAYSECS,
3139
                        'enrolled'  => true,
3140
                        'active'    => false,
3141
                        'suspended' => true,
3142
                    ),
3143
                ),
3144
                'counts' => array(
3145
                    'enrolled'      => 1,
3146
                    'active'        => 0,
3147
                    'suspended'     => 1,
3148
                ),
3149
            ),
3150
            array(
3151
                // One user who is no longer enrolled
3152
                'users' => array(
3153
                    array(
3154
                        'timeend'   => -DAYSECS,
3155
                        'enrolled'  => true,
3156
                        'active'    => false,
3157
                        'suspended' => true,
3158
                    ),
3159
                ),
3160
                'counts' => array(
3161
                    'enrolled'      => 1,
3162
                    'active'        => 0,
3163
                    'suspended'     => 1,
3164
                ),
3165
            ),
3166
            array(
3167
                // One user who is not yet enrolled, and one who is no longer enrolled.
3168
                'users' => array(
3169
                    array(
3170
                        'timeend'   => -DAYSECS,
3171
                        'enrolled'  => true,
3172
                        'active'    => false,
3173
                        'suspended' => true,
3174
                    ),
3175
                    array(
3176
                        'timestart' => DAYSECS,
3177
                        'enrolled'  => true,
3178
                        'active'    => false,
3179
                        'suspended' => true,
3180
                    ),
3181
                ),
3182
                'counts' => array(
3183
                    'enrolled'      => 2,
3184
                    'active'        => 0,
3185
                    'suspended'     => 2,
3186
                ),
3187
            ),
3188
        );
3189
    }
3190
 
3191
    /**
3192
     * @dataProvider get_enrolled_sql_provider
3193
     * @covers ::get_enrolled_users
3194
     * @covers ::get_suspended_userids
3195
     */
11 efrain 3196
    public function test_get_enrolled_sql_course($users, $counts): void {
1 efrain 3197
        global $DB;
3198
 
3199
        $this->resetAfterTest();
3200
 
3201
        $course = $this->getDataGenerator()->create_course();
3202
        $context = context_course::instance($course->id);
3203
        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
3204
        $createdusers = array();
3205
 
3206
        foreach ($users as &$userdata) {
3207
            $user = $this->getDataGenerator()->create_user();
3208
            $userdata['id'] = $user->id;
3209
 
3210
            $timestart  = 0;
3211
            $timeend    = 0;
3212
            $status     = null;
3213
            if (isset($userdata['timestart'])) {
3214
                $timestart = time() + $userdata['timestart'];
3215
            }
3216
            if (isset($userdata['timeend'])) {
3217
                $timeend = time() + $userdata['timeend'];
3218
            }
3219
            if (isset($userdata['status'])) {
3220
                $status = $userdata['status'];
3221
            }
3222
 
3223
            // Enrol the user in the course.
3224
            $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'manual', $timestart, $timeend, $status);
3225
        }
3226
 
3227
        // After all users have been enroled, check expectations.
3228
        $enrolled   = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
3229
        $active     = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
3230
        $suspended  = get_suspended_userids($context);
3231
 
3232
        foreach ($users as $userdata) {
3233
            if (isset($userdata['enrolled']) && $userdata['enrolled']) {
3234
                $this->assertTrue(isset($enrolled[$userdata['id']]));
3235
            } else {
3236
                $this->assertFalse(isset($enrolled[$userdata['id']]));
3237
            }
3238
 
3239
            if (isset($userdata['active']) && $userdata['active']) {
3240
                $this->assertTrue(isset($active[$userdata['id']]));
3241
            } else {
3242
                $this->assertFalse(isset($active[$userdata['id']]));
3243
            }
3244
 
3245
            if (isset($userdata['suspended']) && $userdata['suspended']) {
3246
                $this->assertTrue(isset($suspended[$userdata['id']]));
3247
            } else {
3248
                $this->assertFalse(isset($suspended[$userdata['id']]));
3249
            }
3250
        }
3251
 
3252
        $this->assertCount($counts['enrolled'],     $enrolled);
3253
        $this->assertCount($counts['active'],       $active);
3254
        $this->assertCount($counts['suspended'],    $suspended);
3255
    }
3256
 
3257
    /**
3258
     * A small functional test of permission evaluations.
3259
     */
11 efrain 3260
    public function test_permission_evaluation(): void {
1 efrain 3261
        global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE;
3262
 
3263
        $this->resetAfterTest();
3264
 
3265
        $generator = $this->getDataGenerator();
3266
 
3267
        // Fill the site with some real data.
3268
        $testcategories = array();
3269
        $testcourses = array();
3270
        $testpages = array();
3271
        $testblocks = array();
3272
        $allroles = $DB->get_records_menu('role', array(), 'id', 'shortname, id');
3273
 
3274
        $systemcontext = context_system::instance();
3275
        $frontpagecontext = context_course::instance(SITEID);
3276
 
3277
        // Add block to system context.
3278
        $bi = $generator->create_block('online_users');
3279
        context_block::instance($bi->id);
3280
        $testblocks[] = $bi->id;
3281
 
3282
        // Some users.
3283
        $testusers = array();
3284
        for ($i=0; $i<20; $i++) {
3285
            $user = $generator->create_user();
3286
            $testusers[$i] = $user->id;
3287
            $usercontext = context_user::instance($user->id);
3288
 
3289
            // Add block to user profile.
3290
            $bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id));
3291
            $testblocks[] = $bi->id;
3292
        }
3293
 
3294
        // Add block to frontpage.
3295
        $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id));
3296
        $frontpageblockcontext = context_block::instance($bi->id);
3297
        $testblocks[] = $bi->id;
3298
 
3299
        // Add a resource to frontpage.
3300
        $page = $generator->create_module('page', array('course'=>$SITE->id));
3301
        $testpages[] = $page->cmid;
3302
        $frontpagepagecontext = context_module::instance($page->cmid);
3303
 
3304
        // Add block to frontpage resource.
3305
        $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id));
3306
        $frontpagepageblockcontext = context_block::instance($bi->id);
3307
        $testblocks[] = $bi->id;
3308
 
3309
        // Some nested course categories with courses.
3310
        $manualenrol = enrol_get_plugin('manual');
3311
        $parentcat = 0;
3312
        for ($i=0; $i<5; $i++) {
3313
            $cat = $generator->create_category(array('parent'=>$parentcat));
3314
            $testcategories[] = $cat->id;
3315
            $catcontext = context_coursecat::instance($cat->id);
3316
            $parentcat = $cat->id;
3317
 
3318
            if ($i >= 4) {
3319
                continue;
3320
            }
3321
 
3322
            // Add resource to each category.
3323
            $bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id));
3324
            context_block::instance($bi->id);
3325
 
3326
            // Add a few courses to each category.
3327
            for ($j=0; $j<6; $j++) {
3328
                $course = $generator->create_course(array('category'=>$cat->id));
3329
                $testcourses[] = $course->id;
3330
                $coursecontext = context_course::instance($course->id);
3331
 
3332
                if ($j >= 5) {
3333
                    continue;
3334
                }
3335
                // Add manual enrol instance.
3336
                $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id)));
3337
 
3338
                // Add block to each course.
3339
                $bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
3340
                $testblocks[] = $bi->id;
3341
 
3342
                // Add a resource to each course.
3343
                $page = $generator->create_module('page', array('course'=>$course->id));
3344
                $testpages[] = $page->cmid;
3345
                $modcontext = context_module::instance($page->cmid);
3346
 
3347
                // Add block to each module.
3348
                $bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id));
3349
                $testblocks[] = $bi->id;
3350
            }
3351
        }
3352
 
3353
        // Make sure all contexts were created properly.
3354
        $count = 1; // System.
3355
        $count += $DB->count_records('user', array('deleted'=>0));
3356
        $count += $DB->count_records('course_categories');
3357
        $count += $DB->count_records('course');
3358
        $count += $DB->count_records('course_modules');
3359
        $count += $DB->count_records('block_instances');
3360
        $this->assertEquals($count, $DB->count_records('context'));
3361
        $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
3362
        $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
3363
 
3364
 
3365
        // Test context_helper::get_level_name() method.
3366
 
3367
        $levels = context_helper::get_all_levels();
3368
        foreach ($levels as $level => $classname) {
3369
            $name = context_helper::get_level_name($level);
3370
            $this->assertNotEmpty($name);
3371
        }
3372
 
3373
 
3374
        // Test context::instance_by_id(), context_xxx::instance() methods.
3375
 
3376
        $context = context::instance_by_id($frontpagecontext->id);
3377
        $this->assertSame(CONTEXT_COURSE, $context->contextlevel);
3378
        $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING));
3379
        try {
3380
            context::instance_by_id(-1);
3381
            $this->fail('exception expected');
3382
        } catch (moodle_exception $e) {
3383
            $this->assertTrue(true);
3384
        }
3385
        $this->assertInstanceOf('context_system', context_system::instance());
3386
        $this->assertInstanceOf('context_coursecat', context_coursecat::instance($testcategories[0]));
3387
        $this->assertInstanceOf('context_course', context_course::instance($testcourses[0]));
3388
        $this->assertInstanceOf('context_module', context_module::instance($testpages[0]));
3389
        $this->assertInstanceOf('context_block', context_block::instance($testblocks[0]));
3390
 
3391
        $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING));
3392
        $this->assertFalse(context_course::instance(-1, IGNORE_MISSING));
3393
        $this->assertFalse(context_module::instance(-1, IGNORE_MISSING));
3394
        $this->assertFalse(context_block::instance(-1, IGNORE_MISSING));
3395
        try {
3396
            context_coursecat::instance(-1);
3397
            $this->fail('exception expected');
3398
        } catch (moodle_exception $e) {
3399
            $this->assertTrue(true);
3400
        }
3401
        try {
3402
            context_course::instance(-1);
3403
            $this->fail('exception expected');
3404
        } catch (moodle_exception $e) {
3405
            $this->assertTrue(true);
3406
        }
3407
        try {
3408
            context_module::instance(-1);
3409
            $this->fail('exception expected');
3410
        } catch (moodle_exception $e) {
3411
            $this->assertTrue(true);
3412
        }
3413
        try {
3414
            context_block::instance(-1);
3415
            $this->fail('exception expected');
3416
        } catch (moodle_exception $e) {
3417
            $this->assertTrue(true);
3418
        }
3419
 
3420
 
3421
        // Test $context->get_url(), $context->get_context_name(), $context->get_capabilities() methods.
3422
 
3423
        $testcontexts = array();
3424
        $testcontexts[CONTEXT_SYSTEM]    = context_system::instance();
3425
        $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]);
3426
        $testcontexts[CONTEXT_COURSE]    = context_course::instance($testcourses[0]);
3427
        $testcontexts[CONTEXT_MODULE]    = context_module::instance($testpages[0]);
3428
        $testcontexts[CONTEXT_BLOCK]     = context_block::instance($testblocks[0]);
3429
 
3430
        foreach ($testcontexts as $context) {
3431
            $name = $context->get_context_name(true, true);
3432
            $this->assertNotEmpty($name);
3433
 
3434
            $this->assertInstanceOf('moodle_url', $context->get_url());
3435
 
3436
            $caps = $context->get_capabilities();
3437
            $this->assertTrue(is_array($caps));
3438
            foreach ($caps as $cap) {
3439
                $cap = (array)$cap;
3440
                $this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask'));
3441
            }
3442
        }
3443
        unset($testcontexts);
3444
 
3445
        // Test $context->get_course_context() method.
3446
 
3447
        $this->assertFalse($systemcontext->get_course_context(false));
3448
        try {
3449
            $systemcontext->get_course_context();
3450
            $this->fail('exception expected');
3451
        } catch (moodle_exception $e) {
3452
            $this->assertInstanceOf('coding_exception', $e);
3453
        }
3454
        $context = context_coursecat::instance($testcategories[0]);
3455
        $this->assertFalse($context->get_course_context(false));
3456
        try {
3457
            $context->get_course_context();
3458
            $this->fail('exception expected');
3459
        } catch (moodle_exception $e) {
3460
            $this->assertInstanceOf('coding_exception', $e);
3461
        }
3462
        $this->assertEquals($frontpagecontext, $frontpagecontext->get_course_context(true));
3463
        $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_course_context(true));
3464
        $this->assertEquals($frontpagecontext, $frontpagepageblockcontext->get_course_context(true));
3465
 
3466
 
3467
        // Test $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() methods.
3468
 
3469
        $userid = reset($testusers);
3470
        $usercontext = context_user::instance($userid);
3471
        $this->assertEquals($systemcontext, $usercontext->get_parent_context());
3472
        $this->assertEquals(array($systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts());
3473
        $this->assertEquals(array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts(true));
3474
 
3475
        $this->assertEquals(array(), $systemcontext->get_parent_contexts());
3476
        $this->assertEquals(array($systemcontext->id=>$systemcontext), $systemcontext->get_parent_contexts(true));
3477
        $this->assertEquals(array(), $systemcontext->get_parent_context_ids());
3478
        $this->assertEquals(array($systemcontext->id), $systemcontext->get_parent_context_ids(true));
3479
        $this->assertEquals(array(), $systemcontext->get_parent_context_paths());
3480
        $this->assertEquals(array($systemcontext->id => $systemcontext->path), $systemcontext->get_parent_context_paths(true));
3481
 
3482
        $this->assertEquals($systemcontext, $frontpagecontext->get_parent_context());
3483
        $this->assertEquals(array($systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts());
3484
        $this->assertEquals(array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts(true));
3485
        $this->assertEquals(array($systemcontext->id), $frontpagecontext->get_parent_context_ids());
3486
        $this->assertEquals(array($frontpagecontext->id, $systemcontext->id), $frontpagecontext->get_parent_context_ids(true));
3487
        $this->assertEquals(array($systemcontext->id => $systemcontext->path), $frontpagecontext->get_parent_context_paths());
3488
        $expected = array($systemcontext->id => $systemcontext->path, $frontpagecontext->id => $frontpagecontext->path);
3489
        $this->assertEquals($expected, $frontpagecontext->get_parent_context_paths(true));
3490
 
3491
        $this->assertFalse($systemcontext->get_parent_context());
3492
        $frontpagecontext = context_course::instance($SITE->id);
3493
        $parent = $systemcontext;
3494
        foreach ($testcategories as $catid) {
3495
            $catcontext = context_coursecat::instance($catid);
3496
            $this->assertEquals($parent, $catcontext->get_parent_context());
3497
            $parent = $catcontext;
3498
        }
3499
        $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_parent_context());
3500
        $this->assertEquals($frontpagecontext, $frontpageblockcontext->get_parent_context());
3501
        $this->assertEquals($frontpagepagecontext, $frontpagepageblockcontext->get_parent_context());
3502
 
3503
 
3504
        // Test $context->get_child_contexts() method.
3505
 
3506
        $children = $systemcontext->get_child_contexts();
3507
        $this->resetDebugging();
3508
        $this->assertEquals(count($children)+1, $DB->count_records('context'));
3509
 
3510
        $context = context_coursecat::instance($testcategories[3]);
3511
        $children = $context->get_child_contexts();
3512
        $countcats    = 0;
3513
        $countcourses = 0;
3514
        $countblocks  = 0;
3515
        foreach ($children as $child) {
3516
            if ($child->contextlevel == CONTEXT_COURSECAT) {
3517
                $countcats++;
3518
            }
3519
            if ($child->contextlevel == CONTEXT_COURSE) {
3520
                $countcourses++;
3521
            }
3522
            if ($child->contextlevel == CONTEXT_BLOCK) {
3523
                $countblocks++;
3524
            }
3525
        }
3526
        $this->assertCount(8, $children);
3527
        $this->assertEquals(1, $countcats);
3528
        $this->assertEquals(6, $countcourses);
3529
        $this->assertEquals(1, $countblocks);
3530
 
3531
        $context = context_course::instance($testcourses[2]);
3532
        $children = $context->get_child_contexts();
3533
 
3534
        $context = context_module::instance($testpages[3]);
3535
        $children = $context->get_child_contexts();
3536
        $this->assertCount(1, $children);
3537
 
3538
        $context = context_block::instance($testblocks[1]);
3539
        $children = $context->get_child_contexts();
3540
        $this->assertCount(0, $children);
3541
 
3542
        unset($children);
3543
        unset($countcats);
3544
        unset($countcourses);
3545
        unset($countblocks);
3546
 
3547
 
3548
        // Test context_helper::reset_caches() method.
3549
 
3550
        context_helper::reset_caches();
3551
        $this->assertEquals(0, context_inspection::check_context_cache_size());
3552
        context_course::instance($SITE->id);
3553
        $this->assertEquals(1, context_inspection::check_context_cache_size());
3554
 
3555
 
3556
        // Test context preloading.
3557
 
3558
        context_helper::reset_caches();
3559
        $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')."
3560
                  FROM {context} c
3561
                 WHERE c.contextlevel <> ".CONTEXT_SYSTEM;
3562
        $records = $DB->get_records_sql($sql);
3563
        $firstrecord = reset($records);
3564
        $columns = context_helper::get_preload_record_columns('c');
3565
        $firstrecord = (array)$firstrecord;
3566
        $this->assertSame(array_keys($firstrecord), array_values($columns));
3567
        context_helper::reset_caches();
3568
        foreach ($records as $record) {
3569
            context_helper::preload_from_record($record);
3570
            $this->assertEquals(new stdClass(), $record);
3571
        }
3572
        $this->assertEquals(count($records), context_inspection::check_context_cache_size());
3573
        unset($records);
3574
        unset($columns);
3575
 
3576
        context_helper::reset_caches();
3577
        context_helper::preload_course($SITE->id);
3578
        $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
3579
        $this->assertEquals(3 + $numfrontpagemodules,
3580
            context_inspection::check_context_cache_size()); // Depends on number of default blocks.
3581
 
3582
        // Test assign_capability(), unassign_capability() functions.
3583
 
3584
        $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3585
        $this->assertFalse($rc);
3586
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id);
3587
        $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3588
        $this->assertEquals(CAP_ALLOW, $rc->permission);
3589
        assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id);
3590
        $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3591
        $this->assertEquals(CAP_ALLOW, $rc->permission);
3592
        assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true);
3593
        $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3594
        $this->assertEquals(CAP_PREVENT, $rc->permission);
3595
 
3596
        assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext);
3597
        $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3598
        $this->assertFalse($rc);
3599
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext);
1441 ariadna 3600
        unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext);
1 efrain 3601
        $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
3602
        $this->assertFalse($rc);
1441 ariadna 3603
        unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id);
1 efrain 3604
        unset($rc);
3605
 
3606
        accesslib_clear_all_caches_for_unit_testing(); // Must be done after assign_capability().
3607
 
3608
 
3609
        // Test role_assign(), role_unassign(), role_unassign_all() functions.
3610
 
3611
        $context = context_course::instance($testcourses[1]);
3612
        $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3613
        role_assign($allroles['teacher'], $testusers[1], $context->id);
3614
        role_assign($allroles['teacher'], $testusers[2], $context->id);
3615
        role_assign($allroles['manager'], $testusers[1], $context->id);
3616
        $this->assertEquals(3, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3617
        role_unassign($allroles['teacher'], $testusers[1], $context->id);
3618
        $this->assertEquals(2, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3619
        role_unassign_all(array('contextid'=>$context->id));
3620
        $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
3621
        unset($context);
3622
 
3623
        accesslib_clear_all_caches_for_unit_testing(); // Just in case.
3624
 
3625
 
3626
        // Test has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends functions.
3627
 
3628
        $adminid = get_admin()->id;
3629
        $guestid = $CFG->siteguest;
3630
 
3631
        // Enrol some users into some courses.
3632
        $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST);
3633
        $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST);
3634
        $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id');
3635
        $cm1 = reset($cms);
3636
        $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id');
3637
        $block1 = reset($blocks);
3638
        $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id));
3639
        $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id));
3640
        for ($i=0; $i<9; $i++) {
3641
            $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']);
3642
        }
3643
        $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']);
3644
        $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']);
3645
 
3646
        for ($i=10; $i<15; $i++) {
3647
            $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']);
3648
        }
3649
        $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']);
3650
 
3651
        // Add tons of role assignments - the more the better.
3652
        role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2]));
3653
        role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1]));
3654
        role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id));
3655
        role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id));
3656
        role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id));
3657
        role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id));
3658
        role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id));
3659
        role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id));
3660
 
3661
        role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id));
3662
        role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id));
3663
 
3664
        // Add tons of overrides - the more the better.
3665
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true);
3666
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true);
3667
        assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true);
3668
        assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true);
3669
        assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true);
3670
 
3671
        assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true);
3672
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true);
3673
        assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true);
3674
        assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true);
3675
        assign_capability('mod/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
3676
 
3677
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true);
3678
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true);
3679
        assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true);
3680
        assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true);
3681
 
3682
        assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true);
3683
 
3684
        // Prepare for prohibit test.
3685
        role_assign($allroles['editingteacher'], $testusers[19], context_system::instance());
3686
        role_assign($allroles['teacher'], $testusers[19], context_course::instance($testcourses[17]));
3687
        role_assign($allroles['editingteacher'], $testusers[19], context_course::instance($testcourses[17]));
3688
        assign_capability('moodle/course:update', CAP_PROHIBIT, $allroles['teacher'], context_course::instance($testcourses[17]), true);
3689
 
3690
        accesslib_clear_all_caches_for_unit_testing(); /// Must be done after assign_capability().
3691
 
3692
        // Extra tests for guests and not-logged-in users because they can not be verified by cross checking
3693
        // with get_users_by_capability() where they are ignored.
3694
        $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid));
3695
        $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid));
3696
        $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid));
3697
        $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid));
3698
 
3699
        $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0));
3700
        $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0));
3701
        $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0));
3702
        $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0));
3703
 
3704
        $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11]));
3705
        $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11]));
3706
        $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11]));
3707
        $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11]));
3708
 
3709
        $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9]));
3710
        $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9]));
3711
        $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9]));
3712
 
3713
        // Test prohibits.
3714
        $this->assertTrue(has_capability('moodle/course:update', context_system::instance(), $testusers[19]));
3715
        $ids = get_users_by_capability(context_system::instance(), 'moodle/course:update', 'u.id');
3716
        $this->assertArrayHasKey($testusers[19], $ids);
3717
        $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[17]), $testusers[19]));
3718
        $ids = get_users_by_capability(context_course::instance($testcourses[17]), 'moodle/course:update', 'u.id');
3719
        $this->assertArrayNotHasKey($testusers[19], $ids);
3720
 
3721
        // Test the list of enrolled users.
3722
        $coursecontext = context_course::instance($course1->id);
3723
        $enrolled = get_enrolled_users($coursecontext);
3724
        $this->assertCount(10, $enrolled);
3725
        for ($i=0; $i<10; $i++) {
3726
            $this->assertTrue(isset($enrolled[$testusers[$i]]));
3727
        }
3728
        $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update');
3729
        $this->assertCount(1, $enrolled);
3730
        $this->assertTrue(isset($enrolled[$testusers[9]]));
3731
        unset($enrolled);
3732
 
3733
        // Role switching.
3734
        $userid = $testusers[9];
3735
        $USER = $DB->get_record('user', array('id'=>$userid));
3736
        load_all_capabilities();
3737
        $coursecontext = context_course::instance($course1->id);
3738
        $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
3739
        $this->assertFalse(is_role_switched($course1->id));
3740
        role_switch($allroles['student'], $coursecontext);
3741
        $this->assertTrue(is_role_switched($course1->id));
3742
        $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
3743
        $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
3744
        reload_all_capabilities();
3745
        $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
3746
        role_switch(0, $coursecontext);
3747
        $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
3748
        $userid = $adminid;
3749
        $USER = $DB->get_record('user', array('id'=>$userid));
3750
        load_all_capabilities();
3751
        $coursecontext = context_course::instance($course1->id);
3752
        $blockcontext = context_block::instance($block1->id);
3753
        $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
3754
        role_switch($allroles['student'], $coursecontext);
3755
        $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
3756
        $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
3757
        reload_all_capabilities();
3758
        $this->assertFalse(has_capability('moodle/course:update', $blockcontext));
3759
        load_all_capabilities();
3760
        $this->assertTrue(has_capability('moodle/course:update', $blockcontext));
3761
 
3762
        // Temp course role for enrol.
3763
        $DB->delete_records('cache_flags', array()); // This prevents problem with dirty contexts immediately resetting the temp role - this is a known problem...
3764
        $userid = $testusers[5];
3765
        $roleid = $allroles['editingteacher'];
3766
        $USER = $DB->get_record('user', array('id'=>$userid));
3767
        load_all_capabilities();
3768
        $coursecontext = context_course::instance($course1->id);
3769
        $this->assertFalse(has_capability('moodle/course:update', $coursecontext));
3770
        $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid]));
3771
        load_temp_course_role($coursecontext, $roleid);
3772
        $this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid);
3773
        $this->assertTrue(has_capability('moodle/course:update', $coursecontext));
3774
        remove_temp_course_roles($coursecontext);
3775
        $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
3776
        load_temp_course_role($coursecontext, $roleid);
3777
        reload_all_capabilities();
3778
        $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
3779
        $USER = new stdClass();
3780
        $USER->id = 0;
3781
 
3782
        // Now cross check has_capability() with get_users_by_capability(), each using different code paths,
3783
        // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong,
3784
        // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users).
3785
        $contexts = $DB->get_records('context', array(), 'id');
3786
        $contexts = array_values($contexts);
3787
        $capabilities = $DB->get_records('capabilities', array(), 'id');
3788
        $capabilities = array_values($capabilities);
3789
        $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']);
3790
        $userids = array_values($testusers);
3791
        $userids[] = get_admin()->id;
3792
 
3793
        if (!PHPUNIT_LONGTEST) {
3794
            $contexts = array_slice($contexts, 0, 10);
3795
            $capabilities = array_slice($capabilities, 0, 5);
3796
            $userids = array_slice($userids, 0, 5);
3797
        }
3798
 
3799
        foreach ($userids as $userid) { // No guest or deleted.
3800
            // Each user gets 0-10 random roles.
3801
            $rcount = rand(0, 10);
3802
            for ($j=0; $j<$rcount; $j++) {
3803
                $roleid = $roles[rand(0, count($roles)-1)];
3804
                $contextid = $contexts[rand(0, count($contexts)-1)]->id;
3805
                role_assign($roleid, $userid, $contextid);
3806
            }
3807
        }
3808
 
3809
        $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT);
3810
        $maxoverrides = count($contexts)*10;
3811
        for ($j=0; $j<$maxoverrides; $j++) {
3812
            $roleid = $roles[rand(0, count($roles)-1)];
3813
            $contextid = $contexts[rand(0, count($contexts)-1)]->id;
3814
            $permission = $permissions[rand(0, count($permissions)-1)];
3815
            $capname = $capabilities[rand(0, count($capabilities)-1)]->name;
3816
            assign_capability($capname, $permission, $roleid, $contextid, true);
3817
        }
3818
        unset($permissions);
3819
        unset($roles);
3820
 
3821
        accesslib_clear_all_caches_for_unit_testing(); // must be done after assign_capability().
3822
 
3823
        // Test time - let's set up some real user, just in case the logic for USER affects the others...
3824
        $USER = $DB->get_record('user', array('id'=>$testusers[3]));
3825
        load_all_capabilities();
3826
 
3827
        $userids[] = $CFG->siteguest;
3828
        $userids[] = 0; // Not-logged-in user.
3829
        $userids[] = -1; // Non-existent user.
3830
 
3831
        foreach ($contexts as $crecord) {
3832
            $context = context::instance_by_id($crecord->id);
3833
            if ($coursecontext = $context->get_course_context(false)) {
3834
                $enrolled = get_enrolled_users($context);
3835
            } else {
3836
                $enrolled = array();
3837
            }
3838
            foreach ($capabilities as $cap) {
3839
                $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username');
3840
                if ($enrolled) {
3841
                    $enrolledwithcap = get_enrolled_users($context, $cap->name);
3842
                } else {
3843
                    $enrolledwithcap = array();
3844
                }
3845
                foreach ($userids as $userid) {
3846
                    if ($userid == 0 or isguestuser($userid)) {
3847
                        if ($userid == 0) {
3848
                            $CFG->forcelogin = true;
3849
                            $this->assertFalse(has_capability($cap->name, $context, $userid));
3850
                            unset($CFG->forcelogin);
3851
                        }
3852
                        if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) {
3853
                            $this->assertFalse(has_capability($cap->name, $context, $userid));
3854
                        }
3855
                        $this->assertFalse(isset($allowed[$userid]));
3856
                    } else {
3857
                        if (is_siteadmin($userid)) {
3858
                            $this->assertTrue(has_capability($cap->name, $context, $userid, true));
3859
                        }
3860
                        $hascap = has_capability($cap->name, $context, $userid, false);
3861
                        $this->assertSame($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
3862
                        if (isset($enrolled[$userid])) {
3863
                            $this->assertSame(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
3864
                        }
3865
                    }
3866
                }
3867
            }
3868
        }
3869
        // Back to nobody.
3870
        $USER = new stdClass();
3871
        $USER->id = 0;
3872
        unset($contexts);
3873
        unset($userids);
3874
        unset($capabilities);
3875
 
3876
        // Now let's do all the remaining tests that break our carefully prepared fake site.
3877
 
3878
 
3879
        // Test $context->mark_dirty() method.
3880
 
3881
        $DB->delete_records('cache_flags', array());
3882
        accesslib_clear_all_caches(false);
3883
        $systemcontext->mark_dirty();
3884
        $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
3885
        $this->assertTrue(isset($dirty[$systemcontext->path]));
3886
        $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path]));
3887
 
3888
 
3889
        // Test $context->reload_if_dirty() method.
3890
 
3891
        $DB->delete_records('cache_flags', array());
3892
        accesslib_clear_all_caches(false);
3893
        load_all_capabilities();
3894
        $context = context_course::instance($testcourses[2]);
3895
        $page = $DB->get_record('page', array('course'=>$testcourses[2]));
3896
        $pagecm = get_coursemodule_from_instance('page', $page->id);
3897
        $pagecontext = context_module::instance($pagecm->id);
3898
 
3899
        $context->mark_dirty();
3900
        $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
3901
        $USER->access['test'] = true;
3902
        $context->reload_if_dirty();
3903
        $this->assertFalse(isset($USER->access['test']));
3904
 
3905
        $context->mark_dirty();
3906
        $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
3907
        $USER->access['test'] = true;
3908
        $pagecontext->reload_if_dirty();
3909
        $this->assertFalse(isset($USER->access['test']));
3910
 
3911
 
3912
        // Test context_helper::build_all_paths() method.
3913
 
3914
        $oldcontexts = $DB->get_records('context', array(), 'id');
3915
        $DB->set_field_select('context', 'path', null, "contextlevel <> ".CONTEXT_SYSTEM);
3916
        $DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM);
3917
        context_helper::build_all_paths();
3918
        $newcontexts = $DB->get_records('context', array(), 'id');
3919
        $this->assertEquals($oldcontexts, $newcontexts);
3920
        unset($oldcontexts);
3921
        unset($newcontexts);
3922
 
3923
 
3924
        // Test $context->reset_paths() method.
3925
 
3926
        $context = context_course::instance($testcourses[2]);
3927
        $children = $context->get_child_contexts();
3928
        $context->reset_paths(false);
3929
        $this->assertNull($DB->get_field('context', 'path', array('id'=>$context->id)));
3930
        $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$context->id)));
3931
        foreach ($children as $child) {
3932
            $this->assertNull($DB->get_field('context', 'path', array('id'=>$child->id)));
3933
            $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$child->id)));
3934
        }
3935
        $this->assertEquals(count($children)+1, $DB->count_records('context', array('depth'=>0)));
3936
        $this->assertEquals(count($children)+1, $DB->count_records('context', array('path'=>null)));
3937
 
3938
        $context = context_course::instance($testcourses[2]);
3939
        $context->reset_paths(true);
3940
        $context = context_course::instance($testcourses[2]);
3941
        $this->assertSame($context->path, $DB->get_field('context', 'path', array('id'=>$context->id)));
3942
        $this->assertSame($context->depth, $DB->get_field('context', 'depth', array('id'=>$context->id)));
3943
        $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
3944
        $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
3945
 
3946
 
3947
        // Test $context->update_moved() method.
3948
 
3949
        accesslib_clear_all_caches(false);
3950
        $DB->delete_records('cache_flags', array());
3951
        $course = $DB->get_record('course', array('id'=>$testcourses[0]));
3952
        $context = context_course::instance($course->id);
3953
        $oldpath = $context->path;
3954
        $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
3955
        $categorycontext = context_coursecat::instance($miscid);
3956
        $course->category = $miscid;
3957
        $DB->update_record('course', $course);
3958
        $context->update_moved($categorycontext);
3959
 
3960
        $context = context_course::instance($course->id);
3961
        $this->assertEquals($categorycontext, $context->get_parent_context());
3962
        $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
3963
        $this->assertFalse(isset($dirty[$oldpath]));
3964
        $this->assertTrue(isset($dirty[$context->path]));
3965
 
3966
 
3967
        // Test $context->delete_content() method.
3968
 
3969
        context_helper::reset_caches();
3970
        $context = context_module::instance($testpages[3]);
3971
        $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
3972
        $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
3973
        $context->delete_content();
3974
        $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
3975
        $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
3976
 
3977
 
3978
        // Test $context->delete() method.
3979
 
3980
        context_helper::reset_caches();
3981
        $context = context_module::instance($testpages[4]);
3982
        $this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
3983
        $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
3984
        $bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id));
3985
        $bicontext = context_block::instance($bi->id);
3986
        $DB->delete_records('cache_flags', array());
3987
        $context->delete(); // Should delete also linked blocks.
3988
        $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
3989
        $this->assertFalse(isset($dirty[$context->path]));
3990
        $this->assertFalse($DB->record_exists('context', array('id'=>$context->id)));
3991
        $this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id)));
3992
        $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4])));
3993
        $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id)));
3994
        $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
3995
        context_module::instance($testpages[4]);
3996
 
3997
 
3998
        // Test context_helper::delete_instance() method.
3999
 
4000
        context_helper::reset_caches();
4001
        $lastcourse = array_pop($testcourses);
4002
        $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
4003
        $coursecontext = context_course::instance($lastcourse);
4004
        $this->assertEquals(1, context_inspection::check_context_cache_size());
4005
        $this->assertNotEquals(CONTEXT_COURSE, $coursecontext->instanceid);
4006
        $DB->delete_records('cache_flags', array());
4007
        context_helper::delete_instance(CONTEXT_COURSE, $lastcourse);
4008
        $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
4009
        $this->assertFalse(isset($dirty[$coursecontext->path]));
4010
        $this->assertEquals(0, context_inspection::check_context_cache_size());
4011
        $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
4012
        context_course::instance($lastcourse);
4013
 
4014
 
4015
        // Test context_helper::create_instances() method.
4016
 
4017
        $prevcount = $DB->count_records('context');
4018
        $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
4019
        context_helper::create_instances(null, true);
4020
        $this->assertSame($DB->count_records('context'), $prevcount);
4021
        $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
4022
        $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
4023
 
4024
        $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
4025
        $DB->delete_records('block_instances', array());
4026
        $prevcount = $DB->count_records('context');
4027
        $DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM);
4028
        context_helper::create_instances(null, true);
4029
        $this->assertSame($prevcount, $DB->count_records('context'));
4030
        $this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
4031
        $this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
4032
 
4033
        // Test context_helper::cleanup_instances() method.
4034
 
4035
        $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}");
4036
        $DB->delete_records('course', array('id'=>$lastcourse));
4037
        $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
4038
        $DB->delete_records('course_categories', array('id'=>$lastcategory));
4039
        $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0");
4040
        $DB->delete_records('user', array('id'=>$lastuser));
4041
        $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id));
4042
        $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid));
4043
        context_helper::cleanup_instances();
4044
        $count = 1; // System.
4045
        $count += $DB->count_records('user', array('deleted'=>0));
4046
        $count += $DB->count_records('course_categories');
4047
        $count += $DB->count_records('course');
4048
        $count += $DB->count_records('course_modules');
4049
        $count += $DB->count_records('block_instances');
4050
        $this->assertEquals($count, $DB->count_records('context'));
4051
 
4052
 
4053
        // Test context cache size restrictions.
4054
 
4055
        $testusers= array();
4056
        for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
4057
            $user = $generator->create_user();
4058
            $testusers[$i] = $user->id;
4059
        }
4060
        context_helper::create_instances(null, true);
4061
        context_helper::reset_caches();
4062
        for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
4063
            context_user::instance($testusers[$i]);
4064
            if ($i == CONTEXT_CACHE_MAX_SIZE - 1) {
4065
                $this->assertEquals(CONTEXT_CACHE_MAX_SIZE, context_inspection::check_context_cache_size());
4066
            } else if ($i == CONTEXT_CACHE_MAX_SIZE) {
4067
                // Once the limit is reached roughly 1/3 of records should be removed from cache.
4068
                $this->assertEquals((int)ceil(CONTEXT_CACHE_MAX_SIZE * (2 / 3) + 101),
4069
                    context_inspection::check_context_cache_size());
4070
            }
4071
        }
4072
        // We keep the first 100 cached.
4073
        $prevsize = context_inspection::check_context_cache_size();
4074
        for ($i=0; $i<100; $i++) {
4075
            context_user::instance($testusers[$i]);
4076
            $this->assertEquals($prevsize, context_inspection::check_context_cache_size());
4077
        }
4078
        context_user::instance($testusers[102]);
4079
        $this->assertEquals($prevsize + 1, context_inspection::check_context_cache_size());
4080
        unset($testusers);
4081
    }
4082
 
4083
    /**
4084
     * Helper that verifies a list of capabilities, as returned by
4085
     * $context->get_capabilities() contains certain capabilities.
4086
     *
4087
     * @param array $expected a list of capability names
4088
     * @param array $actual a list of capability info from $context->get_capabilities().
4089
     */
4090
    protected function assert_capability_list_contains($expected, $actual) {
4091
        $actualnames = [];
4092
        foreach ($actual as $cap) {
4093
            $actualnames[] = $cap->name;
4094
        }
4095
        // Verify each expected element exists.
4096
        foreach ($expected as $key => $value) {
4097
            $this->assertContains($value, $actualnames);
4098
        }
4099
    }
4100
 
4101
    /**
4102
     * Test that context_system::get_capabilities returns capabilities relevant to all modules.
4103
     *
4104
     * @covers \context_system::get_capabilities
4105
     */
11 efrain 4106
    public function test_context_module_caps_returned_by_get_capabilities_in_sys_context(): void {
1 efrain 4107
        $actual = context_system::instance()->get_capabilities();
4108
 
4109
        // Just test a few representative capabilities.
4110
        $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
1441 ariadna 4111
                'repository/upload:view', 'tiny/recordrtc:recordaudio'];
1 efrain 4112
 
4113
        $this->assert_capability_list_contains($expectedcapabilities, $actual);
4114
    }
4115
 
4116
    /**
4117
     * Test that context_coursecat::get_capabilities returns capabilities relevant to all modules.
4118
     *
4119
     * @covers \context_coursecat::get_capabilities
4120
     */
11 efrain 4121
    public function test_context_module_caps_returned_by_get_capabilities_in_course_cat_context(): void {
1 efrain 4122
        $this->resetAfterTest(true);
4123
        $generator = $this->getDataGenerator();
4124
        $cat = $generator->create_category();
4125
 
4126
        $actual = context_coursecat::instance($cat->id)->get_capabilities();
4127
 
4128
        // Just test a few representative capabilities.
4129
        $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
1441 ariadna 4130
                'repository/upload:view', 'tiny/recordrtc:recordaudio'];
1 efrain 4131
 
4132
        $this->assert_capability_list_contains($expectedcapabilities, $actual);
4133
    }
4134
 
4135
    /**
4136
     * Test that context_course::get_capabilities returns capabilities relevant to all modules.
4137
     *
4138
     * @covers \context_course::get_capabilities
4139
     */
11 efrain 4140
    public function test_context_module_caps_returned_by_get_capabilities_in_course_context(): void {
1 efrain 4141
        $this->resetAfterTest(true);
4142
        $generator = $this->getDataGenerator();
4143
        $cat = $generator->create_category();
4144
        $course = $generator->create_course(['category' => $cat->id]);
4145
 
4146
        $actual = context_course::instance($course->id)->get_capabilities();
4147
 
4148
        // Just test a few representative capabilities.
4149
        $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
1441 ariadna 4150
                'repository/upload:view', 'tiny/recordrtc:recordaudio'];
1 efrain 4151
 
4152
        $this->assert_capability_list_contains($expectedcapabilities, $actual);
4153
    }
4154
 
4155
    /**
4156
     * Test that context_module::get_capabilities returns capabilities relevant to all modules.
4157
     *
4158
     * @covers \context_module::get_capabilities
4159
     */
11 efrain 4160
    public function test_context_module_caps_returned_by_get_capabilities_mod_context(): void {
1 efrain 4161
        $this->resetAfterTest(true);
4162
        $generator = $this->getDataGenerator();
4163
        $cat = $generator->create_category();
4164
        $course = $generator->create_course(['category' => $cat->id]);
4165
        $page = $generator->create_module('page', ['course' => $course->id]);
4166
 
4167
        $actual = context_module::instance($page->cmid)->get_capabilities();
4168
 
4169
        // Just test a few representative capabilities.
4170
        $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
1441 ariadna 4171
                'repository/upload:view', 'tiny/recordrtc:recordaudio'];
1 efrain 4172
 
4173
        $this->assert_capability_list_contains($expectedcapabilities, $actual);
4174
    }
4175
 
4176
    /**
4177
     * Test that {@see context_block::get_capabilities} returns capabilities relevant to blocks
4178
     *
4179
     * @covers \context_block::get_capabilities
4180
     */
4181
    public function test_context_block_caps_returned_by_get_capabilities_block_context(): void {
4182
        $this->resetAfterTest();
4183
 
4184
        $course = $this->getDataGenerator()->create_course();
4185
        $block = $this->getDataGenerator()->create_block('online_users', [
4186
            'parentcontextid' => context_course::instance($course->id)->id,
4187
        ]);
4188
 
4189
        $capabilities = context_block::instance($block->id)->get_capabilities();
4190
 
4191
        // Just test a few representative capabilities.
4192
        $expected = ['block/online_users:addinstance', 'moodle/block:edit', 'moodle/block:view'];
4193
        $this->assert_capability_list_contains($expected, $capabilities);
4194
 
4195
        // Now test with different sorting.
4196
        $capabilitiesbyname = context_block::instance($block->id)->get_capabilities('riskbitmask');
4197
 
4198
        $capabilitynames = array_column($capabilities, 'name');
4199
        $capabilitynamesordered = array_column($capabilitiesbyname, 'name');
4200
 
4201
        // Each array should contain the same data, ordered differently.
4202
        $this->assertEqualsCanonicalizing($capabilitynames, $capabilitynamesordered);
4203
        $this->assertNotSame($capabilitynames, $capabilitynamesordered);
4204
    }
4205
 
4206
    /**
4207
     * Test that {@see context_user::get_capabilities} returns capabilities relevant to users
4208
     *
4209
     * @covers \context_user::get_capabilities
4210
     */
4211
    public function test_context_user_caps_returned_by_get_capabilities_user_context(): void {
4212
        $this->resetAfterTest();
4213
 
4214
        $user = $this->getDataGenerator()->create_user();
4215
        $capabilities = context_user::instance($user->id)->get_capabilities();
4216
 
4217
        // Just test a few representative capabilities.
4218
        $expected = ['moodle/user:editmessageprofile', 'moodle/user:editprofile', 'moodle/user:viewalldetails'];
4219
        $this->assert_capability_list_contains($expected, $capabilities);
4220
 
4221
        // Now test with different sorting.
4222
        $capabilitiesbyname = context_user::instance($user->id)->get_capabilities('name');
4223
 
4224
        $capabilitynames = array_column($capabilities, 'name');
4225
        $capabilitynamesordered = array_column($capabilitiesbyname, 'name');
4226
 
4227
        // Each array should contain the same data, ordered differently.
4228
        $this->assertEqualsCanonicalizing($capabilitynames, $capabilitynamesordered);
4229
        $this->assertNotSame($capabilitynames, $capabilitynamesordered);
4230
    }
4231
 
4232
    /**
4233
     * Test updating of role capabilities during upgrade
4234
     *
4235
     * @covers ::update_capabilities
4236
     * @covers ::update_capabilities
4237
     */
11 efrain 4238
    public function test_update_capabilities(): void {
1 efrain 4239
        global $DB, $SITE;
4240
 
4241
        $this->resetAfterTest(true);
4242
 
4243
        $froncontext = context_course::instance($SITE->id);
4244
        $student = $DB->get_record('role', array('shortname'=>'student'));
4245
        $teacher = $DB->get_record('role', array('shortname'=>'teacher'));
4246
 
4247
        $existingcaps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
4248
 
4249
        $this->assertFalse(isset($existingcaps['moodle/site:restore']));         // Moved to new 'moodle/restore:restorecourse'.
4250
        $this->assertTrue(isset($existingcaps['moodle/restore:restorecourse'])); // New cap from 'moodle/site:restore'.
4251
        $this->assertTrue(isset($existingcaps['moodle/site:sendmessage']));      // New capability.
4252
        $this->assertTrue(isset($existingcaps['moodle/backup:backupcourse']));
4253
        $this->assertTrue(isset($existingcaps['moodle/backup:backupsection']));  // Cloned from 'moodle/backup:backupcourse'.
4254
        $this->assertTrue(isset($existingcaps['moodle/site:approvecourse']));    // Updated bitmask.
4255
        $this->assertTrue(isset($existingcaps['moodle/course:manageactivities']));
4256
        $this->assertTrue(isset($existingcaps['mod/page:addinstance']));         // Cloned from core 'moodle/course:manageactivities'.
4257
 
4258
        // Fake state before upgrade.
4259
        $DB->set_field('capabilities', 'name', 'moodle/site:restore', array('name'=>'moodle/restore:restorecourse'));
4260
        $DB->set_field('role_capabilities', 'capability', 'moodle/site:restore', array('capability'=>'moodle/restore:restorecourse'));
4261
        assign_capability('moodle/site:restore', CAP_PROHIBIT, $teacher->id, $froncontext->id, true);
4262
        $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/site:restore', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
4263
 
4264
        $DB->delete_records('role_capabilities', array('capability'=>'moodle/site:sendmessage'));
4265
        $DB->delete_records('capabilities', array('name'=>'moodle/site:sendmessage'));
4266
 
4267
        $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupsection'));
4268
        $DB->delete_records('capabilities', array('name'=>'moodle/backup:backupsection'));
4269
        assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $froncontext->id, true);
4270
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $teacher->id, $froncontext->id, true);
4271
 
4272
        $DB->set_field('capabilities', 'riskbitmask', 0, array('name'=>'moodle/site:approvecourse'));
4273
 
4274
        $DB->delete_records('role_capabilities', array('capability'=>'mod/page:addinstance'));
4275
        $DB->delete_records('capabilities', array('name'=>'mod/page:addinstance'));
4276
        assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $student->id, $froncontext->id, true);
4277
        assign_capability('moodle/course:manageactivities', CAP_ALLOW, $teacher->id, $froncontext->id, true);
4278
 
4279
        // Execute core.
4280
        update_capabilities('moodle');
4281
 
4282
        // Only core should be upgraded.
4283
        $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
4284
 
4285
        $this->assertFalse(isset($existingcaps['moodle/site:restore']));
4286
        $this->assertTrue(isset($caps['moodle/restore:restorecourse']));
4287
        $this->assertEquals($existingcaps['moodle/restore:restorecourse'], $caps['moodle/restore:restorecourse']);
4288
        $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/restore:restorecourse', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
4289
        $this->assertEquals($perms1, $perms2);
4290
 
4291
        $this->assertTrue(isset($caps['moodle/site:sendmessage']));
4292
        $this->assertEquals($existingcaps['moodle/site:sendmessage'], $caps['moodle/site:sendmessage']);
4293
 
4294
        $this->assertTrue(isset($caps['moodle/backup:backupsection']));
4295
        $this->assertEquals($existingcaps['moodle/backup:backupsection'], $caps['moodle/backup:backupsection']);
4296
        $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/backup:backupcourse', 'moodle/backup:backupsection'));
4297
        foreach ($roles as $role) {
4298
            $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
4299
            $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupsection', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
4300
            $this->assertEquals($perms1, $perms2);
4301
        }
4302
 
4303
        $this->assertTrue(isset($caps['moodle/site:approvecourse']));
4304
        $this->assertEquals($existingcaps['moodle/site:approvecourse'], $caps['moodle/site:approvecourse']);
4305
 
4306
        $this->assertFalse(isset($caps['mod/page:addinstance']));
4307
 
4308
        // Execute plugin.
4309
        update_capabilities('mod_page');
4310
        $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
4311
        $this->assertTrue(isset($caps['mod/page:addinstance']));
4312
        $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/course:manageactivities', 'mod/page:addinstance'));
4313
        foreach ($roles as $role) {
4314
            $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/course:manageactivities', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
4315
            $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'mod/page:addinstance', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
4316
        }
4317
        $this->assertEquals($perms1, $perms2);
4318
    }
4319
 
4320
    /**
4321
     * Tests update_capabilities when a capability is cloned, but there are existing settings
4322
     * for that capability.
4323
     *
4324
     * Under normal circumstances this shouldn't happen as it is only used for new capabilities,
4325
     * but it's possible there could be incorrect data in database.)
4326
     *
4327
     * @covers ::update_capabilities()
4328
     */
4329
    public function test_update_capabilities_clone_existing(): void {
4330
        global $DB;
4331
 
4332
        $this->resetAfterTest();
4333
 
4334
        // Create activities in a course. In each one, override so manager doesn't have
4335
        // moodle/course:manageactivities. In one of them, also override mod/forum:addinstance
4336
        // to something different.
4337
        $generator = $this->getDataGenerator();
4338
        $course = $generator->create_course();
4339
        $roleid = $DB->get_field('role', 'id', ['shortname' => 'manager']);
4340
        $page1 = $generator->create_module('page', ['course' => $course->id]);
4341
        $context1 = context_module::instance($page1->cmid);
4342
        assign_capability('moodle/course:manageactivities', CAP_PREVENT, $roleid, $context1->id);
4343
        $page2 = $generator->create_module('page', ['course' => $course->id]);
4344
        $context2 = context_module::instance($page2->cmid);
4345
        assign_capability('moodle/course:manageactivities', CAP_PREVENT, $roleid, $context2->id);
4346
        assign_capability('mod/forum:addinstance', CAP_PROHIBIT, $roleid, $context2->id);
4347
 
4348
        // Get rid of one of the capabilities for forum, which clones moodle/course:manageactivities.
4349
        $DB->delete_records('capabilities', ['name' => 'mod/forum:addinstance']);
4350
 
4351
        // Reinstall the capability.
4352
        update_capabilities('mod_forum');
4353
 
4354
        // Check the results: we should duplicate the manageactivities setting (PREVENT).
4355
        $rec1 = $DB->get_record('role_capabilities', ['roleid' => $roleid,
4356
                'contextid' => $context1->id, 'capability' => 'mod/forum:addinstance']);
4357
        $this->assertEquals(CAP_PREVENT, $rec1->permission);
4358
        // The second page, we should overwrite the previous existing permission setting.
4359
        $rec2 = $DB->get_record('role_capabilities', ['roleid' => $roleid,
4360
                'contextid' => $context2->id, 'capability' => 'mod/forum:addinstance']);
4361
        $this->assertEquals(CAP_PREVENT, $rec2->permission);
4362
    }
4363
 
4364
    /**
4365
     * Tests reset_role_capabilities function.
4366
     *
4367
     * @covers ::reset_role_capabilities
4368
     */
11 efrain 4369
    public function test_reset_role_capabilities(): void {
1 efrain 4370
        global $DB;
4371
        $this->resetAfterTest(true);
4372
        $generator = $this->getDataGenerator();
4373
 
4374
        // Create test course and user, enrol one in the other.
4375
        $course = $generator->create_course();
4376
        $user = $generator->create_user();
4377
        $roleid = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST);
4378
        $generator->enrol_user($user->id, $course->id, $roleid);
4379
 
4380
        // Change student role so it DOES have 'mod/forum:addinstance'.
4381
        $systemcontext = context_system::instance();
4382
        assign_capability('mod/forum:addinstance', CAP_ALLOW, $roleid, $systemcontext->id);
4383
 
4384
        // Override course so it does NOT allow students 'mod/forum:viewdiscussion'.
4385
        $coursecontext = context_course::instance($course->id);
4386
        assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $roleid, $coursecontext->id);
4387
 
4388
        // Check expected capabilities so far.
4389
        $this->assertTrue(has_capability('mod/forum:addinstance', $coursecontext, $user));
4390
        $this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user));
4391
 
4392
        // Oops, allowing student to add forums was a mistake, let's reset the role.
4393
        reset_role_capabilities($roleid);
4394
 
4395
        // Check new expected capabilities - role capabilities should have been reset,
4396
        // while the override at course level should remain.
4397
        $this->assertFalse(has_capability('mod/forum:addinstance', $coursecontext, $user));
4398
        $this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user));
4399
    }
4400
 
4401
    /**
4402
     * Tests count_role_users function.
4403
     *
4404
     * @covers ::count_role_users
4405
     */
11 efrain 4406
    public function test_count_role_users(): void {
1 efrain 4407
        global $DB;
4408
        $this->resetAfterTest(true);
4409
        $generator = self::getDataGenerator();
4410
        // Create a course in a category, and some users.
4411
        $category = $generator->create_category();
4412
        $course = $generator->create_course(array('category' => $category->id));
4413
        $user1 = $generator->create_user();
4414
        $user2 = $generator->create_user();
4415
        $user3 = $generator->create_user();
4416
        $user4 = $generator->create_user();
4417
        $user5 = $generator->create_user();
4418
        $roleid1 = $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST);
4419
        $roleid2 = $DB->get_field('role', 'id', array('shortname' => 'coursecreator'), MUST_EXIST);
4420
        // Enrol two users as managers onto the course, and 1 onto the category.
4421
        $generator->enrol_user($user1->id, $course->id, $roleid1);
4422
        $generator->enrol_user($user2->id, $course->id, $roleid1);
4423
        $generator->role_assign($roleid1, $user3->id, context_coursecat::instance($category->id));
4424
        // Enrol 1 user as a coursecreator onto the course, and another onto the category.
4425
        // This is to ensure we do not count users with roles that are not specified.
4426
        $generator->enrol_user($user4->id, $course->id, $roleid2);
4427
        $generator->role_assign($roleid2, $user5->id, context_coursecat::instance($category->id));
4428
        // Check that the correct users are found on the course.
4429
        $this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
4430
        $this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
4431
        // Check for the category.
4432
        $this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), false));
4433
        $this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), true));
4434
        // Have a user with the same role at both the category and course level.
4435
        $generator->role_assign($roleid1, $user1->id, context_coursecat::instance($category->id));
4436
        // The course level checks should remain the same.
4437
        $this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
4438
        $this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
4439
    }
4440
 
4441
    /**
4442
     * Test fetching users by capability.
4443
     *
4444
     * @covers ::get_users_by_capability
4445
     */
11 efrain 4446
    public function test_get_users_by_capability(): void {
1 efrain 4447
        global $DB;
4448
 
4449
        $this->resetAfterTest();
4450
 
4451
        $course = $this->getDataGenerator()->create_course();
4452
        $coursecontext = context_course::instance($course->id);
4453
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
4454
        $teacher = $this->getDataGenerator()->create_user();
4455
        $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
4456
        $student = $this->getDataGenerator()->create_user();
4457
        $guest = $DB->get_record('user', array('username' => 'guest'));
4458
 
4459
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
4460
        role_assign($studentrole->id, $student->id, $coursecontext);
4461
        $admin = $DB->get_record('user', array('username' => 'admin'));
4462
 
4463
        // Note: Here are used default capabilities, the full test is in permission evaluation below,
4464
        // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
4465
        $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
4466
        $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse')));
4467
 
4468
        $users = get_users_by_capability($coursecontext, 'moodle/backup:backupcourse');
4469
 
4470
        $this->assertTrue(array_key_exists($teacher->id, $users));
4471
        $this->assertFalse(array_key_exists($admin->id, $users));
4472
        $this->assertFalse(array_key_exists($student->id, $users));
4473
        $this->assertFalse(array_key_exists($guest->id, $users));
4474
 
4475
        $users = get_users_by_capability($coursecontext, 'moodle/site:approvecourse');
4476
 
4477
        $this->assertFalse(array_key_exists($teacher->id, $users));
4478
        $this->assertFalse(array_key_exists($admin->id, $users));
4479
        $this->assertFalse(array_key_exists($student->id, $users));
4480
        $this->assertFalse(array_key_exists($guest->id, $users));
4481
 
4482
        // Test role override.
4483
        assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
4484
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
4485
 
4486
        $users = get_users_by_capability($coursecontext, 'moodle/backup:backupcourse');
4487
 
4488
        $this->assertFalse(array_key_exists($teacher->id, $users));
4489
        $this->assertFalse(array_key_exists($admin->id, $users));
4490
        $this->assertTrue(array_key_exists($student->id, $users));
4491
        $this->assertFalse(array_key_exists($guest->id, $users));
4492
    }
4493
 
4494
 
4495
    /**
4496
     * @covers ::get_with_capability_sql
4497
     */
11 efrain 4498
    public function test_get_with_capability_sql(): void {
1 efrain 4499
        global $DB;
4500
 
4501
        $this->resetAfterTest();
4502
 
4503
        $course = $this->getDataGenerator()->create_course();
4504
        $coursecontext = context_course::instance($course->id);
4505
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
4506
        $teacher = $this->getDataGenerator()->create_user();
4507
        $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
4508
        $student = $this->getDataGenerator()->create_user();
4509
        $guest = $DB->get_record('user', array('username' => 'guest'));
4510
 
4511
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
4512
        role_assign($studentrole->id, $student->id, $coursecontext);
4513
        $admin = $DB->get_record('user', array('username' => 'admin'));
4514
 
4515
        // Note: Here are used default capabilities, the full test is in permission evaluation below,
4516
        // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
4517
        $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
4518
        $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse')));
4519
 
4520
        list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse');
4521
        $users = $DB->get_records_sql($sql, $params);
4522
 
4523
        $this->assertTrue(array_key_exists($teacher->id, $users));
4524
        $this->assertFalse(array_key_exists($admin->id, $users));
4525
        $this->assertFalse(array_key_exists($student->id, $users));
4526
        $this->assertFalse(array_key_exists($guest->id, $users));
4527
 
4528
        list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/site:approvecourse');
4529
        $users = $DB->get_records_sql($sql, $params);
4530
 
4531
        $this->assertFalse(array_key_exists($teacher->id, $users));
4532
        $this->assertFalse(array_key_exists($admin->id, $users));
4533
        $this->assertFalse(array_key_exists($student->id, $users));
4534
        $this->assertFalse(array_key_exists($guest->id, $users));
4535
 
4536
        // Test role override.
4537
        assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
4538
        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
4539
 
4540
        list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse');
4541
        $users = $DB->get_records_sql($sql, $params);
4542
 
4543
        $this->assertFalse(array_key_exists($teacher->id, $users));
4544
        $this->assertFalse(array_key_exists($admin->id, $users));
4545
        $this->assertTrue(array_key_exists($student->id, $users));
4546
        $this->assertFalse(array_key_exists($guest->id, $users));
4547
    }
4548
 
4549
 
4550
    /**
4551
     * Get the test cases for {@link test_get_with_capability_join_when_overrides_present()}.
4552
     *
4553
     * The particular capabilties used here do not really matter. What is important is
4554
     * that they are capabilities which the Student roles has by default, but the
4555
     * authenticated suser role does not.
4556
     *
4557
     * @return array
4558
     */
1441 ariadna 4559
    public static function get_get_with_capability_join_override_cases(): array {
1 efrain 4560
        return [
4561
                'no overrides' => [true, []],
4562
                'one override' => [true, ['moodle/course:viewscales']],
4563
                'both overrides' => [false, ['moodle/course:viewscales', 'moodle/question:flag']],
4564
        ];
4565
    }
4566
 
4567
    /**
4568
     * Test get_with_capability_join.
4569
     *
4570
     * @dataProvider get_get_with_capability_join_override_cases
4571
     * @covers ::get_with_capability_join
4572
     *
4573
     * @param bool $studentshouldbereturned whether, with this combination of capabilities, the student should be in the results.
4574
     * @param array $capabilitiestoprevent capabilities to override to prevent in the course context.
4575
     */
4576
    public function test_get_with_capability_join_when_overrides_present(
11 efrain 4577
            bool $studentshouldbereturned, array $capabilitiestoprevent): void {
1 efrain 4578
        global $DB;
4579
        $this->resetAfterTest();
4580
        $generator = $this->getDataGenerator();
4581
 
4582
        // Create a course.
4583
        $category = $generator->create_category();
4584
        $course = $generator->create_course(['category' => $category->id]);
4585
 
4586
        // Create a user.
4587
        $student = $generator->create_user();
4588
        $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
4589
        $generator->enrol_user($student->id, $course->id, $studentrole->id);
4590
 
4591
        // This test assumes that by default the student roles has the two
4592
        // capabilities. Check this now in case the role definitions are every changed.
4593
        $coursecontext = context_course::instance($course->id);
4594
        $this->assertTrue(has_capability('moodle/course:viewscales', $coursecontext, $student));
4595
        $this->assertTrue(has_capability('moodle/question:flag', $coursecontext, $student));
4596
 
4597
        // We test cases where there are a varying number of prevent overrides.
4598
        foreach ($capabilitiestoprevent as $capability) {
4599
            role_change_permission($studentrole->id, $coursecontext, $capability, CAP_PREVENT);
4600
        }
4601
 
4602
        // So now, assemble our query using the method under test, and verify that it returns the student.
4603
        $sqljoin = get_with_capability_join($coursecontext,
4604
                ['moodle/course:viewscales', 'moodle/question:flag'], 'u.id');
4605
 
4606
        $users = $DB->get_records_sql("SELECT u.*
4607
                  FROM {user} u
4608
                       {$sqljoin->joins}
4609
                 WHERE {$sqljoin->wheres}", $sqljoin->params);
4610
        if ($studentshouldbereturned) {
4611
            $this->assertEquals([$student->id], array_keys($users));
4612
        } else {
4613
            $this->assertEmpty($users);
4614
        }
4615
    }
4616
 
4617
    /**
4618
     * Test the get_profile_roles() function.
4619
     *
4620
     * @covers ::get_profile_roles
4621
     */
11 efrain 4622
    public function test_get_profile_roles(): void {
1 efrain 4623
        global $DB;
4624
        $this->resetAfterTest();
4625
 
4626
        $course = $this->getDataGenerator()->create_course();
4627
        $coursecontext = context_course::instance($course->id);
4628
 
4629
        // Assign a student role.
4630
        $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
4631
        $user1 = $this->getDataGenerator()->create_user();
4632
        role_assign($studentrole->id, $user1->id, $coursecontext);
4633
 
4634
        // Assign an editing teacher role.
4635
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
4636
        $user2 = $this->getDataGenerator()->create_user();
4637
        role_assign($teacherrole->id, $user2->id, $coursecontext);
4638
 
4639
        // Create a custom role that can be assigned at course level, but don't assign it yet.
4640
        create_role('Custom role', 'customrole', 'Custom course role');
4641
        $customrole = $DB->get_record('role', array('shortname' => 'customrole'), '*', MUST_EXIST);
4642
        set_role_contextlevels($customrole->id, [CONTEXT_COURSE]);
4643
        core_role_set_assign_allowed($teacherrole->id, $customrole->id); // Allow teacher to assign the role in the course.
4644
 
4645
        // Set the site policy 'profileroles' to show student, teacher and non-editing teacher roles (i.e. not the custom role).
4646
        $neteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
4647
        set_config('profileroles', "{$studentrole->id}, {$teacherrole->id}, {$neteacherrole->id}");
4648
 
4649
        // A student in the course (given they can't assign roles) should see those roles which are:
4650
        // - listed in the 'profileroles' site policy AND
4651
        // - are assigned in the course context (or parent contexts).
4652
        // In this case, the non-editing teacher role is not assigned and should not be returned.
4653
        $expected = [
4654
            $teacherrole->id => (object) [
4655
                'id' => $teacherrole->id,
4656
                'name' => '',
4657
                'shortname' => $teacherrole->shortname,
4658
                'sortorder' => $teacherrole->sortorder,
4659
                'coursealias' => null
4660
            ],
4661
            $studentrole->id => (object) [
4662
                'id' => $studentrole->id,
4663
                'name' => '',
4664
                'shortname' => $studentrole->shortname,
4665
                'sortorder' => $studentrole->sortorder,
4666
                'coursealias' => null
4667
            ]
4668
        ];
4669
        $this->setUser($user1);
4670
        $this->assertEquals($expected, get_profile_roles($coursecontext));
4671
 
4672
        // An editing teacher should also see only 2 roles at this stage as only 2 roles are assigned: 'teacher' and 'student'.
4673
        $this->setUser($user2);
4674
        $this->assertEquals($expected, get_profile_roles($coursecontext));
4675
 
4676
        // Assign a custom role in the course.
4677
        $user3 = $this->getDataGenerator()->create_user();
4678
        role_assign($customrole->id, $user3->id, $coursecontext);
4679
 
4680
        // Confirm that the teacher can see the custom role now that it's assigned.
4681
        $expectedteacher = [
4682
            $teacherrole->id => (object) [
4683
                'id' => $teacherrole->id,
4684
                'name' => '',
4685
                'shortname' => $teacherrole->shortname,
4686
                'sortorder' => $teacherrole->sortorder,
4687
                'coursealias' => null
4688
            ],
4689
            $studentrole->id => (object) [
4690
                'id' => $studentrole->id,
4691
                'name' => '',
4692
                'shortname' => $studentrole->shortname,
4693
                'sortorder' => $studentrole->sortorder,
4694
                'coursealias' => null
4695
            ],
4696
            $customrole->id => (object) [
4697
                'id' => $customrole->id,
4698
                'name' => 'Custom role',
4699
                'shortname' => $customrole->shortname,
4700
                'sortorder' => $customrole->sortorder,
4701
                'coursealias' => null
4702
            ]
4703
        ];
4704
        $this->setUser($user2);
4705
        $this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
4706
 
4707
        // And that the student can't, because the role isn't included in the 'profileroles' site policy.
4708
        $expectedstudent = [
4709
            $teacherrole->id => (object) [
4710
                'id' => $teacherrole->id,
4711
                'name' => '',
4712
                'shortname' => $teacherrole->shortname,
4713
                'sortorder' => $teacherrole->sortorder,
4714
                'coursealias' => null
4715
            ],
4716
            $studentrole->id => (object) [
4717
                'id' => $studentrole->id,
4718
                'name' => '',
4719
                'shortname' => $studentrole->shortname,
4720
                'sortorder' => $studentrole->sortorder,
4721
                'coursealias' => null
4722
            ]
4723
        ];
4724
        $this->setUser($user1);
4725
        $this->assertEquals($expectedstudent, get_profile_roles($coursecontext));
4726
 
4727
        // If we have no roles listed in the site policy, the teacher should be able to see the assigned roles.
4728
        $expectedteacher = [
4729
            $studentrole->id => (object) [
4730
                'id' => $studentrole->id,
4731
                'name' => '',
4732
                'shortname' => $studentrole->shortname,
4733
                'sortorder' => $studentrole->sortorder,
4734
                'coursealias' => null
4735
            ],
4736
            $customrole->id => (object) [
4737
                'id' => $customrole->id,
4738
                'name' => 'Custom role',
4739
                'shortname' => $customrole->shortname,
4740
                'sortorder' => $customrole->sortorder,
4741
                'coursealias' => null
4742
            ],
4743
            $teacherrole->id => (object) [
4744
                'id' => $teacherrole->id,
4745
                'name' => '',
4746
                'shortname' => $teacherrole->shortname,
4747
                'sortorder' => $teacherrole->sortorder,
4748
                'coursealias' => null
4749
            ],
4750
        ];
4751
        set_config('profileroles', "");
4752
        $this->setUser($user2);
4753
        $this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
4754
    }
4755
 
4756
    /**
4757
     * Data provider for is_parent_of context checks.
4758
     *
4759
     * @return  array
4760
     */
1441 ariadna 4761
    public static function is_parent_of_provider(): array {
1 efrain 4762
        $provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array {
4763
            return [
4764
                "includeself: true; {$desc}" => [
4765
                    $contextpath,
4766
                    $testpath,
4767
                    true,
4768
                    $expected,
4769
                ],
4770
                "includeself: false; {$desc}" => [
4771
                    $contextpath,
4772
                    $testpath,
4773
                    false,
4774
                    $expected,
4775
                ],
4776
            ];
4777
        };
4778
 
4779
        return array_merge(
4780
            [
4781
                'includeself: true, testing self' => [
4782
                    '/1/4/17/291/1001/17105',
4783
                    '/1/4/17/291/1001/17105',
4784
                    true,
4785
                    true,
4786
                ],
4787
                'includeself: false, testing self' => [
4788
                    '/1/4/17/291/1001/17105',
4789
                    '/1/4/17/291/1001/17105',
4790
                    false,
4791
                    false,
4792
                ],
4793
            ],
4794
            $provideboth(
4795
                'testing parent',
4796
                '/1/4/17/291/1001/17105',
4797
                '/1/4/17/291/1001',
4798
                false
4799
            ),
4800
            $provideboth(
4801
                'testing child',
4802
                '/1/4/17/291/1001',
4803
                '/1/4/17/291/1001/17105',
4804
                true
4805
            ),
4806
            $provideboth(
4807
                'testing grandchild',
4808
                '/1',
4809
                '/1/4/17/291/1001/17105',
4810
                true
4811
            )
4812
        );
4813
    }
4814
 
4815
    /**
4816
     * Ensure that the is_parent_of() function works as anticipated.
4817
     *
4818
     * @dataProvider is_parent_of_provider
4819
     * @covers \context::is_parent_of
4820
     * @covers \context_block::is_parent_of
4821
     * @covers \context_course::is_parent_of
4822
     * @covers \context_coursecat::is_parent_of
4823
     * @covers \context_module::is_parent_of
4824
     * @covers \context_system::is_parent_of
4825
     * @covers \context_user::is_parent_of
4826
     * @param   string $contextpath The path of the context being compared with
4827
     * @param   string $testpath The path of the context being compared
4828
     * @param   bool $testself Whether to check the current context
4829
     * @param   bool $expected The expected result
4830
     */
4831
    public function test_is_parent_of(string $contextpath, string $testpath, bool $testself, bool $expected): void {
4832
        $context = $this->getMockBuilder(\context::class)
4833
            ->disableOriginalConstructor()
4834
            ->onlyMethods([
4835
                'get_url',
4836
                'get_capabilities',
4837
            ])
4838
            ->getMock();
4839
 
4840
        $rcp = new ReflectionProperty($context, '_path');
4841
        $rcp->setValue($context, $contextpath);
4842
 
4843
        $comparisoncontext = $this->getMockBuilder(\context::class)
4844
            ->disableOriginalConstructor()
4845
            ->onlyMethods([
4846
                'get_url',
4847
                'get_capabilities',
4848
            ])
4849
            ->getMock();
4850
 
4851
        $rcp = new ReflectionProperty($comparisoncontext, '_path');
4852
        $rcp->setValue($comparisoncontext, $testpath);
4853
 
4854
        $this->assertEquals($expected, $context->is_parent_of($comparisoncontext, $testself));
4855
    }
4856
 
4857
    /**
4858
     * Data provider for is_child_of context checks.
4859
     *
4860
     * @return  array
4861
     */
1441 ariadna 4862
    public static function is_child_of_provider(): array {
1 efrain 4863
        $provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array {
4864
            return [
4865
                "includeself: true; {$desc}" => [
4866
                    $contextpath,
4867
                    $testpath,
4868
                    true,
4869
                    $expected,
4870
                ],
4871
                "includeself: false; {$desc}" => [
4872
                    $contextpath,
4873
                    $testpath,
4874
                    false,
4875
                    $expected,
4876
                ],
4877
            ];
4878
        };
4879
 
4880
        return array_merge(
4881
            [
4882
                'includeself: true, testing self' => [
4883
                    '/1/4/17/291/1001/17105',
4884
                    '/1/4/17/291/1001/17105',
4885
                    true,
4886
                    true,
4887
                ],
4888
                'includeself: false, testing self' => [
4889
                    '/1/4/17/291/1001/17105',
4890
                    '/1/4/17/291/1001/17105',
4891
                    false,
4892
                    false,
4893
                ],
4894
            ],
4895
            $provideboth(
4896
                'testing child',
4897
                '/1/4/17/291/1001/17105',
4898
                '/1/4/17/291/1001',
4899
                true
4900
            ),
4901
            $provideboth(
4902
                'testing parent',
4903
                '/1/4/17/291/1001',
4904
                '/1/4/17/291/1001/17105',
4905
                false
4906
            ),
4907
            $provideboth(
4908
                'testing grandchild',
4909
                '/1/4/17/291/1001/17105',
4910
                '/1',
4911
                true
4912
            ),
4913
            $provideboth(
4914
                'testing grandparent',
4915
                '/1',
4916
                '/1/4/17/291/1001/17105',
4917
                false
4918
            )
4919
        );
4920
    }
4921
 
4922
    /**
4923
     * Ensure that the is_child_of() function works as anticipated.
4924
     *
4925
     * @dataProvider is_child_of_provider
4926
     * @covers \context::is_child_of
4927
     * @covers \context_block::is_child_of
4928
     * @covers \context_course::is_child_of
4929
     * @covers \context_coursecat::is_child_of
4930
     * @covers \context_module::is_child_of
4931
     * @covers \context_system::is_child_of
4932
     * @covers \context_user::is_child_of
4933
     * @param   string $contextpath The path of the context being compared with
4934
     * @param   string $testpath The path of the context being compared
4935
     * @param   bool $testself Whether to check the current context
4936
     * @param   bool $expected The expected result
4937
     */
4938
    public function test_is_child_of(string $contextpath, string $testpath, bool $testself, bool $expected): void {
4939
        $context = $this->getMockBuilder(\context::class)
4940
            ->disableOriginalConstructor()
4941
            ->onlyMethods([
4942
                'get_url',
4943
                'get_capabilities',
4944
            ])
4945
            ->getMock();
4946
 
4947
        $rcp = new ReflectionProperty($context, '_path');
4948
        $rcp->setValue($context, $contextpath);
4949
 
4950
        $comparisoncontext = $this->getMockBuilder(\context::class)
4951
            ->disableOriginalConstructor()
4952
            ->onlyMethods([
4953
                'get_url',
4954
                'get_capabilities',
4955
            ])
4956
            ->getMock();
4957
 
4958
        $rcp = new ReflectionProperty($comparisoncontext, '_path');
4959
        $rcp->setValue($comparisoncontext, $testpath);
4960
 
4961
        $this->assertEquals($expected, $context->is_child_of($comparisoncontext, $testself));
4962
    }
4963
 
4964
    /**
4965
     * Ensure that the get_parent_contexts() function limits the number of queries it performs.
4966
     *
4967
     * @covers ::get_parent_contexts
4968
     */
11 efrain 4969
    public function test_get_parent_contexts_preload(): void {
1 efrain 4970
        global $DB;
4971
 
4972
        $this->resetAfterTest();
4973
 
4974
        /*
4975
         * Given the following data structure:
4976
         * System
4977
         * - Category
4978
         * --- Category
4979
         * ----- Category
4980
         * ------- Category
4981
         * --------- Course
4982
         * ----------- Activity (Forum)
4983
         */
4984
 
4985
        $contexts = [];
4986
 
4987
        $cat1 = $this->getDataGenerator()->create_category();
4988
        $cat2 = $this->getDataGenerator()->create_category(['parent' => $cat1->id]);
4989
        $cat3 = $this->getDataGenerator()->create_category(['parent' => $cat2->id]);
4990
        $cat4 = $this->getDataGenerator()->create_category(['parent' => $cat3->id]);
4991
        $course = $this->getDataGenerator()->create_course(['category' => $cat4->id]);
4992
        $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
4993
 
4994
        $modcontext = context_module::instance($forum->cmid);
4995
 
4996
        context_helper::reset_caches();
4997
 
4998
        // There should only be a single DB query.
4999
        $predbqueries = $DB->perf_get_reads();
5000
 
5001
        $parents = $modcontext->get_parent_contexts();
5002
        // Note: For some databases There is one read, plus one FETCH, plus one CLOSE.
5003
        // These all show as reads, when there has actually only been a single query.
5004
        $this->assertLessThanOrEqual(3, $DB->perf_get_reads() - $predbqueries);
5005
    }
5006
 
5007
    /**
5008
     * Ensure that get_with_capability_sql and get_with_capability_join respect context locking.
5009
     *
5010
     * @covers ::get_with_capability_join
5011
     * @covers ::get_with_capability_sql
5012
     */
11 efrain 5013
    public function test_get_with_capability_sql_locked(): void {
1 efrain 5014
        global $DB;
5015
 
5016
        $this->resetAfterTest();
5017
 
5018
        $generator = $this->getDataGenerator();
5019
 
5020
        $cat1 = $generator->create_category();
5021
        $cat2 = $generator->create_category();
5022
        $cat1course1 = $generator->create_course(['category' => $cat1->id]);
5023
        $cat1course1forum = $generator->create_module('forum', ['course' => $cat1course1]);
5024
 
5025
        $contexts = (object) [
5026
            'system' => \context_system::instance(),
5027
            'cat1' => \context_coursecat::instance($cat1->id),
5028
            'cat2' => \context_coursecat::instance($cat2->id),
5029
            'cat1course1' => \context_course::instance($cat1course1->id),
5030
            'cat1course1forum' => \context_module::instance($cat1course1forum->cmid),
5031
        ];
5032
 
5033
        // Test with the 'mod/forum:startdiscussion' capability.
5034
        $caput = 'mod/forum:startdiscussion';
5035
 
5036
        // Create a test user.
5037
        $uut = $generator->create_and_enrol($cat1course1, 'teacher');
5038
 
5039
        // Initially the user will be returned by get_users_by_capability.
5040
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5041
        $users = $DB->get_records_sql($sql, $params);
5042
        $this->assertArrayHasKey($uut->id, $users);
5043
 
5044
        // Freezing the forum will remove the user.
5045
        set_config('contextlocking', 1);
5046
        $contexts->cat1course1forum->set_locked(true);
5047
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5048
        $users = $DB->get_records_sql($sql, $params);
5049
        $this->assertArrayNotHasKey($uut->id, $users);
5050
 
5051
        // But not if context locking is disabled.
5052
        set_config('contextlocking', 0);
5053
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5054
        $users = $DB->get_records_sql($sql, $params);
5055
        $this->assertArrayHasKey($uut->id, $users);
5056
 
5057
        $contexts->cat1course1forum->set_locked(false);
5058
 
5059
        // Freezing the course will have the same effect.
5060
        set_config('contextlocking', 1);
5061
        $contexts->cat1course1->set_locked(true);
5062
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5063
        $users = $DB->get_records_sql($sql, $params);
5064
        $this->assertArrayNotHasKey($uut->id, $users);
5065
 
5066
        // But not if context locking is disabled.
5067
        set_config('contextlocking', 0);
5068
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5069
        $users = $DB->get_records_sql($sql, $params);
5070
        $this->assertArrayHasKey($uut->id, $users);
5071
 
5072
        $contexts->cat1course1->set_locked(false);
5073
 
5074
        // Freezing the category will have the same effect.
5075
        set_config('contextlocking', 1);
5076
        $contexts->cat1->set_locked(true);
5077
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5078
        $users = $DB->get_records_sql($sql, $params);
5079
        $this->assertArrayNotHasKey($uut->id, $users);
5080
 
5081
        // But not if context locking is disabled.
5082
        set_config('contextlocking', 0);
5083
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5084
        $users = $DB->get_records_sql($sql, $params);
5085
        $this->assertArrayHasKey($uut->id, $users);
5086
 
5087
        $contexts->cat1->set_locked(false);
5088
 
5089
        // Freezing an unrelated category will have no effect.
5090
        set_config('contextlocking', 1);
5091
        $contexts->cat2->set_locked(true);
5092
        list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
5093
        $users = $DB->get_records_sql($sql, $params);
5094
        $this->assertArrayHasKey($uut->id, $users);
5095
    }
5096
 
5097
    /**
5098
     * Ensure that get_users_by_capability respects context freezing.
5099
     *
5100
     * @covers ::get_users_by_capability
5101
     */
11 efrain 5102
    public function test_get_users_by_capability_locked(): void {
1 efrain 5103
        $this->resetAfterTest();
5104
 
5105
        $generator = $this->getDataGenerator();
5106
 
5107
        $cat1 = $generator->create_category();
5108
        $cat2 = $generator->create_category();
5109
        $cat1course1 = $generator->create_course(['category' => $cat1->id]);
5110
        $cat1course1forum = $generator->create_module('forum', ['course' => $cat1course1]);
5111
 
5112
        $contexts = (object) [
5113
            'system' => \context_system::instance(),
5114
            'cat1' => \context_coursecat::instance($cat1->id),
5115
            'cat2' => \context_coursecat::instance($cat2->id),
5116
            'cat1course1' => \context_course::instance($cat1course1->id),
5117
            'cat1course1forum' => \context_module::instance($cat1course1forum->cmid),
5118
        ];
5119
 
5120
        // Test with the 'mod/forum:startdiscussion' capability.
5121
        $caput = 'mod/forum:startdiscussion';
5122
 
5123
        // Create a test user.
5124
        $uut = $generator->create_and_enrol($cat1course1, 'teacher');
5125
 
5126
        // Initially the user will be returned by get_users_by_capability.
5127
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5128
        $this->assertArrayHasKey($uut->id, $users);
5129
 
5130
        // Freezing the forum will remove the user.
5131
        set_config('contextlocking', 1);
5132
        $contexts->cat1course1forum->set_locked(true);
5133
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5134
        $this->assertArrayNotHasKey($uut->id, $users);
5135
 
5136
        // But not if context locking is disabled.
5137
        set_config('contextlocking', 0);
5138
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5139
        $this->assertArrayHasKey($uut->id, $users);
5140
 
5141
        $contexts->cat1course1forum->set_locked(false);
5142
 
5143
        // Freezing the course will have the same effect.
5144
        set_config('contextlocking', 1);
5145
        $contexts->cat1course1->set_locked(true);
5146
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5147
        $this->assertArrayNotHasKey($uut->id, $users);
5148
 
5149
        // But not if context locking is disabled.
5150
        set_config('contextlocking', 0);
5151
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5152
        $this->assertArrayHasKey($uut->id, $users);
5153
 
5154
        $contexts->cat1course1->set_locked(false);
5155
 
5156
        // Freezing the category will have the same effect.
5157
        set_config('contextlocking', 1);
5158
        $contexts->cat1->set_locked(true);
5159
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5160
        $this->assertArrayNotHasKey($uut->id, $users);
5161
 
5162
        // But not if context locking is disabled.
5163
        set_config('contextlocking', 0);
5164
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5165
        $this->assertArrayHasKey($uut->id, $users);
5166
 
5167
        $contexts->cat1->set_locked(false);
5168
 
5169
        // Freezing an unrelated category will have no effect.
5170
        set_config('contextlocking', 1);
5171
        $contexts->cat2->set_locked(true);
5172
        $users = get_users_by_capability($contexts->cat1course1forum, $caput);
5173
        $this->assertArrayHasKey($uut->id, $users);
5174
    }
5175
 
5176
    /**
5177
     * Test require_all_capabilities.
5178
     *
5179
     * @covers ::require_all_capabilities
5180
     */
11 efrain 5181
    public function test_require_all_capabilities(): void {
1 efrain 5182
        global $DB;
5183
 
5184
        $this->resetAfterTest();
5185
 
5186
        $course = $this->getDataGenerator()->create_course();
5187
        $coursecontext = context_course::instance($course->id);
5188
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
5189
        $teacher = $this->getDataGenerator()->create_user();
5190
        role_assign($teacherrole->id, $teacher->id, $coursecontext);
5191
 
5192
        // Note: Here are used default capabilities, the full test is in permission evaluation bellow,
5193
        // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
5194
        $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupsection')));
5195
        $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
5196
 
5197
        $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
5198
 
5199
        $this->setUser($teacher);
5200
        require_all_capabilities($sca, $coursecontext);
5201
        require_all_capabilities($sca, $coursecontext, $teacher);
5202
 
5203
        // Guest users should not have any of these perms.
5204
        $this->setUser(0);
5205
        $this->expectException(\required_capability_exception::class);
5206
        require_all_capabilities($sca, $coursecontext);
5207
    }
5208
 
5209
    /**
5210
     * Test get_navigation_filter_context.
5211
     *
1441 ariadna 5212
     * @covers \core\context_helper::get_navigation_filter_context
1 efrain 5213
     */
11 efrain 5214
    public function test_get_navigation_filter_context(): void {
1 efrain 5215
        $this->resetAfterTest();
5216
        $course = $this->getDataGenerator()->create_course();
5217
        set_config('filternavigationwithsystemcontext', 0);
5218
        // First test passed values are returned if disabled.
5219
        $this->assertNull(context_helper::get_navigation_filter_context(null));
5220
        $coursecontext = context_course::instance($course->id);
5221
        $filtercontext = context_helper::get_navigation_filter_context($coursecontext);
5222
        $this->assertEquals($coursecontext->id, $filtercontext->id);
5223
 
5224
        // Now test that any input returns system context if enabled.
5225
        set_config('filternavigationwithsystemcontext', 1);
5226
        $filtercontext = context_helper::get_navigation_filter_context(null);
5227
        $this->assertInstanceOf('\context_system', $filtercontext);
5228
        $filtercontext = context_helper::get_navigation_filter_context($coursecontext);
5229
        $this->assertInstanceOf('\context_system', $filtercontext);
5230
    }
1441 ariadna 5231
 
5232
    /**
5233
     * Test access APIs when dealing with deprecated plugin types.
5234
     *
5235
     * Note: this injects a mocked plugin type into core_component and is a slow test that must be run in isolation.
5236
     *
5237
     * @covers ::has_capability
5238
     * @runInSeparateProcess
5239
     */
5240
    public function test_capabilities_deprecated_plugintype(): void {
5241
        $this->resetAfterTest();
5242
 
5243
        global $CFG;
5244
        $this->add_mocked_plugintype('fake', "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins/fake", true);
5245
        $this->add_mocked_plugin('fake', 'fullfeatured', "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins/fake/fullfeatured");
5246
        update_capabilities('fake_fullfeatured');
5247
 
5248
        $this->assertTrue(\core_component::is_deprecated_plugin_type('fake'));
5249
 
5250
        $user = $this->getDataGenerator()->create_user();
5251
        $this->assertNotEmpty(get_capability_info('fake/fullfeatured:fakecapability'));
5252
        $this->assertTrue(has_capability('fake/fullfeatured:fakecapability', \core\context\system::instance(), $user));
5253
        $this->assertEquals('Fullfeatured capability description', get_capability_string('fake/fullfeatured:fakecapability'));
5254
    }
5255
 
5256
    /**
5257
     * Test get_deprecated_capability_info() debugging messages.
5258
     *
5259
     * @covers ::get_deprecated_capability_info
5260
     */
5261
    public function test_get_deprecated_capability_info_debugging(): void {
5262
        $this->resetAfterTest();
5263
        $course = $this->getDataGenerator()->create_course();
5264
        $this->getDataGenerator()->create_and_enrol($course);
5265
        $this->setup_fake_plugin('access');
5266
        // Debugging messages should not be called with valid capability.
5267
        get_capability_info('fake/access:existingcapability');
5268
        $this->assertDebuggingNotCalled();
5269
        // Debugging messages should be called with invalid capability.
5270
        get_capability_info('fake/access:fakecapability');
5271
        $this->assertDebuggingCalled("The capability 'fake/access:fakecapability' is"
5272
            . " deprecated.This capability should not be used anymore.");
5273
        // Debugging messages should not be called with invalid capability with suppression param supplied.
5274
        get_capability_info('fake/access:fakecapability', false);
5275
        $this->assertDebuggingNotCalled();
5276
    }
1 efrain 5277
}
5278
 
5279
/**
5280
 * Context caching fixture
5281
 */
5282
abstract class context_inspection extends \core\context_helper {
5283
    /**
5284
     * Return the cached contexts count for testing purposes.
5285
     *
5286
     * @return int
5287
     */
5288
    public static function check_context_cache_size() {
5289
        return self::$cache_count;
5290
    }
5291
}