mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
1093256560
While this change is not 100% required now, it's good habit and we are checking for it since Moodle 4.4. All the changes in this commit have been applied automatically using the moodle.PHPUnit.TestReturnType sniff and are, exclusively adding the ": void" return types when missing.
5297 lines
246 KiB
PHP
5297 lines
246 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Full functional accesslib test.
|
|
*
|
|
* @package core
|
|
* @category phpunit
|
|
* @copyright 2011 Petr Skoda {@link http://skodak.org}
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Functional test for accesslib.php
|
|
*
|
|
* Note: execution may take many minutes especially on slower servers.
|
|
*/
|
|
class accesslib_test extends advanced_testcase {
|
|
|
|
/**
|
|
* Setup.
|
|
*/
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
$this->resetAfterTest();
|
|
// Turn off the course welcome message, so we can easily test other messages.
|
|
set_config('sendcoursewelcomemessage', 0, 'enrol_manual');
|
|
}
|
|
|
|
/**
|
|
* Verify comparison of context instances in phpunit asserts.
|
|
*/
|
|
public function test_context_comparisons(): void {
|
|
$frontpagecontext1 = context_course::instance(SITEID);
|
|
context_helper::reset_caches();
|
|
$frontpagecontext2 = context_course::instance(SITEID);
|
|
$this->assertEquals($frontpagecontext1, $frontpagecontext2);
|
|
|
|
$user1 = context_user::instance(1);
|
|
$user2 = context_user::instance(2);
|
|
$this->assertNotEquals($user1, $user2);
|
|
}
|
|
|
|
/**
|
|
* Test resetting works.
|
|
*
|
|
* @covers ::accesslib_clear_all_caches_for_unit_testing
|
|
*/
|
|
public function test_accesslib_clear_all_caches(): void {
|
|
global $ACCESSLIB_PRIVATE;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$this->setAdminUser();
|
|
load_all_capabilities();
|
|
|
|
$this->assertNotEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
|
|
accesslib_clear_all_caches_for_unit_testing();
|
|
$this->assertEmpty($ACCESSLIB_PRIVATE->dirtycontexts);
|
|
$this->assertEmpty($ACCESSLIB_PRIVATE->accessdatabyuser);
|
|
}
|
|
|
|
/**
|
|
* Check modifying capability record is not exposed to other code.
|
|
*/
|
|
public function test_capabilities_mutation(): void {
|
|
$oldcap = get_capability_info('moodle/site:config');
|
|
$cap = get_capability_info('moodle/site:config');
|
|
unset($cap->name);
|
|
$newcap = get_capability_info('moodle/site:config');
|
|
|
|
$this->assertFalse(isset($cap->name));
|
|
$this->assertTrue(isset($newcap->name));
|
|
$this->assertTrue(isset($oldcap->name));
|
|
}
|
|
|
|
/**
|
|
* Test getting of role access
|
|
*
|
|
* @covers ::get_role_access
|
|
*/
|
|
public function test_get_role_access(): void {
|
|
global $DB;
|
|
|
|
$roles = $DB->get_records('role');
|
|
foreach ($roles as $role) {
|
|
$access = get_role_access($role->id);
|
|
|
|
$this->assertTrue(is_array($access));
|
|
$this->assertTrue(is_array($access['ra']));
|
|
$this->assertFalse(isset($access['rdef']));
|
|
$this->assertFalse(isset($access['rdef_count']));
|
|
$this->assertFalse(isset($access['loaded']));
|
|
$this->assertTrue(isset($access['time']));
|
|
$this->assertTrue(is_array($access['rsw']));
|
|
}
|
|
|
|
// Note: the data is validated in the functional permission evaluation test at the end of this testcase.
|
|
}
|
|
|
|
/**
|
|
* Test getting of guest role.
|
|
*
|
|
* @covers ::get_guest_role
|
|
*/
|
|
public function test_get_guest_role(): void {
|
|
global $CFG;
|
|
|
|
$guest = get_guest_role();
|
|
$this->assertEquals('guest', $guest->archetype);
|
|
$this->assertEquals('guest', $guest->shortname);
|
|
|
|
$this->assertEquals($CFG->guestroleid, $guest->id);
|
|
}
|
|
|
|
/**
|
|
* Test if user is admin.
|
|
*
|
|
* @covers ::is_siteadmin
|
|
*/
|
|
public function test_is_siteadmin(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$users = $DB->get_records('user');
|
|
|
|
foreach ($users as $user) {
|
|
$this->setUser(0);
|
|
if ($user->username === 'admin') {
|
|
$this->assertTrue(is_siteadmin($user));
|
|
$this->assertTrue(is_siteadmin($user->id));
|
|
$this->setUser($user);
|
|
$this->assertTrue(is_siteadmin());
|
|
$this->assertTrue(is_siteadmin(null));
|
|
} else {
|
|
$this->assertFalse(is_siteadmin($user));
|
|
$this->assertFalse(is_siteadmin($user->id));
|
|
$this->setUser($user);
|
|
$this->assertFalse(is_siteadmin());
|
|
$this->assertFalse(is_siteadmin(null));
|
|
}
|
|
}
|
|
|
|
// Change the site admin list and check that it still works with
|
|
// multiple admins. We do this with userids only (not real user
|
|
// accounts) because it makes the test simpler.
|
|
$before = $CFG->siteadmins;
|
|
set_config('siteadmins', '666,667,668');
|
|
$this->assertTrue(is_siteadmin(666));
|
|
$this->assertTrue(is_siteadmin(667));
|
|
$this->assertTrue(is_siteadmin(668));
|
|
$this->assertFalse(is_siteadmin(669));
|
|
set_config('siteadmins', '13');
|
|
$this->assertTrue(is_siteadmin(13));
|
|
$this->assertFalse(is_siteadmin(666));
|
|
set_config('siteadmins', $before);
|
|
}
|
|
|
|
/**
|
|
* Test if user is enrolled in a course
|
|
*
|
|
* @covers ::is_enrolled
|
|
*/
|
|
public function test_is_enrolled(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Generate data.
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$role = $DB->get_record('role', array('shortname'=>'student'));
|
|
|
|
// There should be a manual enrolment as part of the default install.
|
|
$plugin = enrol_get_plugin('manual');
|
|
$instance = $DB->get_record('enrol', array(
|
|
'courseid' => $course->id,
|
|
'enrol' => 'manual',
|
|
));
|
|
$this->assertNotSame(false, $instance);
|
|
|
|
// Enrol the user in the course.
|
|
$plugin->enrol_user($instance, $user->id, $role->id);
|
|
|
|
// We'll test with the mod/assign:submit capability.
|
|
$capability= 'mod/assign:submit';
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => $capability)));
|
|
|
|
// Switch to our user.
|
|
$this->setUser($user);
|
|
|
|
// Ensure that the user has the capability first.
|
|
$this->assertTrue(has_capability($capability, $coursecontext, $user->id));
|
|
|
|
// We first test whether the user is enrolled on the course as this
|
|
// seeds the cache, then we test for the capability.
|
|
$this->assertTrue(is_enrolled($coursecontext, $user, '', true));
|
|
$this->assertTrue(is_enrolled($coursecontext, $user, $capability));
|
|
|
|
// Prevent the capability for this user role.
|
|
assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
|
|
$this->assertFalse(has_capability($capability, $coursecontext, $user->id));
|
|
|
|
// Again, we seed the cache first by checking initial enrolment,
|
|
// and then we test the actual capability.
|
|
$this->assertTrue(is_enrolled($coursecontext, $user, '', true));
|
|
$this->assertFalse(is_enrolled($coursecontext, $user, $capability));
|
|
}
|
|
|
|
/**
|
|
* Test logged in test.
|
|
*
|
|
* @covers ::isloggedin
|
|
*/
|
|
public function test_isloggedin(): void {
|
|
global $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$USER->id = 0;
|
|
$this->assertFalse(isloggedin());
|
|
$USER->id = 1;
|
|
$this->assertTrue(isloggedin());
|
|
}
|
|
|
|
/**
|
|
* Test guest user test.
|
|
*
|
|
* @covers ::isguestuser
|
|
*/
|
|
public function test_isguestuser(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$guest = $DB->get_record('user', array('username'=>'guest'));
|
|
$this->setUser(0);
|
|
$this->assertFalse(isguestuser());
|
|
$this->setAdminUser();
|
|
$this->assertFalse(isguestuser());
|
|
$this->assertTrue(isguestuser($guest));
|
|
$this->assertTrue(isguestuser($guest->id));
|
|
$this->setUser($guest);
|
|
$this->assertTrue(isguestuser());
|
|
|
|
$users = $DB->get_records('user');
|
|
foreach ($users as $user) {
|
|
if ($user->username === 'guest') {
|
|
continue;
|
|
}
|
|
$this->assertFalse(isguestuser($user));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test capability riskiness.
|
|
*
|
|
* @covers ::is_safe_capability
|
|
*/
|
|
public function test_is_safe_capability(): void {
|
|
global $DB;
|
|
// Note: there is not much to test, just make sure no notices are throw for the most dangerous cap.
|
|
$capability = $DB->get_record('capabilities', array('name'=>'moodle/site:config'), '*', MUST_EXIST);
|
|
$this->assertFalse(is_safe_capability($capability));
|
|
}
|
|
|
|
/**
|
|
* Test context fetching.
|
|
*
|
|
* @covers ::get_context_info_array
|
|
*/
|
|
public function test_get_context_info_array(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$syscontext = context_system::instance();
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$usercontext = context_user::instance($user->id);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$catcontext = context_coursecat::instance($course->category);
|
|
$coursecontext = context_course::instance($course->id);
|
|
$page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
|
|
$modcontext = context_module::instance($page->cmid);
|
|
$cm = get_coursemodule_from_instance('page', $page->id);
|
|
$block1 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
|
|
$block1context = context_block::instance($block1->id);
|
|
$block2 = $this->getDataGenerator()->create_block('online_users', array('parentcontextid'=>$modcontext->id));
|
|
$block2context = context_block::instance($block2->id);
|
|
|
|
$result = get_context_info_array($syscontext->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($syscontext, $result[0]);
|
|
$this->assertNull($result[1]);
|
|
$this->assertNull($result[2]);
|
|
|
|
$result = get_context_info_array($usercontext->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($usercontext, $result[0]);
|
|
$this->assertNull($result[1]);
|
|
$this->assertNull($result[2]);
|
|
|
|
$result = get_context_info_array($catcontext->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($catcontext, $result[0]);
|
|
$this->assertNull($result[1]);
|
|
$this->assertNull($result[2]);
|
|
|
|
$result = get_context_info_array($coursecontext->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($coursecontext, $result[0]);
|
|
$this->assertEquals($course->id, $result[1]->id);
|
|
$this->assertSame($course->shortname, $result[1]->shortname);
|
|
$this->assertNull($result[2]);
|
|
|
|
$result = get_context_info_array($block1context->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($block1context, $result[0]);
|
|
$this->assertEquals($course->id, $result[1]->id);
|
|
$this->assertEquals($course->shortname, $result[1]->shortname);
|
|
$this->assertNull($result[2]);
|
|
|
|
$result = get_context_info_array($modcontext->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($modcontext, $result[0]);
|
|
$this->assertEquals($course->id, $result[1]->id);
|
|
$this->assertSame($course->shortname, $result[1]->shortname);
|
|
$this->assertEquals($cm->id, $result[2]->id);
|
|
|
|
$result = get_context_info_array($block2context->id);
|
|
$this->assertCount(3, $result);
|
|
$this->assertEquals($block2context, $result[0]);
|
|
$this->assertEquals($course->id, $result[1]->id);
|
|
$this->assertSame($course->shortname, $result[1]->shortname);
|
|
$this->assertEquals($cm->id, $result[2]->id);
|
|
}
|
|
|
|
/**
|
|
* Test looking for course contacts.
|
|
*
|
|
* @covers ::has_coursecontact_role
|
|
*/
|
|
public function test_has_coursecontact_role(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$users = $DB->get_records('user');
|
|
|
|
// Nobody is expected to have any course level roles.
|
|
$this->assertNotEmpty($CFG->coursecontact);
|
|
foreach ($users as $user) {
|
|
$this->assertFalse(has_coursecontact_role($user->id));
|
|
}
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$contactroles = preg_split('/,/', $CFG->coursecontact);
|
|
$roleid = reset($contactroles);
|
|
role_assign($roleid, $user->id, context_course::instance($course->id));
|
|
$this->assertTrue(has_coursecontact_role($user->id));
|
|
}
|
|
|
|
/**
|
|
* Test creation of roles.
|
|
*
|
|
* @covers ::create_role
|
|
*/
|
|
public function test_create_role(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create role and get event.
|
|
$sink = $this->redirectEvents();
|
|
$id = create_role('New student role', 'student2', 'New student description', 'student');
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = array_pop($events);
|
|
$role = $DB->get_record('role', ['id' => $id]);
|
|
|
|
$this->assertNotEmpty($role);
|
|
$this->assertSame('New student role', $role->name);
|
|
$this->assertSame('student2', $role->shortname);
|
|
$this->assertSame('New student description', $role->description);
|
|
$this->assertSame('student', $role->archetype);
|
|
|
|
// Test triggered event.
|
|
$this->assertInstanceOf('\core\event\role_created', $event);
|
|
$this->assertSame('role', $event->target);
|
|
$this->assertSame('role', $event->objecttable);
|
|
$this->assertSame((int)$role->id, $event->objectid);
|
|
$this->assertEquals(context_system::instance(), $event->get_context());
|
|
$this->assertSame($role->shortname, $event->other['shortname']);
|
|
$this->assertSame($role->archetype, $event->other['archetype']);
|
|
}
|
|
|
|
/**
|
|
* Test adding of capabilities to roles.
|
|
*
|
|
* @covers ::assign_capability
|
|
*/
|
|
public function test_assign_capability(): void {
|
|
global $DB, $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$syscontext = context_system::instance();
|
|
$frontcontext = context_course::instance(SITEID);
|
|
$student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to student by default.
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
|
|
$this->setUser($user);
|
|
$result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $frontcontext->id);
|
|
$this->assertTrue($result);
|
|
$permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
|
|
$this->assertNotEmpty($permission);
|
|
$this->assertEquals(CAP_ALLOW, $permission->permission);
|
|
$this->assertEquals($user->id, $permission->modifierid);
|
|
|
|
$this->setUser(0);
|
|
$result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, false);
|
|
$this->assertTrue($result);
|
|
$permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
|
|
$this->assertNotEmpty($permission);
|
|
$this->assertEquals(CAP_ALLOW, $permission->permission);
|
|
$this->assertEquals($user->id, $permission->modifierid);
|
|
|
|
$result = assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $frontcontext->id, true);
|
|
$this->assertTrue($result);
|
|
$permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
|
|
$this->assertNotEmpty($permission);
|
|
$this->assertEquals(CAP_PROHIBIT, $permission->permission);
|
|
$this->assertEquals(0, $permission->modifierid);
|
|
|
|
$result = assign_capability('moodle/backup:backupcourse', CAP_INHERIT, $student->id, $frontcontext->id);
|
|
$this->assertTrue($result);
|
|
$permission = $DB->get_record('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$student->id, 'capability'=>'moodle/backup:backupcourse'));
|
|
$this->assertEmpty($permission);
|
|
|
|
// Test event triggered.
|
|
$sink = $this->redirectEvents();
|
|
$capability = 'moodle/backup:backupcourse';
|
|
assign_capability($capability, CAP_ALLOW, $student->id, $syscontext);
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$this->assertCount(1, $events);
|
|
$event = $events[0];
|
|
$this->assertInstanceOf('\core\event\capability_assigned', $event);
|
|
$this->assertSame('role_capabilities', $event->objecttable);
|
|
$this->assertEquals($student->id, $event->objectid);
|
|
$this->assertEquals($syscontext->id, $event->contextid);
|
|
$other = ['capability' => $capability, 'oldpermission' => CAP_INHERIT, 'permission' => CAP_ALLOW];
|
|
$this->assertEquals($other, $event->other);
|
|
$description = "The user id '$USER->id' assigned the '$capability' capability for " .
|
|
"role '$student->id' with 'Allow' permission";
|
|
$this->assertEquals($description, $event->get_description());
|
|
|
|
// Test if the event has different description when updating the capability permission.
|
|
$sink = $this->redirectEvents();
|
|
assign_capability($capability, CAP_PROHIBIT, $student->id, $syscontext, true);
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = $events[0];
|
|
$description = "The user id '$USER->id' changed the '$capability' capability permission for " .
|
|
"role '$student->id' from 'Allow' to 'Prohibit'";
|
|
$this->assertEquals($description, $event->get_description());
|
|
}
|
|
|
|
/**
|
|
* Test removing of capabilities from roles.
|
|
*
|
|
* @covers ::unassign_capability
|
|
*/
|
|
public function test_unassign_capability(): void {
|
|
global $DB, $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$syscontext = context_system::instance();
|
|
$frontcontext = context_course::instance(SITEID);
|
|
$manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability assigned to manager by default.
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
|
|
|
|
$this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
$this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
|
|
$result = unassign_capability('moodle/backup:backupcourse', $manager->id, $syscontext->id);
|
|
$this->assertTrue($result);
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
$this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
unassign_capability('moodle/backup:backupcourse', $manager->id, $frontcontext);
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $frontcontext->id);
|
|
$this->assertTrue($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
|
|
$result = unassign_capability('moodle/backup:backupcourse', $manager->id);
|
|
$this->assertTrue($result);
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('contextid'=>$frontcontext->id, 'roleid'=>$manager->id, 'capability'=>'moodle/backup:backupcourse')));
|
|
|
|
// Test event triggered.
|
|
$sink = $this->redirectEvents();
|
|
$capability = 'moodle/backup:backupcourse';
|
|
unassign_capability($capability, CAP_ALLOW, $manager->id);
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$this->assertCount(1, $events);
|
|
$event = $events[0];
|
|
$this->assertInstanceOf('\core\event\capability_unassigned', $event);
|
|
$this->assertSame('role_capabilities', $event->objecttable);
|
|
$this->assertEquals($manager->id, $event->objectid);
|
|
$this->assertEquals($syscontext->id, $event->contextid);
|
|
$this->assertEquals($capability, $event->other['capability']);
|
|
$description = "The user id id '$USER->id' has unassigned the '$capability' capability for role '$manager->id'";
|
|
$this->assertEquals($description, $event->get_description());
|
|
}
|
|
|
|
/**
|
|
* Test role assigning.
|
|
*
|
|
* @covers ::role_assign
|
|
*/
|
|
public function test_role_assign(): void {
|
|
global $DB, $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$role = $DB->get_record('role', array('shortname'=>'student'));
|
|
|
|
$this->setUser(0);
|
|
$context = context_system::instance();
|
|
$this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
|
|
role_assign($role->id, $user->id, $context->id);
|
|
$ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
|
|
$this->assertNotEmpty($ras);
|
|
$this->assertSame('', $ras->component);
|
|
$this->assertSame('0', $ras->itemid);
|
|
$this->assertEquals($USER->id, $ras->modifierid);
|
|
|
|
$this->setAdminUser();
|
|
$context = context_course::instance($course->id);
|
|
$this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
|
|
role_assign($role->id, $user->id, $context->id, 'enrol_self', 1, 666);
|
|
$ras = $DB->get_record('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id));
|
|
$this->assertNotEmpty($ras);
|
|
$this->assertSame('enrol_self', $ras->component);
|
|
$this->assertSame('1', $ras->itemid);
|
|
$this->assertEquals($USER->id, $ras->modifierid);
|
|
$this->assertEquals(666, $ras->timemodified);
|
|
|
|
// Test event triggered.
|
|
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
$sink = $this->redirectEvents();
|
|
$raid = role_assign($role->id, $user2->id, $context->id);
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$this->assertCount(1, $events);
|
|
$event = $events[0];
|
|
$this->assertInstanceOf('\core\event\role_assigned', $event);
|
|
$this->assertSame('role', $event->target);
|
|
$this->assertSame('role', $event->objecttable);
|
|
$this->assertEquals($role->id, $event->objectid);
|
|
$this->assertEquals($context->id, $event->contextid);
|
|
$this->assertEquals($user2->id, $event->relateduserid);
|
|
$this->assertCount(3, $event->other);
|
|
$this->assertEquals($raid, $event->other['id']);
|
|
$this->assertSame('', $event->other['component']);
|
|
$this->assertEquals(0, $event->other['itemid']);
|
|
$this->assertInstanceOf('moodle_url', $event->get_url());
|
|
}
|
|
|
|
/**
|
|
* Test role unassigning.
|
|
*
|
|
* @covers ::role_unassign
|
|
*/
|
|
public function test_role_unassign(): void {
|
|
global $DB, $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$role = $DB->get_record('role', array('shortname'=>'student'));
|
|
|
|
$context = context_course::instance($course->id);
|
|
role_assign($role->id, $user->id, $context->id);
|
|
$this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
|
|
role_unassign($role->id, $user->id, $context->id);
|
|
$this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
|
|
|
|
role_assign($role->id, $user->id, $context->id, 'enrol_self', 1);
|
|
$this->assertTrue($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
|
|
role_unassign($role->id, $user->id, $context->id, 'enrol_self', 1);
|
|
$this->assertFalse($DB->record_exists('role_assignments', array('userid'=>$user->id, 'roleid'=>$role->id, 'contextid'=>$context->id)));
|
|
|
|
// Test event triggered.
|
|
|
|
role_assign($role->id, $user->id, $context->id);
|
|
$sink = $this->redirectEvents();
|
|
role_unassign($role->id, $user->id, $context->id);
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$this->assertCount(1, $events);
|
|
$event = $events[0];
|
|
$this->assertInstanceOf('\core\event\role_unassigned', $event);
|
|
$this->assertSame('role', $event->target);
|
|
$this->assertSame('role', $event->objecttable);
|
|
$this->assertEquals($role->id, $event->objectid);
|
|
$this->assertEquals($context->id, $event->contextid);
|
|
$this->assertEquals($user->id, $event->relateduserid);
|
|
$this->assertCount(3, $event->other);
|
|
$this->assertSame('', $event->other['component']);
|
|
$this->assertEquals(0, $event->other['itemid']);
|
|
$this->assertInstanceOf('moodle_url', $event->get_url());
|
|
}
|
|
|
|
/**
|
|
* Test role unassigning.
|
|
*
|
|
* @covers ::role_unassign_all
|
|
*/
|
|
public function test_role_unassign_all(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$role = $DB->get_record('role', array('shortname'=>'student'));
|
|
$role2 = $DB->get_record('role', array('shortname'=>'teacher'));
|
|
$syscontext = context_system::instance();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
|
|
$modcontext = context_module::instance($page->cmid);
|
|
|
|
role_assign($role->id, $user->id, $syscontext->id);
|
|
role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
|
|
$this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role->id));
|
|
$this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
|
|
role_assign($role->id, $user->id, $syscontext->id);
|
|
role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
|
|
role_assign($role->id, $user->id, $modcontext->id);
|
|
$this->assertEquals(3, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), false);
|
|
$this->assertEquals(2, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id), true);
|
|
$this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
role_unassign_all(array('userid'=>$user->id));
|
|
$this->assertEquals(0, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
|
|
role_assign($role->id, $user->id, $syscontext->id);
|
|
role_assign($role->id, $user->id, $coursecontext->id, 'enrol_self', 1);
|
|
role_assign($role->id, $user->id, $coursecontext->id);
|
|
role_assign($role->id, $user->id, $modcontext->id);
|
|
$this->assertEquals(4, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
role_unassign_all(array('userid'=>$user->id, 'contextid'=>$coursecontext->id, 'component'=>'enrol_self'), true, true);
|
|
$this->assertEquals(1, $DB->count_records('role_assignments', array('userid'=>$user->id)));
|
|
|
|
// Test events triggered.
|
|
|
|
role_assign($role2->id, $user->id, $coursecontext->id);
|
|
role_assign($role2->id, $user->id, $modcontext->id);
|
|
$sink = $this->redirectEvents();
|
|
role_unassign_all(array('userid'=>$user->id, 'roleid'=>$role2->id));
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$this->assertCount(2, $events);
|
|
$this->assertInstanceOf('\core\event\role_unassigned', $events[0]);
|
|
$this->assertInstanceOf('\core\event\role_unassigned', $events[1]);
|
|
}
|
|
|
|
/**
|
|
* Test role queries.
|
|
*
|
|
* @covers ::get_roles_with_capability
|
|
*/
|
|
public function test_get_roles_with_capability(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$syscontext = context_system::instance();
|
|
$frontcontext = context_course::instance(SITEID);
|
|
$manager = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
|
|
$teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
|
|
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
|
|
$DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse'));
|
|
|
|
$roles = get_roles_with_capability('moodle/backup:backupcourse');
|
|
$this->assertEquals(array(), $roles);
|
|
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $manager->id, $syscontext->id);
|
|
assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $manager->id, $frontcontext->id);
|
|
assign_capability('moodle/backup:backupcourse', CAP_PREVENT, $teacher->id, $frontcontext->id);
|
|
|
|
$roles = get_roles_with_capability('moodle/backup:backupcourse');
|
|
$this->assertEqualsCanonicalizing(array($teacher->id, $manager->id), array_keys($roles), true);
|
|
|
|
$roles = get_roles_with_capability('moodle/backup:backupcourse', CAP_ALLOW);
|
|
$this->assertEqualsCanonicalizing(array($manager->id), array_keys($roles), true);
|
|
|
|
$roles = get_roles_with_capability('moodle/backup:backupcourse', null, $syscontext);
|
|
$this->assertEqualsCanonicalizing(array($manager->id), array_keys($roles), true);
|
|
}
|
|
|
|
/**
|
|
* Test deleting of roles.
|
|
*
|
|
* @covers ::delete_role
|
|
*/
|
|
public function test_delete_role(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$role = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
|
|
$user = $this->getDataGenerator()->create_user();
|
|
role_assign($role->id, $user->id, context_system::instance());
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$rolename = (object)array('roleid'=>$role->id, 'name'=>'Man', 'contextid'=>context_course::instance($course->id)->id);
|
|
$DB->insert_record('role_names', $rolename);
|
|
|
|
$this->assertTrue($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_names', array('roleid'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
|
|
$this->assertTrue($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
|
|
|
|
// Delete role and get event.
|
|
$sink = $this->redirectEvents();
|
|
$result = delete_role($role->id);
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = array_pop($events);
|
|
|
|
$this->assertTrue($result);
|
|
$this->assertFalse($DB->record_exists('role', array('id'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_assignments', array('roleid'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_capabilities', array('roleid'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_names', array('roleid'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_context_levels', array('roleid'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_allow_assign', array('allowassign'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$role->id)));
|
|
$this->assertFalse($DB->record_exists('role_allow_override', array('allowoverride'=>$role->id)));
|
|
|
|
// Test triggered event.
|
|
$this->assertInstanceOf('\core\event\role_deleted', $event);
|
|
$this->assertSame('role', $event->target);
|
|
$this->assertSame('role', $event->objecttable);
|
|
$this->assertSame($role->id, $event->objectid);
|
|
$this->assertEquals(context_system::instance(), $event->get_context());
|
|
$this->assertSame($role->shortname, $event->other['shortname']);
|
|
$this->assertSame($role->description, $event->other['description']);
|
|
$this->assertSame($role->archetype, $event->other['archetype']);
|
|
}
|
|
|
|
/**
|
|
* Test fetching of all roles.
|
|
*
|
|
* @covers ::get_all_roles
|
|
*/
|
|
public function test_get_all_roles(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$allroles = get_all_roles();
|
|
$this->assertIsArray($allroles);
|
|
$initialrolescount = count($allroles);
|
|
$this->assertTrue($initialrolescount >= 8); // There are 8 roles is standard install.
|
|
$rolenames = array_column($allroles, 'shortname');
|
|
foreach (get_role_archetypes() as $archetype) {
|
|
$this->assertContains($archetype, $rolenames);
|
|
}
|
|
|
|
$role = reset($allroles);
|
|
$role = (array)$role;
|
|
|
|
$this->assertEqualsCanonicalizing(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype'),
|
|
array_keys($role));
|
|
|
|
foreach ($allroles as $roleid => $role) {
|
|
$this->assertEquals($role->id, $roleid);
|
|
}
|
|
|
|
$teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherename);
|
|
$otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $otherrename);
|
|
$renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
|
|
|
|
$allroles = get_all_roles($coursecontext);
|
|
$this->assertIsArray($allroles);
|
|
$this->assertCount($initialrolescount + 1, $allroles);
|
|
$role = reset($allroles);
|
|
$role = (array)$role;
|
|
|
|
$this->assertEqualsCanonicalizing(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype', 'coursealias'), array_keys($role));
|
|
|
|
foreach ($allroles as $roleid => $role) {
|
|
$this->assertEquals($role->id, $roleid);
|
|
if (isset($renames[$roleid])) {
|
|
$this->assertSame($renames[$roleid], $role->coursealias);
|
|
} else {
|
|
$this->assertNull($role->coursealias);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test getting of all archetypes.
|
|
*
|
|
* @covers ::get_role_archetypes
|
|
*/
|
|
public function test_get_role_archetypes(): void {
|
|
$archetypes = get_role_archetypes();
|
|
$this->assertCount(8, $archetypes); // There are 8 archetypes in standard install.
|
|
foreach ($archetypes as $k => $v) {
|
|
$this->assertSame($k, $v);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test getting of roles with given archetype.
|
|
*
|
|
* @covers ::get_archetype_roles
|
|
*/
|
|
public function test_get_archetype_roles(): void {
|
|
$this->resetAfterTest();
|
|
|
|
// New install should have at least 1 role for each archetype.
|
|
$archetypes = get_role_archetypes();
|
|
foreach ($archetypes as $archetype) {
|
|
$roles = get_archetype_roles($archetype);
|
|
$this->assertGreaterThanOrEqual(1, count($roles));
|
|
$role = reset($roles);
|
|
$this->assertSame($archetype, $role->archetype);
|
|
}
|
|
|
|
create_role('New student role', 'student2', 'New student description', 'student');
|
|
$roles = get_archetype_roles('student');
|
|
$this->assertGreaterThanOrEqual(2, count($roles));
|
|
}
|
|
|
|
/**
|
|
* Test aliased role names.
|
|
*
|
|
* @covers ::role_get_name
|
|
*/
|
|
public function test_role_get_name(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$allroles = $DB->get_records('role');
|
|
$teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherename);
|
|
$otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $otherrename);
|
|
$renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
|
|
|
|
foreach ($allroles as $role) {
|
|
if (in_array($role->shortname, get_role_archetypes())) {
|
|
// Standard roles do not have a set name.
|
|
$this->assertSame('', $role->name);
|
|
}
|
|
// Get localised name from lang pack.
|
|
$name = role_get_name($role, null, ROLENAME_ORIGINAL);
|
|
$this->assertNotEmpty($name);
|
|
$this->assertNotEquals($role->shortname, $name);
|
|
|
|
if (isset($renames[$role->id])) {
|
|
$this->assertSame($renames[$role->id], role_get_name($role, $coursecontext));
|
|
$this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS));
|
|
$this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
|
|
$this->assertSame("{$renames[$role->id]} ($name)", role_get_name($role, $coursecontext, ROLENAME_BOTH));
|
|
} else {
|
|
$this->assertSame($name, role_get_name($role, $coursecontext));
|
|
$this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ALIAS));
|
|
$this->assertNull(role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
|
|
$this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_BOTH));
|
|
}
|
|
$this->assertSame($name, role_get_name($role));
|
|
$this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ORIGINAL));
|
|
$this->assertSame($name, role_get_name($role, null, ROLENAME_ORIGINAL));
|
|
$this->assertSame($role->shortname, role_get_name($role, $coursecontext, ROLENAME_SHORT));
|
|
$this->assertSame($role->shortname, role_get_name($role, null, ROLENAME_SHORT));
|
|
$this->assertSame("$name ($role->shortname)", role_get_name($role, $coursecontext, ROLENAME_ORIGINALANDSHORT));
|
|
$this->assertSame("$name ($role->shortname)", role_get_name($role, null, ROLENAME_ORIGINALANDSHORT));
|
|
$this->assertNull(role_get_name($role, null, ROLENAME_ALIAS_RAW));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test tweaking of role name arrays.
|
|
*
|
|
* @covers ::role_fix_names
|
|
*/
|
|
public function test_role_fix_names(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
|
|
$student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$anotherid = create_role('Another role', 'another', 'Yet another other role', '');
|
|
$allroles = $DB->get_records('role');
|
|
|
|
$syscontext = context_system::instance();
|
|
$frontcontext = context_course::instance(SITEID);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
|
|
$categorycontext = context_coursecat::instance($category->id);
|
|
|
|
$teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherename);
|
|
$otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $otherrename);
|
|
$renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
|
|
|
|
// Make sure all localname contain proper values for each ROLENAME_ constant,
|
|
// note role_get_name() on frontpage is used to get the original name for future compatibility.
|
|
$roles = $allroles;
|
|
unset($roles[$student->id]); // Remove one role to make sure no role is added or removed.
|
|
$rolenames = array();
|
|
foreach ($roles as $role) {
|
|
$rolenames[$role->id] = $role->name;
|
|
}
|
|
|
|
$alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
|
|
foreach ($alltypes as $type) {
|
|
$fixed = role_fix_names($roles, $coursecontext, $type);
|
|
$this->assertCount(count($roles), $fixed);
|
|
foreach ($fixed as $roleid => $rolename) {
|
|
$this->assertInstanceOf('stdClass', $rolename);
|
|
$role = $allroles[$roleid];
|
|
$name = role_get_name($role, $coursecontext, $type);
|
|
$this->assertSame($name, $rolename->localname);
|
|
}
|
|
$fixed = role_fix_names($rolenames, $coursecontext, $type);
|
|
$this->assertCount(count($rolenames), $fixed);
|
|
foreach ($fixed as $roleid => $rolename) {
|
|
$role = $allroles[$roleid];
|
|
$name = role_get_name($role, $coursecontext, $type);
|
|
$this->assertSame($name, $rolename);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test role default allows.
|
|
*
|
|
* @covers ::get_default_role_archetype_allows
|
|
*/
|
|
public function test_get_default_role_archetype_allows(): void {
|
|
$archetypes = get_role_archetypes();
|
|
foreach ($archetypes as $archetype) {
|
|
|
|
$result = get_default_role_archetype_allows('assign', $archetype);
|
|
$this->assertIsArray($result);
|
|
|
|
$result = get_default_role_archetype_allows('override', $archetype);
|
|
$this->assertIsArray($result);
|
|
|
|
$result = get_default_role_archetype_allows('switch', $archetype);
|
|
$this->assertIsArray($result);
|
|
|
|
$result = get_default_role_archetype_allows('view', $archetype);
|
|
$this->assertIsArray($result);
|
|
}
|
|
|
|
$result = get_default_role_archetype_allows('assign', '');
|
|
$this->assertSame(array(), $result);
|
|
|
|
$result = get_default_role_archetype_allows('override', '');
|
|
$this->assertSame(array(), $result);
|
|
|
|
$result = get_default_role_archetype_allows('switch', '');
|
|
$this->assertSame(array(), $result);
|
|
|
|
$result = get_default_role_archetype_allows('view', '');
|
|
$this->assertSame(array(), $result);
|
|
|
|
$result = get_default_role_archetype_allows('assign', 'wrongarchetype');
|
|
$this->assertSame(array(), $result);
|
|
$this->assertDebuggingCalled();
|
|
|
|
$result = get_default_role_archetype_allows('override', 'wrongarchetype');
|
|
$this->assertSame(array(), $result);
|
|
$this->assertDebuggingCalled();
|
|
|
|
$result = get_default_role_archetype_allows('switch', 'wrongarchetype');
|
|
$this->assertSame(array(), $result);
|
|
$this->assertDebuggingCalled();
|
|
|
|
$result = get_default_role_archetype_allows('view', 'wrongarchetype');
|
|
$this->assertSame(array(), $result);
|
|
$this->assertDebuggingCalled();
|
|
}
|
|
|
|
/**
|
|
* Test allowing of role assignments.
|
|
*
|
|
* @covers ::core_role_set_assign_allowed
|
|
*/
|
|
public function test_core_role_set_assign_allowed(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
|
|
$this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
|
|
core_role_set_assign_allowed($otherid, $student->id);
|
|
$this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
|
|
|
|
// Test event trigger.
|
|
$allowroleassignevent = \core\event\role_allow_assign_updated::create([
|
|
'context' => context_system::instance(),
|
|
'objectid' => $otherid,
|
|
'other' => ['targetroleid' => $student->id]
|
|
]);
|
|
$sink = $this->redirectEvents();
|
|
$allowroleassignevent->trigger();
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = array_pop($events);
|
|
$this->assertInstanceOf('\core\event\role_allow_assign_updated', $event);
|
|
}
|
|
|
|
/**
|
|
* Test allowing of role overrides.
|
|
*
|
|
* @covers ::core_role_set_override_allowed
|
|
*/
|
|
public function test_core_role_set_override_allowed(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
|
|
$this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
|
|
core_role_set_override_allowed($otherid, $student->id);
|
|
$this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
|
|
|
|
// Test event trigger.
|
|
$allowroleassignevent = \core\event\role_allow_override_updated::create([
|
|
'context' => context_system::instance(),
|
|
'objectid' => $otherid,
|
|
'other' => ['targetroleid' => $student->id]
|
|
]);
|
|
$sink = $this->redirectEvents();
|
|
$allowroleassignevent->trigger();
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = array_pop($events);
|
|
$this->assertInstanceOf('\core\event\role_allow_override_updated', $event);
|
|
}
|
|
|
|
/**
|
|
* Test allowing of role switching.
|
|
*
|
|
* @covers ::core_role_set_switch_allowed
|
|
*/
|
|
public function test_core_role_set_switch_allowed(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
|
|
$this->assertFalse($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
|
|
core_role_set_switch_allowed($otherid, $student->id);
|
|
$this->assertTrue($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
|
|
|
|
// Test event trigger.
|
|
$allowroleassignevent = \core\event\role_allow_switch_updated::create([
|
|
'context' => context_system::instance(),
|
|
'objectid' => $otherid,
|
|
'other' => ['targetroleid' => $student->id]
|
|
]);
|
|
$sink = $this->redirectEvents();
|
|
$allowroleassignevent->trigger();
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = array_pop($events);
|
|
$this->assertInstanceOf('\core\event\role_allow_switch_updated', $event);
|
|
}
|
|
|
|
/**
|
|
* Test allowing of role switching.
|
|
*
|
|
* @covers ::core_role_set_view_allowed
|
|
*/
|
|
public function test_core_role_set_view_allowed(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
|
|
$this->assertFalse($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
|
|
core_role_set_view_allowed($otherid, $student->id);
|
|
$this->assertTrue($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
|
|
|
|
// Test event trigger.
|
|
$allowroleassignevent = \core\event\role_allow_view_updated::create([
|
|
'context' => context_system::instance(),
|
|
'objectid' => $otherid,
|
|
'other' => ['targetroleid' => $student->id]
|
|
]);
|
|
$sink = $this->redirectEvents();
|
|
$allowroleassignevent->trigger();
|
|
$events = $sink->get_events();
|
|
$sink->close();
|
|
$event = array_pop($events);
|
|
$this->assertInstanceOf('\core\event\role_allow_view_updated', $event);
|
|
}
|
|
|
|
/**
|
|
* Test returning of assignable roles in context.
|
|
*
|
|
* @covers ::get_assignable_roles
|
|
*/
|
|
public function test_get_assignable_roles(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherename);
|
|
|
|
$studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$student = $this->getDataGenerator()->create_user();
|
|
role_assign($studentrole->id, $student->id, $coursecontext);
|
|
|
|
$contexts = $DB->get_records('context');
|
|
$users = $DB->get_records('user');
|
|
$allroles = $DB->get_records('role');
|
|
|
|
// Evaluate all results for all users in all contexts.
|
|
foreach ($users as $user) {
|
|
$this->setUser($user);
|
|
foreach ($contexts as $contextid => $unused) {
|
|
$context = context_helper::instance_by_id($contextid);
|
|
$roles = get_assignable_roles($context, ROLENAME_SHORT);
|
|
foreach ($allroles as $roleid => $role) {
|
|
if (isset($roles[$roleid])) {
|
|
if (is_siteadmin()) {
|
|
$this->assertTrue($DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid)));
|
|
} else {
|
|
$this->assertTrue(user_can_assign($context, $roleid), "u:$user->id r:$roleid");
|
|
}
|
|
$this->assertEquals($role->shortname, $roles[$roleid]);
|
|
} else {
|
|
$allowed = $DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid));
|
|
if (is_siteadmin()) {
|
|
$this->assertFalse($allowed);
|
|
} else {
|
|
$this->assertFalse($allowed and user_can_assign($context, $roleid), "u:$user->id, r:{$allroles[$roleid]->name}, c:$context->contextlevel");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not-logged-in user.
|
|
$this->setUser(0);
|
|
foreach ($contexts as $contextid => $unused) {
|
|
$context = context_helper::instance_by_id($contextid);
|
|
$roles = get_assignable_roles($context, ROLENAME_SHORT);
|
|
$this->assertSame(array(), $roles);
|
|
}
|
|
|
|
// Test current user.
|
|
$this->setUser(0);
|
|
$admin = $DB->get_record('user', array('username'=>'admin'), '*', MUST_EXIST);
|
|
$roles1 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin);
|
|
$roles2 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin->id);
|
|
$this->setAdminUser();
|
|
$roles3 = get_assignable_roles($coursecontext, ROLENAME_SHORT);
|
|
$this->assertSame($roles1, $roles3);
|
|
$this->assertSame($roles2, $roles3);
|
|
|
|
// Test parameter defaults.
|
|
$this->setAdminUser();
|
|
$roles1 = get_assignable_roles($coursecontext);
|
|
$roles2 = get_assignable_roles($coursecontext, ROLENAME_ALIAS, false, $admin);
|
|
$this->assertEquals($roles2, $roles1);
|
|
|
|
// Verify returned names - let's allow all roles everywhere to simplify this a bit.
|
|
$alllevels = context_helper::get_all_levels();
|
|
$alllevels = array_keys($alllevels);
|
|
foreach ($allroles as $roleid => $role) {
|
|
set_role_contextlevels($roleid, $alllevels);
|
|
}
|
|
$alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
|
|
foreach ($alltypes as $type) {
|
|
$rolenames = role_fix_names($allroles, $coursecontext, $type);
|
|
$roles = get_assignable_roles($coursecontext, $type, false, $admin);
|
|
foreach ($roles as $roleid => $rolename) {
|
|
$this->assertSame($rolenames[$roleid]->localname, $rolename);
|
|
}
|
|
}
|
|
|
|
// Verify counts.
|
|
$alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
|
|
foreach ($alltypes as $type) {
|
|
$roles = get_assignable_roles($coursecontext, $type, false, $admin);
|
|
list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($coursecontext, $type, true, $admin);
|
|
$this->assertEquals($roles, $rolenames);
|
|
foreach ($rolenames as $roleid => $name) {
|
|
if ($roleid == $teacherrole->id or $roleid == $studentrole->id) {
|
|
$this->assertEquals(1, $rolecounts[$roleid]);
|
|
} else {
|
|
$this->assertEquals(0, $rolecounts[$roleid]);
|
|
}
|
|
$this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test user count of assignable roles in context where users are assigned the role via different components.
|
|
*
|
|
* @covers ::get_assignable_roles
|
|
*/
|
|
public function test_get_assignable_roles_distinct_usercount(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest(true);
|
|
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$context = \context_course::instance($course->id);
|
|
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
|
|
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
|
|
|
// Assign each user the student role in course.
|
|
role_assign($studentrole->id, $user1->id, $context->id);
|
|
role_assign($studentrole->id, $user2->id, $context->id);
|
|
|
|
list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
|
|
$this->assertEquals(2, $rolecounts[$studentrole->id]);
|
|
|
|
// Assign first user the student role in course again (this time via 'enrol_self' component).
|
|
role_assign($studentrole->id, $user1->id, $context->id, 'enrol_self', 1);
|
|
|
|
// There are still only two distinct users.
|
|
list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
|
|
$this->assertEquals(2, $rolecounts[$studentrole->id]);
|
|
}
|
|
|
|
/**
|
|
* Test getting of all switchable roles.
|
|
*
|
|
* @covers ::get_switchable_roles
|
|
*/
|
|
public function test_get_switchable_roles(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherename);
|
|
|
|
$contexts = $DB->get_records('context');
|
|
$users = $DB->get_records('user');
|
|
$allroles = $DB->get_records('role');
|
|
|
|
// Evaluate all results for all users in all contexts.
|
|
foreach ($users as $user) {
|
|
$this->setUser($user);
|
|
foreach ($contexts as $contextid => $unused) {
|
|
$context = context_helper::instance_by_id($contextid);
|
|
$roles = get_switchable_roles($context);
|
|
foreach ($allroles as $roleid => $role) {
|
|
if (is_siteadmin()) {
|
|
$this->assertTrue(isset($roles[$roleid]));
|
|
} else {
|
|
$parents = $context->get_parent_context_ids(true);
|
|
$pcontexts = implode(',' , $parents);
|
|
$allowed = $DB->record_exists_sql(
|
|
"SELECT r.id
|
|
FROM {role} r
|
|
JOIN {role_allow_switch} ras ON ras.allowswitch = r.id
|
|
JOIN {role_assignments} ra ON ra.roleid = ras.roleid
|
|
WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
|
|
",
|
|
array('userid'=>$user->id, 'roleid'=>$roleid)
|
|
);
|
|
if (isset($roles[$roleid])) {
|
|
$this->assertTrue($allowed);
|
|
} else {
|
|
$this->assertFalse($allowed);
|
|
}
|
|
}
|
|
|
|
if (isset($roles[$roleid])) {
|
|
$coursecontext = $context->get_course_context(false);
|
|
$this->assertSame(role_get_name($role, $coursecontext), $roles[$roleid]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test getting of all overridable roles.
|
|
*
|
|
* @covers ::get_overridable_roles
|
|
*/
|
|
public function test_get_overridable_roles(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherename);
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
|
|
assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id);
|
|
|
|
$studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$student = $this->getDataGenerator()->create_user();
|
|
role_assign($studentrole->id, $student->id, $coursecontext);
|
|
|
|
$contexts = $DB->get_records('context');
|
|
$users = $DB->get_records('user');
|
|
$allroles = $DB->get_records('role');
|
|
|
|
// Evaluate all results for all users in all contexts.
|
|
foreach ($users as $user) {
|
|
$this->setUser($user);
|
|
foreach ($contexts as $contextid => $unused) {
|
|
$context = context_helper::instance_by_id($contextid);
|
|
$roles = get_overridable_roles($context, ROLENAME_SHORT);
|
|
foreach ($allroles as $roleid => $role) {
|
|
$hascap = has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context);
|
|
if (is_siteadmin()) {
|
|
$this->assertTrue(isset($roles[$roleid]));
|
|
} else {
|
|
$parents = $context->get_parent_context_ids(true);
|
|
$pcontexts = implode(',' , $parents);
|
|
$allowed = $DB->record_exists_sql(
|
|
"SELECT r.id
|
|
FROM {role} r
|
|
JOIN {role_allow_override} rao ON r.id = rao.allowoverride
|
|
JOIN {role_assignments} ra ON rao.roleid = ra.roleid
|
|
WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
|
|
",
|
|
array('userid'=>$user->id, 'roleid'=>$roleid)
|
|
);
|
|
if (isset($roles[$roleid])) {
|
|
$this->assertTrue($hascap);
|
|
$this->assertTrue($allowed);
|
|
} else {
|
|
$this->assertFalse($hascap and $allowed);
|
|
}
|
|
}
|
|
|
|
if (isset($roles[$roleid])) {
|
|
$this->assertEquals($role->shortname, $roles[$roleid]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test parameter defaults.
|
|
$this->setAdminUser();
|
|
$roles1 = get_overridable_roles($coursecontext);
|
|
$roles2 = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
|
|
$this->assertEquals($roles2, $roles1);
|
|
|
|
$alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
|
|
foreach ($alltypes as $type) {
|
|
$rolenames = role_fix_names($allroles, $coursecontext, $type);
|
|
$roles = get_overridable_roles($coursecontext, $type, false);
|
|
foreach ($roles as $roleid => $rolename) {
|
|
$this->assertSame($rolenames[$roleid]->localname, $rolename);
|
|
}
|
|
}
|
|
|
|
// Verify counts.
|
|
$roles = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
|
|
list($rolenames, $rolecounts, $nameswithcounts) = get_overridable_roles($coursecontext, ROLENAME_ALIAS, true);
|
|
$this->assertEquals($roles, $rolenames);
|
|
foreach ($rolenames as $roleid => $name) {
|
|
if ($roleid == $teacherrole->id) {
|
|
$this->assertEquals(1, $rolecounts[$roleid]);
|
|
} else {
|
|
$this->assertEquals(0, $rolecounts[$roleid]);
|
|
}
|
|
$this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test getting of all overridable roles.
|
|
*
|
|
* @covers ::get_viewable_roles
|
|
*/
|
|
public function test_get_viewable_roles_course(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
|
|
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$studentrolerename = (object) array('roleid' => $studentrole->id, 'name' => 'Učitel', 'contextid' => $coursecontext->id);
|
|
$DB->insert_record('role_names', $studentrolerename);
|
|
|
|
// By default teacher can see student.
|
|
$this->setUser($teacher);
|
|
$viewableroles = get_viewable_roles($coursecontext);
|
|
$this->assertContains($studentrolerename->name, array_values($viewableroles));
|
|
// Remove view permission.
|
|
$DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
|
|
$viewableroles = get_viewable_roles($coursecontext);
|
|
// Teacher can no longer see student role.
|
|
$this->assertNotContains($studentrolerename->name, array_values($viewableroles));
|
|
// Allow again teacher to view student.
|
|
core_role_set_view_allowed($teacherrole->id, $studentrole->id);
|
|
// Teacher can now see student role.
|
|
$viewableroles = get_viewable_roles($coursecontext);
|
|
$this->assertContains($studentrolerename->name, array_values($viewableroles));
|
|
}
|
|
|
|
/**
|
|
* Test getting of all overridable roles.
|
|
*
|
|
* @covers ::get_viewable_roles
|
|
*/
|
|
public function test_get_viewable_roles_system(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$context = context_system::instance();
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $context);
|
|
|
|
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$studentrolename = role_get_name($studentrole, $context);
|
|
|
|
// By default teacher can see student.
|
|
$this->setUser($teacher);
|
|
$viewableroles = get_viewable_roles($context);
|
|
$this->assertContains($studentrolename, array_values($viewableroles));
|
|
// Remove view permission.
|
|
$DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
|
|
$viewableroles = get_viewable_roles($context);
|
|
// Teacher can no longer see student role.
|
|
$this->assertNotContains($studentrolename, array_values($viewableroles));
|
|
// Allow again teacher to view student.
|
|
core_role_set_view_allowed($teacherrole->id, $studentrole->id);
|
|
// Teacher can now see student role.
|
|
$viewableroles = get_viewable_roles($context);
|
|
$this->assertContains($studentrolename, array_values($viewableroles));
|
|
}
|
|
|
|
/**
|
|
* Test we have context level defaults.
|
|
*
|
|
* @covers ::get_default_contextlevels
|
|
*/
|
|
public function test_get_default_contextlevels(): void {
|
|
$archetypes = get_role_archetypes();
|
|
$alllevels = context_helper::get_all_levels();
|
|
foreach ($archetypes as $archetype) {
|
|
$defaults = get_default_contextlevels($archetype);
|
|
$this->assertIsArray($defaults);
|
|
foreach ($defaults as $level) {
|
|
$this->assertTrue(isset($alllevels[$level]));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test role context level setup.
|
|
*
|
|
* @covers ::set_role_contextlevels
|
|
*/
|
|
public function test_set_role_contextlevels(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$roleid = create_role('New student role', 'student2', 'New student description', 'student');
|
|
|
|
$this->assertFalse($DB->record_exists('role_context_levels', array('roleid' => $roleid)));
|
|
|
|
set_role_contextlevels($roleid, array(CONTEXT_COURSE, CONTEXT_MODULE));
|
|
$levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
|
|
$this->assertCount(2, $levels);
|
|
$this->assertTrue(isset($levels[CONTEXT_COURSE]));
|
|
$this->assertTrue(isset($levels[CONTEXT_MODULE]));
|
|
|
|
set_role_contextlevels($roleid, array(CONTEXT_COURSE));
|
|
$levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
|
|
$this->assertCount(1, $levels);
|
|
$this->assertTrue(isset($levels[CONTEXT_COURSE]));
|
|
}
|
|
|
|
/**
|
|
* Test getting of role context levels
|
|
*
|
|
* @covers ::get_roles_for_contextlevels
|
|
*/
|
|
public function test_get_roles_for_contextlevels(): void {
|
|
global $DB;
|
|
|
|
$allroles = get_all_roles();
|
|
foreach (context_helper::get_all_levels() as $level => $unused) {
|
|
$roles = get_roles_for_contextlevels($level);
|
|
foreach ($allroles as $roleid => $unused) {
|
|
$exists = $DB->record_exists('role_context_levels', array('contextlevel'=>$level, 'roleid'=>$roleid));
|
|
if (in_array($roleid, $roles)) {
|
|
$this->assertTrue($exists);
|
|
} else {
|
|
$this->assertFalse($exists);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test default enrol roles.
|
|
*
|
|
* @covers ::get_default_enrol_roles
|
|
*/
|
|
public function test_get_default_enrol_roles(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
$id2 = create_role('New student role', 'student2', 'New student description', 'student');
|
|
set_role_contextlevels($id2, array(CONTEXT_COURSE));
|
|
|
|
$allroles = get_all_roles();
|
|
$expected = array($id2=>$allroles[$id2]);
|
|
|
|
foreach (get_roles_for_contextlevels(CONTEXT_COURSE) as $roleid) {
|
|
$expected[$roleid] = $roleid;
|
|
}
|
|
|
|
$roles = get_default_enrol_roles($coursecontext);
|
|
foreach ($allroles as $role) {
|
|
$this->assertEquals(isset($expected[$role->id]), isset($roles[$role->id]));
|
|
if (isset($roles[$role->id])) {
|
|
$this->assertSame(role_get_name($role, $coursecontext), $roles[$role->id]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test getting of role users.
|
|
*
|
|
* @covers ::get_role_users
|
|
*/
|
|
public function test_get_role_users(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$systemcontext = context_system::instance();
|
|
$studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$noeditteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherrename);
|
|
$otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $otherrename);
|
|
|
|
$user1 = $this->getDataGenerator()->create_user(array('firstname'=>'John', 'lastname'=>'Smith'));
|
|
role_assign($teacherrole->id, $user1->id, $coursecontext->id);
|
|
$user2 = $this->getDataGenerator()->create_user(array('firstname'=>'Jan', 'lastname'=>'Kovar'));
|
|
role_assign($teacherrole->id, $user2->id, $systemcontext->id);
|
|
$user3 = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($user3->id, $course->id, $teacherrole->id);
|
|
$user4 = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($user4->id, $course->id, $studentrole->id);
|
|
$this->getDataGenerator()->enrol_user($user4->id, $course->id, $noeditteacherrole->id);
|
|
|
|
$group = $this->getDataGenerator()->create_group(array('courseid'=>$course->id));
|
|
groups_add_member($group, $user3);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext);
|
|
$this->assertCount(2, $users);
|
|
$this->assertArrayHasKey($user1->id, $users);
|
|
$this->assertEquals($users[$user1->id]->id, $user1->id);
|
|
$this->assertEquals($users[$user1->id]->roleid, $teacherrole->id);
|
|
$this->assertEquals($users[$user1->id]->rolename, $teacherrole->name);
|
|
$this->assertEquals($users[$user1->id]->roleshortname, $teacherrole->shortname);
|
|
$this->assertEquals($users[$user1->id]->rolecoursealias, $teacherrename->name);
|
|
$this->assertArrayHasKey($user3->id, $users);
|
|
$this->assertEquals($users[$user3->id]->id, $user3->id);
|
|
$this->assertEquals($users[$user3->id]->roleid, $teacherrole->id);
|
|
$this->assertEquals($users[$user3->id]->rolename, $teacherrole->name);
|
|
$this->assertEquals($users[$user3->id]->roleshortname, $teacherrole->shortname);
|
|
$this->assertEquals($users[$user3->id]->rolecoursealias, $teacherrename->name);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext, true);
|
|
$this->assertCount(3, $users);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext, true, '', null, null, '', 2, 1);
|
|
$this->assertCount(1, $users);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber');
|
|
$this->assertCount(2, $users);
|
|
$this->assertArrayHasKey($user1->id, $users);
|
|
$this->assertArrayHasKey($user3->id, $users);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email');
|
|
$this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
|
|
$this->assertCount(2, $users);
|
|
$this->assertArrayHasKey($user1->id, $users);
|
|
$this->assertObjectHasProperty('lastname', $users[$user1->id]);
|
|
$this->assertObjectHasProperty('firstname', $users[$user1->id]);
|
|
$this->assertArrayHasKey($user3->id, $users);
|
|
$this->assertObjectHasProperty('lastname', $users[$user3->id]);
|
|
$this->assertObjectHasProperty('firstname', $users[$user3->id]);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id AS id_alias');
|
|
$this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
|
|
$this->assertCount(2, $users);
|
|
$this->assertArrayHasKey($user1->id, $users);
|
|
$this->assertObjectHasProperty('id_alias', $users[$user1->id]);
|
|
$this->assertObjectHasProperty('lastname', $users[$user1->id]);
|
|
$this->assertObjectHasProperty('firstname', $users[$user1->id]);
|
|
$this->assertArrayHasKey($user3->id, $users);
|
|
$this->assertObjectHasProperty('id_alias', $users[$user3->id]);
|
|
$this->assertObjectHasProperty('lastname', $users[$user3->id]);
|
|
$this->assertObjectHasProperty('firstname', $users[$user3->id]);
|
|
|
|
$users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
|
|
$this->assertCount(1, $users);
|
|
$this->assertArrayHasKey($user3->id, $users);
|
|
|
|
$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'));
|
|
$this->assertCount(1, $users);
|
|
$this->assertArrayHasKey($user1->id, $users);
|
|
|
|
$users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.id', 'ra.id');
|
|
$this->assertDebuggingNotCalled();
|
|
$users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.userid', 'ra.userid');
|
|
$this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
|
|
'role assignments id (ra.id) as unique field, you can use $fields param for it.');
|
|
$users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false);
|
|
$this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
|
|
'role assignments id (ra.id) as unique field, you can use $fields param for it.');
|
|
$users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext,
|
|
false, 'u.id, u.firstname', 'u.id, u.firstname');
|
|
$this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
|
|
'role assignments id (ra.id) as unique field, you can use $fields param for it.');
|
|
}
|
|
|
|
/**
|
|
* Test used role query.
|
|
*
|
|
* @covers ::get_roles_used_in_context
|
|
*/
|
|
public function test_get_roles_used_in_context(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$systemcontext = context_system::instance();
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$otherid = create_role('Other role', 'other', 'Some other role', '');
|
|
$teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherrename);
|
|
$otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $otherrename);
|
|
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $user1->id, $coursecontext->id);
|
|
|
|
$roles = get_roles_used_in_context($coursecontext);
|
|
$this->assertCount(1, $roles);
|
|
$role = reset($roles);
|
|
$roleid = key($roles);
|
|
$this->assertEquals($roleid, $role->id);
|
|
$this->assertEquals($teacherrole->id, $role->id);
|
|
$this->assertSame($teacherrole->name, $role->name);
|
|
$this->assertSame($teacherrole->shortname, $role->shortname);
|
|
$this->assertEquals($teacherrole->sortorder, $role->sortorder);
|
|
$this->assertSame($teacherrename->name, $role->coursealias);
|
|
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $user2->id, $systemcontext->id);
|
|
role_assign($otherid, $user2->id, $systemcontext->id);
|
|
|
|
$roles = get_roles_used_in_context($systemcontext);
|
|
$this->assertCount(2, $roles);
|
|
}
|
|
|
|
/**
|
|
* Test roles used in course.
|
|
*
|
|
* @covers ::get_user_roles_in_course
|
|
*/
|
|
public function test_get_user_roles_in_course(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$managerrole = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherrename);
|
|
|
|
$roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
|
|
$this->assertTrue(in_array($teacherrole->id, $roleids));
|
|
$this->assertTrue(in_array($studentrole->id, $roleids));
|
|
$this->assertFalse(in_array($managerrole->id, $roleids));
|
|
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $user1->id, $coursecontext->id);
|
|
role_assign($studentrole->id, $user1->id, $coursecontext->id);
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
role_assign($studentrole->id, $user2->id, $coursecontext->id);
|
|
$user3 = $this->getDataGenerator()->create_user();
|
|
$user4 = $this->getDataGenerator()->create_user();
|
|
role_assign($managerrole->id, $user4->id, $coursecontext->id);
|
|
|
|
$this->setAdminUser();
|
|
|
|
$roles = get_user_roles_in_course($user1->id, $course->id);
|
|
$this->assertEquals([
|
|
role_get_name($teacherrole, $coursecontext),
|
|
role_get_name($studentrole, $coursecontext),
|
|
], array_map('strip_tags', explode(', ', $roles)));
|
|
|
|
$roles = get_user_roles_in_course($user2->id, $course->id);
|
|
$this->assertEquals([
|
|
role_get_name($studentrole, $coursecontext),
|
|
], array_map('strip_tags', explode(', ', $roles)));
|
|
|
|
$roles = get_user_roles_in_course($user3->id, $course->id);
|
|
$this->assertEmpty($roles);
|
|
|
|
// Managers should be able to see a link to their own role type, given they can assign it in the context.
|
|
$this->setUser($user4);
|
|
$roles = get_user_roles_in_course($user4->id, $course->id);
|
|
$this->assertEquals([
|
|
role_get_name($managerrole, $coursecontext),
|
|
], array_map('strip_tags', explode(', ', $roles)));
|
|
|
|
// Managers should see 2 roles if viewing a user who has been enrolled as a student and a teacher in the course.
|
|
$roles = get_user_roles_in_course($user1->id, $course->id);
|
|
$this->assertEquals([
|
|
role_get_name($teacherrole, $coursecontext),
|
|
role_get_name($studentrole, $coursecontext),
|
|
], array_map('strip_tags', explode(', ', $roles)));
|
|
|
|
// Students should not see the manager role if viewing a manager's profile.
|
|
$this->setUser($user2);
|
|
$roles = get_user_roles_in_course($user4->id, $course->id);
|
|
$this->assertEmpty($roles); // Should see 0 roles on the manager's profile.
|
|
}
|
|
|
|
/**
|
|
* Test get_user_roles and get_users_roles
|
|
*
|
|
* @covers ::get_user_roles
|
|
*/
|
|
public function test_get_user_roles(): void {
|
|
global $DB, $CFG;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
|
|
$DB->insert_record('role_names', $teacherrename);
|
|
|
|
$roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
|
|
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $user1->id, $coursecontext->id);
|
|
role_assign($studentrole->id, $user1->id, $coursecontext->id);
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
role_assign($studentrole->id, $user2->id, $coursecontext->id);
|
|
$user3 = $this->getDataGenerator()->create_user();
|
|
|
|
$u1roles = get_user_roles($coursecontext, $user1->id);
|
|
|
|
$u2roles = get_user_roles($coursecontext, $user2->id);
|
|
|
|
$allroles = get_users_roles($coursecontext, [], false);
|
|
$specificuserroles = get_users_roles($coursecontext, [$user1->id, $user2->id]);
|
|
$this->assertEquals($u1roles, $allroles[$user1->id]);
|
|
$this->assertEquals($u1roles, $specificuserroles[$user1->id]);
|
|
$this->assertEquals($u2roles, $allroles[$user2->id]);
|
|
$this->assertEquals($u2roles, $specificuserroles[$user2->id]);
|
|
}
|
|
|
|
/**
|
|
* Test has_capability(), has_any_capability() and has_all_capabilities().
|
|
*
|
|
* @covers ::has_capability
|
|
* @covers ::has_any_capability
|
|
* @covers ::has_all_capabilities
|
|
*/
|
|
public function test_has_capability_and_friends(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$admin = $DB->get_record('user', array('username'=>'admin'));
|
|
|
|
// Note: Here are used default capabilities, the full test is in permission evaluation bellow,
|
|
// use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
|
|
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupsection')));
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse')));
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/site:approvecourse')));
|
|
|
|
$sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse', 'moodle/site:approvecourse');
|
|
$sc = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
|
|
|
|
$this->setUser(0);
|
|
$this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext));
|
|
$this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext));
|
|
$this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
|
|
$this->assertFalse(has_any_capability($sca, $coursecontext));
|
|
$this->assertFalse(has_all_capabilities($sca, $coursecontext));
|
|
|
|
$this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $teacher));
|
|
$this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $teacher));
|
|
$this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $teacher));
|
|
$this->assertTrue(has_any_capability($sca, $coursecontext, $teacher));
|
|
$this->assertTrue(has_all_capabilities($sc, $coursecontext, $teacher));
|
|
$this->assertFalse(has_all_capabilities($sca, $coursecontext, $teacher));
|
|
|
|
$this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $admin));
|
|
$this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $admin));
|
|
$this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext, $admin));
|
|
$this->assertTrue(has_any_capability($sca, $coursecontext, $admin));
|
|
$this->assertTrue(has_all_capabilities($sc, $coursecontext, $admin));
|
|
$this->assertTrue(has_all_capabilities($sca, $coursecontext, $admin));
|
|
|
|
$this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, $admin, false));
|
|
$this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, $admin, false));
|
|
$this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $admin, false));
|
|
$this->assertFalse(has_any_capability($sca, $coursecontext, $admin, false));
|
|
$this->assertFalse(has_all_capabilities($sc, $coursecontext, $admin, false));
|
|
$this->assertFalse(has_all_capabilities($sca, $coursecontext, $admin, false));
|
|
|
|
$this->setUser($teacher);
|
|
$this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
|
|
$this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
|
|
$this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
|
|
$this->assertTrue(has_any_capability($sca, $coursecontext));
|
|
$this->assertTrue(has_all_capabilities($sc, $coursecontext));
|
|
$this->assertFalse(has_all_capabilities($sca, $coursecontext));
|
|
|
|
$this->setAdminUser();
|
|
$this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
|
|
$this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
|
|
$this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext));
|
|
$this->assertTrue(has_any_capability($sca, $coursecontext));
|
|
$this->assertTrue(has_all_capabilities($sc, $coursecontext));
|
|
$this->assertTrue(has_all_capabilities($sca, $coursecontext));
|
|
|
|
$this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, 0));
|
|
$this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, 0));
|
|
$this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, 0));
|
|
$this->assertFalse(has_any_capability($sca, $coursecontext, 0));
|
|
$this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
|
|
}
|
|
|
|
/**
|
|
* Utility method to fake a plugin
|
|
*
|
|
* @param string $pluginname plugin name
|
|
* @return void
|
|
*/
|
|
protected function setup_fake_plugin($pluginname) {
|
|
global $CFG;
|
|
// Here we have to hack the component loader so we can insert our fake plugin and test that
|
|
// the access.php works.
|
|
$mockedcomponent = new ReflectionClass(core_component::class);
|
|
$mockedplugins = $mockedcomponent->getProperty('plugins');
|
|
$plugins = $mockedplugins->getValue();
|
|
$plugins['fake'] = [$pluginname => "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins/$pluginname"];
|
|
$mockedplugins->setValue(null, $plugins);
|
|
update_capabilities('fake_access');
|
|
$this->resetDebugging(); // We have debugging messages here that we need to get rid of.
|
|
// End of the component loader mock.
|
|
}
|
|
|
|
/**
|
|
* Test get_deprecated_capability_info()
|
|
*
|
|
* @covers ::get_deprecated_capability_info
|
|
*/
|
|
public function test_get_deprecated_capability_info(): void {
|
|
$this->resetAfterTest();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$user = $this->getDataGenerator()->create_and_enrol($course);
|
|
$this->setup_fake_plugin('access');
|
|
|
|
// For now we have deprecated fake/access:fakecapability.
|
|
$capinfo = get_deprecated_capability_info('fake/access:fakecapability');
|
|
$this->assertNotEmpty($capinfo);
|
|
$this->assertEquals("The capability 'fake/access:fakecapability' is"
|
|
. " deprecated.This capability should not be used anymore.", $capinfo['fullmessage']);
|
|
}
|
|
|
|
/**
|
|
* Test get_deprecated_capability_info() through has_capability
|
|
*
|
|
* @covers ::get_deprecated_capability_info
|
|
*/
|
|
public function test_get_deprecated_capability_info_through_has_capability(): void {
|
|
$this->resetAfterTest();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$user = $this->getDataGenerator()->create_and_enrol($course);
|
|
$this->setup_fake_plugin('access');
|
|
|
|
// For now we have deprecated fake/access:fakecapability.
|
|
$hascap = has_capability('fake/access:fakecapability', $coursecontext, $user);
|
|
$this->assertTrue($hascap);
|
|
$this->assertDebuggingCalled("The capability 'fake/access:fakecapability' is deprecated."
|
|
. "This capability should not be used anymore.");
|
|
}
|
|
|
|
/**
|
|
* Test get_deprecated_capability_info() through get_user_capability_contexts()
|
|
*
|
|
* @covers ::get_deprecated_capability_info
|
|
*/
|
|
public function test_get_deprecated_capability_info_through_get_user_capability_contexts(): void {
|
|
$this->resetAfterTest();
|
|
$category = $this->getDataGenerator()->create_category();
|
|
$course = $this->getDataGenerator()->create_course(['categoryid' => $category->id]);
|
|
$user = $this->getDataGenerator()->create_and_enrol($course);
|
|
$this->setup_fake_plugin('access');
|
|
|
|
// For now we have deprecated fake/access:fakecapability.
|
|
list($categories, $courses) = get_user_capability_contexts('fake/access:fakecapability', false, $user->id);
|
|
$this->assertNotEmpty($courses);
|
|
$this->assertDebuggingCalled("The capability 'fake/access:fakecapability' is deprecated."
|
|
. "This capability should not be used anymore.");
|
|
}
|
|
|
|
/**
|
|
* Test get_deprecated_capability_info with a capability that does not exist
|
|
*
|
|
* @param string $capability the capability name
|
|
* @param array $debugmessages the debug messsages we expect
|
|
* @param bool $expectedexisting does the capability exist
|
|
* @covers ::get_deprecated_capability_info
|
|
* @dataProvider deprecated_capabilities_use_cases
|
|
*/
|
|
public function test_get_deprecated_capability_specific_cases(string $capability, array $debugmessages,
|
|
bool $expectedexisting): void {
|
|
$this->resetAfterTest();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$user = $this->getDataGenerator()->create_and_enrol($course);
|
|
$this->setup_fake_plugin('access');
|
|
|
|
// For now we have deprecated fake/access:fakecapability.
|
|
$this->resetDebugging();
|
|
$hascap = has_capability($capability, $coursecontext, $user);
|
|
$this->assertEquals($expectedexisting, $hascap);
|
|
$this->assertDebuggingCalledCount(count($debugmessages), $debugmessages);
|
|
}
|
|
|
|
/**
|
|
* Specific use case for deprecated capabilities
|
|
*
|
|
* @return array
|
|
*/
|
|
public function deprecated_capabilities_use_cases() {
|
|
return [
|
|
'capability missing' => [
|
|
'fake/access:missingcapability',
|
|
[
|
|
"Capability \"fake/access:missingcapability\" was not found! This has to be fixed in code."
|
|
],
|
|
false
|
|
],
|
|
'replacement no info' => [
|
|
'fake/access:replacementnoinfo',
|
|
[
|
|
"The capability 'fake/access:replacementnoinfo' is deprecated.",
|
|
],
|
|
true
|
|
],
|
|
'replacement missing' => [
|
|
'fake/access:replacementmissing',
|
|
[
|
|
"The capability 'fake/access:replacementmissing' is deprecated.This capability should not be used anymore.",
|
|
],
|
|
true
|
|
],
|
|
'replacement with non existing cap' => [
|
|
'fake/access:replacementwithwrongcapability',
|
|
[
|
|
"Capability 'fake/access:replacementwithwrongcapability' was supposed to be replaced with"
|
|
. " 'fake/access:nonexistingcapabilty', which does not exist !",
|
|
"The capability 'fake/access:replacementwithwrongcapability' is deprecated."
|
|
. "This capability should not be used anymore.It will be replaced by 'fake/access:nonexistingcapabilty'."
|
|
],
|
|
true
|
|
],
|
|
'replacement with existing' => [
|
|
'fake/access:replacementwithexisting', // Existing capability buf for a different role.
|
|
[
|
|
"The capability 'fake/access:replacementwithexisting' is deprecated.This capability should not be used anymore."
|
|
. "It will be replaced by 'fake/access:existingcapability'.",
|
|
],
|
|
false // As the capability is applied to managers, we should not have this capability for this simple user.
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test that assigning a fake cap does not return.
|
|
*
|
|
* @covers ::get_users_by_capability
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::get_with_capability_sql
|
|
* @covers ::has_capability
|
|
*/
|
|
public function test_fake_capability(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
|
|
$fakecapname = 'moodle/fake:capability';
|
|
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$admin = $DB->get_record('user', array('username' => 'admin'));
|
|
|
|
// Test a capability which does not exist.
|
|
// Note: Do not use assign_capability because it will not allow fake caps.
|
|
$DB->insert_record('role_capabilities', (object) [
|
|
'contextid' => $coursecontext->id,
|
|
'roleid' => $teacherrole->id,
|
|
'capability' => $fakecapname,
|
|
'permission' => CAP_ALLOW,
|
|
'timemodified' => time(),
|
|
'modifierid' => 0,
|
|
]);
|
|
|
|
// Check `has_capability`.
|
|
$this->assertFalse(has_capability($fakecapname, $coursecontext, $teacher));
|
|
$this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
|
|
$this->assertFalse(has_capability($fakecapname, $coursecontext, $admin));
|
|
$this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
|
|
|
|
// Check `get_with_capability_sql` (with uses `get_with_capability_join`).
|
|
list($sql, $params) = get_with_capability_sql($coursecontext, $fakecapname);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
|
|
$this->assertFalse(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
|
|
// Check `get_users_by_capability`.
|
|
$users = get_users_by_capability($coursecontext, $fakecapname);
|
|
|
|
$this->assertFalse(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
}
|
|
|
|
/**
|
|
* Test that assigning a fake cap does not return.
|
|
*
|
|
* @covers ::assign_capability
|
|
*/
|
|
public function test_fake_capability_assign(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
|
|
$capability = 'moodle/fake:capability';
|
|
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$admin = $DB->get_record('user', array('username' => 'admin'));
|
|
|
|
$this->expectException('coding_exception');
|
|
$this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
|
|
assign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
|
|
}
|
|
|
|
/**
|
|
* Test that assigning a fake cap does not return.
|
|
*
|
|
* @covers ::unassign_capability
|
|
*/
|
|
public function test_fake_capability_unassign(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
|
|
$capability = 'moodle/fake:capability';
|
|
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
$admin = $DB->get_record('user', array('username' => 'admin'));
|
|
|
|
$this->expectException('coding_exception');
|
|
$this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
|
|
unassign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
|
|
}
|
|
|
|
/**
|
|
* Test that the caching in get_role_definitions() and get_role_definitions_uncached()
|
|
* works as intended.
|
|
*
|
|
* @covers ::get_role_definitions
|
|
* @covers ::role_change_permission
|
|
*/
|
|
public function test_role_definition_caching(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Get some role ids.
|
|
$authenticatedrole = $DB->get_record('role', array('shortname' => 'user'), '*', MUST_EXIST);
|
|
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$emptyroleid = create_role('No capabilities', 'empty', 'A role with no capabilties');
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
// Instantiate the cache instance, since that does DB queries (get_config)
|
|
// and we don't care about those.
|
|
cache::make('core', 'roledefs');
|
|
|
|
// One database query is not necessarily one database read, it seems. Find out how many.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
$rs = $DB->get_recordset('user');
|
|
$rs->close();
|
|
$readsperquery = $DB->perf_get_reads() - $startdbreads;
|
|
|
|
// Now load some role definitions, and check when it queries the database.
|
|
|
|
// Load the capabilities for two roles. Should be one query.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
get_role_definitions([$authenticatedrole->id, $studentrole->id]);
|
|
$this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
|
|
|
|
// Load the capabilities for same two roles. Should not query the DB.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
get_role_definitions([$authenticatedrole->id, $studentrole->id]);
|
|
$this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
|
|
|
|
// Include a third role. Should do one DB query.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
|
|
$this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
|
|
|
|
// Repeat call. No DB queries.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
|
|
$this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
|
|
|
|
// Alter a role.
|
|
role_change_permission($studentrole->id, $coursecontext, 'moodle/course:tag', CAP_ALLOW);
|
|
|
|
// Should now know to do one query.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
get_role_definitions([$authenticatedrole->id, $studentrole->id]);
|
|
$this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
|
|
|
|
// Now clear the in-memory cache, and verify that it does not query the DB.
|
|
// Cannot use accesslib_clear_all_caches_for_unit_testing since that also
|
|
// clears the MUC cache.
|
|
global $ACCESSLIB_PRIVATE;
|
|
$ACCESSLIB_PRIVATE->cacheroledefs = array();
|
|
|
|
// Get all roles. Should not need the DB.
|
|
$startdbreads = $DB->perf_get_reads();
|
|
get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
|
|
$this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
|
|
}
|
|
|
|
/**
|
|
* Tests get_user_capability_course() which checks a capability across all courses.
|
|
*
|
|
* @covers ::get_user_capability_course
|
|
*/
|
|
public function test_get_user_capability_course(): void {
|
|
global $CFG, $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$generator = $this->getDataGenerator();
|
|
$cap = 'moodle/course:view';
|
|
|
|
// The structure being created here is this:
|
|
//
|
|
// All tests work with the single capability 'moodle/course:view'.
|
|
//
|
|
// ROLE DEF/OVERRIDE ROLE ASSIGNS
|
|
// Role: Allow Prohib Empty Def user u1 u2 u3 u4 u5 u6 u7 u8
|
|
// System ALLOW PROHIBIT A E A+E
|
|
// cat1 ALLOW
|
|
// C1 (ALLOW) P
|
|
// C2 ALLOW E P
|
|
// cat2 PREVENT
|
|
// C3 ALLOW E
|
|
// C4
|
|
// Misc. A
|
|
// C5 PREVENT A
|
|
// C6 PROHIBIT
|
|
//
|
|
// Front-page and guest role stuff from the end of this test not included in the diagram.
|
|
|
|
// Create a role which allows course:view and one that prohibits it, and one neither.
|
|
$allowroleid = $generator->create_role();
|
|
$prohibitroleid = $generator->create_role();
|
|
$emptyroleid = $generator->create_role();
|
|
$systemcontext = context_system::instance();
|
|
assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
|
|
assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
|
|
|
|
// Create two categories (nested).
|
|
$cat1 = $generator->create_category();
|
|
$cat2 = $generator->create_category(['parent' => $cat1->id]);
|
|
|
|
// Create six courses - two in cat1, two in cat2, and two in default category.
|
|
// Shortnames are used for a sorting test. Otherwise they are not significant.
|
|
$c1 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Z']);
|
|
$c2 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Y']);
|
|
$c3 = $generator->create_course(['category' => $cat2->id, 'shortname' => 'X']);
|
|
$c4 = $generator->create_course(['category' => $cat2->id]);
|
|
$c5 = $generator->create_course();
|
|
$c6 = $generator->create_course();
|
|
|
|
// Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
|
|
assign_capability($cap, CAP_ALLOW, $emptyroleid,
|
|
context_coursecat::instance($cat1->id)->id);
|
|
assign_capability($cap, CAP_PREVENT, $emptyroleid,
|
|
context_coursecat::instance($cat2->id)->id);
|
|
|
|
// Course overrides: in C5, allow role is prevented; in C6, empty role is prohibited; in
|
|
// C3, empty role is allowed.
|
|
assign_capability($cap, CAP_PREVENT, $allowroleid,
|
|
context_course::instance($c5->id)->id);
|
|
assign_capability($cap, CAP_PROHIBIT, $emptyroleid,
|
|
context_course::instance($c6->id)->id);
|
|
assign_capability($cap, CAP_ALLOW, $emptyroleid,
|
|
context_course::instance($c3->id)->id);
|
|
assign_capability($cap, CAP_ALLOW, $prohibitroleid,
|
|
context_course::instance($c2->id)->id);
|
|
|
|
// User 1 has no roles except default user role.
|
|
$u1 = $generator->create_user();
|
|
|
|
// It returns false (annoyingly) if there are no courses.
|
|
$this->assertFalse(get_user_capability_course($cap, $u1->id, true, '', 'id'));
|
|
|
|
// Final override: in C1, default user role is allowed.
|
|
assign_capability($cap, CAP_ALLOW, $CFG->defaultuserroleid,
|
|
context_course::instance($c1->id)->id);
|
|
|
|
// Should now get C1 only.
|
|
$courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
|
|
$this->assert_course_ids([$c1->id], $courses);
|
|
|
|
// User 2 has allow role (system wide).
|
|
$u2 = $generator->create_user();
|
|
role_assign($allowroleid, $u2->id, $systemcontext->id);
|
|
|
|
// Should get everything except C5.
|
|
$courses = get_user_capability_course($cap, $u2->id, true, '', 'id');
|
|
$this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c6->id], $courses);
|
|
|
|
// User 3 has empty role (system wide).
|
|
$u3 = $generator->create_user();
|
|
role_assign($emptyroleid, $u3->id, $systemcontext->id);
|
|
|
|
// Should get cat 1 courses but not cat2, except C3.
|
|
$courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
|
|
$this->assert_course_ids([$c1->id, $c2->id, $c3->id], $courses);
|
|
|
|
// User 4 has allow and empty role (system wide).
|
|
$u4 = $generator->create_user();
|
|
role_assign($allowroleid, $u4->id, $systemcontext->id);
|
|
role_assign($emptyroleid, $u4->id, $systemcontext->id);
|
|
|
|
// Should get everything except C5 and C6.
|
|
$courses = get_user_capability_course($cap, $u4->id, true, '', 'id');
|
|
$this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id], $courses);
|
|
|
|
// User 5 has allow role in default category only.
|
|
$u5 = $generator->create_user();
|
|
role_assign($allowroleid, $u5->id, context_coursecat::instance($c5->category)->id);
|
|
|
|
// Should get C1 and the default category courses but not C5.
|
|
$courses = get_user_capability_course($cap, $u5->id, true, '', 'id');
|
|
$this->assert_course_ids([$c1->id, $c6->id], $courses);
|
|
|
|
// User 6 has a bunch of course roles: prohibit role in C1, empty role in C3, allow role in
|
|
// C6.
|
|
$u6 = $generator->create_user();
|
|
role_assign($prohibitroleid, $u6->id, context_course::instance($c1->id)->id);
|
|
role_assign($emptyroleid, $u6->id, context_course::instance($c3->id)->id);
|
|
role_assign($allowroleid, $u6->id, context_course::instance($c5->id)->id);
|
|
|
|
// Should get C3 only because the allow role is prevented in C5.
|
|
$courses = get_user_capability_course($cap, $u6->id, true, '', 'id');
|
|
$this->assert_course_ids([$c3->id], $courses);
|
|
|
|
// User 7 has empty role in C2.
|
|
$u7 = $generator->create_user();
|
|
role_assign($emptyroleid, $u7->id, context_course::instance($c2->id)->id);
|
|
|
|
// Should get C1 by the default user role override, and C2 by the cat1 level override.
|
|
$courses = get_user_capability_course($cap, $u7->id, true, '', 'id');
|
|
$this->assert_course_ids([$c1->id, $c2->id], $courses);
|
|
|
|
// User 8 has prohibit role as system context, to verify that prohibits can't be overridden.
|
|
$u8 = $generator->create_user();
|
|
role_assign($prohibitroleid, $u8->id, context_course::instance($c2->id)->id);
|
|
|
|
// Should get C1 by the default user role override, no other courses because the prohibit cannot be overridden.
|
|
$courses = get_user_capability_course($cap, $u8->id, true, '', 'id');
|
|
$this->assert_course_ids([$c1->id], $courses);
|
|
|
|
// Admin user gets everything....
|
|
$courses = get_user_capability_course($cap, get_admin()->id, true, '', 'id');
|
|
$this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c5->id, $c6->id],
|
|
$courses);
|
|
|
|
// Unless you turn off doanything, when it only has the things a user with no role does.
|
|
$courses = get_user_capability_course($cap, get_admin()->id, false, '', 'id');
|
|
$this->assert_course_ids([$c1->id], $courses);
|
|
|
|
// Using u3 as an example, test the limit feature.
|
|
$courses = get_user_capability_course($cap, $u3->id, true, '', 'id', 2);
|
|
$this->assert_course_ids([$c1->id, $c2->id], $courses);
|
|
|
|
// Check sorting.
|
|
$courses = get_user_capability_course($cap, $u3->id, true, '', 'shortname');
|
|
$this->assert_course_ids([$c3->id, $c2->id, $c1->id], $courses);
|
|
|
|
// Check returned fields - default.
|
|
$courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
|
|
$this->assertEquals((object)['id' => $c1->id], $courses[0]);
|
|
|
|
// Add a selection of fields, including the context ones with special handling.
|
|
$courses = get_user_capability_course($cap, $u3->id, true, 'shortname, ctxlevel, ctxdepth, ctxinstance', 'id');
|
|
$this->assertEquals((object)['id' => $c1->id, 'shortname' => 'Z', 'ctxlevel' => 50,
|
|
'ctxdepth' => 3, 'ctxinstance' => $c1->id], $courses[0]);
|
|
|
|
// Test front page role - user 1 has no roles, but if we change the front page role
|
|
// definition so that it has our capability, then they should see the front page course.
|
|
// as well as C1.
|
|
assign_capability($cap, CAP_ALLOW, $CFG->defaultfrontpageroleid, $systemcontext->id);
|
|
$courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
|
|
$this->assert_course_ids([SITEID, $c1->id], $courses);
|
|
|
|
// Check that temporary guest access (in this case, given on course 2 for user 1)
|
|
// also is included, if it has this capability.
|
|
assign_capability($cap, CAP_ALLOW, $CFG->guestroleid, $systemcontext->id);
|
|
$this->setUser($u1);
|
|
load_temp_course_role(context_course::instance($c2->id), $CFG->guestroleid);
|
|
$courses = get_user_capability_course($cap, $USER->id, true, '', 'id');
|
|
$this->assert_course_ids([SITEID, $c1->id, $c2->id], $courses);
|
|
}
|
|
|
|
/**
|
|
* Tests get_user_capability_contexts() which checks a capability across all courses and categories.
|
|
* Testing for categories only because courses results are covered by test_get_user_capability_course.
|
|
*
|
|
* @covers ::get_user_capability_contexts
|
|
*/
|
|
public function test_get_user_capability_contexts(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$generator = $this->getDataGenerator();
|
|
$cap = 'moodle/contentbank:access';
|
|
$defaultcategoryid = 1;
|
|
|
|
// The structure being created here is this:
|
|
//
|
|
// All tests work with the single capability 'moodle/contentbank:access'.
|
|
// ROLE DEF/OVERRIDE .
|
|
// Role: Allow Prohibit Empty .
|
|
// System ALLOW PROHIBIT .
|
|
// cat1 PREVENT ALLOW ALLOW .
|
|
// cat3 ALLOW PROHIBIT .
|
|
// cat2 PROHIBIT PROHIBIT PROHIBIT .
|
|
|
|
// Create a role which allows contentbank:access and one that prohibits it, and one neither.
|
|
$allowroleid = $generator->create_role();
|
|
$prohibitroleid = $generator->create_role();
|
|
$emptyroleid = $generator->create_role();
|
|
$systemcontext = context_system::instance();
|
|
assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
|
|
assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
|
|
|
|
// Create three categories (two of them nested).
|
|
$cat1 = $generator->create_category(['name' => 'Aardvarks']);
|
|
$cat2 = $generator->create_category(['name' => 'Badgers']);
|
|
$cat3 = $generator->create_category(['parent' => $cat1->id, 'name' => 'Cheetahs']);
|
|
|
|
// Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
|
|
assign_capability($cap, CAP_ALLOW, $emptyroleid,
|
|
context_coursecat::instance($cat1->id)->id);
|
|
assign_capability($cap, CAP_PREVENT, $emptyroleid,
|
|
context_coursecat::instance($cat2->id)->id);
|
|
|
|
// Course category overrides: in cat1, allow role is prevented and prohibit role is allowed;
|
|
// in Cat2, allow role is prohibited.
|
|
assign_capability($cap, CAP_PREVENT, $allowroleid,
|
|
context_coursecat::instance($cat1->id)->id);
|
|
assign_capability($cap, CAP_ALLOW, $prohibitroleid,
|
|
context_coursecat::instance($cat1->id)->id);
|
|
assign_capability($cap, CAP_PROHIBIT, $allowroleid,
|
|
context_coursecat::instance($cat2->id)->id);
|
|
|
|
// User 1 has no roles except default user role.
|
|
$u1 = $generator->create_user();
|
|
|
|
// It returns false (annoyingly) if there are no course categories.
|
|
list($categories, $courses) = get_user_capability_contexts($cap, true, $u1->id);
|
|
$this->assertFalse($categories);
|
|
|
|
// User 2 has allow role (system wide).
|
|
$u2 = $generator->create_user();
|
|
role_assign($allowroleid, $u2->id, $systemcontext->id);
|
|
|
|
// Should get $defaultcategory only. cat2 is prohibited; cat1 is prevented, so cat3 is not allowed.
|
|
list($categories, $courses) = get_user_capability_contexts($cap, true, $u2->id);
|
|
// Using same assert_course_ids helper even when we are checking course category ids.
|
|
$this->assert_course_ids([$defaultcategoryid], $categories);
|
|
|
|
// User 3 has empty role (system wide).
|
|
$u3 = $generator->create_user();
|
|
role_assign($emptyroleid, $u3->id, $systemcontext->id);
|
|
|
|
// Should get cat1 and cat3. cat2 is prohibited; no access to system level. Sorted by category name.
|
|
list($categories, $courses) = get_user_capability_contexts($cap, true, $u3->id, true, '', '', '', 'name');
|
|
$this->assert_course_ids([$cat1->id, $cat3->id], $categories);
|
|
|
|
// User 4 has prohibit role (system wide).
|
|
$u4 = $generator->create_user();
|
|
role_assign($prohibitroleid, $u4->id, $systemcontext->id);
|
|
|
|
// Should not get any, because all of them are prohibited at system level.
|
|
// Even if we try to allow an specific category.
|
|
list($categories, $courses) = get_user_capability_contexts($cap, true, $u4->id);
|
|
$this->assertFalse($categories);
|
|
}
|
|
|
|
/**
|
|
* Extracts an array of course ids to make the above test script shorter.
|
|
*
|
|
* @param int[] $expected Array of expected course ids
|
|
* @param stdClass[] $courses Array of course objects
|
|
*/
|
|
protected function assert_course_ids(array $expected, array $courses) {
|
|
$courseids = array_map(function($c) {
|
|
return $c->id;
|
|
}, $courses);
|
|
$this->assertEquals($expected, $courseids);
|
|
}
|
|
|
|
/**
|
|
* Test if course creator future capability lookup works.
|
|
*
|
|
* @covers ::guess_if_creator_will_have_course_capability
|
|
* @covers ::has_capability
|
|
*/
|
|
public function test_guess_if_creator_will_have_course_capability(): void {
|
|
global $DB, $CFG, $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$category = $this->getDataGenerator()->create_category();
|
|
$course = $this->getDataGenerator()->create_course(array('category'=>$category->id));
|
|
|
|
$syscontext = context_system::instance();
|
|
$categorycontext = context_coursecat::instance($category->id);
|
|
$coursecontext = context_course::instance($course->id);
|
|
$studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
|
|
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
|
|
$creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST);
|
|
$managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
|
|
|
|
$this->assertEquals($teacherrole->id, $CFG->creatornewroleid);
|
|
|
|
$creator = $this->getDataGenerator()->create_user();
|
|
$manager = $this->getDataGenerator()->create_user();
|
|
role_assign($managerrole->id, $manager->id, $categorycontext);
|
|
|
|
$this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator));
|
|
$this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
|
|
|
|
$this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
|
|
$this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id));
|
|
|
|
$this->assertEquals(0, $USER->id);
|
|
$this->assertFalse(has_capability('moodle/course:view', $categorycontext));
|
|
$this->assertFalse(has_capability('moodle/role:assign', $categorycontext));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
|
|
|
|
$this->setUser($manager);
|
|
$this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
|
|
$this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
|
|
$this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
|
|
|
|
$this->setAdminUser();
|
|
$this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
|
|
$this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
|
|
$this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
|
|
$this->setUser(0);
|
|
|
|
role_assign($creatorrole->id, $creator->id, $categorycontext);
|
|
|
|
$this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
|
|
|
|
$this->setUser($creator);
|
|
$this->assertFalse(has_capability('moodle/role:assign', $categorycontext, null));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, null));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, null));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, null));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, null));
|
|
$this->setUser(0);
|
|
|
|
set_config('creatornewroleid', $studentrole->id);
|
|
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
|
|
|
|
set_config('creatornewroleid', $teacherrole->id);
|
|
|
|
role_change_permission($managerrole->id, $categorycontext, 'moodle/course:visibility', CAP_PREVENT);
|
|
role_assign($creatorrole->id, $manager->id, $categorycontext);
|
|
|
|
$this->assertTrue(has_capability('moodle/course:view', $categorycontext, $manager));
|
|
$this->assertTrue(has_capability('moodle/course:view', $coursecontext, $manager));
|
|
$this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
|
|
$this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
|
|
role_change_permission($managerrole->id, $categorycontext, 'moodle/course:view', CAP_PREVENT);
|
|
$this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
|
|
$this->getDataGenerator()->enrol_user($manager->id, $course->id, 0);
|
|
|
|
$this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
|
|
$this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager));
|
|
$this->assertTrue(is_enrolled($coursecontext, $manager));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
$this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager));
|
|
$this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager));
|
|
|
|
// Test problems.
|
|
|
|
try {
|
|
guess_if_creator_will_have_course_capability('moodle/course:visibility', $syscontext, $creator);
|
|
$this->fail('Exception expected when non course/category context passed to guess_if_creator_will_have_course_capability()');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertInstanceOf('coding_exception', $e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test require_capability() exceptions.
|
|
*
|
|
* @covers ::require_capability
|
|
*/
|
|
public function test_require_capability(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$syscontext = context_system::instance();
|
|
|
|
$this->setUser(0);
|
|
$this->assertFalse(has_capability('moodle/site:config', $syscontext));
|
|
try {
|
|
require_capability('moodle/site:config', $syscontext);
|
|
$this->fail('Exception expected from require_capability()');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertInstanceOf('required_capability_exception', $e);
|
|
}
|
|
$this->setAdminUser();
|
|
$this->assertFalse(has_capability('moodle/site:config', $syscontext, 0));
|
|
try {
|
|
require_capability('moodle/site:config', $syscontext, 0);
|
|
$this->fail('Exception expected from require_capability()');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertInstanceOf('required_capability_exception', $e);
|
|
}
|
|
$this->assertFalse(has_capability('moodle/site:config', $syscontext, null, false));
|
|
try {
|
|
require_capability('moodle/site:config', $syscontext, null, false);
|
|
$this->fail('Exception expected from require_capability()');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertInstanceOf('required_capability_exception', $e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test that enrolled users SQL does not return any values for users in
|
|
* other courses.
|
|
*
|
|
*
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_enrolled_sql
|
|
* @covers ::get_enrolled_with_capabilities_join
|
|
* @covers ::get_enrolled_join
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::groups_get_members_join
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_different_course(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$context = context_course::instance($course->id);
|
|
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$user = $this->getDataGenerator()->create_user();
|
|
|
|
// This user should not appear anywhere, we're not interested in that context.
|
|
$course2 = $this->getDataGenerator()->create_course();
|
|
$this->getDataGenerator()->enrol_user($user->id, $course2->id, $student->id);
|
|
|
|
$enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
|
|
$active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
|
|
$suspended = get_suspended_userids($context);
|
|
|
|
$this->assertFalse(isset($enrolled[$user->id]));
|
|
$this->assertFalse(isset($active[$user->id]));
|
|
$this->assertFalse(isset($suspended[$user->id]));
|
|
$this->assertCount(0, $enrolled);
|
|
$this->assertCount(0, $active);
|
|
$this->assertCount(0, $suspended);
|
|
}
|
|
|
|
/**
|
|
* Test that enrolled users SQL does not return any values for role
|
|
* assignments without an enrolment.
|
|
*
|
|
*
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_enrolled_sql
|
|
* @covers ::get_enrolled_with_capabilities_join
|
|
* @covers ::get_enrolled_join
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::groups_get_members_join
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_role_only(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$context = context_course::instance($course->id);
|
|
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$user = $this->getDataGenerator()->create_user();
|
|
|
|
// Role assignment is not the same as course enrollment.
|
|
role_assign($student->id, $user->id, $context->id);
|
|
|
|
$enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
|
|
$active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
|
|
$suspended = get_suspended_userids($context);
|
|
|
|
$this->assertFalse(isset($enrolled[$user->id]));
|
|
$this->assertFalse(isset($active[$user->id]));
|
|
$this->assertFalse(isset($suspended[$user->id]));
|
|
$this->assertCount(0, $enrolled);
|
|
$this->assertCount(0, $active);
|
|
$this->assertCount(0, $suspended);
|
|
}
|
|
|
|
/**
|
|
* Test that multiple enrolments for the same user are counted correctly.
|
|
*
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_enrolled_sql
|
|
* @covers ::get_enrolled_with_capabilities_join
|
|
* @covers ::get_enrolled_join
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::groups_get_members_join
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_multiple_enrolments(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$context = context_course::instance($course->id);
|
|
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$user = $this->getDataGenerator()->create_user();
|
|
|
|
// Add a suspended enrol.
|
|
$selfinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'self'));
|
|
$selfplugin = enrol_get_plugin('self');
|
|
$selfplugin->update_status($selfinstance, ENROL_INSTANCE_ENABLED);
|
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'self', 0, 0, ENROL_USER_SUSPENDED);
|
|
|
|
// Should be enrolled, but not active - user is suspended.
|
|
$enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
|
|
$active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
|
|
$suspended = get_suspended_userids($context);
|
|
|
|
$this->assertTrue(isset($enrolled[$user->id]));
|
|
$this->assertFalse(isset($active[$user->id]));
|
|
$this->assertTrue(isset($suspended[$user->id]));
|
|
$this->assertCount(1, $enrolled);
|
|
$this->assertCount(0, $active);
|
|
$this->assertCount(1, $suspended);
|
|
|
|
// Add an active enrol for the user. Any active enrol makes them enrolled.
|
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id);
|
|
|
|
// User should be active now.
|
|
$enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
|
|
$active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
|
|
$suspended = get_suspended_userids($context);
|
|
|
|
$this->assertTrue(isset($enrolled[$user->id]));
|
|
$this->assertTrue(isset($active[$user->id]));
|
|
$this->assertFalse(isset($suspended[$user->id]));
|
|
$this->assertCount(1, $enrolled);
|
|
$this->assertCount(1, $active);
|
|
$this->assertCount(0, $suspended);
|
|
|
|
}
|
|
|
|
/**
|
|
* Test that enrolled users returns only users in those groups that are
|
|
* specified.
|
|
*
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_enrolled_sql
|
|
* @covers ::get_enrolled_with_capabilities_join
|
|
* @covers ::get_enrolled_join
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::groups_get_members_join
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_userswithgroups(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$systemcontext = context_system::instance();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
|
|
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
|
|
|
|
$group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
|
|
groups_add_member($group1, $user1);
|
|
$group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
|
|
groups_add_member($group2, $user2);
|
|
|
|
// Get user from group 1.
|
|
$group1users = get_enrolled_users($coursecontext, '', $group1->id);
|
|
$this->assertCount(1, $group1users);
|
|
$this->assertArrayHasKey($user1->id, $group1users);
|
|
$this->assertEquals(1, count_enrolled_users($coursecontext, '', $group1->id));
|
|
|
|
// Get user from group 2.
|
|
$group2users = get_enrolled_users($coursecontext, '', $group2->id);
|
|
$this->assertCount(1, $group2users);
|
|
$this->assertArrayHasKey($user2->id, $group2users);
|
|
$this->assertEquals(1, count_enrolled_users($coursecontext, '', $group2->id));
|
|
|
|
// Get users from multiple groups.
|
|
$groupusers = get_enrolled_users($coursecontext, '', [$group1->id, $group2->id]);
|
|
$this->assertCount(2, $groupusers);
|
|
$this->assertArrayHasKey($user1->id, $groupusers);
|
|
$this->assertArrayHasKey($user2->id, $groupusers);
|
|
$this->assertEquals(2, count_enrolled_users($coursecontext, '', [$group1->id, $group2->id]));
|
|
}
|
|
|
|
/**
|
|
* Test that enrolled users SQL does not return any values for users
|
|
* without a group when $context is not a valid course context.
|
|
*
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_enrolled_sql
|
|
* @covers ::get_enrolled_with_capabilities_join
|
|
* @covers ::get_enrolled_join
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::groups_get_members_join
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_userswithoutgroup(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$systemcontext = context_system::instance();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
|
|
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
|
|
|
|
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
|
groups_add_member($group, $user1);
|
|
|
|
$enrolled = get_enrolled_users($coursecontext);
|
|
$this->assertCount(2, $enrolled);
|
|
|
|
// Get users without any group on the course context.
|
|
$enrolledwithoutgroup = get_enrolled_users($coursecontext, '', USERSWITHOUTGROUP);
|
|
$this->assertCount(1, $enrolledwithoutgroup);
|
|
$this->assertFalse(isset($enrolledwithoutgroup[$user1->id]));
|
|
|
|
// Get users without any group on the system context (it should throw an exception).
|
|
$this->expectException('coding_exception');
|
|
get_enrolled_users($systemcontext, '', USERSWITHOUTGROUP);
|
|
}
|
|
|
|
/**
|
|
* Test that enrolled users returns only users in those groups that are
|
|
* specified, and they are allowed to see members of.
|
|
*
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_enrolled_sql
|
|
* @covers ::get_enrolled_with_capabilities_join
|
|
* @covers ::get_enrolled_join
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::groups_get_members_join
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_userswithhiddengroups(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
$user3 = $this->getDataGenerator()->create_user();
|
|
$user4 = $this->getDataGenerator()->create_user();
|
|
$user5 = $this->getDataGenerator()->create_user();
|
|
$user6 = $this->getDataGenerator()->create_user();
|
|
|
|
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user3->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user4->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user5->id, $course->id);
|
|
$this->getDataGenerator()->enrol_user($user6->id, $course->id);
|
|
|
|
$group1 = $this->getDataGenerator()->create_group([
|
|
'courseid' => $course->id,
|
|
'visibility' => GROUPS_VISIBILITY_ALL,
|
|
]);
|
|
groups_add_member($group1, $user1);
|
|
$group2 = $this->getDataGenerator()->create_group([
|
|
'courseid' => $course->id,
|
|
'visibility' => GROUPS_VISIBILITY_MEMBERS,
|
|
]);
|
|
groups_add_member($group2, $user2);
|
|
groups_add_member($group2, $user5);
|
|
$group3 = $this->getDataGenerator()->create_group([
|
|
'courseid' => $course->id,
|
|
'visibility' => GROUPS_VISIBILITY_OWN,
|
|
]);
|
|
groups_add_member($group3, $user3);
|
|
groups_add_member($group3, $user6);
|
|
$group4 = $this->getDataGenerator()->create_group([
|
|
'courseid' => $course->id,
|
|
'visibility' => GROUPS_VISIBILITY_NONE,
|
|
]);
|
|
groups_add_member($group4, $user4);
|
|
|
|
$groupids = [$group1->id, $group2->id, $group3->id, $group4->id];
|
|
// User 1 can only see members of Group 1.
|
|
$this->setUser($user1);
|
|
$user1groupusers = get_enrolled_users($coursecontext, '', $groupids);
|
|
$this->assertCount(1, $user1groupusers);
|
|
$this->assertArrayHasKey($user1->id, $user1groupusers);
|
|
$this->assertEquals(1, count_enrolled_users($coursecontext, '', $groupids));
|
|
// User 2 can see all members of Group 1 and Group 2.
|
|
$this->setUser($user2);
|
|
$user2groupusers = get_enrolled_users($coursecontext, '', $groupids);
|
|
$this->assertCount(3, $user2groupusers);
|
|
$this->assertArrayHasKey($user1->id, $user2groupusers);
|
|
$this->assertArrayHasKey($user2->id, $user2groupusers);
|
|
$this->assertArrayHasKey($user5->id, $user2groupusers);
|
|
$this->assertEquals(3, count_enrolled_users($coursecontext, '', $groupids));
|
|
// User 3 can see members of Group 1, and themselves in Group 3 but not other members.
|
|
$this->setUser($user3);
|
|
$user3groupusers = get_enrolled_users($coursecontext, '', $groupids);
|
|
$this->assertCount(2, $user3groupusers);
|
|
$this->assertArrayHasKey($user1->id, $user3groupusers);
|
|
$this->assertArrayHasKey($user3->id, $user3groupusers);
|
|
$this->assertEquals(2, count_enrolled_users($coursecontext, '', $groupids));
|
|
// User 4 can only see members of Group 1.
|
|
$this->setUser($user4);
|
|
$user4groupusers = get_enrolled_users($coursecontext, '', $groupids);
|
|
$this->assertCount(1, $user4groupusers);
|
|
$this->assertArrayHasKey($user1->id, $user4groupusers);
|
|
$this->assertEquals(1, count_enrolled_users($coursecontext, '', $groupids));
|
|
}
|
|
|
|
public function get_enrolled_sql_provider() {
|
|
return array(
|
|
array(
|
|
// Two users who are enrolled.
|
|
'users' => array(
|
|
array(
|
|
'enrolled' => true,
|
|
'active' => true,
|
|
),
|
|
array(
|
|
'enrolled' => true,
|
|
'active' => true,
|
|
),
|
|
),
|
|
'counts' => array(
|
|
'enrolled' => 2,
|
|
'active' => 2,
|
|
'suspended' => 0,
|
|
),
|
|
),
|
|
array(
|
|
// A user who is suspended.
|
|
'users' => array(
|
|
array(
|
|
'status' => ENROL_USER_SUSPENDED,
|
|
'enrolled' => true,
|
|
'suspended' => true,
|
|
),
|
|
),
|
|
'counts' => array(
|
|
'enrolled' => 1,
|
|
'active' => 0,
|
|
'suspended' => 1,
|
|
),
|
|
),
|
|
array(
|
|
// One of each.
|
|
'users' => array(
|
|
array(
|
|
'enrolled' => true,
|
|
'active' => true,
|
|
),
|
|
array(
|
|
'status' => ENROL_USER_SUSPENDED,
|
|
'enrolled' => true,
|
|
'suspended' => true,
|
|
),
|
|
),
|
|
'counts' => array(
|
|
'enrolled' => 2,
|
|
'active' => 1,
|
|
'suspended' => 1,
|
|
),
|
|
),
|
|
array(
|
|
// One user who is not yet enrolled.
|
|
'users' => array(
|
|
array(
|
|
'timestart' => DAYSECS,
|
|
'enrolled' => true,
|
|
'active' => false,
|
|
'suspended' => true,
|
|
),
|
|
),
|
|
'counts' => array(
|
|
'enrolled' => 1,
|
|
'active' => 0,
|
|
'suspended' => 1,
|
|
),
|
|
),
|
|
array(
|
|
// One user who is no longer enrolled
|
|
'users' => array(
|
|
array(
|
|
'timeend' => -DAYSECS,
|
|
'enrolled' => true,
|
|
'active' => false,
|
|
'suspended' => true,
|
|
),
|
|
),
|
|
'counts' => array(
|
|
'enrolled' => 1,
|
|
'active' => 0,
|
|
'suspended' => 1,
|
|
),
|
|
),
|
|
array(
|
|
// One user who is not yet enrolled, and one who is no longer enrolled.
|
|
'users' => array(
|
|
array(
|
|
'timeend' => -DAYSECS,
|
|
'enrolled' => true,
|
|
'active' => false,
|
|
'suspended' => true,
|
|
),
|
|
array(
|
|
'timestart' => DAYSECS,
|
|
'enrolled' => true,
|
|
'active' => false,
|
|
'suspended' => true,
|
|
),
|
|
),
|
|
'counts' => array(
|
|
'enrolled' => 2,
|
|
'active' => 0,
|
|
'suspended' => 2,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider get_enrolled_sql_provider
|
|
* @covers ::get_enrolled_users
|
|
* @covers ::get_suspended_userids
|
|
*/
|
|
public function test_get_enrolled_sql_course($users, $counts): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$context = context_course::instance($course->id);
|
|
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$createdusers = array();
|
|
|
|
foreach ($users as &$userdata) {
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$userdata['id'] = $user->id;
|
|
|
|
$timestart = 0;
|
|
$timeend = 0;
|
|
$status = null;
|
|
if (isset($userdata['timestart'])) {
|
|
$timestart = time() + $userdata['timestart'];
|
|
}
|
|
if (isset($userdata['timeend'])) {
|
|
$timeend = time() + $userdata['timeend'];
|
|
}
|
|
if (isset($userdata['status'])) {
|
|
$status = $userdata['status'];
|
|
}
|
|
|
|
// Enrol the user in the course.
|
|
$this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'manual', $timestart, $timeend, $status);
|
|
}
|
|
|
|
// After all users have been enroled, check expectations.
|
|
$enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false);
|
|
$active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true);
|
|
$suspended = get_suspended_userids($context);
|
|
|
|
foreach ($users as $userdata) {
|
|
if (isset($userdata['enrolled']) && $userdata['enrolled']) {
|
|
$this->assertTrue(isset($enrolled[$userdata['id']]));
|
|
} else {
|
|
$this->assertFalse(isset($enrolled[$userdata['id']]));
|
|
}
|
|
|
|
if (isset($userdata['active']) && $userdata['active']) {
|
|
$this->assertTrue(isset($active[$userdata['id']]));
|
|
} else {
|
|
$this->assertFalse(isset($active[$userdata['id']]));
|
|
}
|
|
|
|
if (isset($userdata['suspended']) && $userdata['suspended']) {
|
|
$this->assertTrue(isset($suspended[$userdata['id']]));
|
|
} else {
|
|
$this->assertFalse(isset($suspended[$userdata['id']]));
|
|
}
|
|
}
|
|
|
|
$this->assertCount($counts['enrolled'], $enrolled);
|
|
$this->assertCount($counts['active'], $active);
|
|
$this->assertCount($counts['suspended'], $suspended);
|
|
}
|
|
|
|
/**
|
|
* A small functional test of permission evaluations.
|
|
*/
|
|
public function test_permission_evaluation(): void {
|
|
global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$generator = $this->getDataGenerator();
|
|
|
|
// Fill the site with some real data.
|
|
$testcategories = array();
|
|
$testcourses = array();
|
|
$testpages = array();
|
|
$testblocks = array();
|
|
$allroles = $DB->get_records_menu('role', array(), 'id', 'shortname, id');
|
|
|
|
$systemcontext = context_system::instance();
|
|
$frontpagecontext = context_course::instance(SITEID);
|
|
|
|
// Add block to system context.
|
|
$bi = $generator->create_block('online_users');
|
|
context_block::instance($bi->id);
|
|
$testblocks[] = $bi->id;
|
|
|
|
// Some users.
|
|
$testusers = array();
|
|
for ($i=0; $i<20; $i++) {
|
|
$user = $generator->create_user();
|
|
$testusers[$i] = $user->id;
|
|
$usercontext = context_user::instance($user->id);
|
|
|
|
// Add block to user profile.
|
|
$bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id));
|
|
$testblocks[] = $bi->id;
|
|
}
|
|
|
|
// Add block to frontpage.
|
|
$bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id));
|
|
$frontpageblockcontext = context_block::instance($bi->id);
|
|
$testblocks[] = $bi->id;
|
|
|
|
// Add a resource to frontpage.
|
|
$page = $generator->create_module('page', array('course'=>$SITE->id));
|
|
$testpages[] = $page->cmid;
|
|
$frontpagepagecontext = context_module::instance($page->cmid);
|
|
|
|
// Add block to frontpage resource.
|
|
$bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id));
|
|
$frontpagepageblockcontext = context_block::instance($bi->id);
|
|
$testblocks[] = $bi->id;
|
|
|
|
// Some nested course categories with courses.
|
|
$manualenrol = enrol_get_plugin('manual');
|
|
$parentcat = 0;
|
|
for ($i=0; $i<5; $i++) {
|
|
$cat = $generator->create_category(array('parent'=>$parentcat));
|
|
$testcategories[] = $cat->id;
|
|
$catcontext = context_coursecat::instance($cat->id);
|
|
$parentcat = $cat->id;
|
|
|
|
if ($i >= 4) {
|
|
continue;
|
|
}
|
|
|
|
// Add resource to each category.
|
|
$bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id));
|
|
context_block::instance($bi->id);
|
|
|
|
// Add a few courses to each category.
|
|
for ($j=0; $j<6; $j++) {
|
|
$course = $generator->create_course(array('category'=>$cat->id));
|
|
$testcourses[] = $course->id;
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
if ($j >= 5) {
|
|
continue;
|
|
}
|
|
// Add manual enrol instance.
|
|
$manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id)));
|
|
|
|
// Add block to each course.
|
|
$bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id));
|
|
$testblocks[] = $bi->id;
|
|
|
|
// Add a resource to each course.
|
|
$page = $generator->create_module('page', array('course'=>$course->id));
|
|
$testpages[] = $page->cmid;
|
|
$modcontext = context_module::instance($page->cmid);
|
|
|
|
// Add block to each module.
|
|
$bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id));
|
|
$testblocks[] = $bi->id;
|
|
}
|
|
}
|
|
|
|
// Make sure all contexts were created properly.
|
|
$count = 1; // System.
|
|
$count += $DB->count_records('user', array('deleted'=>0));
|
|
$count += $DB->count_records('course_categories');
|
|
$count += $DB->count_records('course');
|
|
$count += $DB->count_records('course_modules');
|
|
$count += $DB->count_records('block_instances');
|
|
$this->assertEquals($count, $DB->count_records('context'));
|
|
$this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
|
|
$this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
|
|
|
|
|
|
// Test context_helper::get_level_name() method.
|
|
|
|
$levels = context_helper::get_all_levels();
|
|
foreach ($levels as $level => $classname) {
|
|
$name = context_helper::get_level_name($level);
|
|
$this->assertNotEmpty($name);
|
|
}
|
|
|
|
|
|
// Test context::instance_by_id(), context_xxx::instance() methods.
|
|
|
|
$context = context::instance_by_id($frontpagecontext->id);
|
|
$this->assertSame(CONTEXT_COURSE, $context->contextlevel);
|
|
$this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING));
|
|
try {
|
|
context::instance_by_id(-1);
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
$this->assertInstanceOf('context_system', context_system::instance());
|
|
$this->assertInstanceOf('context_coursecat', context_coursecat::instance($testcategories[0]));
|
|
$this->assertInstanceOf('context_course', context_course::instance($testcourses[0]));
|
|
$this->assertInstanceOf('context_module', context_module::instance($testpages[0]));
|
|
$this->assertInstanceOf('context_block', context_block::instance($testblocks[0]));
|
|
|
|
$this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING));
|
|
$this->assertFalse(context_course::instance(-1, IGNORE_MISSING));
|
|
$this->assertFalse(context_module::instance(-1, IGNORE_MISSING));
|
|
$this->assertFalse(context_block::instance(-1, IGNORE_MISSING));
|
|
try {
|
|
context_coursecat::instance(-1);
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
try {
|
|
context_course::instance(-1);
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
try {
|
|
context_module::instance(-1);
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
try {
|
|
context_block::instance(-1);
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
|
|
// Test $context->get_url(), $context->get_context_name(), $context->get_capabilities() methods.
|
|
|
|
$testcontexts = array();
|
|
$testcontexts[CONTEXT_SYSTEM] = context_system::instance();
|
|
$testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]);
|
|
$testcontexts[CONTEXT_COURSE] = context_course::instance($testcourses[0]);
|
|
$testcontexts[CONTEXT_MODULE] = context_module::instance($testpages[0]);
|
|
$testcontexts[CONTEXT_BLOCK] = context_block::instance($testblocks[0]);
|
|
|
|
foreach ($testcontexts as $context) {
|
|
$name = $context->get_context_name(true, true);
|
|
$this->assertNotEmpty($name);
|
|
|
|
$this->assertInstanceOf('moodle_url', $context->get_url());
|
|
|
|
$caps = $context->get_capabilities();
|
|
$this->assertTrue(is_array($caps));
|
|
foreach ($caps as $cap) {
|
|
$cap = (array)$cap;
|
|
$this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask'));
|
|
}
|
|
}
|
|
unset($testcontexts);
|
|
|
|
// Test $context->get_course_context() method.
|
|
|
|
$this->assertFalse($systemcontext->get_course_context(false));
|
|
try {
|
|
$systemcontext->get_course_context();
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertInstanceOf('coding_exception', $e);
|
|
}
|
|
$context = context_coursecat::instance($testcategories[0]);
|
|
$this->assertFalse($context->get_course_context(false));
|
|
try {
|
|
$context->get_course_context();
|
|
$this->fail('exception expected');
|
|
} catch (moodle_exception $e) {
|
|
$this->assertInstanceOf('coding_exception', $e);
|
|
}
|
|
$this->assertEquals($frontpagecontext, $frontpagecontext->get_course_context(true));
|
|
$this->assertEquals($frontpagecontext, $frontpagepagecontext->get_course_context(true));
|
|
$this->assertEquals($frontpagecontext, $frontpagepageblockcontext->get_course_context(true));
|
|
|
|
|
|
// Test $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() methods.
|
|
|
|
$userid = reset($testusers);
|
|
$usercontext = context_user::instance($userid);
|
|
$this->assertEquals($systemcontext, $usercontext->get_parent_context());
|
|
$this->assertEquals(array($systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts());
|
|
$this->assertEquals(array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts(true));
|
|
|
|
$this->assertEquals(array(), $systemcontext->get_parent_contexts());
|
|
$this->assertEquals(array($systemcontext->id=>$systemcontext), $systemcontext->get_parent_contexts(true));
|
|
$this->assertEquals(array(), $systemcontext->get_parent_context_ids());
|
|
$this->assertEquals(array($systemcontext->id), $systemcontext->get_parent_context_ids(true));
|
|
$this->assertEquals(array(), $systemcontext->get_parent_context_paths());
|
|
$this->assertEquals(array($systemcontext->id => $systemcontext->path), $systemcontext->get_parent_context_paths(true));
|
|
|
|
$this->assertEquals($systemcontext, $frontpagecontext->get_parent_context());
|
|
$this->assertEquals(array($systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts());
|
|
$this->assertEquals(array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts(true));
|
|
$this->assertEquals(array($systemcontext->id), $frontpagecontext->get_parent_context_ids());
|
|
$this->assertEquals(array($frontpagecontext->id, $systemcontext->id), $frontpagecontext->get_parent_context_ids(true));
|
|
$this->assertEquals(array($systemcontext->id => $systemcontext->path), $frontpagecontext->get_parent_context_paths());
|
|
$expected = array($systemcontext->id => $systemcontext->path, $frontpagecontext->id => $frontpagecontext->path);
|
|
$this->assertEquals($expected, $frontpagecontext->get_parent_context_paths(true));
|
|
|
|
$this->assertFalse($systemcontext->get_parent_context());
|
|
$frontpagecontext = context_course::instance($SITE->id);
|
|
$parent = $systemcontext;
|
|
foreach ($testcategories as $catid) {
|
|
$catcontext = context_coursecat::instance($catid);
|
|
$this->assertEquals($parent, $catcontext->get_parent_context());
|
|
$parent = $catcontext;
|
|
}
|
|
$this->assertEquals($frontpagecontext, $frontpagepagecontext->get_parent_context());
|
|
$this->assertEquals($frontpagecontext, $frontpageblockcontext->get_parent_context());
|
|
$this->assertEquals($frontpagepagecontext, $frontpagepageblockcontext->get_parent_context());
|
|
|
|
|
|
// Test $context->get_child_contexts() method.
|
|
|
|
$children = $systemcontext->get_child_contexts();
|
|
$this->resetDebugging();
|
|
$this->assertEquals(count($children)+1, $DB->count_records('context'));
|
|
|
|
$context = context_coursecat::instance($testcategories[3]);
|
|
$children = $context->get_child_contexts();
|
|
$countcats = 0;
|
|
$countcourses = 0;
|
|
$countblocks = 0;
|
|
foreach ($children as $child) {
|
|
if ($child->contextlevel == CONTEXT_COURSECAT) {
|
|
$countcats++;
|
|
}
|
|
if ($child->contextlevel == CONTEXT_COURSE) {
|
|
$countcourses++;
|
|
}
|
|
if ($child->contextlevel == CONTEXT_BLOCK) {
|
|
$countblocks++;
|
|
}
|
|
}
|
|
$this->assertCount(8, $children);
|
|
$this->assertEquals(1, $countcats);
|
|
$this->assertEquals(6, $countcourses);
|
|
$this->assertEquals(1, $countblocks);
|
|
|
|
$context = context_course::instance($testcourses[2]);
|
|
$children = $context->get_child_contexts();
|
|
|
|
$context = context_module::instance($testpages[3]);
|
|
$children = $context->get_child_contexts();
|
|
$this->assertCount(1, $children);
|
|
|
|
$context = context_block::instance($testblocks[1]);
|
|
$children = $context->get_child_contexts();
|
|
$this->assertCount(0, $children);
|
|
|
|
unset($children);
|
|
unset($countcats);
|
|
unset($countcourses);
|
|
unset($countblocks);
|
|
|
|
|
|
// Test context_helper::reset_caches() method.
|
|
|
|
context_helper::reset_caches();
|
|
$this->assertEquals(0, context_inspection::check_context_cache_size());
|
|
context_course::instance($SITE->id);
|
|
$this->assertEquals(1, context_inspection::check_context_cache_size());
|
|
|
|
|
|
// Test context preloading.
|
|
|
|
context_helper::reset_caches();
|
|
$sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')."
|
|
FROM {context} c
|
|
WHERE c.contextlevel <> ".CONTEXT_SYSTEM;
|
|
$records = $DB->get_records_sql($sql);
|
|
$firstrecord = reset($records);
|
|
$columns = context_helper::get_preload_record_columns('c');
|
|
$firstrecord = (array)$firstrecord;
|
|
$this->assertSame(array_keys($firstrecord), array_values($columns));
|
|
context_helper::reset_caches();
|
|
foreach ($records as $record) {
|
|
context_helper::preload_from_record($record);
|
|
$this->assertEquals(new stdClass(), $record);
|
|
}
|
|
$this->assertEquals(count($records), context_inspection::check_context_cache_size());
|
|
unset($records);
|
|
unset($columns);
|
|
|
|
context_helper::reset_caches();
|
|
context_helper::preload_course($SITE->id);
|
|
$numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
|
|
$this->assertEquals(3 + $numfrontpagemodules,
|
|
context_inspection::check_context_cache_size()); // Depends on number of default blocks.
|
|
|
|
// Test assign_capability(), unassign_capability() functions.
|
|
|
|
$rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
|
|
$this->assertFalse($rc);
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id);
|
|
$rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
|
|
$this->assertEquals(CAP_ALLOW, $rc->permission);
|
|
assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id);
|
|
$rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
|
|
$this->assertEquals(CAP_ALLOW, $rc->permission);
|
|
assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true);
|
|
$rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
|
|
$this->assertEquals(CAP_PREVENT, $rc->permission);
|
|
|
|
assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext);
|
|
$rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
|
|
$this->assertFalse($rc);
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext);
|
|
unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true);
|
|
$rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups'));
|
|
$this->assertFalse($rc);
|
|
unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true);
|
|
unset($rc);
|
|
|
|
accesslib_clear_all_caches_for_unit_testing(); // Must be done after assign_capability().
|
|
|
|
|
|
// Test role_assign(), role_unassign(), role_unassign_all() functions.
|
|
|
|
$context = context_course::instance($testcourses[1]);
|
|
$this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
|
|
role_assign($allroles['teacher'], $testusers[1], $context->id);
|
|
role_assign($allroles['teacher'], $testusers[2], $context->id);
|
|
role_assign($allroles['manager'], $testusers[1], $context->id);
|
|
$this->assertEquals(3, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
|
|
role_unassign($allroles['teacher'], $testusers[1], $context->id);
|
|
$this->assertEquals(2, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
|
|
role_unassign_all(array('contextid'=>$context->id));
|
|
$this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id)));
|
|
unset($context);
|
|
|
|
accesslib_clear_all_caches_for_unit_testing(); // Just in case.
|
|
|
|
|
|
// Test has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends functions.
|
|
|
|
$adminid = get_admin()->id;
|
|
$guestid = $CFG->siteguest;
|
|
|
|
// Enrol some users into some courses.
|
|
$course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST);
|
|
$course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST);
|
|
$cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id');
|
|
$cm1 = reset($cms);
|
|
$blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id');
|
|
$block1 = reset($blocks);
|
|
$instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id));
|
|
$instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id));
|
|
for ($i=0; $i<9; $i++) {
|
|
$manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']);
|
|
}
|
|
$manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']);
|
|
$manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']);
|
|
|
|
for ($i=10; $i<15; $i++) {
|
|
$manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']);
|
|
}
|
|
$manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']);
|
|
|
|
// Add tons of role assignments - the more the better.
|
|
role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2]));
|
|
role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1]));
|
|
role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id));
|
|
role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id));
|
|
role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id));
|
|
role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id));
|
|
role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id));
|
|
role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id));
|
|
|
|
role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id));
|
|
role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id));
|
|
|
|
// Add tons of overrides - the more the better.
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true);
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true);
|
|
assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true);
|
|
assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true);
|
|
assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true);
|
|
|
|
assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true);
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true);
|
|
assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true);
|
|
assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true);
|
|
assign_capability('mod/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
|
|
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true);
|
|
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true);
|
|
assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true);
|
|
assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true);
|
|
|
|
assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true);
|
|
|
|
// Prepare for prohibit test.
|
|
role_assign($allroles['editingteacher'], $testusers[19], context_system::instance());
|
|
role_assign($allroles['teacher'], $testusers[19], context_course::instance($testcourses[17]));
|
|
role_assign($allroles['editingteacher'], $testusers[19], context_course::instance($testcourses[17]));
|
|
assign_capability('moodle/course:update', CAP_PROHIBIT, $allroles['teacher'], context_course::instance($testcourses[17]), true);
|
|
|
|
accesslib_clear_all_caches_for_unit_testing(); /// Must be done after assign_capability().
|
|
|
|
// Extra tests for guests and not-logged-in users because they can not be verified by cross checking
|
|
// with get_users_by_capability() where they are ignored.
|
|
$this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid));
|
|
$this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid));
|
|
$this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid));
|
|
$this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid));
|
|
|
|
$this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0));
|
|
$this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0));
|
|
$this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0));
|
|
$this->assertFalse(has_capability('mod/page:view', $systemcontext, 0));
|
|
|
|
$this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11]));
|
|
$this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11]));
|
|
$this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11]));
|
|
$this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11]));
|
|
|
|
$this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9]));
|
|
$this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9]));
|
|
$this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9]));
|
|
|
|
// Test prohibits.
|
|
$this->assertTrue(has_capability('moodle/course:update', context_system::instance(), $testusers[19]));
|
|
$ids = get_users_by_capability(context_system::instance(), 'moodle/course:update', 'u.id');
|
|
$this->assertArrayHasKey($testusers[19], $ids);
|
|
$this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[17]), $testusers[19]));
|
|
$ids = get_users_by_capability(context_course::instance($testcourses[17]), 'moodle/course:update', 'u.id');
|
|
$this->assertArrayNotHasKey($testusers[19], $ids);
|
|
|
|
// Test the list of enrolled users.
|
|
$coursecontext = context_course::instance($course1->id);
|
|
$enrolled = get_enrolled_users($coursecontext);
|
|
$this->assertCount(10, $enrolled);
|
|
for ($i=0; $i<10; $i++) {
|
|
$this->assertTrue(isset($enrolled[$testusers[$i]]));
|
|
}
|
|
$enrolled = get_enrolled_users($coursecontext, 'moodle/course:update');
|
|
$this->assertCount(1, $enrolled);
|
|
$this->assertTrue(isset($enrolled[$testusers[9]]));
|
|
unset($enrolled);
|
|
|
|
// Role switching.
|
|
$userid = $testusers[9];
|
|
$USER = $DB->get_record('user', array('id'=>$userid));
|
|
load_all_capabilities();
|
|
$coursecontext = context_course::instance($course1->id);
|
|
$this->assertTrue(has_capability('moodle/course:update', $coursecontext));
|
|
$this->assertFalse(is_role_switched($course1->id));
|
|
role_switch($allroles['student'], $coursecontext);
|
|
$this->assertTrue(is_role_switched($course1->id));
|
|
$this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
|
|
$this->assertFalse(has_capability('moodle/course:update', $coursecontext));
|
|
reload_all_capabilities();
|
|
$this->assertFalse(has_capability('moodle/course:update', $coursecontext));
|
|
role_switch(0, $coursecontext);
|
|
$this->assertTrue(has_capability('moodle/course:update', $coursecontext));
|
|
$userid = $adminid;
|
|
$USER = $DB->get_record('user', array('id'=>$userid));
|
|
load_all_capabilities();
|
|
$coursecontext = context_course::instance($course1->id);
|
|
$blockcontext = context_block::instance($block1->id);
|
|
$this->assertTrue(has_capability('moodle/course:update', $blockcontext));
|
|
role_switch($allroles['student'], $coursecontext);
|
|
$this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]);
|
|
$this->assertFalse(has_capability('moodle/course:update', $blockcontext));
|
|
reload_all_capabilities();
|
|
$this->assertFalse(has_capability('moodle/course:update', $blockcontext));
|
|
load_all_capabilities();
|
|
$this->assertTrue(has_capability('moodle/course:update', $blockcontext));
|
|
|
|
// Temp course role for enrol.
|
|
$DB->delete_records('cache_flags', array()); // This prevents problem with dirty contexts immediately resetting the temp role - this is a known problem...
|
|
$userid = $testusers[5];
|
|
$roleid = $allroles['editingteacher'];
|
|
$USER = $DB->get_record('user', array('id'=>$userid));
|
|
load_all_capabilities();
|
|
$coursecontext = context_course::instance($course1->id);
|
|
$this->assertFalse(has_capability('moodle/course:update', $coursecontext));
|
|
$this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid]));
|
|
load_temp_course_role($coursecontext, $roleid);
|
|
$this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid);
|
|
$this->assertTrue(has_capability('moodle/course:update', $coursecontext));
|
|
remove_temp_course_roles($coursecontext);
|
|
$this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
|
|
load_temp_course_role($coursecontext, $roleid);
|
|
reload_all_capabilities();
|
|
$this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid));
|
|
$USER = new stdClass();
|
|
$USER->id = 0;
|
|
|
|
// Now cross check has_capability() with get_users_by_capability(), each using different code paths,
|
|
// they have to be kept in sync, usually only one of them breaks, so we know when something is wrong,
|
|
// at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users).
|
|
$contexts = $DB->get_records('context', array(), 'id');
|
|
$contexts = array_values($contexts);
|
|
$capabilities = $DB->get_records('capabilities', array(), 'id');
|
|
$capabilities = array_values($capabilities);
|
|
$roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']);
|
|
$userids = array_values($testusers);
|
|
$userids[] = get_admin()->id;
|
|
|
|
if (!PHPUNIT_LONGTEST) {
|
|
$contexts = array_slice($contexts, 0, 10);
|
|
$capabilities = array_slice($capabilities, 0, 5);
|
|
$userids = array_slice($userids, 0, 5);
|
|
}
|
|
|
|
foreach ($userids as $userid) { // No guest or deleted.
|
|
// Each user gets 0-10 random roles.
|
|
$rcount = rand(0, 10);
|
|
for ($j=0; $j<$rcount; $j++) {
|
|
$roleid = $roles[rand(0, count($roles)-1)];
|
|
$contextid = $contexts[rand(0, count($contexts)-1)]->id;
|
|
role_assign($roleid, $userid, $contextid);
|
|
}
|
|
}
|
|
|
|
$permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT);
|
|
$maxoverrides = count($contexts)*10;
|
|
for ($j=0; $j<$maxoverrides; $j++) {
|
|
$roleid = $roles[rand(0, count($roles)-1)];
|
|
$contextid = $contexts[rand(0, count($contexts)-1)]->id;
|
|
$permission = $permissions[rand(0, count($permissions)-1)];
|
|
$capname = $capabilities[rand(0, count($capabilities)-1)]->name;
|
|
assign_capability($capname, $permission, $roleid, $contextid, true);
|
|
}
|
|
unset($permissions);
|
|
unset($roles);
|
|
|
|
accesslib_clear_all_caches_for_unit_testing(); // must be done after assign_capability().
|
|
|
|
// Test time - let's set up some real user, just in case the logic for USER affects the others...
|
|
$USER = $DB->get_record('user', array('id'=>$testusers[3]));
|
|
load_all_capabilities();
|
|
|
|
$userids[] = $CFG->siteguest;
|
|
$userids[] = 0; // Not-logged-in user.
|
|
$userids[] = -1; // Non-existent user.
|
|
|
|
foreach ($contexts as $crecord) {
|
|
$context = context::instance_by_id($crecord->id);
|
|
if ($coursecontext = $context->get_course_context(false)) {
|
|
$enrolled = get_enrolled_users($context);
|
|
} else {
|
|
$enrolled = array();
|
|
}
|
|
foreach ($capabilities as $cap) {
|
|
$allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username');
|
|
if ($enrolled) {
|
|
$enrolledwithcap = get_enrolled_users($context, $cap->name);
|
|
} else {
|
|
$enrolledwithcap = array();
|
|
}
|
|
foreach ($userids as $userid) {
|
|
if ($userid == 0 or isguestuser($userid)) {
|
|
if ($userid == 0) {
|
|
$CFG->forcelogin = true;
|
|
$this->assertFalse(has_capability($cap->name, $context, $userid));
|
|
unset($CFG->forcelogin);
|
|
}
|
|
if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) {
|
|
$this->assertFalse(has_capability($cap->name, $context, $userid));
|
|
}
|
|
$this->assertFalse(isset($allowed[$userid]));
|
|
} else {
|
|
if (is_siteadmin($userid)) {
|
|
$this->assertTrue(has_capability($cap->name, $context, $userid, true));
|
|
}
|
|
$hascap = has_capability($cap->name, $context, $userid, false);
|
|
$this->assertSame($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
|
|
if (isset($enrolled[$userid])) {
|
|
$this->assertSame(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Back to nobody.
|
|
$USER = new stdClass();
|
|
$USER->id = 0;
|
|
unset($contexts);
|
|
unset($userids);
|
|
unset($capabilities);
|
|
|
|
// Now let's do all the remaining tests that break our carefully prepared fake site.
|
|
|
|
|
|
// Test $context->mark_dirty() method.
|
|
|
|
$DB->delete_records('cache_flags', array());
|
|
accesslib_clear_all_caches(false);
|
|
$systemcontext->mark_dirty();
|
|
$dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
|
|
$this->assertTrue(isset($dirty[$systemcontext->path]));
|
|
$this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path]));
|
|
|
|
|
|
// Test $context->reload_if_dirty() method.
|
|
|
|
$DB->delete_records('cache_flags', array());
|
|
accesslib_clear_all_caches(false);
|
|
load_all_capabilities();
|
|
$context = context_course::instance($testcourses[2]);
|
|
$page = $DB->get_record('page', array('course'=>$testcourses[2]));
|
|
$pagecm = get_coursemodule_from_instance('page', $page->id);
|
|
$pagecontext = context_module::instance($pagecm->id);
|
|
|
|
$context->mark_dirty();
|
|
$this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
|
|
$USER->access['test'] = true;
|
|
$context->reload_if_dirty();
|
|
$this->assertFalse(isset($USER->access['test']));
|
|
|
|
$context->mark_dirty();
|
|
$this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
|
|
$USER->access['test'] = true;
|
|
$pagecontext->reload_if_dirty();
|
|
$this->assertFalse(isset($USER->access['test']));
|
|
|
|
|
|
// Test context_helper::build_all_paths() method.
|
|
|
|
$oldcontexts = $DB->get_records('context', array(), 'id');
|
|
$DB->set_field_select('context', 'path', null, "contextlevel <> ".CONTEXT_SYSTEM);
|
|
$DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM);
|
|
context_helper::build_all_paths();
|
|
$newcontexts = $DB->get_records('context', array(), 'id');
|
|
$this->assertEquals($oldcontexts, $newcontexts);
|
|
unset($oldcontexts);
|
|
unset($newcontexts);
|
|
|
|
|
|
// Test $context->reset_paths() method.
|
|
|
|
$context = context_course::instance($testcourses[2]);
|
|
$children = $context->get_child_contexts();
|
|
$context->reset_paths(false);
|
|
$this->assertNull($DB->get_field('context', 'path', array('id'=>$context->id)));
|
|
$this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$context->id)));
|
|
foreach ($children as $child) {
|
|
$this->assertNull($DB->get_field('context', 'path', array('id'=>$child->id)));
|
|
$this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$child->id)));
|
|
}
|
|
$this->assertEquals(count($children)+1, $DB->count_records('context', array('depth'=>0)));
|
|
$this->assertEquals(count($children)+1, $DB->count_records('context', array('path'=>null)));
|
|
|
|
$context = context_course::instance($testcourses[2]);
|
|
$context->reset_paths(true);
|
|
$context = context_course::instance($testcourses[2]);
|
|
$this->assertSame($context->path, $DB->get_field('context', 'path', array('id'=>$context->id)));
|
|
$this->assertSame($context->depth, $DB->get_field('context', 'depth', array('id'=>$context->id)));
|
|
$this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
|
|
$this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
|
|
|
|
|
|
// Test $context->update_moved() method.
|
|
|
|
accesslib_clear_all_caches(false);
|
|
$DB->delete_records('cache_flags', array());
|
|
$course = $DB->get_record('course', array('id'=>$testcourses[0]));
|
|
$context = context_course::instance($course->id);
|
|
$oldpath = $context->path;
|
|
$miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
|
|
$categorycontext = context_coursecat::instance($miscid);
|
|
$course->category = $miscid;
|
|
$DB->update_record('course', $course);
|
|
$context->update_moved($categorycontext);
|
|
|
|
$context = context_course::instance($course->id);
|
|
$this->assertEquals($categorycontext, $context->get_parent_context());
|
|
$dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
|
|
$this->assertFalse(isset($dirty[$oldpath]));
|
|
$this->assertTrue(isset($dirty[$context->path]));
|
|
|
|
|
|
// Test $context->delete_content() method.
|
|
|
|
context_helper::reset_caches();
|
|
$context = context_module::instance($testpages[3]);
|
|
$this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
|
|
$this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
|
|
$context->delete_content();
|
|
$this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
|
|
$this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
|
|
|
|
|
|
// Test $context->delete() method.
|
|
|
|
context_helper::reset_caches();
|
|
$context = context_module::instance($testpages[4]);
|
|
$this->assertTrue($DB->record_exists('context', array('id'=>$context->id)));
|
|
$this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
|
|
$bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id));
|
|
$bicontext = context_block::instance($bi->id);
|
|
$DB->delete_records('cache_flags', array());
|
|
$context->delete(); // Should delete also linked blocks.
|
|
$dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
|
|
$this->assertFalse(isset($dirty[$context->path]));
|
|
$this->assertFalse($DB->record_exists('context', array('id'=>$context->id)));
|
|
$this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id)));
|
|
$this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4])));
|
|
$this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id)));
|
|
$this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id)));
|
|
context_module::instance($testpages[4]);
|
|
|
|
|
|
// Test context_helper::delete_instance() method.
|
|
|
|
context_helper::reset_caches();
|
|
$lastcourse = array_pop($testcourses);
|
|
$this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
|
|
$coursecontext = context_course::instance($lastcourse);
|
|
$this->assertEquals(1, context_inspection::check_context_cache_size());
|
|
$this->assertNotEquals(CONTEXT_COURSE, $coursecontext->instanceid);
|
|
$DB->delete_records('cache_flags', array());
|
|
context_helper::delete_instance(CONTEXT_COURSE, $lastcourse);
|
|
$dirty = get_cache_flags('accesslib/dirtycontexts', time()-2);
|
|
$this->assertFalse(isset($dirty[$coursecontext->path]));
|
|
$this->assertEquals(0, context_inspection::check_context_cache_size());
|
|
$this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse)));
|
|
context_course::instance($lastcourse);
|
|
|
|
|
|
// Test context_helper::create_instances() method.
|
|
|
|
$prevcount = $DB->count_records('context');
|
|
$DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
|
|
context_helper::create_instances(null, true);
|
|
$this->assertSame($DB->count_records('context'), $prevcount);
|
|
$this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
|
|
$this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
|
|
|
|
$DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK));
|
|
$DB->delete_records('block_instances', array());
|
|
$prevcount = $DB->count_records('context');
|
|
$DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM);
|
|
context_helper::create_instances(null, true);
|
|
$this->assertSame($prevcount, $DB->count_records('context'));
|
|
$this->assertEquals(0, $DB->count_records('context', array('depth'=>0)));
|
|
$this->assertEquals(0, $DB->count_records('context', array('path'=>null)));
|
|
|
|
// Test context_helper::cleanup_instances() method.
|
|
|
|
$lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}");
|
|
$DB->delete_records('course', array('id'=>$lastcourse));
|
|
$lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}");
|
|
$DB->delete_records('course_categories', array('id'=>$lastcategory));
|
|
$lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0");
|
|
$DB->delete_records('user', array('id'=>$lastuser));
|
|
$DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id));
|
|
$DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid));
|
|
context_helper::cleanup_instances();
|
|
$count = 1; // System.
|
|
$count += $DB->count_records('user', array('deleted'=>0));
|
|
$count += $DB->count_records('course_categories');
|
|
$count += $DB->count_records('course');
|
|
$count += $DB->count_records('course_modules');
|
|
$count += $DB->count_records('block_instances');
|
|
$this->assertEquals($count, $DB->count_records('context'));
|
|
|
|
|
|
// Test context cache size restrictions.
|
|
|
|
$testusers= array();
|
|
for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
|
|
$user = $generator->create_user();
|
|
$testusers[$i] = $user->id;
|
|
}
|
|
context_helper::create_instances(null, true);
|
|
context_helper::reset_caches();
|
|
for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) {
|
|
context_user::instance($testusers[$i]);
|
|
if ($i == CONTEXT_CACHE_MAX_SIZE - 1) {
|
|
$this->assertEquals(CONTEXT_CACHE_MAX_SIZE, context_inspection::check_context_cache_size());
|
|
} else if ($i == CONTEXT_CACHE_MAX_SIZE) {
|
|
// Once the limit is reached roughly 1/3 of records should be removed from cache.
|
|
$this->assertEquals((int)ceil(CONTEXT_CACHE_MAX_SIZE * (2 / 3) + 101),
|
|
context_inspection::check_context_cache_size());
|
|
}
|
|
}
|
|
// We keep the first 100 cached.
|
|
$prevsize = context_inspection::check_context_cache_size();
|
|
for ($i=0; $i<100; $i++) {
|
|
context_user::instance($testusers[$i]);
|
|
$this->assertEquals($prevsize, context_inspection::check_context_cache_size());
|
|
}
|
|
context_user::instance($testusers[102]);
|
|
$this->assertEquals($prevsize + 1, context_inspection::check_context_cache_size());
|
|
unset($testusers);
|
|
|
|
|
|
|
|
// Test basic test of legacy functions.
|
|
// Note: watch out, the fake site might be pretty borked already.
|
|
|
|
$this->assertEquals(get_system_context(), context_system::instance());
|
|
$this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER);
|
|
|
|
foreach ($DB->get_records('context') as $contextid => $record) {
|
|
$context = context::instance_by_id($contextid);
|
|
$this->assertEquals($context, get_context_instance($record->contextlevel, $record->instanceid));
|
|
$this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER);
|
|
}
|
|
|
|
// Make sure a debugging is thrown.
|
|
get_context_instance($record->contextlevel, $record->instanceid);
|
|
$this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER);
|
|
get_system_context();
|
|
$this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER);
|
|
}
|
|
|
|
/**
|
|
* Helper that verifies a list of capabilities, as returned by
|
|
* $context->get_capabilities() contains certain capabilities.
|
|
*
|
|
* @param array $expected a list of capability names
|
|
* @param array $actual a list of capability info from $context->get_capabilities().
|
|
*/
|
|
protected function assert_capability_list_contains($expected, $actual) {
|
|
$actualnames = [];
|
|
foreach ($actual as $cap) {
|
|
$actualnames[] = $cap->name;
|
|
}
|
|
// Verify each expected element exists.
|
|
foreach ($expected as $key => $value) {
|
|
$this->assertContains($value, $actualnames);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test that context_system::get_capabilities returns capabilities relevant to all modules.
|
|
*
|
|
* @covers \context_system::get_capabilities
|
|
*/
|
|
public function test_context_module_caps_returned_by_get_capabilities_in_sys_context(): void {
|
|
$actual = context_system::instance()->get_capabilities();
|
|
|
|
// Just test a few representative capabilities.
|
|
$expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
|
|
'repository/upload:view', 'atto/recordrtc:recordaudio'];
|
|
|
|
$this->assert_capability_list_contains($expectedcapabilities, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that context_coursecat::get_capabilities returns capabilities relevant to all modules.
|
|
*
|
|
* @covers \context_coursecat::get_capabilities
|
|
*/
|
|
public function test_context_module_caps_returned_by_get_capabilities_in_course_cat_context(): void {
|
|
$this->resetAfterTest(true);
|
|
$generator = $this->getDataGenerator();
|
|
$cat = $generator->create_category();
|
|
|
|
$actual = context_coursecat::instance($cat->id)->get_capabilities();
|
|
|
|
// Just test a few representative capabilities.
|
|
$expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
|
|
'repository/upload:view', 'atto/recordrtc:recordaudio'];
|
|
|
|
$this->assert_capability_list_contains($expectedcapabilities, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that context_course::get_capabilities returns capabilities relevant to all modules.
|
|
*
|
|
* @covers \context_course::get_capabilities
|
|
*/
|
|
public function test_context_module_caps_returned_by_get_capabilities_in_course_context(): void {
|
|
$this->resetAfterTest(true);
|
|
$generator = $this->getDataGenerator();
|
|
$cat = $generator->create_category();
|
|
$course = $generator->create_course(['category' => $cat->id]);
|
|
|
|
$actual = context_course::instance($course->id)->get_capabilities();
|
|
|
|
// Just test a few representative capabilities.
|
|
$expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
|
|
'repository/upload:view', 'atto/recordrtc:recordaudio'];
|
|
|
|
$this->assert_capability_list_contains($expectedcapabilities, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that context_module::get_capabilities returns capabilities relevant to all modules.
|
|
*
|
|
* @covers \context_module::get_capabilities
|
|
*/
|
|
public function test_context_module_caps_returned_by_get_capabilities_mod_context(): void {
|
|
$this->resetAfterTest(true);
|
|
$generator = $this->getDataGenerator();
|
|
$cat = $generator->create_category();
|
|
$course = $generator->create_course(['category' => $cat->id]);
|
|
$page = $generator->create_module('page', ['course' => $course->id]);
|
|
|
|
$actual = context_module::instance($page->cmid)->get_capabilities();
|
|
|
|
// Just test a few representative capabilities.
|
|
$expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames',
|
|
'repository/upload:view', 'atto/recordrtc:recordaudio'];
|
|
|
|
$this->assert_capability_list_contains($expectedcapabilities, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that {@see context_block::get_capabilities} returns capabilities relevant to blocks
|
|
*
|
|
* @covers \context_block::get_capabilities
|
|
*/
|
|
public function test_context_block_caps_returned_by_get_capabilities_block_context(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$block = $this->getDataGenerator()->create_block('online_users', [
|
|
'parentcontextid' => context_course::instance($course->id)->id,
|
|
]);
|
|
|
|
$capabilities = context_block::instance($block->id)->get_capabilities();
|
|
|
|
// Just test a few representative capabilities.
|
|
$expected = ['block/online_users:addinstance', 'moodle/block:edit', 'moodle/block:view'];
|
|
$this->assert_capability_list_contains($expected, $capabilities);
|
|
|
|
// Now test with different sorting.
|
|
$capabilitiesbyname = context_block::instance($block->id)->get_capabilities('riskbitmask');
|
|
|
|
$capabilitynames = array_column($capabilities, 'name');
|
|
$capabilitynamesordered = array_column($capabilitiesbyname, 'name');
|
|
|
|
// Each array should contain the same data, ordered differently.
|
|
$this->assertEqualsCanonicalizing($capabilitynames, $capabilitynamesordered);
|
|
$this->assertNotSame($capabilitynames, $capabilitynamesordered);
|
|
}
|
|
|
|
/**
|
|
* Test that {@see context_user::get_capabilities} returns capabilities relevant to users
|
|
*
|
|
* @covers \context_user::get_capabilities
|
|
*/
|
|
public function test_context_user_caps_returned_by_get_capabilities_user_context(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$capabilities = context_user::instance($user->id)->get_capabilities();
|
|
|
|
// Just test a few representative capabilities.
|
|
$expected = ['moodle/user:editmessageprofile', 'moodle/user:editprofile', 'moodle/user:viewalldetails'];
|
|
$this->assert_capability_list_contains($expected, $capabilities);
|
|
|
|
// Now test with different sorting.
|
|
$capabilitiesbyname = context_user::instance($user->id)->get_capabilities('name');
|
|
|
|
$capabilitynames = array_column($capabilities, 'name');
|
|
$capabilitynamesordered = array_column($capabilitiesbyname, 'name');
|
|
|
|
// Each array should contain the same data, ordered differently.
|
|
$this->assertEqualsCanonicalizing($capabilitynames, $capabilitynamesordered);
|
|
$this->assertNotSame($capabilitynames, $capabilitynamesordered);
|
|
}
|
|
|
|
/**
|
|
* Test updating of role capabilities during upgrade
|
|
*
|
|
* @covers ::update_capabilities
|
|
* @covers ::update_capabilities
|
|
*/
|
|
public function test_update_capabilities(): void {
|
|
global $DB, $SITE;
|
|
|
|
$this->resetAfterTest(true);
|
|
|
|
$froncontext = context_course::instance($SITE->id);
|
|
$student = $DB->get_record('role', array('shortname'=>'student'));
|
|
$teacher = $DB->get_record('role', array('shortname'=>'teacher'));
|
|
|
|
$existingcaps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
|
|
|
|
$this->assertFalse(isset($existingcaps['moodle/site:restore'])); // Moved to new 'moodle/restore:restorecourse'.
|
|
$this->assertTrue(isset($existingcaps['moodle/restore:restorecourse'])); // New cap from 'moodle/site:restore'.
|
|
$this->assertTrue(isset($existingcaps['moodle/site:sendmessage'])); // New capability.
|
|
$this->assertTrue(isset($existingcaps['moodle/backup:backupcourse']));
|
|
$this->assertTrue(isset($existingcaps['moodle/backup:backupsection'])); // Cloned from 'moodle/backup:backupcourse'.
|
|
$this->assertTrue(isset($existingcaps['moodle/site:approvecourse'])); // Updated bitmask.
|
|
$this->assertTrue(isset($existingcaps['moodle/course:manageactivities']));
|
|
$this->assertTrue(isset($existingcaps['mod/page:addinstance'])); // Cloned from core 'moodle/course:manageactivities'.
|
|
|
|
// Fake state before upgrade.
|
|
$DB->set_field('capabilities', 'name', 'moodle/site:restore', array('name'=>'moodle/restore:restorecourse'));
|
|
$DB->set_field('role_capabilities', 'capability', 'moodle/site:restore', array('capability'=>'moodle/restore:restorecourse'));
|
|
assign_capability('moodle/site:restore', CAP_PROHIBIT, $teacher->id, $froncontext->id, true);
|
|
$perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/site:restore', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
|
|
|
|
$DB->delete_records('role_capabilities', array('capability'=>'moodle/site:sendmessage'));
|
|
$DB->delete_records('capabilities', array('name'=>'moodle/site:sendmessage'));
|
|
|
|
$DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupsection'));
|
|
$DB->delete_records('capabilities', array('name'=>'moodle/backup:backupsection'));
|
|
assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $froncontext->id, true);
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $teacher->id, $froncontext->id, true);
|
|
|
|
$DB->set_field('capabilities', 'riskbitmask', 0, array('name'=>'moodle/site:approvecourse'));
|
|
|
|
$DB->delete_records('role_capabilities', array('capability'=>'mod/page:addinstance'));
|
|
$DB->delete_records('capabilities', array('name'=>'mod/page:addinstance'));
|
|
assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $student->id, $froncontext->id, true);
|
|
assign_capability('moodle/course:manageactivities', CAP_ALLOW, $teacher->id, $froncontext->id, true);
|
|
|
|
// Execute core.
|
|
update_capabilities('moodle');
|
|
|
|
// Only core should be upgraded.
|
|
$caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
|
|
|
|
$this->assertFalse(isset($existingcaps['moodle/site:restore']));
|
|
$this->assertTrue(isset($caps['moodle/restore:restorecourse']));
|
|
$this->assertEquals($existingcaps['moodle/restore:restorecourse'], $caps['moodle/restore:restorecourse']);
|
|
$perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/restore:restorecourse', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission'));
|
|
$this->assertEquals($perms1, $perms2);
|
|
|
|
$this->assertTrue(isset($caps['moodle/site:sendmessage']));
|
|
$this->assertEquals($existingcaps['moodle/site:sendmessage'], $caps['moodle/site:sendmessage']);
|
|
|
|
$this->assertTrue(isset($caps['moodle/backup:backupsection']));
|
|
$this->assertEquals($existingcaps['moodle/backup:backupsection'], $caps['moodle/backup:backupsection']);
|
|
$roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/backup:backupcourse', 'moodle/backup:backupsection'));
|
|
foreach ($roles as $role) {
|
|
$perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
|
|
$perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupsection', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
|
|
$this->assertEquals($perms1, $perms2);
|
|
}
|
|
|
|
$this->assertTrue(isset($caps['moodle/site:approvecourse']));
|
|
$this->assertEquals($existingcaps['moodle/site:approvecourse'], $caps['moodle/site:approvecourse']);
|
|
|
|
$this->assertFalse(isset($caps['mod/page:addinstance']));
|
|
|
|
// Execute plugin.
|
|
update_capabilities('mod_page');
|
|
$caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask');
|
|
$this->assertTrue(isset($caps['mod/page:addinstance']));
|
|
$roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/course:manageactivities', 'mod/page:addinstance'));
|
|
foreach ($roles as $role) {
|
|
$perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/course:manageactivities', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
|
|
$perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'mod/page:addinstance', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission'));
|
|
}
|
|
$this->assertEquals($perms1, $perms2);
|
|
}
|
|
|
|
/**
|
|
* Checks install performance in update_capabilities.
|
|
*
|
|
* @covers ::update_capabilities()
|
|
*/
|
|
public function test_update_capabilities_install_performance(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Get rid of all the capabilities for forum.
|
|
$testmodule = 'forum';
|
|
$DB->delete_records_select('capabilities', 'name LIKE ?', ['mod/' . $testmodule . ':%']);
|
|
|
|
$beforeq = $DB->perf_get_queries();
|
|
update_capabilities('mod_' . $testmodule);
|
|
$afterq = $DB->perf_get_queries();
|
|
|
|
// In my testing there are currently 237 queries; there were 373 before a performance
|
|
// fix. This test confirms performance doesn't degrade to near the previous level.
|
|
$this->assertLessThan(300, $afterq - $beforeq);
|
|
}
|
|
|
|
/**
|
|
* Checks install performance in update_capabilities when a new capability is cloned.
|
|
*
|
|
* This only has impact if there are a significant number of overrides of the existing
|
|
* capability.
|
|
*
|
|
* @covers ::update_capabilities()
|
|
*/
|
|
public function test_update_capabilities_clone_performance(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create a bunch of activities in a course. In each one, override so manager doesn't have
|
|
// moodle/course:manageactivities.
|
|
$generator = $this->getDataGenerator();
|
|
$course = $generator->create_course();
|
|
$roleid = $DB->get_field('role', 'id', ['shortname' => 'manager']);
|
|
for ($i = 0; $i < 100; $i++) {
|
|
$page = $generator->create_module('page', ['course' => $course->id]);
|
|
$contextid = context_module::instance($page->cmid)->id;
|
|
assign_capability('moodle/course:manageactivities', CAP_PREVENT, $roleid, $contextid);
|
|
}
|
|
|
|
// Get rid of one of the capabilities for forum, which clones moodle/course:manageactivities.
|
|
$DB->delete_records('capabilities', ['name' => 'mod/forum:addinstance']);
|
|
|
|
// Clear the context cache to simulate a realistic situation where we don't already have
|
|
// all those contexts in the cache.
|
|
accesslib_clear_all_caches_for_unit_testing();
|
|
|
|
$beforeq = $DB->perf_get_queries();
|
|
update_capabilities('mod_forum');
|
|
$afterq = $DB->perf_get_queries();
|
|
|
|
// In my testing there are currently 214 queries after performance was improved for cloning,
|
|
// compared to 414 before. This test confirms performance doesn't degrade to near the
|
|
// previous level.
|
|
$this->assertLessThan(300, $afterq - $beforeq);
|
|
}
|
|
|
|
/**
|
|
* Tests update_capabilities when a capability is cloned, but there are existing settings
|
|
* for that capability.
|
|
*
|
|
* Under normal circumstances this shouldn't happen as it is only used for new capabilities,
|
|
* but it's possible there could be incorrect data in database.)
|
|
*
|
|
* @covers ::update_capabilities()
|
|
*/
|
|
public function test_update_capabilities_clone_existing(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create activities in a course. In each one, override so manager doesn't have
|
|
// moodle/course:manageactivities. In one of them, also override mod/forum:addinstance
|
|
// to something different.
|
|
$generator = $this->getDataGenerator();
|
|
$course = $generator->create_course();
|
|
$roleid = $DB->get_field('role', 'id', ['shortname' => 'manager']);
|
|
$page1 = $generator->create_module('page', ['course' => $course->id]);
|
|
$context1 = context_module::instance($page1->cmid);
|
|
assign_capability('moodle/course:manageactivities', CAP_PREVENT, $roleid, $context1->id);
|
|
$page2 = $generator->create_module('page', ['course' => $course->id]);
|
|
$context2 = context_module::instance($page2->cmid);
|
|
assign_capability('moodle/course:manageactivities', CAP_PREVENT, $roleid, $context2->id);
|
|
assign_capability('mod/forum:addinstance', CAP_PROHIBIT, $roleid, $context2->id);
|
|
|
|
// Get rid of one of the capabilities for forum, which clones moodle/course:manageactivities.
|
|
$DB->delete_records('capabilities', ['name' => 'mod/forum:addinstance']);
|
|
|
|
// Reinstall the capability.
|
|
update_capabilities('mod_forum');
|
|
|
|
// Check the results: we should duplicate the manageactivities setting (PREVENT).
|
|
$rec1 = $DB->get_record('role_capabilities', ['roleid' => $roleid,
|
|
'contextid' => $context1->id, 'capability' => 'mod/forum:addinstance']);
|
|
$this->assertEquals(CAP_PREVENT, $rec1->permission);
|
|
// The second page, we should overwrite the previous existing permission setting.
|
|
$rec2 = $DB->get_record('role_capabilities', ['roleid' => $roleid,
|
|
'contextid' => $context2->id, 'capability' => 'mod/forum:addinstance']);
|
|
$this->assertEquals(CAP_PREVENT, $rec2->permission);
|
|
}
|
|
|
|
/**
|
|
* Tests reset_role_capabilities function.
|
|
*
|
|
* @covers ::reset_role_capabilities
|
|
*/
|
|
public function test_reset_role_capabilities(): void {
|
|
global $DB;
|
|
$this->resetAfterTest(true);
|
|
$generator = $this->getDataGenerator();
|
|
|
|
// Create test course and user, enrol one in the other.
|
|
$course = $generator->create_course();
|
|
$user = $generator->create_user();
|
|
$roleid = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST);
|
|
$generator->enrol_user($user->id, $course->id, $roleid);
|
|
|
|
// Change student role so it DOES have 'mod/forum:addinstance'.
|
|
$systemcontext = context_system::instance();
|
|
assign_capability('mod/forum:addinstance', CAP_ALLOW, $roleid, $systemcontext->id);
|
|
|
|
// Override course so it does NOT allow students 'mod/forum:viewdiscussion'.
|
|
$coursecontext = context_course::instance($course->id);
|
|
assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $roleid, $coursecontext->id);
|
|
|
|
// Check expected capabilities so far.
|
|
$this->assertTrue(has_capability('mod/forum:addinstance', $coursecontext, $user));
|
|
$this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user));
|
|
|
|
// Oops, allowing student to add forums was a mistake, let's reset the role.
|
|
reset_role_capabilities($roleid);
|
|
|
|
// Check new expected capabilities - role capabilities should have been reset,
|
|
// while the override at course level should remain.
|
|
$this->assertFalse(has_capability('mod/forum:addinstance', $coursecontext, $user));
|
|
$this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user));
|
|
}
|
|
|
|
/**
|
|
* Tests count_role_users function.
|
|
*
|
|
* @covers ::count_role_users
|
|
*/
|
|
public function test_count_role_users(): void {
|
|
global $DB;
|
|
$this->resetAfterTest(true);
|
|
$generator = self::getDataGenerator();
|
|
// Create a course in a category, and some users.
|
|
$category = $generator->create_category();
|
|
$course = $generator->create_course(array('category' => $category->id));
|
|
$user1 = $generator->create_user();
|
|
$user2 = $generator->create_user();
|
|
$user3 = $generator->create_user();
|
|
$user4 = $generator->create_user();
|
|
$user5 = $generator->create_user();
|
|
$roleid1 = $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST);
|
|
$roleid2 = $DB->get_field('role', 'id', array('shortname' => 'coursecreator'), MUST_EXIST);
|
|
// Enrol two users as managers onto the course, and 1 onto the category.
|
|
$generator->enrol_user($user1->id, $course->id, $roleid1);
|
|
$generator->enrol_user($user2->id, $course->id, $roleid1);
|
|
$generator->role_assign($roleid1, $user3->id, context_coursecat::instance($category->id));
|
|
// Enrol 1 user as a coursecreator onto the course, and another onto the category.
|
|
// This is to ensure we do not count users with roles that are not specified.
|
|
$generator->enrol_user($user4->id, $course->id, $roleid2);
|
|
$generator->role_assign($roleid2, $user5->id, context_coursecat::instance($category->id));
|
|
// Check that the correct users are found on the course.
|
|
$this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
|
|
$this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
|
|
// Check for the category.
|
|
$this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), false));
|
|
$this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), true));
|
|
// Have a user with the same role at both the category and course level.
|
|
$generator->role_assign($roleid1, $user1->id, context_coursecat::instance($category->id));
|
|
// The course level checks should remain the same.
|
|
$this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
|
|
$this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
|
|
}
|
|
|
|
/**
|
|
* Test fetching users by capability.
|
|
*
|
|
* @covers ::get_users_by_capability
|
|
*/
|
|
public function test_get_users_by_capability(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$student = $this->getDataGenerator()->create_user();
|
|
$guest = $DB->get_record('user', array('username' => 'guest'));
|
|
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
role_assign($studentrole->id, $student->id, $coursecontext);
|
|
$admin = $DB->get_record('user', array('username' => 'admin'));
|
|
|
|
// Note: Here are used default capabilities, the full test is in permission evaluation below,
|
|
// use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse')));
|
|
|
|
$users = get_users_by_capability($coursecontext, 'moodle/backup:backupcourse');
|
|
|
|
$this->assertTrue(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
$this->assertFalse(array_key_exists($student->id, $users));
|
|
$this->assertFalse(array_key_exists($guest->id, $users));
|
|
|
|
$users = get_users_by_capability($coursecontext, 'moodle/site:approvecourse');
|
|
|
|
$this->assertFalse(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
$this->assertFalse(array_key_exists($student->id, $users));
|
|
$this->assertFalse(array_key_exists($guest->id, $users));
|
|
|
|
// Test role override.
|
|
assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
|
|
|
|
$users = get_users_by_capability($coursecontext, 'moodle/backup:backupcourse');
|
|
|
|
$this->assertFalse(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
$this->assertTrue(array_key_exists($student->id, $users));
|
|
$this->assertFalse(array_key_exists($guest->id, $users));
|
|
}
|
|
|
|
|
|
/**
|
|
* @covers ::get_with_capability_sql
|
|
*/
|
|
public function test_get_with_capability_sql(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$student = $this->getDataGenerator()->create_user();
|
|
$guest = $DB->get_record('user', array('username' => 'guest'));
|
|
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
role_assign($studentrole->id, $student->id, $coursecontext);
|
|
$admin = $DB->get_record('user', array('username' => 'admin'));
|
|
|
|
// Note: Here are used default capabilities, the full test is in permission evaluation below,
|
|
// use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse')));
|
|
|
|
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse');
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
|
|
$this->assertTrue(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
$this->assertFalse(array_key_exists($student->id, $users));
|
|
$this->assertFalse(array_key_exists($guest->id, $users));
|
|
|
|
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/site:approvecourse');
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
|
|
$this->assertFalse(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
$this->assertFalse(array_key_exists($student->id, $users));
|
|
$this->assertFalse(array_key_exists($guest->id, $users));
|
|
|
|
// Test role override.
|
|
assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
|
|
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
|
|
|
|
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse');
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
|
|
$this->assertFalse(array_key_exists($teacher->id, $users));
|
|
$this->assertFalse(array_key_exists($admin->id, $users));
|
|
$this->assertTrue(array_key_exists($student->id, $users));
|
|
$this->assertFalse(array_key_exists($guest->id, $users));
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the test cases for {@link test_get_with_capability_join_when_overrides_present()}.
|
|
*
|
|
* The particular capabilties used here do not really matter. What is important is
|
|
* that they are capabilities which the Student roles has by default, but the
|
|
* authenticated suser role does not.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_get_with_capability_join_override_cases() {
|
|
return [
|
|
'no overrides' => [true, []],
|
|
'one override' => [true, ['moodle/course:viewscales']],
|
|
'both overrides' => [false, ['moodle/course:viewscales', 'moodle/question:flag']],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test get_with_capability_join.
|
|
*
|
|
* @dataProvider get_get_with_capability_join_override_cases
|
|
* @covers ::get_with_capability_join
|
|
*
|
|
* @param bool $studentshouldbereturned whether, with this combination of capabilities, the student should be in the results.
|
|
* @param array $capabilitiestoprevent capabilities to override to prevent in the course context.
|
|
*/
|
|
public function test_get_with_capability_join_when_overrides_present(
|
|
bool $studentshouldbereturned, array $capabilitiestoprevent): void {
|
|
global $DB;
|
|
$this->resetAfterTest();
|
|
$generator = $this->getDataGenerator();
|
|
|
|
// Create a course.
|
|
$category = $generator->create_category();
|
|
$course = $generator->create_course(['category' => $category->id]);
|
|
|
|
// Create a user.
|
|
$student = $generator->create_user();
|
|
$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
|
|
$generator->enrol_user($student->id, $course->id, $studentrole->id);
|
|
|
|
// This test assumes that by default the student roles has the two
|
|
// capabilities. Check this now in case the role definitions are every changed.
|
|
$coursecontext = context_course::instance($course->id);
|
|
$this->assertTrue(has_capability('moodle/course:viewscales', $coursecontext, $student));
|
|
$this->assertTrue(has_capability('moodle/question:flag', $coursecontext, $student));
|
|
|
|
// We test cases where there are a varying number of prevent overrides.
|
|
foreach ($capabilitiestoprevent as $capability) {
|
|
role_change_permission($studentrole->id, $coursecontext, $capability, CAP_PREVENT);
|
|
}
|
|
|
|
// So now, assemble our query using the method under test, and verify that it returns the student.
|
|
$sqljoin = get_with_capability_join($coursecontext,
|
|
['moodle/course:viewscales', 'moodle/question:flag'], 'u.id');
|
|
|
|
$users = $DB->get_records_sql("SELECT u.*
|
|
FROM {user} u
|
|
{$sqljoin->joins}
|
|
WHERE {$sqljoin->wheres}", $sqljoin->params);
|
|
if ($studentshouldbereturned) {
|
|
$this->assertEquals([$student->id], array_keys($users));
|
|
} else {
|
|
$this->assertEmpty($users);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test the get_profile_roles() function.
|
|
*
|
|
* @covers ::get_profile_roles
|
|
*/
|
|
public function test_get_profile_roles(): void {
|
|
global $DB;
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
|
|
// Assign a student role.
|
|
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
role_assign($studentrole->id, $user1->id, $coursecontext);
|
|
|
|
// Assign an editing teacher role.
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $user2->id, $coursecontext);
|
|
|
|
// Create a custom role that can be assigned at course level, but don't assign it yet.
|
|
create_role('Custom role', 'customrole', 'Custom course role');
|
|
$customrole = $DB->get_record('role', array('shortname' => 'customrole'), '*', MUST_EXIST);
|
|
set_role_contextlevels($customrole->id, [CONTEXT_COURSE]);
|
|
core_role_set_assign_allowed($teacherrole->id, $customrole->id); // Allow teacher to assign the role in the course.
|
|
|
|
// Set the site policy 'profileroles' to show student, teacher and non-editing teacher roles (i.e. not the custom role).
|
|
$neteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
|
|
set_config('profileroles', "{$studentrole->id}, {$teacherrole->id}, {$neteacherrole->id}");
|
|
|
|
// A student in the course (given they can't assign roles) should see those roles which are:
|
|
// - listed in the 'profileroles' site policy AND
|
|
// - are assigned in the course context (or parent contexts).
|
|
// In this case, the non-editing teacher role is not assigned and should not be returned.
|
|
$expected = [
|
|
$teacherrole->id => (object) [
|
|
'id' => $teacherrole->id,
|
|
'name' => '',
|
|
'shortname' => $teacherrole->shortname,
|
|
'sortorder' => $teacherrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
$studentrole->id => (object) [
|
|
'id' => $studentrole->id,
|
|
'name' => '',
|
|
'shortname' => $studentrole->shortname,
|
|
'sortorder' => $studentrole->sortorder,
|
|
'coursealias' => null
|
|
]
|
|
];
|
|
$this->setUser($user1);
|
|
$this->assertEquals($expected, get_profile_roles($coursecontext));
|
|
|
|
// An editing teacher should also see only 2 roles at this stage as only 2 roles are assigned: 'teacher' and 'student'.
|
|
$this->setUser($user2);
|
|
$this->assertEquals($expected, get_profile_roles($coursecontext));
|
|
|
|
// Assign a custom role in the course.
|
|
$user3 = $this->getDataGenerator()->create_user();
|
|
role_assign($customrole->id, $user3->id, $coursecontext);
|
|
|
|
// Confirm that the teacher can see the custom role now that it's assigned.
|
|
$expectedteacher = [
|
|
$teacherrole->id => (object) [
|
|
'id' => $teacherrole->id,
|
|
'name' => '',
|
|
'shortname' => $teacherrole->shortname,
|
|
'sortorder' => $teacherrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
$studentrole->id => (object) [
|
|
'id' => $studentrole->id,
|
|
'name' => '',
|
|
'shortname' => $studentrole->shortname,
|
|
'sortorder' => $studentrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
$customrole->id => (object) [
|
|
'id' => $customrole->id,
|
|
'name' => 'Custom role',
|
|
'shortname' => $customrole->shortname,
|
|
'sortorder' => $customrole->sortorder,
|
|
'coursealias' => null
|
|
]
|
|
];
|
|
$this->setUser($user2);
|
|
$this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
|
|
|
|
// And that the student can't, because the role isn't included in the 'profileroles' site policy.
|
|
$expectedstudent = [
|
|
$teacherrole->id => (object) [
|
|
'id' => $teacherrole->id,
|
|
'name' => '',
|
|
'shortname' => $teacherrole->shortname,
|
|
'sortorder' => $teacherrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
$studentrole->id => (object) [
|
|
'id' => $studentrole->id,
|
|
'name' => '',
|
|
'shortname' => $studentrole->shortname,
|
|
'sortorder' => $studentrole->sortorder,
|
|
'coursealias' => null
|
|
]
|
|
];
|
|
$this->setUser($user1);
|
|
$this->assertEquals($expectedstudent, get_profile_roles($coursecontext));
|
|
|
|
// If we have no roles listed in the site policy, the teacher should be able to see the assigned roles.
|
|
$expectedteacher = [
|
|
$studentrole->id => (object) [
|
|
'id' => $studentrole->id,
|
|
'name' => '',
|
|
'shortname' => $studentrole->shortname,
|
|
'sortorder' => $studentrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
$customrole->id => (object) [
|
|
'id' => $customrole->id,
|
|
'name' => 'Custom role',
|
|
'shortname' => $customrole->shortname,
|
|
'sortorder' => $customrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
$teacherrole->id => (object) [
|
|
'id' => $teacherrole->id,
|
|
'name' => '',
|
|
'shortname' => $teacherrole->shortname,
|
|
'sortorder' => $teacherrole->sortorder,
|
|
'coursealias' => null
|
|
],
|
|
];
|
|
set_config('profileroles', "");
|
|
$this->setUser($user2);
|
|
$this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
|
|
}
|
|
|
|
/**
|
|
* Data provider for is_parent_of context checks.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function is_parent_of_provider(): array {
|
|
$provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array {
|
|
return [
|
|
"includeself: true; {$desc}" => [
|
|
$contextpath,
|
|
$testpath,
|
|
true,
|
|
$expected,
|
|
],
|
|
"includeself: false; {$desc}" => [
|
|
$contextpath,
|
|
$testpath,
|
|
false,
|
|
$expected,
|
|
],
|
|
];
|
|
};
|
|
|
|
return array_merge(
|
|
[
|
|
'includeself: true, testing self' => [
|
|
'/1/4/17/291/1001/17105',
|
|
'/1/4/17/291/1001/17105',
|
|
true,
|
|
true,
|
|
],
|
|
'includeself: false, testing self' => [
|
|
'/1/4/17/291/1001/17105',
|
|
'/1/4/17/291/1001/17105',
|
|
false,
|
|
false,
|
|
],
|
|
],
|
|
$provideboth(
|
|
'testing parent',
|
|
'/1/4/17/291/1001/17105',
|
|
'/1/4/17/291/1001',
|
|
false
|
|
),
|
|
$provideboth(
|
|
'testing child',
|
|
'/1/4/17/291/1001',
|
|
'/1/4/17/291/1001/17105',
|
|
true
|
|
),
|
|
$provideboth(
|
|
'testing grandchild',
|
|
'/1',
|
|
'/1/4/17/291/1001/17105',
|
|
true
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Ensure that the is_parent_of() function works as anticipated.
|
|
*
|
|
* @dataProvider is_parent_of_provider
|
|
* @covers \context::is_parent_of
|
|
* @covers \context_block::is_parent_of
|
|
* @covers \context_course::is_parent_of
|
|
* @covers \context_coursecat::is_parent_of
|
|
* @covers \context_module::is_parent_of
|
|
* @covers \context_system::is_parent_of
|
|
* @covers \context_user::is_parent_of
|
|
* @param string $contextpath The path of the context being compared with
|
|
* @param string $testpath The path of the context being compared
|
|
* @param bool $testself Whether to check the current context
|
|
* @param bool $expected The expected result
|
|
*/
|
|
public function test_is_parent_of(string $contextpath, string $testpath, bool $testself, bool $expected): void {
|
|
$context = $this->getMockBuilder(\context::class)
|
|
->disableOriginalConstructor()
|
|
->onlyMethods([
|
|
'get_url',
|
|
'get_capabilities',
|
|
])
|
|
->getMock();
|
|
|
|
$rcp = new ReflectionProperty($context, '_path');
|
|
$rcp->setValue($context, $contextpath);
|
|
|
|
$comparisoncontext = $this->getMockBuilder(\context::class)
|
|
->disableOriginalConstructor()
|
|
->onlyMethods([
|
|
'get_url',
|
|
'get_capabilities',
|
|
])
|
|
->getMock();
|
|
|
|
$rcp = new ReflectionProperty($comparisoncontext, '_path');
|
|
$rcp->setValue($comparisoncontext, $testpath);
|
|
|
|
$this->assertEquals($expected, $context->is_parent_of($comparisoncontext, $testself));
|
|
}
|
|
|
|
/**
|
|
* Data provider for is_child_of context checks.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function is_child_of_provider(): array {
|
|
$provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array {
|
|
return [
|
|
"includeself: true; {$desc}" => [
|
|
$contextpath,
|
|
$testpath,
|
|
true,
|
|
$expected,
|
|
],
|
|
"includeself: false; {$desc}" => [
|
|
$contextpath,
|
|
$testpath,
|
|
false,
|
|
$expected,
|
|
],
|
|
];
|
|
};
|
|
|
|
return array_merge(
|
|
[
|
|
'includeself: true, testing self' => [
|
|
'/1/4/17/291/1001/17105',
|
|
'/1/4/17/291/1001/17105',
|
|
true,
|
|
true,
|
|
],
|
|
'includeself: false, testing self' => [
|
|
'/1/4/17/291/1001/17105',
|
|
'/1/4/17/291/1001/17105',
|
|
false,
|
|
false,
|
|
],
|
|
],
|
|
$provideboth(
|
|
'testing child',
|
|
'/1/4/17/291/1001/17105',
|
|
'/1/4/17/291/1001',
|
|
true
|
|
),
|
|
$provideboth(
|
|
'testing parent',
|
|
'/1/4/17/291/1001',
|
|
'/1/4/17/291/1001/17105',
|
|
false
|
|
),
|
|
$provideboth(
|
|
'testing grandchild',
|
|
'/1/4/17/291/1001/17105',
|
|
'/1',
|
|
true
|
|
),
|
|
$provideboth(
|
|
'testing grandparent',
|
|
'/1',
|
|
'/1/4/17/291/1001/17105',
|
|
false
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Ensure that the is_child_of() function works as anticipated.
|
|
*
|
|
* @dataProvider is_child_of_provider
|
|
* @covers \context::is_child_of
|
|
* @covers \context_block::is_child_of
|
|
* @covers \context_course::is_child_of
|
|
* @covers \context_coursecat::is_child_of
|
|
* @covers \context_module::is_child_of
|
|
* @covers \context_system::is_child_of
|
|
* @covers \context_user::is_child_of
|
|
* @param string $contextpath The path of the context being compared with
|
|
* @param string $testpath The path of the context being compared
|
|
* @param bool $testself Whether to check the current context
|
|
* @param bool $expected The expected result
|
|
*/
|
|
public function test_is_child_of(string $contextpath, string $testpath, bool $testself, bool $expected): void {
|
|
$context = $this->getMockBuilder(\context::class)
|
|
->disableOriginalConstructor()
|
|
->onlyMethods([
|
|
'get_url',
|
|
'get_capabilities',
|
|
])
|
|
->getMock();
|
|
|
|
$rcp = new ReflectionProperty($context, '_path');
|
|
$rcp->setValue($context, $contextpath);
|
|
|
|
$comparisoncontext = $this->getMockBuilder(\context::class)
|
|
->disableOriginalConstructor()
|
|
->onlyMethods([
|
|
'get_url',
|
|
'get_capabilities',
|
|
])
|
|
->getMock();
|
|
|
|
$rcp = new ReflectionProperty($comparisoncontext, '_path');
|
|
$rcp->setValue($comparisoncontext, $testpath);
|
|
|
|
$this->assertEquals($expected, $context->is_child_of($comparisoncontext, $testself));
|
|
}
|
|
|
|
/**
|
|
* Ensure that the get_parent_contexts() function limits the number of queries it performs.
|
|
*
|
|
* @covers ::get_parent_contexts
|
|
*/
|
|
public function test_get_parent_contexts_preload(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
/*
|
|
* Given the following data structure:
|
|
* System
|
|
* - Category
|
|
* --- Category
|
|
* ----- Category
|
|
* ------- Category
|
|
* --------- Course
|
|
* ----------- Activity (Forum)
|
|
*/
|
|
|
|
$contexts = [];
|
|
|
|
$cat1 = $this->getDataGenerator()->create_category();
|
|
$cat2 = $this->getDataGenerator()->create_category(['parent' => $cat1->id]);
|
|
$cat3 = $this->getDataGenerator()->create_category(['parent' => $cat2->id]);
|
|
$cat4 = $this->getDataGenerator()->create_category(['parent' => $cat3->id]);
|
|
$course = $this->getDataGenerator()->create_course(['category' => $cat4->id]);
|
|
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
|
|
|
|
$modcontext = context_module::instance($forum->cmid);
|
|
|
|
context_helper::reset_caches();
|
|
|
|
// There should only be a single DB query.
|
|
$predbqueries = $DB->perf_get_reads();
|
|
|
|
$parents = $modcontext->get_parent_contexts();
|
|
// Note: For some databases There is one read, plus one FETCH, plus one CLOSE.
|
|
// These all show as reads, when there has actually only been a single query.
|
|
$this->assertLessThanOrEqual(3, $DB->perf_get_reads() - $predbqueries);
|
|
}
|
|
|
|
/**
|
|
* Ensure that get_with_capability_sql and get_with_capability_join respect context locking.
|
|
*
|
|
* @covers ::get_with_capability_join
|
|
* @covers ::get_with_capability_sql
|
|
*/
|
|
public function test_get_with_capability_sql_locked(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$generator = $this->getDataGenerator();
|
|
|
|
$cat1 = $generator->create_category();
|
|
$cat2 = $generator->create_category();
|
|
$cat1course1 = $generator->create_course(['category' => $cat1->id]);
|
|
$cat1course1forum = $generator->create_module('forum', ['course' => $cat1course1]);
|
|
|
|
$contexts = (object) [
|
|
'system' => \context_system::instance(),
|
|
'cat1' => \context_coursecat::instance($cat1->id),
|
|
'cat2' => \context_coursecat::instance($cat2->id),
|
|
'cat1course1' => \context_course::instance($cat1course1->id),
|
|
'cat1course1forum' => \context_module::instance($cat1course1forum->cmid),
|
|
];
|
|
|
|
// Test with the 'mod/forum:startdiscussion' capability.
|
|
$caput = 'mod/forum:startdiscussion';
|
|
|
|
// Create a test user.
|
|
$uut = $generator->create_and_enrol($cat1course1, 'teacher');
|
|
|
|
// Initially the user will be returned by get_users_by_capability.
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
// Freezing the forum will remove the user.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat1course1forum->set_locked(true);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayNotHasKey($uut->id, $users);
|
|
|
|
// But not if context locking is disabled.
|
|
set_config('contextlocking', 0);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
$contexts->cat1course1forum->set_locked(false);
|
|
|
|
// Freezing the course will have the same effect.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat1course1->set_locked(true);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayNotHasKey($uut->id, $users);
|
|
|
|
// But not if context locking is disabled.
|
|
set_config('contextlocking', 0);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
$contexts->cat1course1->set_locked(false);
|
|
|
|
// Freezing the category will have the same effect.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat1->set_locked(true);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayNotHasKey($uut->id, $users);
|
|
|
|
// But not if context locking is disabled.
|
|
set_config('contextlocking', 0);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
$contexts->cat1->set_locked(false);
|
|
|
|
// Freezing an unrelated category will have no effect.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat2->set_locked(true);
|
|
list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput);
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
}
|
|
|
|
/**
|
|
* Ensure that get_users_by_capability respects context freezing.
|
|
*
|
|
* @covers ::get_users_by_capability
|
|
*/
|
|
public function test_get_users_by_capability_locked(): void {
|
|
$this->resetAfterTest();
|
|
|
|
$generator = $this->getDataGenerator();
|
|
|
|
$cat1 = $generator->create_category();
|
|
$cat2 = $generator->create_category();
|
|
$cat1course1 = $generator->create_course(['category' => $cat1->id]);
|
|
$cat1course1forum = $generator->create_module('forum', ['course' => $cat1course1]);
|
|
|
|
$contexts = (object) [
|
|
'system' => \context_system::instance(),
|
|
'cat1' => \context_coursecat::instance($cat1->id),
|
|
'cat2' => \context_coursecat::instance($cat2->id),
|
|
'cat1course1' => \context_course::instance($cat1course1->id),
|
|
'cat1course1forum' => \context_module::instance($cat1course1forum->cmid),
|
|
];
|
|
|
|
// Test with the 'mod/forum:startdiscussion' capability.
|
|
$caput = 'mod/forum:startdiscussion';
|
|
|
|
// Create a test user.
|
|
$uut = $generator->create_and_enrol($cat1course1, 'teacher');
|
|
|
|
// Initially the user will be returned by get_users_by_capability.
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
// Freezing the forum will remove the user.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat1course1forum->set_locked(true);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayNotHasKey($uut->id, $users);
|
|
|
|
// But not if context locking is disabled.
|
|
set_config('contextlocking', 0);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
$contexts->cat1course1forum->set_locked(false);
|
|
|
|
// Freezing the course will have the same effect.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat1course1->set_locked(true);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayNotHasKey($uut->id, $users);
|
|
|
|
// But not if context locking is disabled.
|
|
set_config('contextlocking', 0);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
$contexts->cat1course1->set_locked(false);
|
|
|
|
// Freezing the category will have the same effect.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat1->set_locked(true);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayNotHasKey($uut->id, $users);
|
|
|
|
// But not if context locking is disabled.
|
|
set_config('contextlocking', 0);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
|
|
$contexts->cat1->set_locked(false);
|
|
|
|
// Freezing an unrelated category will have no effect.
|
|
set_config('contextlocking', 1);
|
|
$contexts->cat2->set_locked(true);
|
|
$users = get_users_by_capability($contexts->cat1course1forum, $caput);
|
|
$this->assertArrayHasKey($uut->id, $users);
|
|
}
|
|
|
|
/**
|
|
* Test require_all_capabilities.
|
|
*
|
|
* @covers ::require_all_capabilities
|
|
*/
|
|
public function test_require_all_capabilities(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$coursecontext = context_course::instance($course->id);
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
|
$teacher = $this->getDataGenerator()->create_user();
|
|
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
|
|
|
// Note: Here are used default capabilities, the full test is in permission evaluation bellow,
|
|
// use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupsection')));
|
|
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
|
|
|
|
$sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
|
|
|
|
$this->setUser($teacher);
|
|
require_all_capabilities($sca, $coursecontext);
|
|
require_all_capabilities($sca, $coursecontext, $teacher);
|
|
|
|
// Guest users should not have any of these perms.
|
|
$this->setUser(0);
|
|
$this->expectException(\required_capability_exception::class);
|
|
require_all_capabilities($sca, $coursecontext);
|
|
}
|
|
|
|
/**
|
|
* Test get_navigation_filter_context.
|
|
*
|
|
* @covers ::get_navigation_filter_context
|
|
*/
|
|
public function test_get_navigation_filter_context(): void {
|
|
$this->resetAfterTest();
|
|
$course = $this->getDataGenerator()->create_course();
|
|
set_config('filternavigationwithsystemcontext', 0);
|
|
// First test passed values are returned if disabled.
|
|
$this->assertNull(context_helper::get_navigation_filter_context(null));
|
|
$coursecontext = context_course::instance($course->id);
|
|
$filtercontext = context_helper::get_navigation_filter_context($coursecontext);
|
|
$this->assertEquals($coursecontext->id, $filtercontext->id);
|
|
|
|
// Now test that any input returns system context if enabled.
|
|
set_config('filternavigationwithsystemcontext', 1);
|
|
$filtercontext = context_helper::get_navigation_filter_context(null);
|
|
$this->assertInstanceOf('\context_system', $filtercontext);
|
|
$filtercontext = context_helper::get_navigation_filter_context($coursecontext);
|
|
$this->assertInstanceOf('\context_system', $filtercontext);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Context caching fixture
|
|
*/
|
|
abstract class context_inspection extends \core\context_helper {
|
|
/**
|
|
* Return the cached contexts count for testing purposes.
|
|
*
|
|
* @return int
|
|
*/
|
|
public static function check_context_cache_size() {
|
|
return self::$cache_count;
|
|
}
|
|
}
|