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