Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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->assertEqualsCanonicalizing(array($teacher->id, $manager->id), array_keys($roles), true);
 680  
 681          $roles = get_roles_with_capability('moodle/backup:backupcourse', CAP_ALLOW);
 682          $this->assertEqualsCanonicalizing(array($manager->id), array_keys($roles), true);
 683  
 684          $roles = get_roles_with_capability('moodle/backup:backupcourse', null, $syscontext);
 685          $this->assertEqualsCanonicalizing(array($manager->id), array_keys($roles), 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->assertEqualsCanonicalizing(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype'),
 766              array_keys($role));
 767  
 768          foreach ($allroles as $roleid => $role) {
 769              $this->assertEquals($role->id, $roleid);
 770          }
 771  
 772          $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
 773          $course = $this->getDataGenerator()->create_course();
 774          $coursecontext = context_course::instance($course->id);
 775          $otherid = create_role('Other role', 'other', 'Some other role', '');
 776          $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
 777          $DB->insert_record('role_names', $teacherename);
 778          $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
 779          $DB->insert_record('role_names', $otherrename);
 780          $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
 781  
 782          $allroles = get_all_roles($coursecontext);
 783          $this->assertIsArray($allroles);
 784          $this->assertCount($initialrolescount + 1, $allroles);
 785          $role = reset($allroles);
 786          $role = (array)$role;
 787  
 788          $this->assertEqualsCanonicalizing(array('id', 'name', 'shortname', 'description', 'sortorder', 'archetype', 'coursealias'), array_keys($role));
 789  
 790          foreach ($allroles as $roleid => $role) {
 791              $this->assertEquals($role->id, $roleid);
 792              if (isset($renames[$roleid])) {
 793                  $this->assertSame($renames[$roleid], $role->coursealias);
 794              } else {
 795                  $this->assertNull($role->coursealias);
 796              }
 797          }
 798      }
 799  
 800      /**
 801       * Test getting of all archetypes.
 802       */
 803      public function test_get_role_archetypes() {
 804          $archetypes = get_role_archetypes();
 805          $this->assertCount(8, $archetypes); // There are 8 archetypes in standard install.
 806          foreach ($archetypes as $k => $v) {
 807              $this->assertSame($k, $v);
 808          }
 809      }
 810  
 811      /**
 812       * Test getting of roles with given archetype.
 813       */
 814      public function test_get_archetype_roles() {
 815          $this->resetAfterTest();
 816  
 817          // New install should have at least 1 role for each archetype.
 818          $archetypes = get_role_archetypes();
 819          foreach ($archetypes as $archetype) {
 820              $roles = get_archetype_roles($archetype);
 821              $this->assertGreaterThanOrEqual(1, count($roles));
 822              $role = reset($roles);
 823              $this->assertSame($archetype, $role->archetype);
 824          }
 825  
 826          create_role('New student role', 'student2', 'New student description', 'student');
 827          $roles = get_archetype_roles('student');
 828          $this->assertGreaterThanOrEqual(2, count($roles));
 829      }
 830  
 831      /**
 832       * Test aliased role names.
 833       */
 834      public function test_role_get_name() {
 835          global $DB;
 836  
 837          $this->resetAfterTest();
 838  
 839          $allroles = $DB->get_records('role');
 840          $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
 841          $course = $this->getDataGenerator()->create_course();
 842          $coursecontext = context_course::instance($course->id);
 843          $otherid = create_role('Other role', 'other', 'Some other role', '');
 844          $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
 845          $DB->insert_record('role_names', $teacherename);
 846          $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
 847          $DB->insert_record('role_names', $otherrename);
 848          $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
 849  
 850          foreach ($allroles as $role) {
 851              if (in_array($role->shortname, get_role_archetypes())) {
 852                  // Standard roles do not have a set name.
 853                  $this->assertSame('', $role->name);
 854              }
 855              // Get localised name from lang pack.
 856              $name = role_get_name($role, null, ROLENAME_ORIGINAL);
 857              $this->assertNotEmpty($name);
 858              $this->assertNotEquals($role->shortname, $name);
 859  
 860              if (isset($renames[$role->id])) {
 861                  $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext));
 862                  $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS));
 863                  $this->assertSame($renames[$role->id], role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
 864                  $this->assertSame("{$renames[$role->id]} ($name)", role_get_name($role, $coursecontext, ROLENAME_BOTH));
 865              } else {
 866                  $this->assertSame($name, role_get_name($role, $coursecontext));
 867                  $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ALIAS));
 868                  $this->assertNull(role_get_name($role, $coursecontext, ROLENAME_ALIAS_RAW));
 869                  $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_BOTH));
 870              }
 871              $this->assertSame($name, role_get_name($role));
 872              $this->assertSame($name, role_get_name($role, $coursecontext, ROLENAME_ORIGINAL));
 873              $this->assertSame($name, role_get_name($role, null, ROLENAME_ORIGINAL));
 874              $this->assertSame($role->shortname, role_get_name($role, $coursecontext, ROLENAME_SHORT));
 875              $this->assertSame($role->shortname, role_get_name($role, null, ROLENAME_SHORT));
 876              $this->assertSame("$name ($role->shortname)", role_get_name($role, $coursecontext, ROLENAME_ORIGINALANDSHORT));
 877              $this->assertSame("$name ($role->shortname)", role_get_name($role, null, ROLENAME_ORIGINALANDSHORT));
 878              $this->assertNull(role_get_name($role, null, ROLENAME_ALIAS_RAW));
 879          }
 880      }
 881  
 882      /**
 883       * Test tweaking of role name arrays.
 884       */
 885      public function test_role_fix_names() {
 886          global $DB;
 887  
 888          $this->resetAfterTest();
 889  
 890          $teacher = $DB->get_record('role', array('shortname'=>'teacher'), '*', MUST_EXIST);
 891          $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
 892          $otherid = create_role('Other role', 'other', 'Some other role', '');
 893          $anotherid = create_role('Another role', 'another', 'Yet another other role', '');
 894          $allroles = $DB->get_records('role');
 895  
 896          $syscontext = context_system::instance();
 897          $frontcontext = context_course::instance(SITEID);
 898          $course = $this->getDataGenerator()->create_course();
 899          $coursecontext = context_course::instance($course->id);
 900          $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
 901          $categorycontext = context_coursecat::instance($category->id);
 902  
 903          $teacherename = (object)array('roleid'=>$teacher->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
 904          $DB->insert_record('role_names', $teacherename);
 905          $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
 906          $DB->insert_record('role_names', $otherrename);
 907          $renames = $DB->get_records_menu('role_names', array('contextid'=>$coursecontext->id), '', 'roleid, name');
 908  
 909          // Make sure all localname contain proper values for each ROLENAME_ constant,
 910          // note role_get_name() on frontpage is used to get the original name for future compatibility.
 911          $roles = $allroles;
 912          unset($roles[$student->id]); // Remove one role to make sure no role is added or removed.
 913          $rolenames = array();
 914          foreach ($roles as $role) {
 915              $rolenames[$role->id] = $role->name;
 916          }
 917  
 918          $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
 919          foreach ($alltypes as $type) {
 920              $fixed = role_fix_names($roles, $coursecontext, $type);
 921              $this->assertCount(count($roles), $fixed);
 922              foreach ($fixed as $roleid => $rolename) {
 923                  $this->assertInstanceOf('stdClass', $rolename);
 924                  $role = $allroles[$roleid];
 925                  $name = role_get_name($role, $coursecontext, $type);
 926                  $this->assertSame($name, $rolename->localname);
 927              }
 928              $fixed = role_fix_names($rolenames, $coursecontext, $type);
 929              $this->assertCount(count($rolenames), $fixed);
 930              foreach ($fixed as $roleid => $rolename) {
 931                  $role = $allroles[$roleid];
 932                  $name = role_get_name($role, $coursecontext, $type);
 933                  $this->assertSame($name, $rolename);
 934              }
 935          }
 936      }
 937  
 938      /**
 939       * Test role default allows.
 940       */
 941      public function test_get_default_role_archetype_allows() {
 942          $archetypes = get_role_archetypes();
 943          foreach ($archetypes as $archetype) {
 944  
 945              $result = get_default_role_archetype_allows('assign', $archetype);
 946              $this->assertIsArray($result);
 947  
 948              $result = get_default_role_archetype_allows('override', $archetype);
 949              $this->assertIsArray($result);
 950  
 951              $result = get_default_role_archetype_allows('switch', $archetype);
 952              $this->assertIsArray($result);
 953  
 954              $result = get_default_role_archetype_allows('view', $archetype);
 955              $this->assertIsArray($result);
 956          }
 957  
 958          $result = get_default_role_archetype_allows('assign', '');
 959          $this->assertSame(array(), $result);
 960  
 961          $result = get_default_role_archetype_allows('override', '');
 962          $this->assertSame(array(), $result);
 963  
 964          $result = get_default_role_archetype_allows('switch', '');
 965          $this->assertSame(array(), $result);
 966  
 967          $result = get_default_role_archetype_allows('view', '');
 968          $this->assertSame(array(), $result);
 969  
 970          $result = get_default_role_archetype_allows('assign', 'wrongarchetype');
 971          $this->assertSame(array(), $result);
 972          $this->assertDebuggingCalled();
 973  
 974          $result = get_default_role_archetype_allows('override', 'wrongarchetype');
 975          $this->assertSame(array(), $result);
 976          $this->assertDebuggingCalled();
 977  
 978          $result = get_default_role_archetype_allows('switch', 'wrongarchetype');
 979          $this->assertSame(array(), $result);
 980          $this->assertDebuggingCalled();
 981  
 982          $result = get_default_role_archetype_allows('view', 'wrongarchetype');
 983          $this->assertSame(array(), $result);
 984          $this->assertDebuggingCalled();
 985      }
 986  
 987      /**
 988       * Test allowing of role assignments.
 989       */
 990      public function test_core_role_set_assign_allowed() {
 991          global $DB, $CFG;
 992  
 993          $this->resetAfterTest();
 994  
 995          $otherid = create_role('Other role', 'other', 'Some other role', '');
 996          $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
 997  
 998          $this->assertFalse($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
 999          core_role_set_assign_allowed($otherid, $student->id);
1000          $this->assertTrue($DB->record_exists('role_allow_assign', array('roleid'=>$otherid, 'allowassign'=>$student->id)));
1001  
1002          // Test event trigger.
1003          $allowroleassignevent = \core\event\role_allow_assign_updated::create([
1004              'context' => context_system::instance(),
1005              'objectid' => $otherid,
1006              'other' => ['targetroleid' => $student->id]
1007          ]);
1008          $sink = $this->redirectEvents();
1009          $allowroleassignevent->trigger();
1010          $events = $sink->get_events();
1011          $sink->close();
1012          $event = array_pop($events);
1013          $this->assertInstanceOf('\core\event\role_allow_assign_updated', $event);
1014          $mode = 'assign';
1015          $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1016          $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1017          $this->assertEventLegacyLogData($expectedlegacylog, $event);
1018      }
1019  
1020      /**
1021       * Test allowing of role overrides.
1022       */
1023      public function test_core_role_set_override_allowed() {
1024          global $DB, $CFG;
1025  
1026          $this->resetAfterTest();
1027  
1028          $otherid = create_role('Other role', 'other', 'Some other role', '');
1029          $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1030  
1031          $this->assertFalse($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
1032          core_role_set_override_allowed($otherid, $student->id);
1033          $this->assertTrue($DB->record_exists('role_allow_override', array('roleid'=>$otherid, 'allowoverride'=>$student->id)));
1034  
1035          // Test event trigger.
1036          $allowroleassignevent = \core\event\role_allow_override_updated::create([
1037              'context' => context_system::instance(),
1038              'objectid' => $otherid,
1039              'other' => ['targetroleid' => $student->id]
1040          ]);
1041          $sink = $this->redirectEvents();
1042          $allowroleassignevent->trigger();
1043          $events = $sink->get_events();
1044          $sink->close();
1045          $event = array_pop($events);
1046          $this->assertInstanceOf('\core\event\role_allow_override_updated', $event);
1047          $mode = 'override';
1048          $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1049          $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1050          $this->assertEventLegacyLogData($expectedlegacylog, $event);
1051      }
1052  
1053      /**
1054       * Test allowing of role switching.
1055       */
1056      public function test_core_role_set_switch_allowed() {
1057          global $DB, $CFG;
1058  
1059          $this->resetAfterTest();
1060  
1061          $otherid = create_role('Other role', 'other', 'Some other role', '');
1062          $student = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1063  
1064          $this->assertFalse($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1065          core_role_set_switch_allowed($otherid, $student->id);
1066          $this->assertTrue($DB->record_exists('role_allow_switch', array('roleid'=>$otherid, 'allowswitch'=>$student->id)));
1067  
1068          // Test event trigger.
1069          $allowroleassignevent = \core\event\role_allow_switch_updated::create([
1070              'context' => context_system::instance(),
1071              'objectid' => $otherid,
1072              'other' => ['targetroleid' => $student->id]
1073          ]);
1074          $sink = $this->redirectEvents();
1075          $allowroleassignevent->trigger();
1076          $events = $sink->get_events();
1077          $sink->close();
1078          $event = array_pop($events);
1079          $this->assertInstanceOf('\core\event\role_allow_switch_updated', $event);
1080          $mode = 'switch';
1081          $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1082          $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1083          $this->assertEventLegacyLogData($expectedlegacylog, $event);
1084      }
1085  
1086      /**
1087       * Test allowing of role switching.
1088       */
1089      public function test_core_role_set_view_allowed() {
1090          global $DB, $CFG;
1091  
1092          $this->resetAfterTest();
1093  
1094          $otherid = create_role('Other role', 'other', 'Some other role', '');
1095          $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1096  
1097          $this->assertFalse($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
1098          core_role_set_view_allowed($otherid, $student->id);
1099          $this->assertTrue($DB->record_exists('role_allow_view', array('roleid' => $otherid, 'allowview' => $student->id)));
1100  
1101          // Test event trigger.
1102          $allowroleassignevent = \core\event\role_allow_view_updated::create([
1103              'context' => context_system::instance(),
1104              'objectid' => $otherid,
1105              'other' => ['targetroleid' => $student->id]
1106          ]);
1107          $sink = $this->redirectEvents();
1108          $allowroleassignevent->trigger();
1109          $events = $sink->get_events();
1110          $sink->close();
1111          $event = array_pop($events);
1112          $this->assertInstanceOf('\core\event\role_allow_view_updated', $event);
1113          $mode = 'view';
1114          $baseurl = new moodle_url('/admin/roles/allow.php', array('mode' => $mode));
1115          $expectedlegacylog = array(SITEID, 'role', 'edit allow ' . $mode, str_replace($CFG->wwwroot . '/', '', $baseurl));
1116          $this->assertEventLegacyLogData($expectedlegacylog, $event);
1117      }
1118  
1119      /**
1120       * Test returning of assignable roles in context.
1121       */
1122      public function test_get_assignable_roles() {
1123          global $DB;
1124  
1125          $this->resetAfterTest();
1126  
1127          $course = $this->getDataGenerator()->create_course();
1128          $coursecontext = context_course::instance($course->id);
1129  
1130          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1131          $teacher = $this->getDataGenerator()->create_user();
1132          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1133          $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1134          $DB->insert_record('role_names', $teacherename);
1135  
1136          $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1137          $student = $this->getDataGenerator()->create_user();
1138          role_assign($studentrole->id, $student->id, $coursecontext);
1139  
1140          $contexts = $DB->get_records('context');
1141          $users = $DB->get_records('user');
1142          $allroles = $DB->get_records('role');
1143  
1144          // Evaluate all results for all users in all contexts.
1145          foreach ($users as $user) {
1146              $this->setUser($user);
1147              foreach ($contexts as $contextid => $unused) {
1148                  $context = context_helper::instance_by_id($contextid);
1149                  $roles = get_assignable_roles($context, ROLENAME_SHORT);
1150                  foreach ($allroles as $roleid => $role) {
1151                      if (isset($roles[$roleid])) {
1152                          if (is_siteadmin()) {
1153                              $this->assertTrue($DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid)));
1154                          } else {
1155                              $this->assertTrue(user_can_assign($context, $roleid), "u:$user->id r:$roleid");
1156                          }
1157                          $this->assertEquals($role->shortname, $roles[$roleid]);
1158                      } else {
1159                          $allowed = $DB->record_exists('role_context_levels', array('contextlevel'=>$context->contextlevel, 'roleid'=>$roleid));
1160                          if (is_siteadmin()) {
1161                              $this->assertFalse($allowed);
1162                          } else {
1163                              $this->assertFalse($allowed and user_can_assign($context, $roleid), "u:$user->id, r:{$allroles[$roleid]->name}, c:$context->contextlevel");
1164                          }
1165                      }
1166                  }
1167              }
1168          }
1169  
1170          // Not-logged-in user.
1171          $this->setUser(0);
1172          foreach ($contexts as $contextid => $unused) {
1173              $context = context_helper::instance_by_id($contextid);
1174              $roles = get_assignable_roles($context, ROLENAME_SHORT);
1175              $this->assertSame(array(), $roles);
1176          }
1177  
1178          // Test current user.
1179          $this->setUser(0);
1180          $admin = $DB->get_record('user', array('username'=>'admin'), '*', MUST_EXIST);
1181          $roles1 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin);
1182          $roles2 = get_assignable_roles($coursecontext, ROLENAME_SHORT, false, $admin->id);
1183          $this->setAdminUser();
1184          $roles3 = get_assignable_roles($coursecontext, ROLENAME_SHORT);
1185          $this->assertSame($roles1, $roles3);
1186          $this->assertSame($roles2, $roles3);
1187  
1188          // Test parameter defaults.
1189          $this->setAdminUser();
1190          $roles1 = get_assignable_roles($coursecontext);
1191          $roles2 = get_assignable_roles($coursecontext, ROLENAME_ALIAS, false, $admin);
1192          $this->assertEquals($roles2, $roles1);
1193  
1194          // Verify returned names - let's allow all roles everywhere to simplify this a bit.
1195          $alllevels = context_helper::get_all_levels();
1196          $alllevels = array_keys($alllevels);
1197          foreach ($allroles as $roleid => $role) {
1198              set_role_contextlevels($roleid, $alllevels);
1199          }
1200          $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1201          foreach ($alltypes as $type) {
1202              $rolenames = role_fix_names($allroles, $coursecontext, $type);
1203              $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1204              foreach ($roles as $roleid => $rolename) {
1205                  $this->assertSame($rolenames[$roleid]->localname, $rolename);
1206              }
1207          }
1208  
1209          // Verify counts.
1210          $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1211          foreach ($alltypes as $type) {
1212              $roles = get_assignable_roles($coursecontext, $type, false, $admin);
1213              list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($coursecontext, $type, true, $admin);
1214              $this->assertEquals($roles, $rolenames);
1215              foreach ($rolenames as $roleid => $name) {
1216                  if ($roleid == $teacherrole->id or $roleid == $studentrole->id) {
1217                      $this->assertEquals(1, $rolecounts[$roleid]);
1218                  } else {
1219                      $this->assertEquals(0, $rolecounts[$roleid]);
1220                  }
1221                  $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1222              }
1223          }
1224      }
1225  
1226      /**
1227       * Test user count of assignable roles in context where users are assigned the role via different components.
1228       */
1229      public function test_get_assignable_roles_distinct_usercount() {
1230          global $DB;
1231  
1232          $this->resetAfterTest(true);
1233  
1234          $this->setAdminUser();
1235  
1236          $course = $this->getDataGenerator()->create_course();
1237          $context = \context_course::instance($course->id);
1238  
1239          $user1 = $this->getDataGenerator()->create_user();
1240          $user2 = $this->getDataGenerator()->create_user();
1241  
1242          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
1243  
1244          // Assign each user the student role in course.
1245          role_assign($studentrole->id, $user1->id, $context->id);
1246          role_assign($studentrole->id, $user2->id, $context->id);
1247  
1248          list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
1249          $this->assertEquals(2, $rolecounts[$studentrole->id]);
1250  
1251          // Assign first user the student role in course again (this time via 'enrol_self' component).
1252          role_assign($studentrole->id, $user1->id, $context->id, 'enrol_self', 1);
1253  
1254          // There are still only two distinct users.
1255          list($rolenames, $rolecounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_SHORT, true);
1256          $this->assertEquals(2, $rolecounts[$studentrole->id]);
1257      }
1258  
1259      /**
1260       * Test getting of all switchable roles.
1261       */
1262      public function test_get_switchable_roles() {
1263          global $DB;
1264  
1265          $this->resetAfterTest();
1266  
1267          $course = $this->getDataGenerator()->create_course();
1268          $coursecontext = context_course::instance($course->id);
1269  
1270          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1271          $teacher = $this->getDataGenerator()->create_user();
1272          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1273          $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1274          $DB->insert_record('role_names', $teacherename);
1275  
1276          $contexts = $DB->get_records('context');
1277          $users = $DB->get_records('user');
1278          $allroles = $DB->get_records('role');
1279  
1280          // Evaluate all results for all users in all contexts.
1281          foreach ($users as $user) {
1282              $this->setUser($user);
1283              foreach ($contexts as $contextid => $unused) {
1284                  $context = context_helper::instance_by_id($contextid);
1285                  $roles = get_switchable_roles($context);
1286                  foreach ($allroles as $roleid => $role) {
1287                      if (is_siteadmin()) {
1288                          $this->assertTrue(isset($roles[$roleid]));
1289                      } else {
1290                          $parents = $context->get_parent_context_ids(true);
1291                          $pcontexts = implode(',' , $parents);
1292                          $allowed = $DB->record_exists_sql(
1293                              "SELECT r.id
1294                                 FROM {role} r
1295                                 JOIN {role_allow_switch} ras ON ras.allowswitch = r.id
1296                                 JOIN {role_assignments} ra ON ra.roleid = ras.roleid
1297                                WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1298                              ",
1299                              array('userid'=>$user->id, 'roleid'=>$roleid)
1300                          );
1301                          if (isset($roles[$roleid])) {
1302                              $this->assertTrue($allowed);
1303                          } else {
1304                              $this->assertFalse($allowed);
1305                          }
1306                      }
1307  
1308                      if (isset($roles[$roleid])) {
1309                          $coursecontext = $context->get_course_context(false);
1310                          $this->assertSame(role_get_name($role, $coursecontext), $roles[$roleid]);
1311                      }
1312                  }
1313              }
1314          }
1315      }
1316  
1317      /**
1318       * Test getting of all overridable roles.
1319       */
1320      public function test_get_overridable_roles() {
1321          global $DB;
1322  
1323          $this->resetAfterTest();
1324  
1325          $course = $this->getDataGenerator()->create_course();
1326          $coursecontext = context_course::instance($course->id);
1327  
1328          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1329          $teacher = $this->getDataGenerator()->create_user();
1330          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1331          $teacherename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1332          $DB->insert_record('role_names', $teacherename);
1333          $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse'))); // Any capability is ok.
1334          assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id);
1335  
1336          $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1337          $student = $this->getDataGenerator()->create_user();
1338          role_assign($studentrole->id, $student->id, $coursecontext);
1339  
1340          $contexts = $DB->get_records('context');
1341          $users = $DB->get_records('user');
1342          $allroles = $DB->get_records('role');
1343  
1344          // Evaluate all results for all users in all contexts.
1345          foreach ($users as $user) {
1346              $this->setUser($user);
1347              foreach ($contexts as $contextid => $unused) {
1348                  $context = context_helper::instance_by_id($contextid);
1349                  $roles = get_overridable_roles($context, ROLENAME_SHORT);
1350                  foreach ($allroles as $roleid => $role) {
1351                      $hascap = has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context);
1352                      if (is_siteadmin()) {
1353                          $this->assertTrue(isset($roles[$roleid]));
1354                      } else {
1355                          $parents = $context->get_parent_context_ids(true);
1356                          $pcontexts = implode(',' , $parents);
1357                          $allowed = $DB->record_exists_sql(
1358                              "SELECT r.id
1359                                 FROM {role} r
1360                                 JOIN {role_allow_override} rao ON r.id = rao.allowoverride
1361                                 JOIN {role_assignments} ra ON rao.roleid = ra.roleid
1362                                WHERE ra.userid = :userid AND ra.contextid IN ($pcontexts) AND r.id = :roleid
1363                              ",
1364                              array('userid'=>$user->id, 'roleid'=>$roleid)
1365                          );
1366                          if (isset($roles[$roleid])) {
1367                              $this->assertTrue($hascap);
1368                              $this->assertTrue($allowed);
1369                          } else {
1370                              $this->assertFalse($hascap and $allowed);
1371                          }
1372                      }
1373  
1374                      if (isset($roles[$roleid])) {
1375                          $this->assertEquals($role->shortname, $roles[$roleid]);
1376                      }
1377                  }
1378              }
1379          }
1380  
1381          // Test parameter defaults.
1382          $this->setAdminUser();
1383          $roles1 = get_overridable_roles($coursecontext);
1384          $roles2 = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1385          $this->assertEquals($roles2, $roles1);
1386  
1387          $alltypes = array(ROLENAME_ALIAS, ROLENAME_ALIAS_RAW, ROLENAME_BOTH, ROLENAME_ORIGINAL, ROLENAME_ORIGINALANDSHORT, ROLENAME_SHORT);
1388          foreach ($alltypes as $type) {
1389              $rolenames = role_fix_names($allroles, $coursecontext, $type);
1390              $roles = get_overridable_roles($coursecontext, $type, false);
1391              foreach ($roles as $roleid => $rolename) {
1392                  $this->assertSame($rolenames[$roleid]->localname, $rolename);
1393              }
1394          }
1395  
1396          // Verify counts.
1397          $roles = get_overridable_roles($coursecontext, ROLENAME_ALIAS, false);
1398          list($rolenames, $rolecounts, $nameswithcounts) = get_overridable_roles($coursecontext, ROLENAME_ALIAS, true);
1399          $this->assertEquals($roles, $rolenames);
1400          foreach ($rolenames as $roleid => $name) {
1401              if ($roleid == $teacherrole->id) {
1402                  $this->assertEquals(1, $rolecounts[$roleid]);
1403              } else {
1404                  $this->assertEquals(0, $rolecounts[$roleid]);
1405              }
1406              $this->assertSame("$name ($rolecounts[$roleid])", $nameswithcounts[$roleid]);
1407          }
1408      }
1409  
1410      /**
1411       * Test getting of all overridable roles.
1412       */
1413      public function test_get_viewable_roles_course() {
1414          global $DB;
1415  
1416          $this->resetAfterTest();
1417  
1418          $course = $this->getDataGenerator()->create_course();
1419          $coursecontext = context_course::instance($course->id);
1420  
1421          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1422          $teacher = $this->getDataGenerator()->create_user();
1423          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1424  
1425          $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1426          $studentrolerename = (object) array('roleid' => $studentrole->id, 'name' => 'Učitel', 'contextid' => $coursecontext->id);
1427          $DB->insert_record('role_names', $studentrolerename);
1428  
1429          // By default teacher can see student.
1430          $this->setUser($teacher);
1431          $viewableroles = get_viewable_roles($coursecontext);
1432          $this->assertContains($studentrolerename->name, array_values($viewableroles));
1433          // Remove view permission.
1434          $DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
1435          $viewableroles = get_viewable_roles($coursecontext);
1436          // Teacher can no longer see student role.
1437          $this->assertNotContains($studentrolerename->name, array_values($viewableroles));
1438          // Allow again teacher to view student.
1439          core_role_set_view_allowed($teacherrole->id, $studentrole->id);
1440          // Teacher can now see student role.
1441          $viewableroles = get_viewable_roles($coursecontext);
1442          $this->assertContains($studentrolerename->name, array_values($viewableroles));
1443      }
1444  
1445      /**
1446       * Test getting of all overridable roles.
1447       */
1448      public function test_get_viewable_roles_system() {
1449          global $DB;
1450  
1451          $this->resetAfterTest();
1452  
1453          $context = context_system::instance();
1454  
1455          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1456          $teacher = $this->getDataGenerator()->create_user();
1457          role_assign($teacherrole->id, $teacher->id, $context);
1458  
1459          $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1460          $studentrolename = role_get_name($studentrole, $context);
1461  
1462          // By default teacher can see student.
1463          $this->setUser($teacher);
1464          $viewableroles = get_viewable_roles($context);
1465          $this->assertContains($studentrolename, array_values($viewableroles));
1466          // Remove view permission.
1467          $DB->delete_records('role_allow_view', array('roleid' => $teacherrole->id, 'allowview' => $studentrole->id));
1468          $viewableroles = get_viewable_roles($context);
1469          // Teacher can no longer see student role.
1470          $this->assertNotContains($studentrolename, array_values($viewableroles));
1471          // Allow again teacher to view student.
1472          core_role_set_view_allowed($teacherrole->id, $studentrole->id);
1473          // Teacher can now see student role.
1474          $viewableroles = get_viewable_roles($context);
1475          $this->assertContains($studentrolename, array_values($viewableroles));
1476      }
1477  
1478      /**
1479       * Test we have context level defaults.
1480       */
1481      public function test_get_default_contextlevels() {
1482          $archetypes = get_role_archetypes();
1483          $alllevels = context_helper::get_all_levels();
1484          foreach ($archetypes as $archetype) {
1485              $defaults = get_default_contextlevels($archetype);
1486              $this->assertIsArray($defaults);
1487              foreach ($defaults as $level) {
1488                  $this->assertTrue(isset($alllevels[$level]));
1489              }
1490          }
1491      }
1492  
1493      /**
1494       * Test role context level setup.
1495       */
1496      public function test_set_role_contextlevels() {
1497          global $DB;
1498  
1499          $this->resetAfterTest();
1500  
1501          $roleid = create_role('New student role', 'student2', 'New student description', 'student');
1502  
1503          $this->assertFalse($DB->record_exists('role_context_levels', array('roleid' => $roleid)));
1504  
1505          set_role_contextlevels($roleid, array(CONTEXT_COURSE, CONTEXT_MODULE));
1506          $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1507          $this->assertCount(2, $levels);
1508          $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1509          $this->assertTrue(isset($levels[CONTEXT_MODULE]));
1510  
1511          set_role_contextlevels($roleid, array(CONTEXT_COURSE));
1512          $levels = $DB->get_records('role_context_levels', array('roleid' => $roleid), '', 'contextlevel, contextlevel');
1513          $this->assertCount(1, $levels);
1514          $this->assertTrue(isset($levels[CONTEXT_COURSE]));
1515      }
1516  
1517      /**
1518       * Test getting of role context levels
1519       */
1520      public function test_get_roles_for_contextlevels() {
1521          global $DB;
1522  
1523          $allroles = get_all_roles();
1524          foreach (context_helper::get_all_levels() as $level => $unused) {
1525              $roles = get_roles_for_contextlevels($level);
1526              foreach ($allroles as $roleid => $unused) {
1527                  $exists = $DB->record_exists('role_context_levels', array('contextlevel'=>$level, 'roleid'=>$roleid));
1528                  if (in_array($roleid, $roles)) {
1529                      $this->assertTrue($exists);
1530                  } else {
1531                      $this->assertFalse($exists);
1532                  }
1533              }
1534          }
1535      }
1536  
1537      /**
1538       * Test default enrol roles.
1539       */
1540      public function test_get_default_enrol_roles() {
1541          $this->resetAfterTest();
1542  
1543          $course = $this->getDataGenerator()->create_course();
1544          $coursecontext = context_course::instance($course->id);
1545  
1546          $id2 = create_role('New student role', 'student2', 'New student description', 'student');
1547          set_role_contextlevels($id2, array(CONTEXT_COURSE));
1548  
1549          $allroles = get_all_roles();
1550          $expected = array($id2=>$allroles[$id2]);
1551  
1552          foreach (get_roles_for_contextlevels(CONTEXT_COURSE) as $roleid) {
1553              $expected[$roleid] = $roleid;
1554          }
1555  
1556          $roles = get_default_enrol_roles($coursecontext);
1557          foreach ($allroles as $role) {
1558              $this->assertEquals(isset($expected[$role->id]), isset($roles[$role->id]));
1559              if (isset($roles[$role->id])) {
1560                  $this->assertSame(role_get_name($role, $coursecontext), $roles[$role->id]);
1561              }
1562          }
1563      }
1564  
1565      /**
1566       * Test getting of role users.
1567       */
1568      public function test_get_role_users() {
1569          global $DB;
1570  
1571          $this->resetAfterTest();
1572  
1573          $systemcontext = context_system::instance();
1574          $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1575          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1576          $noeditteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
1577          $course = $this->getDataGenerator()->create_course();
1578          $coursecontext = context_course::instance($course->id);
1579          $otherid = create_role('Other role', 'other', 'Some other role', '');
1580          $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1581          $DB->insert_record('role_names', $teacherrename);
1582          $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1583          $DB->insert_record('role_names', $otherrename);
1584  
1585          $user1 = $this->getDataGenerator()->create_user(array('firstname'=>'John', 'lastname'=>'Smith'));
1586          role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1587          $user2 = $this->getDataGenerator()->create_user(array('firstname'=>'Jan', 'lastname'=>'Kovar'));
1588          role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1589          $user3 = $this->getDataGenerator()->create_user();
1590          $this->getDataGenerator()->enrol_user($user3->id, $course->id, $teacherrole->id);
1591          $user4 = $this->getDataGenerator()->create_user();
1592          $this->getDataGenerator()->enrol_user($user4->id, $course->id, $studentrole->id);
1593          $this->getDataGenerator()->enrol_user($user4->id, $course->id, $noeditteacherrole->id);
1594  
1595          $group = $this->getDataGenerator()->create_group(array('courseid'=>$course->id));
1596          groups_add_member($group, $user3);
1597  
1598          $users = get_role_users($teacherrole->id, $coursecontext);
1599          $this->assertCount(2, $users);
1600          $this->assertArrayHasKey($user1->id, $users);
1601          $this->assertEquals($users[$user1->id]->id, $user1->id);
1602          $this->assertEquals($users[$user1->id]->roleid, $teacherrole->id);
1603          $this->assertEquals($users[$user1->id]->rolename, $teacherrole->name);
1604          $this->assertEquals($users[$user1->id]->roleshortname, $teacherrole->shortname);
1605          $this->assertEquals($users[$user1->id]->rolecoursealias, $teacherrename->name);
1606          $this->assertArrayHasKey($user3->id, $users);
1607          $this->assertEquals($users[$user3->id]->id, $user3->id);
1608          $this->assertEquals($users[$user3->id]->roleid, $teacherrole->id);
1609          $this->assertEquals($users[$user3->id]->rolename, $teacherrole->name);
1610          $this->assertEquals($users[$user3->id]->roleshortname, $teacherrole->shortname);
1611          $this->assertEquals($users[$user3->id]->rolecoursealias, $teacherrename->name);
1612  
1613          $users = get_role_users($teacherrole->id, $coursecontext, true);
1614          $this->assertCount(3, $users);
1615  
1616          $users = get_role_users($teacherrole->id, $coursecontext, true, '', null, null, '', 2, 1);
1617          $this->assertCount(1, $users);
1618  
1619          $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber');
1620          $this->assertCount(2, $users);
1621          $this->assertArrayHasKey($user1->id, $users);
1622          $this->assertArrayHasKey($user3->id, $users);
1623  
1624          $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email');
1625          $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1626          $this->assertCount(2, $users);
1627          $this->assertArrayHasKey($user1->id, $users);
1628          $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
1629          $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
1630          $this->assertArrayHasKey($user3->id, $users);
1631          $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
1632          $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
1633  
1634          $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id AS id_alias');
1635          $this->assertDebuggingCalled('get_role_users() adding u.lastname, u.firstname to the query result because they were required by $sort but missing in $fields');
1636          $this->assertCount(2, $users);
1637          $this->assertArrayHasKey($user1->id, $users);
1638          $this->assertObjectHasAttribute('id_alias', $users[$user1->id]);
1639          $this->assertObjectHasAttribute('lastname', $users[$user1->id]);
1640          $this->assertObjectHasAttribute('firstname', $users[$user1->id]);
1641          $this->assertArrayHasKey($user3->id, $users);
1642          $this->assertObjectHasAttribute('id_alias', $users[$user3->id]);
1643          $this->assertObjectHasAttribute('lastname', $users[$user3->id]);
1644          $this->assertObjectHasAttribute('firstname', $users[$user3->id]);
1645  
1646          $users = get_role_users($teacherrole->id, $coursecontext, false, 'u.id, u.email, u.idnumber', 'u.idnumber', null, $group->id);
1647          $this->assertCount(1, $users);
1648          $this->assertArrayHasKey($user3->id, $users);
1649  
1650          $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'));
1651          $this->assertCount(1, $users);
1652          $this->assertArrayHasKey($user1->id, $users);
1653  
1654          $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.id', 'ra.id');
1655          $this->assertDebuggingNotCalled();
1656          $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false, 'ra.userid', 'ra.userid');
1657          $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1658              'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1659          $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext, false);
1660          $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1661              'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1662          $users = get_role_users(array($noeditteacherrole->id, $studentrole->id), $coursecontext,
1663              false, 'u.id, u.firstname', 'u.id, u.firstname');
1664          $this->assertDebuggingCalled('get_role_users() without specifying one single roleid needs to be called prefixing ' .
1665              'role assignments id (ra.id) as unique field, you can use $fields param for it.');
1666      }
1667  
1668      /**
1669       * Test used role query.
1670       */
1671      public function test_get_roles_used_in_context() {
1672          global $DB;
1673  
1674          $this->resetAfterTest();
1675  
1676          $systemcontext = context_system::instance();
1677          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1678          $course = $this->getDataGenerator()->create_course();
1679          $coursecontext = context_course::instance($course->id);
1680          $otherid = create_role('Other role', 'other', 'Some other role', '');
1681          $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1682          $DB->insert_record('role_names', $teacherrename);
1683          $otherrename = (object)array('roleid'=>$otherid, 'name'=>'Ostatní', 'contextid'=>$coursecontext->id);
1684          $DB->insert_record('role_names', $otherrename);
1685  
1686          $user1 = $this->getDataGenerator()->create_user();
1687          role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1688  
1689          $roles = get_roles_used_in_context($coursecontext);
1690          $this->assertCount(1, $roles);
1691          $role = reset($roles);
1692          $roleid = key($roles);
1693          $this->assertEquals($roleid, $role->id);
1694          $this->assertEquals($teacherrole->id, $role->id);
1695          $this->assertSame($teacherrole->name, $role->name);
1696          $this->assertSame($teacherrole->shortname, $role->shortname);
1697          $this->assertEquals($teacherrole->sortorder, $role->sortorder);
1698          $this->assertSame($teacherrename->name, $role->coursealias);
1699  
1700          $user2 = $this->getDataGenerator()->create_user();
1701          role_assign($teacherrole->id, $user2->id, $systemcontext->id);
1702          role_assign($otherid, $user2->id, $systemcontext->id);
1703  
1704          $roles = get_roles_used_in_context($systemcontext);
1705          $this->assertCount(2, $roles);
1706      }
1707  
1708      /**
1709       * Test roles used in course.
1710       */
1711      public function test_get_user_roles_in_course() {
1712          global $DB, $CFG;
1713  
1714          $this->resetAfterTest();
1715  
1716          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1717          $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1718          $managerrole = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
1719          $course = $this->getDataGenerator()->create_course();
1720          $coursecontext = context_course::instance($course->id);
1721          $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1722          $DB->insert_record('role_names', $teacherrename);
1723  
1724          $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1725          $this->assertTrue(in_array($teacherrole->id, $roleids));
1726          $this->assertTrue(in_array($studentrole->id, $roleids));
1727          $this->assertFalse(in_array($managerrole->id, $roleids));
1728  
1729          $user1 = $this->getDataGenerator()->create_user();
1730          role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1731          role_assign($studentrole->id, $user1->id, $coursecontext->id);
1732          $user2 = $this->getDataGenerator()->create_user();
1733          role_assign($studentrole->id, $user2->id, $coursecontext->id);
1734          $user3 = $this->getDataGenerator()->create_user();
1735          $user4 = $this->getDataGenerator()->create_user();
1736          role_assign($managerrole->id, $user4->id, $coursecontext->id);
1737  
1738          $this->setAdminUser();
1739  
1740          $roles = get_user_roles_in_course($user1->id, $course->id);
1741          $this->assertEquals([
1742              role_get_name($teacherrole, $coursecontext),
1743              role_get_name($studentrole, $coursecontext),
1744          ], array_map('strip_tags', explode(', ', $roles)));
1745  
1746          $roles = get_user_roles_in_course($user2->id, $course->id);
1747          $this->assertEquals([
1748              role_get_name($studentrole, $coursecontext),
1749          ], array_map('strip_tags', explode(', ', $roles)));
1750  
1751          $roles = get_user_roles_in_course($user3->id, $course->id);
1752          $this->assertEmpty($roles);
1753  
1754          // Managers should be able to see a link to their own role type, given they can assign it in the context.
1755          $this->setUser($user4);
1756          $roles = get_user_roles_in_course($user4->id, $course->id);
1757          $this->assertEquals([
1758              role_get_name($managerrole, $coursecontext),
1759          ], array_map('strip_tags', explode(', ', $roles)));
1760  
1761          // Managers should see 2 roles if viewing a user who has been enrolled as a student and a teacher in the course.
1762          $roles = get_user_roles_in_course($user1->id, $course->id);
1763          $this->assertEquals([
1764              role_get_name($teacherrole, $coursecontext),
1765              role_get_name($studentrole, $coursecontext),
1766          ], array_map('strip_tags', explode(', ', $roles)));
1767  
1768          // Students should not see the manager role if viewing a manager's profile.
1769          $this->setUser($user2);
1770          $roles = get_user_roles_in_course($user4->id, $course->id);
1771          $this->assertEmpty($roles); // Should see 0 roles on the manager's profile.
1772      }
1773  
1774      /**
1775       * Test get_user_roles and get_users_roles
1776       */
1777      public function test_get_user_roles() {
1778          global $DB, $CFG;
1779  
1780          $this->resetAfterTest();
1781  
1782          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1783          $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
1784          $course = $this->getDataGenerator()->create_course();
1785          $coursecontext = context_course::instance($course->id);
1786          $teacherrename = (object)array('roleid'=>$teacherrole->id, 'name'=>'Učitel', 'contextid'=>$coursecontext->id);
1787          $DB->insert_record('role_names', $teacherrename);
1788  
1789          $roleids = explode(',', $CFG->profileroles); // Should include teacher and student in new installs.
1790  
1791          $user1 = $this->getDataGenerator()->create_user();
1792          role_assign($teacherrole->id, $user1->id, $coursecontext->id);
1793          role_assign($studentrole->id, $user1->id, $coursecontext->id);
1794          $user2 = $this->getDataGenerator()->create_user();
1795          role_assign($studentrole->id, $user2->id, $coursecontext->id);
1796          $user3 = $this->getDataGenerator()->create_user();
1797  
1798          $u1roles = get_user_roles($coursecontext, $user1->id);
1799  
1800          $u2roles = get_user_roles($coursecontext, $user2->id);
1801  
1802          $allroles = get_users_roles($coursecontext, [], false);
1803          $specificuserroles = get_users_roles($coursecontext, [$user1->id, $user2->id]);
1804          $this->assertEquals($u1roles, $allroles[$user1->id]);
1805          $this->assertEquals($u1roles, $specificuserroles[$user1->id]);
1806          $this->assertEquals($u2roles, $allroles[$user2->id]);
1807          $this->assertEquals($u2roles, $specificuserroles[$user2->id]);
1808      }
1809  
1810      /**
1811       * Test has_capability(), has_any_capability() and has_all_capabilities().
1812       */
1813      public function test_has_capability_and_friends() {
1814          global $DB;
1815  
1816          $this->resetAfterTest();
1817  
1818          $course = $this->getDataGenerator()->create_course();
1819          $coursecontext = context_course::instance($course->id);
1820          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
1821          $teacher = $this->getDataGenerator()->create_user();
1822          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1823          $admin = $DB->get_record('user', array('username'=>'admin'));
1824  
1825          // Note: Here are used default capabilities, the full test is in permission evaluation bellow,
1826          // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
1827  
1828          $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupsection')));
1829          $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/backup:backupcourse')));
1830          $this->assertTrue($DB->record_exists('capabilities', array('name'=>'moodle/site:approvecourse')));
1831  
1832          $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse', 'moodle/site:approvecourse');
1833          $sc = array('moodle/backup:backupsection', 'moodle/backup:backupcourse');
1834  
1835          $this->setUser(0);
1836          $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext));
1837          $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext));
1838          $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1839          $this->assertFalse(has_any_capability($sca, $coursecontext));
1840          $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1841  
1842          $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $teacher));
1843          $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $teacher));
1844          $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $teacher));
1845          $this->assertTrue(has_any_capability($sca, $coursecontext, $teacher));
1846          $this->assertTrue(has_all_capabilities($sc, $coursecontext, $teacher));
1847          $this->assertFalse(has_all_capabilities($sca, $coursecontext, $teacher));
1848  
1849          $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext, $admin));
1850          $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext, $admin));
1851          $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext, $admin));
1852          $this->assertTrue(has_any_capability($sca, $coursecontext, $admin));
1853          $this->assertTrue(has_all_capabilities($sc, $coursecontext, $admin));
1854          $this->assertTrue(has_all_capabilities($sca, $coursecontext, $admin));
1855  
1856          $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, $admin, false));
1857          $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, $admin, false));
1858          $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, $admin, false));
1859          $this->assertFalse(has_any_capability($sca, $coursecontext, $admin, false));
1860          $this->assertFalse(has_all_capabilities($sc, $coursecontext, $admin, false));
1861          $this->assertFalse(has_all_capabilities($sca, $coursecontext, $admin, false));
1862  
1863          $this->setUser($teacher);
1864          $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1865          $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1866          $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext));
1867          $this->assertTrue(has_any_capability($sca, $coursecontext));
1868          $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1869          $this->assertFalse(has_all_capabilities($sca, $coursecontext));
1870  
1871          $this->setAdminUser();
1872          $this->assertTrue(has_capability('moodle/backup:backupsection', $coursecontext));
1873          $this->assertTrue(has_capability('moodle/backup:backupcourse', $coursecontext));
1874          $this->assertTrue(has_capability('moodle/site:approvecourse', $coursecontext));
1875          $this->assertTrue(has_any_capability($sca, $coursecontext));
1876          $this->assertTrue(has_all_capabilities($sc, $coursecontext));
1877          $this->assertTrue(has_all_capabilities($sca, $coursecontext));
1878  
1879          $this->assertFalse(has_capability('moodle/backup:backupsection', $coursecontext, 0));
1880          $this->assertFalse(has_capability('moodle/backup:backupcourse', $coursecontext, 0));
1881          $this->assertFalse(has_capability('moodle/site:approvecourse', $coursecontext, 0));
1882          $this->assertFalse(has_any_capability($sca, $coursecontext, 0));
1883          $this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
1884      }
1885  
1886      /**
1887       * Test that assigning a fake cap does not return.
1888       */
1889      public function test_fake_capability() {
1890          global $DB;
1891  
1892          $this->resetAfterTest();
1893  
1894          $course = $this->getDataGenerator()->create_course();
1895          $coursecontext = context_course::instance($course->id);
1896          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1897          $teacher = $this->getDataGenerator()->create_user();
1898  
1899          $fakecapname = 'moodle/fake:capability';
1900  
1901          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1902          $admin = $DB->get_record('user', array('username' => 'admin'));
1903  
1904          // Test a capability which does not exist.
1905          // Note: Do not use assign_capability because it will not allow fake caps.
1906          $DB->insert_record('role_capabilities', (object) [
1907              'contextid' => $coursecontext->id,
1908              'roleid' => $teacherrole->id,
1909              'capability' => $fakecapname,
1910              'permission' => CAP_ALLOW,
1911              'timemodified' => time(),
1912              'modifierid' => 0,
1913          ]);
1914  
1915          // Check `has_capability`.
1916          $this->assertFalse(has_capability($fakecapname, $coursecontext, $teacher));
1917          $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
1918          $this->assertFalse(has_capability($fakecapname, $coursecontext, $admin));
1919          $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
1920  
1921          // Check `get_with_capability_sql` (with uses `get_with_capability_join`).
1922          list($sql, $params) = get_with_capability_sql($coursecontext, $fakecapname);
1923          $users = $DB->get_records_sql($sql, $params);
1924  
1925          $this->assertFalse(array_key_exists($teacher->id, $users));
1926          $this->assertFalse(array_key_exists($admin->id, $users));
1927  
1928          // Check `get_users_by_capability`.
1929          $users = get_users_by_capability($coursecontext, $fakecapname);
1930  
1931          $this->assertFalse(array_key_exists($teacher->id, $users));
1932          $this->assertFalse(array_key_exists($admin->id, $users));
1933      }
1934  
1935      /**
1936       * Test that assigning a fake cap does not return.
1937       */
1938      public function test_fake_capability_assign() {
1939          global $DB;
1940  
1941          $this->resetAfterTest();
1942  
1943          $course = $this->getDataGenerator()->create_course();
1944          $coursecontext = context_course::instance($course->id);
1945          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1946          $teacher = $this->getDataGenerator()->create_user();
1947  
1948          $capability = 'moodle/fake:capability';
1949  
1950          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1951          $admin = $DB->get_record('user', array('username' => 'admin'));
1952  
1953          $this->expectException('coding_exception');
1954          $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
1955          assign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
1956      }
1957  
1958      /**
1959       * Test that assigning a fake cap does not return.
1960       */
1961      public function test_fake_capability_unassign() {
1962          global $DB;
1963  
1964          $this->resetAfterTest();
1965  
1966          $course = $this->getDataGenerator()->create_course();
1967          $coursecontext = context_course::instance($course->id);
1968          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
1969          $teacher = $this->getDataGenerator()->create_user();
1970  
1971          $capability = 'moodle/fake:capability';
1972  
1973          role_assign($teacherrole->id, $teacher->id, $coursecontext);
1974          $admin = $DB->get_record('user', array('username' => 'admin'));
1975  
1976          $this->expectException('coding_exception');
1977          $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
1978          unassign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
1979      }
1980  
1981      /**
1982       * Test that the caching in get_role_definitions() and get_role_definitions_uncached()
1983       * works as intended.
1984       */
1985      public function test_role_definition_caching() {
1986          global $DB;
1987  
1988          $this->resetAfterTest();
1989  
1990          // Get some role ids.
1991          $authenticatedrole = $DB->get_record('role', array('shortname' => 'user'), '*', MUST_EXIST);
1992          $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
1993          $emptyroleid = create_role('No capabilities', 'empty', 'A role with no capabilties');
1994          $course = $this->getDataGenerator()->create_course();
1995          $coursecontext = context_course::instance($course->id);
1996  
1997          // Instantiate the cache instance, since that does DB queries (get_config)
1998          // and we don't care about those.
1999          cache::make('core', 'roledefs');
2000  
2001          // One database query is not necessarily one database read, it seems. Find out how many.
2002          $startdbreads = $DB->perf_get_reads();
2003          $rs = $DB->get_recordset('user');
2004          $rs->close();
2005          $readsperquery = $DB->perf_get_reads() - $startdbreads;
2006  
2007          // Now load some role definitions, and check when it queries the database.
2008  
2009          // Load the capabilities for two roles. Should be one query.
2010          $startdbreads = $DB->perf_get_reads();
2011          get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2012          $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2013  
2014          // Load the capabilities for same two roles. Should not query the DB.
2015          $startdbreads = $DB->perf_get_reads();
2016          get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2017          $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2018  
2019          // Include a third role. Should do one DB query.
2020          $startdbreads = $DB->perf_get_reads();
2021          get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2022          $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2023  
2024          // Repeat call. No DB queries.
2025          $startdbreads = $DB->perf_get_reads();
2026          get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2027          $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2028  
2029          // Alter a role.
2030          role_change_permission($studentrole->id, $coursecontext, 'moodle/course:tag', CAP_ALLOW);
2031  
2032          // Should now know to do one query.
2033          $startdbreads = $DB->perf_get_reads();
2034          get_role_definitions([$authenticatedrole->id, $studentrole->id]);
2035          $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2036  
2037          // Now clear the in-memory cache, and verify that it does not query the DB.
2038          // Cannot use accesslib_clear_all_caches_for_unit_testing since that also
2039          // clears the MUC cache.
2040          global $ACCESSLIB_PRIVATE;
2041          $ACCESSLIB_PRIVATE->cacheroledefs = array();
2042  
2043          // Get all roles. Should not need the DB.
2044          $startdbreads = $DB->perf_get_reads();
2045          get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]);
2046          $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads);
2047      }
2048  
2049      /**
2050       * Tests get_user_capability_course() which checks a capability across all courses.
2051       */
2052      public function test_get_user_capability_course() {
2053          global $CFG, $USER;
2054  
2055          $this->resetAfterTest();
2056  
2057          $generator = $this->getDataGenerator();
2058          $cap = 'moodle/course:view';
2059  
2060          // The structure being created here is this:
2061          //
2062          // All tests work with the single capability 'moodle/course:view'.
2063          //
2064          //             ROLE DEF/OVERRIDE                        ROLE ASSIGNS
2065          //    Role:  Allow    Prohib    Empty   Def user      u1  u2  u3  u4   u5  u6  u7  u8
2066          // System    ALLOW    PROHIBIT                            A   E   A+E
2067          //   cat1                       ALLOW
2068          //     C1                               (ALLOW)                            P
2069          //     C2             ALLOW                                                    E   P
2070          //     cat2                     PREVENT
2071          //       C3                     ALLOW                                      E
2072          //       C4
2073          //   Misc.                                                             A
2074          //     C5    PREVENT                                                       A
2075          //     C6                       PROHIBIT
2076          //
2077          // Front-page and guest role stuff from the end of this test not included in the diagram.
2078  
2079          // Create a role which allows course:view and one that prohibits it, and one neither.
2080          $allowroleid = $generator->create_role();
2081          $prohibitroleid = $generator->create_role();
2082          $emptyroleid = $generator->create_role();
2083          $systemcontext = context_system::instance();
2084          assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
2085          assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
2086  
2087          // Create two categories (nested).
2088          $cat1 = $generator->create_category();
2089          $cat2 = $generator->create_category(['parent' => $cat1->id]);
2090  
2091          // Create six courses - two in cat1, two in cat2, and two in default category.
2092          // Shortnames are used for a sorting test. Otherwise they are not significant.
2093          $c1 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Z']);
2094          $c2 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Y']);
2095          $c3 = $generator->create_course(['category' => $cat2->id, 'shortname' => 'X']);
2096          $c4 = $generator->create_course(['category' => $cat2->id]);
2097          $c5 = $generator->create_course();
2098          $c6 = $generator->create_course();
2099  
2100          // Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
2101          assign_capability($cap, CAP_ALLOW, $emptyroleid,
2102                  context_coursecat::instance($cat1->id)->id);
2103          assign_capability($cap, CAP_PREVENT, $emptyroleid,
2104                  context_coursecat::instance($cat2->id)->id);
2105  
2106          // Course overrides: in C5, allow role is prevented; in C6, empty role is prohibited; in
2107          // C3, empty role is allowed.
2108          assign_capability($cap, CAP_PREVENT, $allowroleid,
2109                  context_course::instance($c5->id)->id);
2110          assign_capability($cap, CAP_PROHIBIT, $emptyroleid,
2111                  context_course::instance($c6->id)->id);
2112          assign_capability($cap, CAP_ALLOW, $emptyroleid,
2113                  context_course::instance($c3->id)->id);
2114          assign_capability($cap, CAP_ALLOW, $prohibitroleid,
2115                  context_course::instance($c2->id)->id);
2116  
2117          // User 1 has no roles except default user role.
2118          $u1 = $generator->create_user();
2119  
2120          // It returns false (annoyingly) if there are no courses.
2121          $this->assertFalse(get_user_capability_course($cap, $u1->id, true, '', 'id'));
2122  
2123          // Final override: in C1, default user role is allowed.
2124          assign_capability($cap, CAP_ALLOW, $CFG->defaultuserroleid,
2125                  context_course::instance($c1->id)->id);
2126  
2127          // Should now get C1 only.
2128          $courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
2129          $this->assert_course_ids([$c1->id], $courses);
2130  
2131          // User 2 has allow role (system wide).
2132          $u2 = $generator->create_user();
2133          role_assign($allowroleid, $u2->id, $systemcontext->id);
2134  
2135          // Should get everything except C5.
2136          $courses = get_user_capability_course($cap, $u2->id, true, '', 'id');
2137          $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c6->id], $courses);
2138  
2139          // User 3 has empty role (system wide).
2140          $u3 = $generator->create_user();
2141          role_assign($emptyroleid, $u3->id, $systemcontext->id);
2142  
2143          // Should get cat 1 courses but not cat2, except C3.
2144          $courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
2145          $this->assert_course_ids([$c1->id, $c2->id, $c3->id], $courses);
2146  
2147          // User 4 has allow and empty role (system wide).
2148          $u4 = $generator->create_user();
2149          role_assign($allowroleid, $u4->id, $systemcontext->id);
2150          role_assign($emptyroleid, $u4->id, $systemcontext->id);
2151  
2152          // Should get everything except C5 and C6.
2153          $courses = get_user_capability_course($cap, $u4->id, true, '', 'id');
2154          $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id], $courses);
2155  
2156          // User 5 has allow role in default category only.
2157          $u5 = $generator->create_user();
2158          role_assign($allowroleid, $u5->id, context_coursecat::instance($c5->category)->id);
2159  
2160          // Should get C1 and the default category courses but not C5.
2161          $courses = get_user_capability_course($cap, $u5->id, true, '', 'id');
2162          $this->assert_course_ids([$c1->id, $c6->id], $courses);
2163  
2164          // User 6 has a bunch of course roles: prohibit role in C1, empty role in C3, allow role in
2165          // C6.
2166          $u6 = $generator->create_user();
2167          role_assign($prohibitroleid, $u6->id, context_course::instance($c1->id)->id);
2168          role_assign($emptyroleid, $u6->id, context_course::instance($c3->id)->id);
2169          role_assign($allowroleid, $u6->id, context_course::instance($c5->id)->id);
2170  
2171          // Should get C3 only because the allow role is prevented in C5.
2172          $courses = get_user_capability_course($cap, $u6->id, true, '', 'id');
2173          $this->assert_course_ids([$c3->id], $courses);
2174  
2175          // User 7 has empty role in C2.
2176          $u7 = $generator->create_user();
2177          role_assign($emptyroleid, $u7->id, context_course::instance($c2->id)->id);
2178  
2179          // Should get C1 by the default user role override, and C2 by the cat1 level override.
2180          $courses = get_user_capability_course($cap, $u7->id, true, '', 'id');
2181          $this->assert_course_ids([$c1->id, $c2->id], $courses);
2182  
2183          // User 8 has prohibit role as system context, to verify that prohibits can't be overridden.
2184          $u8 = $generator->create_user();
2185          role_assign($prohibitroleid, $u8->id, context_course::instance($c2->id)->id);
2186  
2187          // Should get C1 by the default user role override, no other courses because the prohibit cannot be overridden.
2188          $courses = get_user_capability_course($cap, $u8->id, true, '', 'id');
2189          $this->assert_course_ids([$c1->id], $courses);
2190  
2191          // Admin user gets everything....
2192          $courses = get_user_capability_course($cap, get_admin()->id, true, '', 'id');
2193          $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c5->id, $c6->id],
2194                  $courses);
2195  
2196          // Unless you turn off doanything, when it only has the things a user with no role does.
2197          $courses = get_user_capability_course($cap, get_admin()->id, false, '', 'id');
2198          $this->assert_course_ids([$c1->id], $courses);
2199  
2200          // Using u3 as an example, test the limit feature.
2201          $courses = get_user_capability_course($cap, $u3->id, true, '', 'id', 2);
2202          $this->assert_course_ids([$c1->id, $c2->id], $courses);
2203  
2204          // Check sorting.
2205          $courses = get_user_capability_course($cap, $u3->id, true, '', 'shortname');
2206          $this->assert_course_ids([$c3->id, $c2->id, $c1->id], $courses);
2207  
2208          // Check returned fields - default.
2209          $courses = get_user_capability_course($cap, $u3->id, true, '', 'id');
2210          $this->assertEquals((object)['id' => $c1->id], $courses[0]);
2211  
2212          // Add a selection of fields, including the context ones with special handling.
2213          $courses = get_user_capability_course($cap, $u3->id, true, 'shortname, ctxlevel, ctxdepth, ctxinstance', 'id');
2214          $this->assertEquals((object)['id' => $c1->id, 'shortname' => 'Z', 'ctxlevel' => 50,
2215                  'ctxdepth' => 3, 'ctxinstance' => $c1->id], $courses[0]);
2216  
2217          // Test front page role - user 1 has no roles, but if we change the front page role
2218          // definition so that it has our capability, then they should see the front page course.
2219          // as well as C1.
2220          assign_capability($cap, CAP_ALLOW, $CFG->defaultfrontpageroleid, $systemcontext->id);
2221          $courses = get_user_capability_course($cap, $u1->id, true, '', 'id');
2222          $this->assert_course_ids([SITEID, $c1->id], $courses);
2223  
2224          // Check that temporary guest access (in this case, given on course 2 for user 1)
2225          // also is included, if it has this capability.
2226          assign_capability($cap, CAP_ALLOW, $CFG->guestroleid, $systemcontext->id);
2227          $this->setUser($u1);
2228          load_temp_course_role(context_course::instance($c2->id), $CFG->guestroleid);
2229          $courses = get_user_capability_course($cap, $USER->id, true, '', 'id');
2230          $this->assert_course_ids([SITEID, $c1->id, $c2->id], $courses);
2231      }
2232  
2233      /**
2234       * Extracts an array of course ids to make the above test script shorter.
2235       *
2236       * @param int[] $expected Array of expected course ids
2237       * @param stdClass[] $courses Array of course objects
2238       */
2239      protected function assert_course_ids(array $expected, array $courses) {
2240          $courseids = array_map(function($c) {
2241              return $c->id;
2242          }, $courses);
2243          $this->assertEquals($expected, $courseids);
2244      }
2245  
2246      /**
2247       * Test if course creator future capability lookup works.
2248       */
2249      public function test_guess_if_creator_will_have_course_capability() {
2250          global $DB, $CFG, $USER;
2251  
2252          $this->resetAfterTest();
2253  
2254          $category = $this->getDataGenerator()->create_category();
2255          $course = $this->getDataGenerator()->create_course(array('category'=>$category->id));
2256  
2257          $syscontext = context_system::instance();
2258          $categorycontext = context_coursecat::instance($category->id);
2259          $coursecontext = context_course::instance($course->id);
2260          $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST);
2261          $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
2262          $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST);
2263          $managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST);
2264  
2265          $this->assertEquals($teacherrole->id, $CFG->creatornewroleid);
2266  
2267          $creator = $this->getDataGenerator()->create_user();
2268          $manager = $this->getDataGenerator()->create_user();
2269          role_assign($managerrole->id, $manager->id, $categorycontext);
2270  
2271          $this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator));
2272          $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
2273          $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2274          $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator));
2275          $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator));
2276          $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator));
2277  
2278          $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager));
2279          $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager));
2280          $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager));
2281          $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id));
2282          $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id));
2283  
2284          $this->assertEquals(0, $USER->id);
2285          $this->assertFalse(has_capability('moodle/course:view', $categorycontext));
2286          $this->assertFalse(has_capability('moodle/role:assign', $categorycontext));
2287          $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext));
2288          $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext));
2289          $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2290          $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2291  
2292          $this->setUser($manager);
2293          $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
2294          $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
2295          $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
2296          $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2297          $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2298  
2299          $this->setAdminUser();
2300          $this->assertTrue(has_capability('moodle/role:assign', $categorycontext));
2301          $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext));
2302          $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext));
2303          $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext));
2304          $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext));
2305          $this->setUser(0);
2306  
2307          role_assign($creatorrole->id, $creator->id, $categorycontext);
2308  
2309          $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator));
2310          $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator));
2311          $this->assertFalse(