Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

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