Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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 * Test that assigning a fake cap does not return. 1975 * 1976 * @covers ::get_users_by_capability 1977 * @covers ::get_with_capability_join 1978 * @covers ::get_with_capability_sql 1979 * @covers ::has_capability 1980 */ 1981 public function test_fake_capability() { 1982 global $DB; 1983 1984 $this->resetAfterTest(); 1985 1986 $course = $this->getDataGenerator()->create_course(); 1987 $coursecontext = context_course::instance($course->id); 1988 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 1989 $teacher = $this->getDataGenerator()->create_user(); 1990 1991 $fakecapname = 'moodle/fake:capability'; 1992 1993 role_assign($teacherrole->id, $teacher->id, $coursecontext); 1994 $admin = $DB->get_record('user', array('username' => 'admin')); 1995 1996 // Test a capability which does not exist. 1997 // Note: Do not use assign_capability because it will not allow fake caps. 1998 $DB->insert_record('role_capabilities', (object) [ 1999 'contextid' => $coursecontext->id, 2000 'roleid' => $teacherrole->id, 2001 'capability' => $fakecapname, 2002 'permission' => CAP_ALLOW, 2003 'timemodified' => time(), 2004 'modifierid' => 0, 2005 ]); 2006 2007 // Check `has_capability`. 2008 $this->assertFalse(has_capability($fakecapname, $coursecontext, $teacher)); 2009 $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code."); 2010 $this->assertFalse(has_capability($fakecapname, $coursecontext, $admin)); 2011 $this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code."); 2012 2013 // Check `get_with_capability_sql` (with uses `get_with_capability_join`). 2014 list($sql, $params) = get_with_capability_sql($coursecontext, $fakecapname); 2015 $users = $DB->get_records_sql($sql, $params); 2016 2017 $this->assertFalse(array_key_exists($teacher->id, $users)); 2018 $this->assertFalse(array_key_exists($admin->id, $users)); 2019 2020 // Check `get_users_by_capability`. 2021 $users = get_users_by_capability($coursecontext, $fakecapname); 2022 2023 $this->assertFalse(array_key_exists($teacher->id, $users)); 2024 $this->assertFalse(array_key_exists($admin->id, $users)); 2025 } 2026 2027 /** 2028 * Test that assigning a fake cap does not return. 2029 * 2030 * @covers ::assign_capability 2031 */ 2032 public function test_fake_capability_assign() { 2033 global $DB; 2034 2035 $this->resetAfterTest(); 2036 2037 $course = $this->getDataGenerator()->create_course(); 2038 $coursecontext = context_course::instance($course->id); 2039 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 2040 $teacher = $this->getDataGenerator()->create_user(); 2041 2042 $capability = 'moodle/fake:capability'; 2043 2044 role_assign($teacherrole->id, $teacher->id, $coursecontext); 2045 $admin = $DB->get_record('user', array('username' => 'admin')); 2046 2047 $this->expectException('coding_exception'); 2048 $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code."); 2049 assign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext); 2050 } 2051 2052 /** 2053 * Test that assigning a fake cap does not return. 2054 * 2055 * @covers ::unassign_capability 2056 */ 2057 public function test_fake_capability_unassign() { 2058 global $DB; 2059 2060 $this->resetAfterTest(); 2061 2062 $course = $this->getDataGenerator()->create_course(); 2063 $coursecontext = context_course::instance($course->id); 2064 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 2065 $teacher = $this->getDataGenerator()->create_user(); 2066 2067 $capability = 'moodle/fake:capability'; 2068 2069 role_assign($teacherrole->id, $teacher->id, $coursecontext); 2070 $admin = $DB->get_record('user', array('username' => 'admin')); 2071 2072 $this->expectException('coding_exception'); 2073 $this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code."); 2074 unassign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext); 2075 } 2076 2077 /** 2078 * Test that the caching in get_role_definitions() and get_role_definitions_uncached() 2079 * works as intended. 2080 * 2081 * @covers ::get_role_definitions 2082 * @covers ::role_change_permission 2083 */ 2084 public function test_role_definition_caching() { 2085 global $DB; 2086 2087 $this->resetAfterTest(); 2088 2089 // Get some role ids. 2090 $authenticatedrole = $DB->get_record('role', array('shortname' => 'user'), '*', MUST_EXIST); 2091 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2092 $emptyroleid = create_role('No capabilities', 'empty', 'A role with no capabilties'); 2093 $course = $this->getDataGenerator()->create_course(); 2094 $coursecontext = context_course::instance($course->id); 2095 2096 // Instantiate the cache instance, since that does DB queries (get_config) 2097 // and we don't care about those. 2098 cache::make('core', 'roledefs'); 2099 2100 // One database query is not necessarily one database read, it seems. Find out how many. 2101 $startdbreads = $DB->perf_get_reads(); 2102 $rs = $DB->get_recordset('user'); 2103 $rs->close(); 2104 $readsperquery = $DB->perf_get_reads() - $startdbreads; 2105 2106 // Now load some role definitions, and check when it queries the database. 2107 2108 // Load the capabilities for two roles. Should be one query. 2109 $startdbreads = $DB->perf_get_reads(); 2110 get_role_definitions([$authenticatedrole->id, $studentrole->id]); 2111 $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads); 2112 2113 // Load the capabilities for same two roles. Should not query the DB. 2114 $startdbreads = $DB->perf_get_reads(); 2115 get_role_definitions([$authenticatedrole->id, $studentrole->id]); 2116 $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads); 2117 2118 // Include a third role. Should do one DB query. 2119 $startdbreads = $DB->perf_get_reads(); 2120 get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]); 2121 $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads); 2122 2123 // Repeat call. No DB queries. 2124 $startdbreads = $DB->perf_get_reads(); 2125 get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]); 2126 $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads); 2127 2128 // Alter a role. 2129 role_change_permission($studentrole->id, $coursecontext, 'moodle/course:tag', CAP_ALLOW); 2130 2131 // Should now know to do one query. 2132 $startdbreads = $DB->perf_get_reads(); 2133 get_role_definitions([$authenticatedrole->id, $studentrole->id]); 2134 $this->assertEquals(1 * $readsperquery, $DB->perf_get_reads() - $startdbreads); 2135 2136 // Now clear the in-memory cache, and verify that it does not query the DB. 2137 // Cannot use accesslib_clear_all_caches_for_unit_testing since that also 2138 // clears the MUC cache. 2139 global $ACCESSLIB_PRIVATE; 2140 $ACCESSLIB_PRIVATE->cacheroledefs = array(); 2141 2142 // Get all roles. Should not need the DB. 2143 $startdbreads = $DB->perf_get_reads(); 2144 get_role_definitions([$authenticatedrole->id, $studentrole->id, $emptyroleid]); 2145 $this->assertEquals(0 * $readsperquery, $DB->perf_get_reads() - $startdbreads); 2146 } 2147 2148 /** 2149 * Tests get_user_capability_course() which checks a capability across all courses. 2150 * 2151 * @covers ::get_user_capability_course 2152 */ 2153 public function test_get_user_capability_course() { 2154 global $CFG, $USER; 2155 2156 $this->resetAfterTest(); 2157 2158 $generator = $this->getDataGenerator(); 2159 $cap = 'moodle/course:view'; 2160 2161 // The structure being created here is this: 2162 // 2163 // All tests work with the single capability 'moodle/course:view'. 2164 // 2165 // ROLE DEF/OVERRIDE ROLE ASSIGNS 2166 // Role: Allow Prohib Empty Def user u1 u2 u3 u4 u5 u6 u7 u8 2167 // System ALLOW PROHIBIT A E A+E 2168 // cat1 ALLOW 2169 // C1 (ALLOW) P 2170 // C2 ALLOW E P 2171 // cat2 PREVENT 2172 // C3 ALLOW E 2173 // C4 2174 // Misc. A 2175 // C5 PREVENT A 2176 // C6 PROHIBIT 2177 // 2178 // Front-page and guest role stuff from the end of this test not included in the diagram. 2179 2180 // Create a role which allows course:view and one that prohibits it, and one neither. 2181 $allowroleid = $generator->create_role(); 2182 $prohibitroleid = $generator->create_role(); 2183 $emptyroleid = $generator->create_role(); 2184 $systemcontext = context_system::instance(); 2185 assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id); 2186 assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id); 2187 2188 // Create two categories (nested). 2189 $cat1 = $generator->create_category(); 2190 $cat2 = $generator->create_category(['parent' => $cat1->id]); 2191 2192 // Create six courses - two in cat1, two in cat2, and two in default category. 2193 // Shortnames are used for a sorting test. Otherwise they are not significant. 2194 $c1 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Z']); 2195 $c2 = $generator->create_course(['category' => $cat1->id, 'shortname' => 'Y']); 2196 $c3 = $generator->create_course(['category' => $cat2->id, 'shortname' => 'X']); 2197 $c4 = $generator->create_course(['category' => $cat2->id]); 2198 $c5 = $generator->create_course(); 2199 $c6 = $generator->create_course(); 2200 2201 // Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented. 2202 assign_capability($cap, CAP_ALLOW, $emptyroleid, 2203 context_coursecat::instance($cat1->id)->id); 2204 assign_capability($cap, CAP_PREVENT, $emptyroleid, 2205 context_coursecat::instance($cat2->id)->id); 2206 2207 // Course overrides: in C5, allow role is prevented; in C6, empty role is prohibited; in 2208 // C3, empty role is allowed. 2209 assign_capability($cap, CAP_PREVENT, $allowroleid, 2210 context_course::instance($c5->id)->id); 2211 assign_capability($cap, CAP_PROHIBIT, $emptyroleid, 2212 context_course::instance($c6->id)->id); 2213 assign_capability($cap, CAP_ALLOW, $emptyroleid, 2214 context_course::instance($c3->id)->id); 2215 assign_capability($cap, CAP_ALLOW, $prohibitroleid, 2216 context_course::instance($c2->id)->id); 2217 2218 // User 1 has no roles except default user role. 2219 $u1 = $generator->create_user(); 2220 2221 // It returns false (annoyingly) if there are no courses. 2222 $this->assertFalse(get_user_capability_course($cap, $u1->id, true, '', 'id')); 2223 2224 // Final override: in C1, default user role is allowed. 2225 assign_capability($cap, CAP_ALLOW, $CFG->defaultuserroleid, 2226 context_course::instance($c1->id)->id); 2227 2228 // Should now get C1 only. 2229 $courses = get_user_capability_course($cap, $u1->id, true, '', 'id'); 2230 $this->assert_course_ids([$c1->id], $courses); 2231 2232 // User 2 has allow role (system wide). 2233 $u2 = $generator->create_user(); 2234 role_assign($allowroleid, $u2->id, $systemcontext->id); 2235 2236 // Should get everything except C5. 2237 $courses = get_user_capability_course($cap, $u2->id, true, '', 'id'); 2238 $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c6->id], $courses); 2239 2240 // User 3 has empty role (system wide). 2241 $u3 = $generator->create_user(); 2242 role_assign($emptyroleid, $u3->id, $systemcontext->id); 2243 2244 // Should get cat 1 courses but not cat2, except C3. 2245 $courses = get_user_capability_course($cap, $u3->id, true, '', 'id'); 2246 $this->assert_course_ids([$c1->id, $c2->id, $c3->id], $courses); 2247 2248 // User 4 has allow and empty role (system wide). 2249 $u4 = $generator->create_user(); 2250 role_assign($allowroleid, $u4->id, $systemcontext->id); 2251 role_assign($emptyroleid, $u4->id, $systemcontext->id); 2252 2253 // Should get everything except C5 and C6. 2254 $courses = get_user_capability_course($cap, $u4->id, true, '', 'id'); 2255 $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id], $courses); 2256 2257 // User 5 has allow role in default category only. 2258 $u5 = $generator->create_user(); 2259 role_assign($allowroleid, $u5->id, context_coursecat::instance($c5->category)->id); 2260 2261 // Should get C1 and the default category courses but not C5. 2262 $courses = get_user_capability_course($cap, $u5->id, true, '', 'id'); 2263 $this->assert_course_ids([$c1->id, $c6->id], $courses); 2264 2265 // User 6 has a bunch of course roles: prohibit role in C1, empty role in C3, allow role in 2266 // C6. 2267 $u6 = $generator->create_user(); 2268 role_assign($prohibitroleid, $u6->id, context_course::instance($c1->id)->id); 2269 role_assign($emptyroleid, $u6->id, context_course::instance($c3->id)->id); 2270 role_assign($allowroleid, $u6->id, context_course::instance($c5->id)->id); 2271 2272 // Should get C3 only because the allow role is prevented in C5. 2273 $courses = get_user_capability_course($cap, $u6->id, true, '', 'id'); 2274 $this->assert_course_ids([$c3->id], $courses); 2275 2276 // User 7 has empty role in C2. 2277 $u7 = $generator->create_user(); 2278 role_assign($emptyroleid, $u7->id, context_course::instance($c2->id)->id); 2279 2280 // Should get C1 by the default user role override, and C2 by the cat1 level override. 2281 $courses = get_user_capability_course($cap, $u7->id, true, '', 'id'); 2282 $this->assert_course_ids([$c1->id, $c2->id], $courses); 2283 2284 // User 8 has prohibit role as system context, to verify that prohibits can't be overridden. 2285 $u8 = $generator->create_user(); 2286 role_assign($prohibitroleid, $u8->id, context_course::instance($c2->id)->id); 2287 2288 // Should get C1 by the default user role override, no other courses because the prohibit cannot be overridden. 2289 $courses = get_user_capability_course($cap, $u8->id, true, '', 'id'); 2290 $this->assert_course_ids([$c1->id], $courses); 2291 2292 // Admin user gets everything.... 2293 $courses = get_user_capability_course($cap, get_admin()->id, true, '', 'id'); 2294 $this->assert_course_ids([SITEID, $c1->id, $c2->id, $c3->id, $c4->id, $c5->id, $c6->id], 2295 $courses); 2296 2297 // Unless you turn off doanything, when it only has the things a user with no role does. 2298 $courses = get_user_capability_course($cap, get_admin()->id, false, '', 'id'); 2299 $this->assert_course_ids([$c1->id], $courses); 2300 2301 // Using u3 as an example, test the limit feature. 2302 $courses = get_user_capability_course($cap, $u3->id, true, '', 'id', 2); 2303 $this->assert_course_ids([$c1->id, $c2->id], $courses); 2304 2305 // Check sorting. 2306 $courses = get_user_capability_course($cap, $u3->id, true, '', 'shortname'); 2307 $this->assert_course_ids([$c3->id, $c2->id, $c1->id], $courses); 2308 2309 // Check returned fields - default. 2310 $courses = get_user_capability_course($cap, $u3->id, true, '', 'id'); 2311 $this->assertEquals((object)['id' => $c1->id], $courses[0]); 2312 2313 // Add a selection of fields, including the context ones with special handling. 2314 $courses = get_user_capability_course($cap, $u3->id, true, 'shortname, ctxlevel, ctxdepth, ctxinstance', 'id'); 2315 $this->assertEquals((object)['id' => $c1->id, 'shortname' => 'Z', 'ctxlevel' => 50, 2316 'ctxdepth' => 3, 'ctxinstance' => $c1->id], $courses[0]); 2317 2318 // Test front page role - user 1 has no roles, but if we change the front page role 2319 // definition so that it has our capability, then they should see the front page course. 2320 // as well as C1. 2321 assign_capability($cap, CAP_ALLOW, $CFG->defaultfrontpageroleid, $systemcontext->id); 2322 $courses = get_user_capability_course($cap, $u1->id, true, '', 'id'); 2323 $this->assert_course_ids([SITEID, $c1->id], $courses); 2324 2325 // Check that temporary guest access (in this case, given on course 2 for user 1) 2326 // also is included, if it has this capability. 2327 assign_capability($cap, CAP_ALLOW, $CFG->guestroleid, $systemcontext->id); 2328 $this->setUser($u1); 2329 load_temp_course_role(context_course::instance($c2->id), $CFG->guestroleid); 2330 $courses = get_user_capability_course($cap, $USER->id, true, '', 'id'); 2331 $this->assert_course_ids([SITEID, $c1->id, $c2->id], $courses); 2332 } 2333 2334 /** 2335 * Tests get_user_capability_contexts() which checks a capability across all courses and categories. 2336 * Testing for categories only because courses results are covered by test_get_user_capability_course. 2337 * 2338 * @covers ::get_user_capability_contexts 2339 */ 2340 public function test_get_user_capability_contexts() { 2341 $this->resetAfterTest(); 2342 2343 $generator = $this->getDataGenerator(); 2344 $cap = 'moodle/contentbank:access'; 2345 $defaultcategoryid = 1; 2346 2347 // The structure being created here is this: 2348 // 2349 // All tests work with the single capability 'moodle/contentbank:access'. 2350 // ROLE DEF/OVERRIDE . 2351 // Role: Allow Prohibit Empty . 2352 // System ALLOW PROHIBIT . 2353 // cat1 PREVENT ALLOW ALLOW . 2354 // cat3 ALLOW PROHIBIT . 2355 // cat2 PROHIBIT PROHIBIT PROHIBIT . 2356 2357 // Create a role which allows contentbank:access and one that prohibits it, and one neither. 2358 $allowroleid = $generator->create_role(); 2359 $prohibitroleid = $generator->create_role(); 2360 $emptyroleid = $generator->create_role(); 2361 $systemcontext = context_system::instance(); 2362 assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id); 2363 assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id); 2364 2365 // Create three categories (two of them nested). 2366 $cat1 = $generator->create_category(['name' => 'Aardvarks']); 2367 $cat2 = $generator->create_category(['name' => 'Badgers']); 2368 $cat3 = $generator->create_category(['parent' => $cat1->id, 'name' => 'Cheetahs']); 2369 2370 // Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented. 2371 assign_capability($cap, CAP_ALLOW, $emptyroleid, 2372 context_coursecat::instance($cat1->id)->id); 2373 assign_capability($cap, CAP_PREVENT, $emptyroleid, 2374 context_coursecat::instance($cat2->id)->id); 2375 2376 // Course category overrides: in cat1, allow role is prevented and prohibit role is allowed; 2377 // in Cat2, allow role is prohibited. 2378 assign_capability($cap, CAP_PREVENT, $allowroleid, 2379 context_coursecat::instance($cat1->id)->id); 2380 assign_capability($cap, CAP_ALLOW, $prohibitroleid, 2381 context_coursecat::instance($cat1->id)->id); 2382 assign_capability($cap, CAP_PROHIBIT, $allowroleid, 2383 context_coursecat::instance($cat2->id)->id); 2384 2385 // User 1 has no roles except default user role. 2386 $u1 = $generator->create_user(); 2387 2388 // It returns false (annoyingly) if there are no course categories. 2389 list($categories, $courses) = get_user_capability_contexts($cap, true, $u1->id); 2390 $this->assertFalse($categories); 2391 2392 // User 2 has allow role (system wide). 2393 $u2 = $generator->create_user(); 2394 role_assign($allowroleid, $u2->id, $systemcontext->id); 2395 2396 // Should get $defaultcategory only. cat2 is prohibited; cat1 is prevented, so cat3 is not allowed. 2397 list($categories, $courses) = get_user_capability_contexts($cap, true, $u2->id); 2398 // Using same assert_course_ids helper even when we are checking course category ids. 2399 $this->assert_course_ids([$defaultcategoryid], $categories); 2400 2401 // User 3 has empty role (system wide). 2402 $u3 = $generator->create_user(); 2403 role_assign($emptyroleid, $u3->id, $systemcontext->id); 2404 2405 // Should get cat1 and cat3. cat2 is prohibited; no access to system level. Sorted by category name. 2406 list($categories, $courses) = get_user_capability_contexts($cap, true, $u3->id, true, '', '', '', 'name'); 2407 $this->assert_course_ids([$cat1->id, $cat3->id], $categories); 2408 2409 // User 4 has prohibit role (system wide). 2410 $u4 = $generator->create_user(); 2411 role_assign($prohibitroleid, $u4->id, $systemcontext->id); 2412 2413 // Should not get any, because all of them are prohibited at system level. 2414 // Even if we try to allow an specific category. 2415 list($categories, $courses) = get_user_capability_contexts($cap, true, $u4->id); 2416 $this->assertFalse($categories); 2417 } 2418 2419 /** 2420 * Extracts an array of course ids to make the above test script shorter. 2421 * 2422 * @param int[] $expected Array of expected course ids 2423 * @param stdClass[] $courses Array of course objects 2424 */ 2425 protected function assert_course_ids(array $expected, array $courses) { 2426 $courseids = array_map(function($c) { 2427 return $c->id; 2428 }, $courses); 2429 $this->assertEquals($expected, $courseids); 2430 } 2431 2432 /** 2433 * Test if course creator future capability lookup works. 2434 * 2435 * @covers ::guess_if_creator_will_have_course_capability 2436 * @covers ::has_capability 2437 */ 2438 public function test_guess_if_creator_will_have_course_capability() { 2439 global $DB, $CFG, $USER; 2440 2441 $this->resetAfterTest(); 2442 2443 $category = $this->getDataGenerator()->create_category(); 2444 $course = $this->getDataGenerator()->create_course(array('category'=>$category->id)); 2445 2446 $syscontext = context_system::instance(); 2447 $categorycontext = context_coursecat::instance($category->id); 2448 $coursecontext = context_course::instance($course->id); 2449 $studentrole = $DB->get_record('role', array('shortname'=>'student'), '*', MUST_EXIST); 2450 $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST); 2451 $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'), '*', MUST_EXIST); 2452 $managerrole = $DB->get_record('role', array('shortname'=>'manager'), '*', MUST_EXIST); 2453 2454 $this->assertEquals($teacherrole->id, $CFG->creatornewroleid); 2455 2456 $creator = $this->getDataGenerator()->create_user(); 2457 $manager = $this->getDataGenerator()->create_user(); 2458 role_assign($managerrole->id, $manager->id, $categorycontext); 2459 2460 $this->assertFalse(has_capability('moodle/course:view', $categorycontext, $creator)); 2461 $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator)); 2462 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator)); 2463 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator)); 2464 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator)); 2465 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator)); 2466 2467 $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager)); 2468 $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext, $manager)); 2469 $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext, $manager)); 2470 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager->id)); 2471 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager->id)); 2472 2473 $this->assertEquals(0, $USER->id); 2474 $this->assertFalse(has_capability('moodle/course:view', $categorycontext)); 2475 $this->assertFalse(has_capability('moodle/role:assign', $categorycontext)); 2476 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext)); 2477 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext)); 2478 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext)); 2479 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext)); 2480 2481 $this->setUser($manager); 2482 $this->assertTrue(has_capability('moodle/role:assign', $categorycontext)); 2483 $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext)); 2484 $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext)); 2485 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext)); 2486 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext)); 2487 2488 $this->setAdminUser(); 2489 $this->assertTrue(has_capability('moodle/role:assign', $categorycontext)); 2490 $this->assertTrue(has_capability('moodle/course:visibility', $categorycontext)); 2491 $this->assertTrue(has_capability('moodle/course:visibility', $coursecontext)); 2492 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext)); 2493 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext)); 2494 $this->setUser(0); 2495 2496 role_assign($creatorrole->id, $creator->id, $categorycontext); 2497 2498 $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, $creator)); 2499 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator)); 2500 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator)); 2501 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator)); 2502 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator)); 2503 2504 $this->setUser($creator); 2505 $this->assertFalse(has_capability('moodle/role:assign', $categorycontext, null)); 2506 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, null)); 2507 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, null)); 2508 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, null)); 2509 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, null)); 2510 $this->setUser(0); 2511 2512 set_config('creatornewroleid', $studentrole->id); 2513 2514 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $creator)); 2515 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $creator)); 2516 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $creator)); 2517 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $creator)); 2518 2519 set_config('creatornewroleid', $teacherrole->id); 2520 2521 role_change_permission($managerrole->id, $categorycontext, 'moodle/course:visibility', CAP_PREVENT); 2522 role_assign($creatorrole->id, $manager->id, $categorycontext); 2523 2524 $this->assertTrue(has_capability('moodle/course:view', $categorycontext, $manager)); 2525 $this->assertTrue(has_capability('moodle/course:view', $coursecontext, $manager)); 2526 $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager)); 2527 $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager)); 2528 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager)); 2529 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager)); 2530 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager)); 2531 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager)); 2532 2533 role_change_permission($managerrole->id, $categorycontext, 'moodle/course:view', CAP_PREVENT); 2534 $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager)); 2535 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager)); 2536 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager)); 2537 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager)); 2538 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager)); 2539 2540 $this->getDataGenerator()->enrol_user($manager->id, $course->id, 0); 2541 2542 $this->assertTrue(has_capability('moodle/role:assign', $categorycontext, $manager)); 2543 $this->assertTrue(has_capability('moodle/role:assign', $coursecontext, $manager)); 2544 $this->assertTrue(is_enrolled($coursecontext, $manager)); 2545 $this->assertFalse(has_capability('moodle/course:visibility', $categorycontext, $manager)); 2546 $this->assertFalse(has_capability('moodle/course:visibility', $coursecontext, $manager)); 2547 $this->assertTrue(guess_if_creator_will_have_course_capability('moodle/course:visibility', $categorycontext, $manager)); 2548 $this->assertFalse(guess_if_creator_will_have_course_capability('moodle/course:visibility', $coursecontext, $manager)); 2549 2550 // Test problems. 2551 2552 try { 2553 guess_if_creator_will_have_course_capability('moodle/course:visibility', $syscontext, $creator); 2554 $this->fail('Exception expected when non course/category context passed to guess_if_creator_will_have_course_capability()'); 2555 } catch (moodle_exception $e) { 2556 $this->assertInstanceOf('coding_exception', $e); 2557 } 2558 } 2559 2560 /** 2561 * Test require_capability() exceptions. 2562 * 2563 * @covers ::require_capability 2564 */ 2565 public function test_require_capability() { 2566 $this->resetAfterTest(); 2567 2568 $syscontext = context_system::instance(); 2569 2570 $this->setUser(0); 2571 $this->assertFalse(has_capability('moodle/site:config', $syscontext)); 2572 try { 2573 require_capability('moodle/site:config', $syscontext); 2574 $this->fail('Exception expected from require_capability()'); 2575 } catch (moodle_exception $e) { 2576 $this->assertInstanceOf('required_capability_exception', $e); 2577 } 2578 $this->setAdminUser(); 2579 $this->assertFalse(has_capability('moodle/site:config', $syscontext, 0)); 2580 try { 2581 require_capability('moodle/site:config', $syscontext, 0); 2582 $this->fail('Exception expected from require_capability()'); 2583 } catch (moodle_exception $e) { 2584 $this->assertInstanceOf('required_capability_exception', $e); 2585 } 2586 $this->assertFalse(has_capability('moodle/site:config', $syscontext, null, false)); 2587 try { 2588 require_capability('moodle/site:config', $syscontext, null, false); 2589 $this->fail('Exception expected from require_capability()'); 2590 } catch (moodle_exception $e) { 2591 $this->assertInstanceOf('required_capability_exception', $e); 2592 } 2593 } 2594 2595 /** 2596 * Test that enrolled users SQL does not return any values for users in 2597 * other courses. 2598 * 2599 * @covers ::get_enrolled_users 2600 * @covers ::get_suspended_userids 2601 */ 2602 public function test_get_enrolled_sql_different_course() { 2603 global $DB; 2604 2605 $this->resetAfterTest(); 2606 2607 $course = $this->getDataGenerator()->create_course(); 2608 $context = context_course::instance($course->id); 2609 $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2610 $user = $this->getDataGenerator()->create_user(); 2611 2612 // This user should not appear anywhere, we're not interested in that context. 2613 $course2 = $this->getDataGenerator()->create_course(); 2614 $this->getDataGenerator()->enrol_user($user->id, $course2->id, $student->id); 2615 2616 $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false); 2617 $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true); 2618 $suspended = get_suspended_userids($context); 2619 2620 $this->assertFalse(isset($enrolled[$user->id])); 2621 $this->assertFalse(isset($active[$user->id])); 2622 $this->assertFalse(isset($suspended[$user->id])); 2623 $this->assertCount(0, $enrolled); 2624 $this->assertCount(0, $active); 2625 $this->assertCount(0, $suspended); 2626 } 2627 2628 /** 2629 * Test that enrolled users SQL does not return any values for role 2630 * assignments without an enrolment. 2631 * 2632 * @covers ::get_enrolled_users 2633 * @covers ::get_suspended_userids 2634 */ 2635 public function test_get_enrolled_sql_role_only() { 2636 global $DB; 2637 2638 $this->resetAfterTest(); 2639 2640 $course = $this->getDataGenerator()->create_course(); 2641 $context = context_course::instance($course->id); 2642 $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2643 $user = $this->getDataGenerator()->create_user(); 2644 2645 // Role assignment is not the same as course enrollment. 2646 role_assign($student->id, $user->id, $context->id); 2647 2648 $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false); 2649 $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true); 2650 $suspended = get_suspended_userids($context); 2651 2652 $this->assertFalse(isset($enrolled[$user->id])); 2653 $this->assertFalse(isset($active[$user->id])); 2654 $this->assertFalse(isset($suspended[$user->id])); 2655 $this->assertCount(0, $enrolled); 2656 $this->assertCount(0, $active); 2657 $this->assertCount(0, $suspended); 2658 } 2659 2660 /** 2661 * Test that multiple enrolments for the same user are counted correctly. 2662 * 2663 * @covers ::get_enrolled_users 2664 * @covers ::get_suspended_userids 2665 */ 2666 public function test_get_enrolled_sql_multiple_enrolments() { 2667 global $DB; 2668 2669 $this->resetAfterTest(); 2670 2671 $course = $this->getDataGenerator()->create_course(); 2672 $context = context_course::instance($course->id); 2673 $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2674 $user = $this->getDataGenerator()->create_user(); 2675 2676 // Add a suspended enrol. 2677 $selfinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'self')); 2678 $selfplugin = enrol_get_plugin('self'); 2679 $selfplugin->update_status($selfinstance, ENROL_INSTANCE_ENABLED); 2680 $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'self', 0, 0, ENROL_USER_SUSPENDED); 2681 2682 // Should be enrolled, but not active - user is suspended. 2683 $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false); 2684 $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true); 2685 $suspended = get_suspended_userids($context); 2686 2687 $this->assertTrue(isset($enrolled[$user->id])); 2688 $this->assertFalse(isset($active[$user->id])); 2689 $this->assertTrue(isset($suspended[$user->id])); 2690 $this->assertCount(1, $enrolled); 2691 $this->assertCount(0, $active); 2692 $this->assertCount(1, $suspended); 2693 2694 // Add an active enrol for the user. Any active enrol makes them enrolled. 2695 $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id); 2696 2697 // User should be active now. 2698 $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false); 2699 $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true); 2700 $suspended = get_suspended_userids($context); 2701 2702 $this->assertTrue(isset($enrolled[$user->id])); 2703 $this->assertTrue(isset($active[$user->id])); 2704 $this->assertFalse(isset($suspended[$user->id])); 2705 $this->assertCount(1, $enrolled); 2706 $this->assertCount(1, $active); 2707 $this->assertCount(0, $suspended); 2708 2709 } 2710 2711 /** 2712 * Test that enrolled users SQL does not return any values for users 2713 * without a group when $context is not a valid course context. 2714 * 2715 * @covers ::get_enrolled_users 2716 */ 2717 public function test_get_enrolled_sql_userswithoutgroup() { 2718 global $DB; 2719 2720 $this->resetAfterTest(); 2721 2722 $systemcontext = context_system::instance(); 2723 $course = $this->getDataGenerator()->create_course(); 2724 $coursecontext = context_course::instance($course->id); 2725 $user1 = $this->getDataGenerator()->create_user(); 2726 $user2 = $this->getDataGenerator()->create_user(); 2727 2728 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 2729 $this->getDataGenerator()->enrol_user($user2->id, $course->id); 2730 2731 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 2732 groups_add_member($group, $user1); 2733 2734 $enrolled = get_enrolled_users($coursecontext); 2735 $this->assertCount(2, $enrolled); 2736 2737 // Get users without any group on the course context. 2738 $enrolledwithoutgroup = get_enrolled_users($coursecontext, '', USERSWITHOUTGROUP); 2739 $this->assertCount(1, $enrolledwithoutgroup); 2740 $this->assertFalse(isset($enrolledwithoutgroup[$user1->id])); 2741 2742 // Get users without any group on the system context (it should throw an exception). 2743 $this->expectException('coding_exception'); 2744 get_enrolled_users($systemcontext, '', USERSWITHOUTGROUP); 2745 } 2746 2747 public function get_enrolled_sql_provider() { 2748 return array( 2749 array( 2750 // Two users who are enrolled. 2751 'users' => array( 2752 array( 2753 'enrolled' => true, 2754 'active' => true, 2755 ), 2756 array( 2757 'enrolled' => true, 2758 'active' => true, 2759 ), 2760 ), 2761 'counts' => array( 2762 'enrolled' => 2, 2763 'active' => 2, 2764 'suspended' => 0, 2765 ), 2766 ), 2767 array( 2768 // A user who is suspended. 2769 'users' => array( 2770 array( 2771 'status' => ENROL_USER_SUSPENDED, 2772 'enrolled' => true, 2773 'suspended' => true, 2774 ), 2775 ), 2776 'counts' => array( 2777 'enrolled' => 1, 2778 'active' => 0, 2779 'suspended' => 1, 2780 ), 2781 ), 2782 array( 2783 // One of each. 2784 'users' => array( 2785 array( 2786 'enrolled' => true, 2787 'active' => true, 2788 ), 2789 array( 2790 'status' => ENROL_USER_SUSPENDED, 2791 'enrolled' => true, 2792 'suspended' => true, 2793 ), 2794 ), 2795 'counts' => array( 2796 'enrolled' => 2, 2797 'active' => 1, 2798 'suspended' => 1, 2799 ), 2800 ), 2801 array( 2802 // One user who is not yet enrolled. 2803 'users' => array( 2804 array( 2805 'timestart' => DAYSECS, 2806 'enrolled' => true, 2807 'active' => false, 2808 'suspended' => true, 2809 ), 2810 ), 2811 'counts' => array( 2812 'enrolled' => 1, 2813 'active' => 0, 2814 'suspended' => 1, 2815 ), 2816 ), 2817 array( 2818 // One user who is no longer enrolled 2819 'users' => array( 2820 array( 2821 'timeend' => -DAYSECS, 2822 'enrolled' => true, 2823 'active' => false, 2824 'suspended' => true, 2825 ), 2826 ), 2827 'counts' => array( 2828 'enrolled' => 1, 2829 'active' => 0, 2830 'suspended' => 1, 2831 ), 2832 ), 2833 array( 2834 // One user who is not yet enrolled, and one who is no longer enrolled. 2835 'users' => array( 2836 array( 2837 'timeend' => -DAYSECS, 2838 'enrolled' => true, 2839 'active' => false, 2840 'suspended' => true, 2841 ), 2842 array( 2843 'timestart' => DAYSECS, 2844 'enrolled' => true, 2845 'active' => false, 2846 'suspended' => true, 2847 ), 2848 ), 2849 'counts' => array( 2850 'enrolled' => 2, 2851 'active' => 0, 2852 'suspended' => 2, 2853 ), 2854 ), 2855 ); 2856 } 2857 2858 /** 2859 * @dataProvider get_enrolled_sql_provider 2860 * @covers ::get_enrolled_users 2861 * @covers ::get_suspended_userids 2862 */ 2863 public function test_get_enrolled_sql_course($users, $counts) { 2864 global $DB; 2865 2866 $this->resetAfterTest(); 2867 2868 $course = $this->getDataGenerator()->create_course(); 2869 $context = context_course::instance($course->id); 2870 $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 2871 $createdusers = array(); 2872 2873 foreach ($users as &$userdata) { 2874 $user = $this->getDataGenerator()->create_user(); 2875 $userdata['id'] = $user->id; 2876 2877 $timestart = 0; 2878 $timeend = 0; 2879 $status = null; 2880 if (isset($userdata['timestart'])) { 2881 $timestart = time() + $userdata['timestart']; 2882 } 2883 if (isset($userdata['timeend'])) { 2884 $timeend = time() + $userdata['timeend']; 2885 } 2886 if (isset($userdata['status'])) { 2887 $status = $userdata['status']; 2888 } 2889 2890 // Enrol the user in the course. 2891 $this->getDataGenerator()->enrol_user($user->id, $course->id, $student->id, 'manual', $timestart, $timeend, $status); 2892 } 2893 2894 // After all users have been enroled, check expectations. 2895 $enrolled = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, false); 2896 $active = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true); 2897 $suspended = get_suspended_userids($context); 2898 2899 foreach ($users as $userdata) { 2900 if (isset($userdata['enrolled']) && $userdata['enrolled']) { 2901 $this->assertTrue(isset($enrolled[$userdata['id']])); 2902 } else { 2903 $this->assertFalse(isset($enrolled[$userdata['id']])); 2904 } 2905 2906 if (isset($userdata['active']) && $userdata['active']) { 2907 $this->assertTrue(isset($active[$userdata['id']])); 2908 } else { 2909 $this->assertFalse(isset($active[$userdata['id']])); 2910 } 2911 2912 if (isset($userdata['suspended']) && $userdata['suspended']) { 2913 $this->assertTrue(isset($suspended[$userdata['id']])); 2914 } else { 2915 $this->assertFalse(isset($suspended[$userdata['id']])); 2916 } 2917 } 2918 2919 $this->assertCount($counts['enrolled'], $enrolled); 2920 $this->assertCount($counts['active'], $active); 2921 $this->assertCount($counts['suspended'], $suspended); 2922 } 2923 2924 /** 2925 * A small functional test of permission evaluations. 2926 */ 2927 public function test_permission_evaluation() { 2928 global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE; 2929 2930 $this->resetAfterTest(); 2931 2932 $generator = $this->getDataGenerator(); 2933 2934 // Fill the site with some real data. 2935 $testcategories = array(); 2936 $testcourses = array(); 2937 $testpages = array(); 2938 $testblocks = array(); 2939 $allroles = $DB->get_records_menu('role', array(), 'id', 'shortname, id'); 2940 2941 $systemcontext = context_system::instance(); 2942 $frontpagecontext = context_course::instance(SITEID); 2943 2944 // Add block to system context. 2945 $bi = $generator->create_block('online_users'); 2946 context_block::instance($bi->id); 2947 $testblocks[] = $bi->id; 2948 2949 // Some users. 2950 $testusers = array(); 2951 for ($i=0; $i<20; $i++) { 2952 $user = $generator->create_user(); 2953 $testusers[$i] = $user->id; 2954 $usercontext = context_user::instance($user->id); 2955 2956 // Add block to user profile. 2957 $bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id)); 2958 $testblocks[] = $bi->id; 2959 } 2960 2961 // Add block to frontpage. 2962 $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id)); 2963 $frontpageblockcontext = context_block::instance($bi->id); 2964 $testblocks[] = $bi->id; 2965 2966 // Add a resource to frontpage. 2967 $page = $generator->create_module('page', array('course'=>$SITE->id)); 2968 $testpages[] = $page->cmid; 2969 $frontpagepagecontext = context_module::instance($page->cmid); 2970 2971 // Add block to frontpage resource. 2972 $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id)); 2973 $frontpagepageblockcontext = context_block::instance($bi->id); 2974 $testblocks[] = $bi->id; 2975 2976 // Some nested course categories with courses. 2977 $manualenrol = enrol_get_plugin('manual'); 2978 $parentcat = 0; 2979 for ($i=0; $i<5; $i++) { 2980 $cat = $generator->create_category(array('parent'=>$parentcat)); 2981 $testcategories[] = $cat->id; 2982 $catcontext = context_coursecat::instance($cat->id); 2983 $parentcat = $cat->id; 2984 2985 if ($i >= 4) { 2986 continue; 2987 } 2988 2989 // Add resource to each category. 2990 $bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id)); 2991 context_block::instance($bi->id); 2992 2993 // Add a few courses to each category. 2994 for ($j=0; $j<6; $j++) { 2995 $course = $generator->create_course(array('category'=>$cat->id)); 2996 $testcourses[] = $course->id; 2997 $coursecontext = context_course::instance($course->id); 2998 2999 if ($j >= 5) { 3000 continue; 3001 } 3002 // Add manual enrol instance. 3003 $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id))); 3004 3005 // Add block to each course. 3006 $bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id)); 3007 $testblocks[] = $bi->id; 3008 3009 // Add a resource to each course. 3010 $page = $generator->create_module('page', array('course'=>$course->id)); 3011 $testpages[] = $page->cmid; 3012 $modcontext = context_module::instance($page->cmid); 3013 3014 // Add block to each module. 3015 $bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id)); 3016 $testblocks[] = $bi->id; 3017 } 3018 } 3019 3020 // Make sure all contexts were created properly. 3021 $count = 1; // System. 3022 $count += $DB->count_records('user', array('deleted'=>0)); 3023 $count += $DB->count_records('course_categories'); 3024 $count += $DB->count_records('course'); 3025 $count += $DB->count_records('course_modules'); 3026 $count += $DB->count_records('block_instances'); 3027 $this->assertEquals($count, $DB->count_records('context')); 3028 $this->assertEquals(0, $DB->count_records('context', array('depth'=>0))); 3029 $this->assertEquals(0, $DB->count_records('context', array('path'=>null))); 3030 3031 3032 // Test context_helper::get_level_name() method. 3033 3034 $levels = context_helper::get_all_levels(); 3035 foreach ($levels as $level => $classname) { 3036 $name = context_helper::get_level_name($level); 3037 $this->assertNotEmpty($name); 3038 } 3039 3040 3041 // Test context::instance_by_id(), context_xxx::instance() methods. 3042 3043 $context = context::instance_by_id($frontpagecontext->id); 3044 $this->assertSame(CONTEXT_COURSE, $context->contextlevel); 3045 $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING)); 3046 try { 3047 context::instance_by_id(-1); 3048 $this->fail('exception expected'); 3049 } catch (moodle_exception $e) { 3050 $this->assertTrue(true); 3051 } 3052 $this->assertInstanceOf('context_system', context_system::instance()); 3053 $this->assertInstanceOf('context_coursecat', context_coursecat::instance($testcategories[0])); 3054 $this->assertInstanceOf('context_course', context_course::instance($testcourses[0])); 3055 $this->assertInstanceOf('context_module', context_module::instance($testpages[0])); 3056 $this->assertInstanceOf('context_block', context_block::instance($testblocks[0])); 3057 3058 $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING)); 3059 $this->assertFalse(context_course::instance(-1, IGNORE_MISSING)); 3060 $this->assertFalse(context_module::instance(-1, IGNORE_MISSING)); 3061 $this->assertFalse(context_block::instance(-1, IGNORE_MISSING)); 3062 try { 3063 context_coursecat::instance(-1); 3064 $this->fail('exception expected'); 3065 } catch (moodle_exception $e) { 3066 $this->assertTrue(true); 3067 } 3068 try { 3069 context_course::instance(-1); 3070 $this->fail('exception expected'); 3071 } catch (moodle_exception $e) { 3072 $this->assertTrue(true); 3073 } 3074 try { 3075 context_module::instance(-1); 3076 $this->fail('exception expected'); 3077 } catch (moodle_exception $e) { 3078 $this->assertTrue(true); 3079 } 3080 try { 3081 context_block::instance(-1); 3082 $this->fail('exception expected'); 3083 } catch (moodle_exception $e) { 3084 $this->assertTrue(true); 3085 } 3086 3087 3088 // Test $context->get_url(), $context->get_context_name(), $context->get_capabilities() methods. 3089 3090 $testcontexts = array(); 3091 $testcontexts[CONTEXT_SYSTEM] = context_system::instance(); 3092 $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]); 3093 $testcontexts[CONTEXT_COURSE] = context_course::instance($testcourses[0]); 3094 $testcontexts[CONTEXT_MODULE] = context_module::instance($testpages[0]); 3095 $testcontexts[CONTEXT_BLOCK] = context_block::instance($testblocks[0]); 3096 3097 foreach ($testcontexts as $context) { 3098 $name = $context->get_context_name(true, true); 3099 $this->assertNotEmpty($name); 3100 3101 $this->assertInstanceOf('moodle_url', $context->get_url()); 3102 3103 $caps = $context->get_capabilities(); 3104 $this->assertTrue(is_array($caps)); 3105 foreach ($caps as $cap) { 3106 $cap = (array)$cap; 3107 $this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask')); 3108 } 3109 } 3110 unset($testcontexts); 3111 3112 // Test $context->get_course_context() method. 3113 3114 $this->assertFalse($systemcontext->get_course_context(false)); 3115 try { 3116 $systemcontext->get_course_context(); 3117 $this->fail('exception expected'); 3118 } catch (moodle_exception $e) { 3119 $this->assertInstanceOf('coding_exception', $e); 3120 } 3121 $context = context_coursecat::instance($testcategories[0]); 3122 $this->assertFalse($context->get_course_context(false)); 3123 try { 3124 $context->get_course_context(); 3125 $this->fail('exception expected'); 3126 } catch (moodle_exception $e) { 3127 $this->assertInstanceOf('coding_exception', $e); 3128 } 3129 $this->assertEquals($frontpagecontext, $frontpagecontext->get_course_context(true)); 3130 $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_course_context(true)); 3131 $this->assertEquals($frontpagecontext, $frontpagepageblockcontext->get_course_context(true)); 3132 3133 3134 // Test $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() methods. 3135 3136 $userid = reset($testusers); 3137 $usercontext = context_user::instance($userid); 3138 $this->assertEquals($systemcontext, $usercontext->get_parent_context()); 3139 $this->assertEquals(array($systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts()); 3140 $this->assertEquals(array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext), $usercontext->get_parent_contexts(true)); 3141 3142 $this->assertEquals(array(), $systemcontext->get_parent_contexts()); 3143 $this->assertEquals(array($systemcontext->id=>$systemcontext), $systemcontext->get_parent_contexts(true)); 3144 $this->assertEquals(array(), $systemcontext->get_parent_context_ids()); 3145 $this->assertEquals(array($systemcontext->id), $systemcontext->get_parent_context_ids(true)); 3146 $this->assertEquals(array(), $systemcontext->get_parent_context_paths()); 3147 $this->assertEquals(array($systemcontext->id => $systemcontext->path), $systemcontext->get_parent_context_paths(true)); 3148 3149 $this->assertEquals($systemcontext, $frontpagecontext->get_parent_context()); 3150 $this->assertEquals(array($systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts()); 3151 $this->assertEquals(array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext), $frontpagecontext->get_parent_contexts(true)); 3152 $this->assertEquals(array($systemcontext->id), $frontpagecontext->get_parent_context_ids()); 3153 $this->assertEquals(array($frontpagecontext->id, $systemcontext->id), $frontpagecontext->get_parent_context_ids(true)); 3154 $this->assertEquals(array($systemcontext->id => $systemcontext->path), $frontpagecontext->get_parent_context_paths()); 3155 $expected = array($systemcontext->id => $systemcontext->path, $frontpagecontext->id => $frontpagecontext->path); 3156 $this->assertEquals($expected, $frontpagecontext->get_parent_context_paths(true)); 3157 3158 $this->assertFalse($systemcontext->get_parent_context()); 3159 $frontpagecontext = context_course::instance($SITE->id); 3160 $parent = $systemcontext; 3161 foreach ($testcategories as $catid) { 3162 $catcontext = context_coursecat::instance($catid); 3163 $this->assertEquals($parent, $catcontext->get_parent_context()); 3164 $parent = $catcontext; 3165 } 3166 $this->assertEquals($frontpagecontext, $frontpagepagecontext->get_parent_context()); 3167 $this->assertEquals($frontpagecontext, $frontpageblockcontext->get_parent_context()); 3168 $this->assertEquals($frontpagepagecontext, $frontpagepageblockcontext->get_parent_context()); 3169 3170 3171 // Test $context->get_child_contexts() method. 3172 3173 $children = $systemcontext->get_child_contexts(); 3174 $this->resetDebugging(); 3175 $this->assertEquals(count($children)+1, $DB->count_records('context')); 3176 3177 $context = context_coursecat::instance($testcategories[3]); 3178 $children = $context->get_child_contexts(); 3179 $countcats = 0; 3180 $countcourses = 0; 3181 $countblocks = 0; 3182 foreach ($children as $child) { 3183 if ($child->contextlevel == CONTEXT_COURSECAT) { 3184 $countcats++; 3185 } 3186 if ($child->contextlevel == CONTEXT_COURSE) { 3187 $countcourses++; 3188 } 3189 if ($child->contextlevel == CONTEXT_BLOCK) { 3190 $countblocks++; 3191 } 3192 } 3193 $this->assertCount(8, $children); 3194 $this->assertEquals(1, $countcats); 3195 $this->assertEquals(6, $countcourses); 3196 $this->assertEquals(1, $countblocks); 3197 3198 $context = context_course::instance($testcourses[2]); 3199 $children = $context->get_child_contexts(); 3200 3201 $context = context_module::instance($testpages[3]); 3202 $children = $context->get_child_contexts(); 3203 $this->assertCount(1, $children); 3204 3205 $context = context_block::instance($testblocks[1]); 3206 $children = $context->get_child_contexts(); 3207 $this->assertCount(0, $children); 3208 3209 unset($children); 3210 unset($countcats); 3211 unset($countcourses); 3212 unset($countblocks); 3213 3214 3215 // Test context_helper::reset_caches() method. 3216 3217 context_helper::reset_caches(); 3218 $this->assertEquals(0, context_inspection::test_context_cache_size()); 3219 context_course::instance($SITE->id); 3220 $this->assertEquals(1, context_inspection::test_context_cache_size()); 3221 3222 3223 // Test context preloading. 3224 3225 context_helper::reset_caches(); 3226 $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')." 3227 FROM {context} c 3228 WHERE c.contextlevel <> ".CONTEXT_SYSTEM; 3229 $records = $DB->get_records_sql($sql); 3230 $firstrecord = reset($records); 3231 $columns = context_helper::get_preload_record_columns('c'); 3232 $firstrecord = (array)$firstrecord; 3233 $this->assertSame(array_keys($firstrecord), array_values($columns)); 3234 context_helper::reset_caches(); 3235 foreach ($records as $record) { 3236 context_helper::preload_from_record($record); 3237 $this->assertEquals(new stdClass(), $record); 3238 } 3239 $this->assertEquals(count($records), context_inspection::test_context_cache_size()); 3240 unset($records); 3241 unset($columns); 3242 3243 context_helper::reset_caches(); 3244 context_helper::preload_course($SITE->id); 3245 $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id)); 3246 $this->assertEquals(3 + $numfrontpagemodules, context_inspection::test_context_cache_size()); // Depends on number of default blocks. 3247 3248 // Test assign_capability(), unassign_capability() functions. 3249 3250 $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); 3251 $this->assertFalse($rc); 3252 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id); 3253 $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); 3254 $this->assertEquals(CAP_ALLOW, $rc->permission); 3255 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id); 3256 $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); 3257 $this->assertEquals(CAP_ALLOW, $rc->permission); 3258 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true); 3259 $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); 3260 $this->assertEquals(CAP_PREVENT, $rc->permission); 3261 3262 assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext); 3263 $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); 3264 $this->assertFalse($rc); 3265 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext); 3266 unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true); 3267 $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); 3268 $this->assertFalse($rc); 3269 unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true); 3270 unset($rc); 3271 3272 accesslib_clear_all_caches_for_unit_testing(); // Must be done after assign_capability(). 3273 3274 3275 // Test role_assign(), role_unassign(), role_unassign_all() functions. 3276 3277 $context = context_course::instance($testcourses[1]); 3278 $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id))); 3279 role_assign($allroles['teacher'], $testusers[1], $context->id); 3280 role_assign($allroles['teacher'], $testusers[2], $context->id); 3281 role_assign($allroles['manager'], $testusers[1], $context->id); 3282 $this->assertEquals(3, $DB->count_records('role_assignments', array('contextid'=>$context->id))); 3283 role_unassign($allroles['teacher'], $testusers[1], $context->id); 3284 $this->assertEquals(2, $DB->count_records('role_assignments', array('contextid'=>$context->id))); 3285 role_unassign_all(array('contextid'=>$context->id)); 3286 $this->assertEquals(0, $DB->count_records('role_assignments', array('contextid'=>$context->id))); 3287 unset($context); 3288 3289 accesslib_clear_all_caches_for_unit_testing(); // Just in case. 3290 3291 3292 // Test has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends functions. 3293 3294 $adminid = get_admin()->id; 3295 $guestid = $CFG->siteguest; 3296 3297 // Enrol some users into some courses. 3298 $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST); 3299 $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST); 3300 $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id'); 3301 $cm1 = reset($cms); 3302 $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id'); 3303 $block1 = reset($blocks); 3304 $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id)); 3305 $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id)); 3306 for ($i=0; $i<9; $i++) { 3307 $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']); 3308 } 3309 $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']); 3310 $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']); 3311 3312 for ($i=10; $i<15; $i++) { 3313 $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']); 3314 } 3315 $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']); 3316 3317 // Add tons of role assignments - the more the better. 3318 role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2])); 3319 role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1])); 3320 role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id)); 3321 role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id)); 3322 role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id)); 3323 role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id)); 3324 role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id)); 3325 role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id)); 3326 3327 role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id)); 3328 role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id)); 3329 3330 // Add tons of overrides - the more the better. 3331 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true); 3332 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true); 3333 assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true); 3334 assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true); 3335 assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true); 3336 3337 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true); 3338 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true); 3339 assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true); 3340 assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true); 3341 assign_capability('mod/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true); 3342 3343 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true); 3344 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true); 3345 assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true); 3346 assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true); 3347 3348 assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true); 3349 3350 // Prepare for prohibit test. 3351 role_assign($allroles['editingteacher'], $testusers[19], context_system::instance()); 3352 role_assign($allroles['teacher'], $testusers[19], context_course::instance($testcourses[17])); 3353 role_assign($allroles['editingteacher'], $testusers[19], context_course::instance($testcourses[17])); 3354 assign_capability('moodle/course:update', CAP_PROHIBIT, $allroles['teacher'], context_course::instance($testcourses[17]), true); 3355 3356 accesslib_clear_all_caches_for_unit_testing(); /// Must be done after assign_capability(). 3357 3358 // Extra tests for guests and not-logged-in users because they can not be verified by cross checking 3359 // with get_users_by_capability() where they are ignored. 3360 $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid)); 3361 $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid)); 3362 $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid)); 3363 $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid)); 3364 3365 $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0)); 3366 $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0)); 3367 $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0)); 3368 $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0)); 3369 3370 $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11])); 3371 $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11])); 3372 $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11])); 3373 $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11])); 3374 3375 $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9])); 3376 $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9])); 3377 $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9])); 3378 3379 // Test prohibits. 3380 $this->assertTrue(has_capability('moodle/course:update', context_system::instance(), $testusers[19])); 3381 $ids = get_users_by_capability(context_system::instance(), 'moodle/course:update', 'u.id'); 3382 $this->assertArrayHasKey($testusers[19], $ids); 3383 $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[17]), $testusers[19])); 3384 $ids = get_users_by_capability(context_course::instance($testcourses[17]), 'moodle/course:update', 'u.id'); 3385 $this->assertArrayNotHasKey($testusers[19], $ids); 3386 3387 // Test the list of enrolled users. 3388 $coursecontext = context_course::instance($course1->id); 3389 $enrolled = get_enrolled_users($coursecontext); 3390 $this->assertCount(10, $enrolled); 3391 for ($i=0; $i<10; $i++) { 3392 $this->assertTrue(isset($enrolled[$testusers[$i]])); 3393 } 3394 $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update'); 3395 $this->assertCount(1, $enrolled); 3396 $this->assertTrue(isset($enrolled[$testusers[9]])); 3397 unset($enrolled); 3398 3399 // Role switching. 3400 $userid = $testusers[9]; 3401 $USER = $DB->get_record('user', array('id'=>$userid)); 3402 load_all_capabilities(); 3403 $coursecontext = context_course::instance($course1->id); 3404 $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); 3405 $this->assertFalse(is_role_switched($course1->id)); 3406 role_switch($allroles['student'], $coursecontext); 3407 $this->assertTrue(is_role_switched($course1->id)); 3408 $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]); 3409 $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); 3410 reload_all_capabilities(); 3411 $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); 3412 role_switch(0, $coursecontext); 3413 $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); 3414 $userid = $adminid; 3415 $USER = $DB->get_record('user', array('id'=>$userid)); 3416 load_all_capabilities(); 3417 $coursecontext = context_course::instance($course1->id); 3418 $blockcontext = context_block::instance($block1->id); 3419 $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); 3420 role_switch($allroles['student'], $coursecontext); 3421 $this->assertEquals($allroles['student'], $USER->access['rsw'][$coursecontext->path]); 3422 $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); 3423 reload_all_capabilities(); 3424 $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); 3425 load_all_capabilities(); 3426 $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); 3427 3428 // Temp course role for enrol. 3429 $DB->delete_records('cache_flags', array()); // This prevents problem with dirty contexts immediately resetting the temp role - this is a known problem... 3430 $userid = $testusers[5]; 3431 $roleid = $allroles['editingteacher']; 3432 $USER = $DB->get_record('user', array('id'=>$userid)); 3433 load_all_capabilities(); 3434 $coursecontext = context_course::instance($course1->id); 3435 $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); 3436 $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid])); 3437 load_temp_course_role($coursecontext, $roleid); 3438 $this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid); 3439 $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); 3440 remove_temp_course_roles($coursecontext); 3441 $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); 3442 load_temp_course_role($coursecontext, $roleid); 3443 reload_all_capabilities(); 3444 $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); 3445 $USER = new stdClass(); 3446 $USER->id = 0; 3447 3448 // Now cross check has_capability() with get_users_by_capability(), each using different code paths, 3449 // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong, 3450 // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users). 3451 $contexts = $DB->get_records('context', array(), 'id'); 3452 $contexts = array_values($contexts); 3453 $capabilities = $DB->get_records('capabilities', array(), 'id'); 3454 $capabilities = array_values($capabilities); 3455 $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']); 3456 $userids = array_values($testusers); 3457 $userids[] = get_admin()->id; 3458 3459 if (!PHPUNIT_LONGTEST) { 3460 $contexts = array_slice($contexts, 0, 10); 3461 $capabilities = array_slice($capabilities, 0, 5); 3462 $userids = array_slice($userids, 0, 5); 3463 } 3464 3465 foreach ($userids as $userid) { // No guest or deleted. 3466 // Each user gets 0-10 random roles. 3467 $rcount = rand(0, 10); 3468 for ($j=0; $j<$rcount; $j++) { 3469 $roleid = $roles[rand(0, count($roles)-1)]; 3470 $contextid = $contexts[rand(0, count($contexts)-1)]->id; 3471 role_assign($roleid, $userid, $contextid); 3472 } 3473 } 3474 3475 $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT); 3476 $maxoverrides = count($contexts)*10; 3477 for ($j=0; $j<$maxoverrides; $j++) { 3478 $roleid = $roles[rand(0, count($roles)-1)]; 3479 $contextid = $contexts[rand(0, count($contexts)-1)]->id; 3480 $permission = $permissions[rand(0, count($permissions)-1)]; 3481 $capname = $capabilities[rand(0, count($capabilities)-1)]->name; 3482 assign_capability($capname, $permission, $roleid, $contextid, true); 3483 } 3484 unset($permissions); 3485 unset($roles); 3486 3487 accesslib_clear_all_caches_for_unit_testing(); // must be done after assign_capability(). 3488 3489 // Test time - let's set up some real user, just in case the logic for USER affects the others... 3490 $USER = $DB->get_record('user', array('id'=>$testusers[3])); 3491 load_all_capabilities(); 3492 3493 $userids[] = $CFG->siteguest; 3494 $userids[] = 0; // Not-logged-in user. 3495 $userids[] = -1; // Non-existent user. 3496 3497 foreach ($contexts as $crecord) { 3498 $context = context::instance_by_id($crecord->id); 3499 if ($coursecontext = $context->get_course_context(false)) { 3500 $enrolled = get_enrolled_users($context); 3501 } else { 3502 $enrolled = array(); 3503 } 3504 foreach ($capabilities as $cap) { 3505 $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username'); 3506 if ($enrolled) { 3507 $enrolledwithcap = get_enrolled_users($context, $cap->name); 3508 } else { 3509 $enrolledwithcap = array(); 3510 } 3511 foreach ($userids as $userid) { 3512 if ($userid == 0 or isguestuser($userid)) { 3513 if ($userid == 0) { 3514 $CFG->forcelogin = true; 3515 $this->assertFalse(has_capability($cap->name, $context, $userid)); 3516 unset($CFG->forcelogin); 3517 } 3518 if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) { 3519 $this->assertFalse(has_capability($cap->name, $context, $userid)); 3520 } 3521 $this->assertFalse(isset($allowed[$userid])); 3522 } else { 3523 if (is_siteadmin($userid)) { 3524 $this->assertTrue(has_capability($cap->name, $context, $userid, true)); 3525 } 3526 $hascap = has_capability($cap->name, $context, $userid, false); 3527 $this->assertSame($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." "); 3528 if (isset($enrolled[$userid])) { 3529 $this->assertSame(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." "); 3530 } 3531 } 3532 } 3533 } 3534 } 3535 // Back to nobody. 3536 $USER = new stdClass(); 3537 $USER->id = 0; 3538 unset($contexts); 3539 unset($userids); 3540 unset($capabilities); 3541 3542 // Now let's do all the remaining tests that break our carefully prepared fake site. 3543 3544 3545 // Test $context->mark_dirty() method. 3546 3547 $DB->delete_records('cache_flags', array()); 3548 accesslib_clear_all_caches(false); 3549 $systemcontext->mark_dirty(); 3550 $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); 3551 $this->assertTrue(isset($dirty[$systemcontext->path])); 3552 $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path])); 3553 3554 3555 // Test $context->reload_if_dirty() method. 3556 3557 $DB->delete_records('cache_flags', array()); 3558 accesslib_clear_all_caches(false); 3559 load_all_capabilities(); 3560 $context = context_course::instance($testcourses[2]); 3561 $page = $DB->get_record('page', array('course'=>$testcourses[2])); 3562 $pagecm = get_coursemodule_from_instance('page', $page->id); 3563 $pagecontext = context_module::instance($pagecm->id); 3564 3565 $context->mark_dirty(); 3566 $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); 3567 $USER->access['test'] = true; 3568 $context->reload_if_dirty(); 3569 $this->assertFalse(isset($USER->access['test'])); 3570 3571 $context->mark_dirty(); 3572 $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); 3573 $USER->access['test'] = true; 3574 $pagecontext->reload_if_dirty(); 3575 $this->assertFalse(isset($USER->access['test'])); 3576 3577 3578 // Test context_helper::build_all_paths() method. 3579 3580 $oldcontexts = $DB->get_records('context', array(), 'id'); 3581 $DB->set_field_select('context', 'path', null, "contextlevel <> ".CONTEXT_SYSTEM); 3582 $DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM); 3583 context_helper::build_all_paths(); 3584 $newcontexts = $DB->get_records('context', array(), 'id'); 3585 $this->assertEquals($oldcontexts, $newcontexts); 3586 unset($oldcontexts); 3587 unset($newcontexts); 3588 3589 3590 // Test $context->reset_paths() method. 3591 3592 $context = context_course::instance($testcourses[2]); 3593 $children = $context->get_child_contexts(); 3594 $context->reset_paths(false); 3595 $this->assertNull($DB->get_field('context', 'path', array('id'=>$context->id))); 3596 $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$context->id))); 3597 foreach ($children as $child) { 3598 $this->assertNull($DB->get_field('context', 'path', array('id'=>$child->id))); 3599 $this->assertEquals(0, $DB->get_field('context', 'depth', array('id'=>$child->id))); 3600 } 3601 $this->assertEquals(count($children)+1, $DB->count_records('context', array('depth'=>0))); 3602 $this->assertEquals(count($children)+1, $DB->count_records('context', array('path'=>null))); 3603 3604 $context = context_course::instance($testcourses[2]); 3605 $context->reset_paths(true); 3606 $context = context_course::instance($testcourses[2]); 3607 $this->assertSame($context->path, $DB->get_field('context', 'path', array('id'=>$context->id))); 3608 $this->assertSame($context->depth, $DB->get_field('context', 'depth', array('id'=>$context->id))); 3609 $this->assertEquals(0, $DB->count_records('context', array('depth'=>0))); 3610 $this->assertEquals(0, $DB->count_records('context', array('path'=>null))); 3611 3612 3613 // Test $context->update_moved() method. 3614 3615 accesslib_clear_all_caches(false); 3616 $DB->delete_records('cache_flags', array()); 3617 $course = $DB->get_record('course', array('id'=>$testcourses[0])); 3618 $context = context_course::instance($course->id); 3619 $oldpath = $context->path; 3620 $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); 3621 $categorycontext = context_coursecat::instance($miscid); 3622 $course->category = $miscid; 3623 $DB->update_record('course', $course); 3624 $context->update_moved($categorycontext); 3625 3626 $context = context_course::instance($course->id); 3627 $this->assertEquals($categorycontext, $context->get_parent_context()); 3628 $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); 3629 $this->assertFalse(isset($dirty[$oldpath])); 3630 $this->assertTrue(isset($dirty[$context->path])); 3631 3632 3633 // Test $context->delete_content() method. 3634 3635 context_helper::reset_caches(); 3636 $context = context_module::instance($testpages[3]); 3637 $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); 3638 $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); 3639 $context->delete_content(); 3640 $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); 3641 $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); 3642 3643 3644 // Test $context->delete() method. 3645 3646 context_helper::reset_caches(); 3647 $context = context_module::instance($testpages[4]); 3648 $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); 3649 $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); 3650 $bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id)); 3651 $bicontext = context_block::instance($bi->id); 3652 $DB->delete_records('cache_flags', array()); 3653 $context->delete(); // Should delete also linked blocks. 3654 $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); 3655 $this->assertFalse(isset($dirty[$context->path])); 3656 $this->assertFalse($DB->record_exists('context', array('id'=>$context->id))); 3657 $this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id))); 3658 $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4]))); 3659 $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id))); 3660 $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); 3661 context_module::instance($testpages[4]); 3662 3663 3664 // Test context_helper::delete_instance() method. 3665 3666 context_helper::reset_caches(); 3667 $lastcourse = array_pop($testcourses); 3668 $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse))); 3669 $coursecontext = context_course::instance($lastcourse); 3670 $this->assertEquals(1, context_inspection::test_context_cache_size()); 3671 $this->assertNotEquals(CONTEXT_COURSE, $coursecontext->instanceid); 3672 $DB->delete_records('cache_flags', array()); 3673 context_helper::delete_instance(CONTEXT_COURSE, $lastcourse); 3674 $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); 3675 $this->assertFalse(isset($dirty[$coursecontext->path])); 3676 $this->assertEquals(0, context_inspection::test_context_cache_size()); 3677 $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse))); 3678 context_course::instance($lastcourse); 3679 3680 3681 // Test context_helper::create_instances() method. 3682 3683 $prevcount = $DB->count_records('context'); 3684 $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); 3685 context_helper::create_instances(null, true); 3686 $this->assertSame($DB->count_records('context'), $prevcount); 3687 $this->assertEquals(0, $DB->count_records('context', array('depth'=>0))); 3688 $this->assertEquals(0, $DB->count_records('context', array('path'=>null))); 3689 3690 $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); 3691 $DB->delete_records('block_instances', array()); 3692 $prevcount = $DB->count_records('context'); 3693 $DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM); 3694 context_helper::create_instances(null, true); 3695 $this->assertSame($prevcount, $DB->count_records('context')); 3696 $this->assertEquals(0, $DB->count_records('context', array('depth'=>0))); 3697 $this->assertEquals(0, $DB->count_records('context', array('path'=>null))); 3698 3699 // Test context_helper::cleanup_instances() method. 3700 3701 $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); 3702 $DB->delete_records('course', array('id'=>$lastcourse)); 3703 $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); 3704 $DB->delete_records('course_categories', array('id'=>$lastcategory)); 3705 $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); 3706 $DB->delete_records('user', array('id'=>$lastuser)); 3707 $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id)); 3708 $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid)); 3709 context_helper::cleanup_instances(); 3710 $count = 1; // System. 3711 $count += $DB->count_records('user', array('deleted'=>0)); 3712 $count += $DB->count_records('course_categories'); 3713 $count += $DB->count_records('course'); 3714 $count += $DB->count_records('course_modules'); 3715 $count += $DB->count_records('block_instances'); 3716 $this->assertEquals($count, $DB->count_records('context')); 3717 3718 3719 // Test context cache size restrictions. 3720 3721 $testusers= array(); 3722 for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) { 3723 $user = $generator->create_user(); 3724 $testusers[$i] = $user->id; 3725 } 3726 context_helper::create_instances(null, true); 3727 context_helper::reset_caches(); 3728 for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) { 3729 context_user::instance($testusers[$i]); 3730 if ($i == CONTEXT_CACHE_MAX_SIZE - 1) { 3731 $this->assertEquals(CONTEXT_CACHE_MAX_SIZE, context_inspection::test_context_cache_size()); 3732 } else if ($i == CONTEXT_CACHE_MAX_SIZE) { 3733 // Once the limit is reached roughly 1/3 of records should be removed from cache. 3734 $this->assertEquals((int)ceil(CONTEXT_CACHE_MAX_SIZE * (2/3) + 101), context_inspection::test_context_cache_size()); 3735 } 3736 } 3737 // We keep the first 100 cached. 3738 $prevsize = context_inspection::test_context_cache_size(); 3739 for ($i=0; $i<100; $i++) { 3740 context_user::instance($testusers[$i]); 3741 $this->assertEquals($prevsize, context_inspection::test_context_cache_size()); 3742 } 3743 context_user::instance($testusers[102]); 3744 $this->assertEquals($prevsize+1, context_inspection::test_context_cache_size()); 3745 unset($testusers); 3746 3747 3748 3749 // Test basic test of legacy functions. 3750 // Note: watch out, the fake site might be pretty borked already. 3751 3752 $this->assertEquals(get_system_context(), context_system::instance()); 3753 $this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER); 3754 3755 foreach ($DB->get_records('context') as $contextid => $record) { 3756 $context = context::instance_by_id($contextid); 3757 $this->assertEquals($context, get_context_instance($record->contextlevel, $record->instanceid)); 3758 $this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER); 3759 } 3760 3761 // Make sure a debugging is thrown. 3762 get_context_instance($record->contextlevel, $record->instanceid); 3763 $this->assertDebuggingCalled('get_context_instance() is deprecated, please use context_xxxx::instance() instead.', DEBUG_DEVELOPER); 3764 get_system_context(); 3765 $this->assertDebuggingCalled('get_system_context() is deprecated, please use context_system::instance() instead.', DEBUG_DEVELOPER); 3766 } 3767 3768 /** 3769 * Helper that verifies a list of capabilities, as returned by 3770 * $context->get_capabilities() contains certain capabilities. 3771 * 3772 * @param array $expected a list of capability names 3773 * @param array $actual a list of capability info from $context->get_capabilities(). 3774 */ 3775 protected function assert_capability_list_contains($expected, $actual) { 3776 $actualnames = []; 3777 foreach ($actual as $cap) { 3778 $actualnames[] = $cap->name; 3779 } 3780 // Verify each expected element exists. 3781 foreach ($expected as $key => $value) { 3782 $this->assertContains($value, $actualnames); 3783 } 3784 } 3785 3786 /** 3787 * Test that context_system::get_capabilities returns capabilities relevant to all modules. 3788 * 3789 * @covers \context_system::get_capabilities 3790 */ 3791 public function test_context_module_caps_returned_by_get_capabilities_in_sys_context() { 3792 $actual = context_system::instance()->get_capabilities(); 3793 3794 // Just test a few representative capabilities. 3795 $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames', 3796 'repository/upload:view', 'atto/recordrtc:recordaudio']; 3797 3798 $this->assert_capability_list_contains($expectedcapabilities, $actual); 3799 } 3800 3801 /** 3802 * Test that context_coursecat::get_capabilities returns capabilities relevant to all modules. 3803 * 3804 * @covers \context_coursecat::get_capabilities 3805 */ 3806 public function test_context_module_caps_returned_by_get_capabilities_in_course_cat_context() { 3807 $this->resetAfterTest(true); 3808 $generator = $this->getDataGenerator(); 3809 $cat = $generator->create_category(); 3810 3811 $actual = context_coursecat::instance($cat->id)->get_capabilities(); 3812 3813 // Just test a few representative capabilities. 3814 $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames', 3815 'repository/upload:view', 'atto/recordrtc:recordaudio']; 3816 3817 $this->assert_capability_list_contains($expectedcapabilities, $actual); 3818 } 3819 3820 /** 3821 * Test that context_course::get_capabilities returns capabilities relevant to all modules. 3822 * 3823 * @covers \context_course::get_capabilities 3824 */ 3825 public function test_context_module_caps_returned_by_get_capabilities_in_course_context() { 3826 $this->resetAfterTest(true); 3827 $generator = $this->getDataGenerator(); 3828 $cat = $generator->create_category(); 3829 $course = $generator->create_course(['category' => $cat->id]); 3830 3831 $actual = context_course::instance($course->id)->get_capabilities(); 3832 3833 // Just test a few representative capabilities. 3834 $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames', 3835 'repository/upload:view', 'atto/recordrtc:recordaudio']; 3836 3837 $this->assert_capability_list_contains($expectedcapabilities, $actual); 3838 } 3839 3840 /** 3841 * Test that context_module::get_capabilities returns capabilities relevant to all modules. 3842 * 3843 * @covers \context_module::get_capabilities 3844 */ 3845 public function test_context_module_caps_returned_by_get_capabilities_mod_context() { 3846 $this->resetAfterTest(true); 3847 $generator = $this->getDataGenerator(); 3848 $cat = $generator->create_category(); 3849 $course = $generator->create_course(['category' => $cat->id]); 3850 $page = $generator->create_module('page', ['course' => $course->id]); 3851 3852 $actual = context_module::instance($page->cmid)->get_capabilities(); 3853 3854 // Just test a few representative capabilities. 3855 $expectedcapabilities = ['moodle/site:accessallgroups', 'moodle/site:viewfullnames', 3856 'repository/upload:view', 'atto/recordrtc:recordaudio']; 3857 3858 $this->assert_capability_list_contains($expectedcapabilities, $actual); 3859 } 3860 3861 /** 3862 * Test that {@see context_block::get_capabilities} returns capabilities relevant to blocks 3863 * 3864 * @covers \context_block::get_capabilities 3865 */ 3866 public function test_context_block_caps_returned_by_get_capabilities_block_context(): void { 3867 $this->resetAfterTest(); 3868 3869 $course = $this->getDataGenerator()->create_course(); 3870 $block = $this->getDataGenerator()->create_block('online_users', [ 3871 'parentcontextid' => context_course::instance($course->id)->id, 3872 ]); 3873 3874 $capabilities = context_block::instance($block->id)->get_capabilities(); 3875 3876 // Just test a few representative capabilities. 3877 $expected = ['block/online_users:addinstance', 'moodle/block:edit', 'moodle/block:view']; 3878 $this->assert_capability_list_contains($expected, $capabilities); 3879 3880 // Now test with different sorting. 3881 $capabilitiesbyname = context_block::instance($block->id)->get_capabilities('riskbitmask'); 3882 3883 $capabilitynames = array_column($capabilities, 'name'); 3884 $capabilitynamesordered = array_column($capabilitiesbyname, 'name'); 3885 3886 // Each array should contain the same data, ordered differently. 3887 $this->assertEqualsCanonicalizing($capabilitynames, $capabilitynamesordered); 3888 $this->assertNotSame($capabilitynames, $capabilitynamesordered); 3889 } 3890 3891 /** 3892 * Test that {@see context_user::get_capabilities} returns capabilities relevant to users 3893 * 3894 * @covers \context_user::get_capabilities 3895 */ 3896 public function test_context_user_caps_returned_by_get_capabilities_user_context(): void { 3897 $this->resetAfterTest(); 3898 3899 $user = $this->getDataGenerator()->create_user(); 3900 $capabilities = context_user::instance($user->id)->get_capabilities(); 3901 3902 // Just test a few representative capabilities. 3903 $expected = ['moodle/user:editmessageprofile', 'moodle/user:editprofile', 'moodle/user:viewalldetails']; 3904 $this->assert_capability_list_contains($expected, $capabilities); 3905 3906 // Now test with different sorting. 3907 $capabilitiesbyname = context_user::instance($user->id)->get_capabilities('name'); 3908 3909 $capabilitynames = array_column($capabilities, 'name'); 3910 $capabilitynamesordered = array_column($capabilitiesbyname, 'name'); 3911 3912 // Each array should contain the same data, ordered differently. 3913 $this->assertEqualsCanonicalizing($capabilitynames, $capabilitynamesordered); 3914 $this->assertNotSame($capabilitynames, $capabilitynamesordered); 3915 } 3916 3917 /** 3918 * Test updating of role capabilities during upgrade 3919 * 3920 * @covers ::update_capabilities 3921 * @covers ::update_capabilities 3922 */ 3923 public function test_update_capabilities() { 3924 global $DB, $SITE; 3925 3926 $this->resetAfterTest(true); 3927 3928 $froncontext = context_course::instance($SITE->id); 3929 $student = $DB->get_record('role', array('shortname'=>'student')); 3930 $teacher = $DB->get_record('role', array('shortname'=>'teacher')); 3931 3932 $existingcaps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask'); 3933 3934 $this->assertFalse(isset($existingcaps['moodle/site:restore'])); // Moved to new 'moodle/restore:restorecourse'. 3935 $this->assertTrue(isset($existingcaps['moodle/restore:restorecourse'])); // New cap from 'moodle/site:restore'. 3936 $this->assertTrue(isset($existingcaps['moodle/site:sendmessage'])); // New capability. 3937 $this->assertTrue(isset($existingcaps['moodle/backup:backupcourse'])); 3938 $this->assertTrue(isset($existingcaps['moodle/backup:backupsection'])); // Cloned from 'moodle/backup:backupcourse'. 3939 $this->assertTrue(isset($existingcaps['moodle/site:approvecourse'])); // Updated bitmask. 3940 $this->assertTrue(isset($existingcaps['moodle/course:manageactivities'])); 3941 $this->assertTrue(isset($existingcaps['mod/page:addinstance'])); // Cloned from core 'moodle/course:manageactivities'. 3942 3943 // Fake state before upgrade. 3944 $DB->set_field('capabilities', 'name', 'moodle/site:restore', array('name'=>'moodle/restore:restorecourse')); 3945 $DB->set_field('role_capabilities', 'capability', 'moodle/site:restore', array('capability'=>'moodle/restore:restorecourse')); 3946 assign_capability('moodle/site:restore', CAP_PROHIBIT, $teacher->id, $froncontext->id, true); 3947 $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/site:restore', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission')); 3948 3949 $DB->delete_records('role_capabilities', array('capability'=>'moodle/site:sendmessage')); 3950 $DB->delete_records('capabilities', array('name'=>'moodle/site:sendmessage')); 3951 3952 $DB->delete_records('role_capabilities', array('capability'=>'moodle/backup:backupsection')); 3953 $DB->delete_records('capabilities', array('name'=>'moodle/backup:backupsection')); 3954 assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $student->id, $froncontext->id, true); 3955 assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $teacher->id, $froncontext->id, true); 3956 3957 $DB->set_field('capabilities', 'riskbitmask', 0, array('name'=>'moodle/site:approvecourse')); 3958 3959 $DB->delete_records('role_capabilities', array('capability'=>'mod/page:addinstance')); 3960 $DB->delete_records('capabilities', array('name'=>'mod/page:addinstance')); 3961 assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $student->id, $froncontext->id, true); 3962 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $teacher->id, $froncontext->id, true); 3963 3964 // Execute core. 3965 update_capabilities('moodle'); 3966 3967 // Only core should be upgraded. 3968 $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask'); 3969 3970 $this->assertFalse(isset($existingcaps['moodle/site:restore'])); 3971 $this->assertTrue(isset($caps['moodle/restore:restorecourse'])); 3972 $this->assertEquals($existingcaps['moodle/restore:restorecourse'], $caps['moodle/restore:restorecourse']); 3973 $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/restore:restorecourse', 'roleid'=>$teacher->id), 'contextid, permission', 'contextid, permission')); 3974 $this->assertEquals($perms1, $perms2); 3975 3976 $this->assertTrue(isset($caps['moodle/site:sendmessage'])); 3977 $this->assertEquals($existingcaps['moodle/site:sendmessage'], $caps['moodle/site:sendmessage']); 3978 3979 $this->assertTrue(isset($caps['moodle/backup:backupsection'])); 3980 $this->assertEquals($existingcaps['moodle/backup:backupsection'], $caps['moodle/backup:backupsection']); 3981 $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/backup:backupcourse', 'moodle/backup:backupsection')); 3982 foreach ($roles as $role) { 3983 $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupcourse', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission')); 3984 $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/backup:backupsection', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission')); 3985 $this->assertEquals($perms1, $perms2); 3986 } 3987 3988 $this->assertTrue(isset($caps['moodle/site:approvecourse'])); 3989 $this->assertEquals($existingcaps['moodle/site:approvecourse'], $caps['moodle/site:approvecourse']); 3990 3991 $this->assertFalse(isset($caps['mod/page:addinstance'])); 3992 3993 // Execute plugin. 3994 update_capabilities('mod_page'); 3995 $caps = $DB->get_records('capabilities', array(), 'id', 'name, captype, contextlevel, component, riskbitmask'); 3996 $this->assertTrue(isset($caps['mod/page:addinstance'])); 3997 $roles = $DB->get_records_sql('SELECT DISTINCT roleid AS id FROM {role_capabilities} WHERE capability=? OR capability=?', array('moodle/course:manageactivities', 'mod/page:addinstance')); 3998 foreach ($roles as $role) { 3999 $perms1 = array_values($DB->get_records('role_capabilities', array('capability'=>'moodle/course:manageactivities', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission')); 4000 $perms2 = array_values($DB->get_records('role_capabilities', array('capability'=>'mod/page:addinstance', 'roleid'=>$role->id), 'contextid, permission', 'contextid, permission')); 4001 } 4002 $this->assertEquals($perms1, $perms2); 4003 } 4004 4005 /** 4006 * Tests reset_role_capabilities function. 4007 * 4008 * @covers ::reset_role_capabilities 4009 */ 4010 public function test_reset_role_capabilities() { 4011 global $DB; 4012 $this->resetAfterTest(true); 4013 $generator = $this->getDataGenerator(); 4014 4015 // Create test course and user, enrol one in the other. 4016 $course = $generator->create_course(); 4017 $user = $generator->create_user(); 4018 $roleid = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST); 4019 $generator->enrol_user($user->id, $course->id, $roleid); 4020 4021 // Change student role so it DOES have 'mod/forum:addinstance'. 4022 $systemcontext = context_system::instance(); 4023 assign_capability('mod/forum:addinstance', CAP_ALLOW, $roleid, $systemcontext->id); 4024 4025 // Override course so it does NOT allow students 'mod/forum:viewdiscussion'. 4026 $coursecontext = context_course::instance($course->id); 4027 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $roleid, $coursecontext->id); 4028 4029 // Check expected capabilities so far. 4030 $this->assertTrue(has_capability('mod/forum:addinstance', $coursecontext, $user)); 4031 $this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user)); 4032 4033 // Oops, allowing student to add forums was a mistake, let's reset the role. 4034 reset_role_capabilities($roleid); 4035 4036 // Check new expected capabilities - role capabilities should have been reset, 4037 // while the override at course level should remain. 4038 $this->assertFalse(has_capability('mod/forum:addinstance', $coursecontext, $user)); 4039 $this->assertFalse(has_capability('mod/forum:viewdiscussion', $coursecontext, $user)); 4040 } 4041 4042 /** 4043 * Tests count_role_users function. 4044 * 4045 * @covers ::count_role_users 4046 */ 4047 public function test_count_role_users() { 4048 global $DB; 4049 $this->resetAfterTest(true); 4050 $generator = self::getDataGenerator(); 4051 // Create a course in a category, and some users. 4052 $category = $generator->create_category(); 4053 $course = $generator->create_course(array('category' => $category->id)); 4054 $user1 = $generator->create_user(); 4055 $user2 = $generator->create_user(); 4056 $user3 = $generator->create_user(); 4057 $user4 = $generator->create_user(); 4058 $user5 = $generator->create_user(); 4059 $roleid1 = $DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST); 4060 $roleid2 = $DB->get_field('role', 'id', array('shortname' => 'coursecreator'), MUST_EXIST); 4061 // Enrol two users as managers onto the course, and 1 onto the category. 4062 $generator->enrol_user($user1->id, $course->id, $roleid1); 4063 $generator->enrol_user($user2->id, $course->id, $roleid1); 4064 $generator->role_assign($roleid1, $user3->id, context_coursecat::instance($category->id)); 4065 // Enrol 1 user as a coursecreator onto the course, and another onto the category. 4066 // This is to ensure we do not count users with roles that are not specified. 4067 $generator->enrol_user($user4->id, $course->id, $roleid2); 4068 $generator->role_assign($roleid2, $user5->id, context_coursecat::instance($category->id)); 4069 // Check that the correct users are found on the course. 4070 $this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false)); 4071 $this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true)); 4072 // Check for the category. 4073 $this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), false)); 4074 $this->assertEquals(1, count_role_users($roleid1, context_coursecat::instance($category->id), true)); 4075 // Have a user with the same role at both the category and course level. 4076 $generator->role_assign($roleid1, $user1->id, context_coursecat::instance($category->id)); 4077 // The course level checks should remain the same. 4078 $this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false)); 4079 $this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true)); 4080 } 4081 4082 /** 4083 * Test fetching users by capability. 4084 * 4085 * @covers ::get_users_by_capability 4086 */ 4087 public function test_get_users_by_capability() { 4088 global $DB; 4089 4090 $this->resetAfterTest(); 4091 4092 $course = $this->getDataGenerator()->create_course(); 4093 $coursecontext = context_course::instance($course->id); 4094 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 4095 $teacher = $this->getDataGenerator()->create_user(); 4096 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 4097 $student = $this->getDataGenerator()->create_user(); 4098 $guest = $DB->get_record('user', array('username' => 'guest')); 4099 4100 role_assign($teacherrole->id, $teacher->id, $coursecontext); 4101 role_assign($studentrole->id, $student->id, $coursecontext); 4102 $admin = $DB->get_record('user', array('username' => 'admin')); 4103 4104 // Note: Here are used default capabilities, the full test is in permission evaluation below, 4105 // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user. 4106 $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse'))); 4107 $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse'))); 4108 4109 $users = get_users_by_capability($coursecontext, 'moodle/backup:backupcourse'); 4110 4111 $this->assertTrue(array_key_exists($teacher->id, $users)); 4112 $this->assertFalse(array_key_exists($admin->id, $users)); 4113 $this->assertFalse(array_key_exists($student->id, $users)); 4114 $this->assertFalse(array_key_exists($guest->id, $users)); 4115 4116 $users = get_users_by_capability($coursecontext, 'moodle/site:approvecourse'); 4117 4118 $this->assertFalse(array_key_exists($teacher->id, $users)); 4119 $this->assertFalse(array_key_exists($admin->id, $users)); 4120 $this->assertFalse(array_key_exists($student->id, $users)); 4121 $this->assertFalse(array_key_exists($guest->id, $users)); 4122 4123 // Test role override. 4124 assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true); 4125 assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true); 4126 4127 $users = get_users_by_capability($coursecontext, 'moodle/backup:backupcourse'); 4128 4129 $this->assertFalse(array_key_exists($teacher->id, $users)); 4130 $this->assertFalse(array_key_exists($admin->id, $users)); 4131 $this->assertTrue(array_key_exists($student->id, $users)); 4132 $this->assertFalse(array_key_exists($guest->id, $users)); 4133 } 4134 4135 4136 /** 4137 * @covers ::get_with_capability_sql 4138 */ 4139 public function test_get_with_capability_sql() { 4140 global $DB; 4141 4142 $this->resetAfterTest(); 4143 4144 $course = $this->getDataGenerator()->create_course(); 4145 $coursecontext = context_course::instance($course->id); 4146 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 4147 $teacher = $this->getDataGenerator()->create_user(); 4148 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 4149 $student = $this->getDataGenerator()->create_user(); 4150 $guest = $DB->get_record('user', array('username' => 'guest')); 4151 4152 role_assign($teacherrole->id, $teacher->id, $coursecontext); 4153 role_assign($studentrole->id, $student->id, $coursecontext); 4154 $admin = $DB->get_record('user', array('username' => 'admin')); 4155 4156 // Note: Here are used default capabilities, the full test is in permission evaluation below, 4157 // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user. 4158 $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse'))); 4159 $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse'))); 4160 4161 list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse'); 4162 $users = $DB->get_records_sql($sql, $params); 4163 4164 $this->assertTrue(array_key_exists($teacher->id, $users)); 4165 $this->assertFalse(array_key_exists($admin->id, $users)); 4166 $this->assertFalse(array_key_exists($student->id, $users)); 4167 $this->assertFalse(array_key_exists($guest->id, $users)); 4168 4169 list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/site:approvecourse'); 4170 $users = $DB->get_records_sql($sql, $params); 4171 4172 $this->assertFalse(array_key_exists($teacher->id, $users)); 4173 $this->assertFalse(array_key_exists($admin->id, $users)); 4174 $this->assertFalse(array_key_exists($student->id, $users)); 4175 $this->assertFalse(array_key_exists($guest->id, $users)); 4176 4177 // Test role override. 4178 assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true); 4179 assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true); 4180 4181 list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse'); 4182 $users = $DB->get_records_sql($sql, $params); 4183 4184 $this->assertFalse(array_key_exists($teacher->id, $users)); 4185 $this->assertFalse(array_key_exists($admin->id, $users)); 4186 $this->assertTrue(array_key_exists($student->id, $users)); 4187 $this->assertFalse(array_key_exists($guest->id, $users)); 4188 } 4189 4190 4191 /** 4192 * Get the test cases for {@link test_get_with_capability_join_when_overrides_present()}. 4193 * 4194 * The particular capabilties used here do not really matter. What is important is 4195 * that they are capabilities which the Student roles has by default, but the 4196 * authenticated suser role does not. 4197 * 4198 * @return array 4199 */ 4200 public function get_get_with_capability_join_override_cases() { 4201 return [ 4202 'no overrides' => [true, []], 4203 'one override' => [true, ['moodle/course:viewscales']], 4204 'both overrides' => [false, ['moodle/course:viewscales', 'moodle/question:flag']], 4205 ]; 4206 } 4207 4208 /** 4209 * Test get_with_capability_join. 4210 * 4211 * @dataProvider get_get_with_capability_join_override_cases 4212 * @covers ::get_with_capability_join 4213 * 4214 * @param bool $studentshouldbereturned whether, with this combination of capabilities, the student should be in the results. 4215 * @param array $capabilitiestoprevent capabilities to override to prevent in the course context. 4216 */ 4217 public function test_get_with_capability_join_when_overrides_present( 4218 bool $studentshouldbereturned, array $capabilitiestoprevent) { 4219 global $DB; 4220 $this->resetAfterTest(); 4221 $generator = $this->getDataGenerator(); 4222 4223 // Create a course. 4224 $category = $generator->create_category(); 4225 $course = $generator->create_course(['category' => $category->id]); 4226 4227 // Create a user. 4228 $student = $generator->create_user(); 4229 $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST); 4230 $generator->enrol_user($student->id, $course->id, $studentrole->id); 4231 4232 // This test assumes that by default the student roles has the two 4233 // capabilities. Check this now in case the role definitions are every changed. 4234 $coursecontext = context_course::instance($course->id); 4235 $this->assertTrue(has_capability('moodle/course:viewscales', $coursecontext, $student)); 4236 $this->assertTrue(has_capability('moodle/question:flag', $coursecontext, $student)); 4237 4238 // We test cases where there are a varying number of prevent overrides. 4239 foreach ($capabilitiestoprevent as $capability) { 4240 role_change_permission($studentrole->id, $coursecontext, $capability, CAP_PREVENT); 4241 } 4242 4243 // So now, assemble our query using the method under test, and verify that it returns the student. 4244 $sqljoin = get_with_capability_join($coursecontext, 4245 ['moodle/course:viewscales', 'moodle/question:flag'], 'u.id'); 4246 4247 $users = $DB->get_records_sql("SELECT u.* 4248 FROM {user} u 4249 {$sqljoin->joins} 4250 WHERE {$sqljoin->wheres}", $sqljoin->params); 4251 if ($studentshouldbereturned) { 4252 $this->assertEquals([$student->id], array_keys($users)); 4253 } else { 4254 $this->assertEmpty($users); 4255 } 4256 } 4257 4258 /** 4259 * Test the get_profile_roles() function. 4260 * 4261 * @covers ::get_profile_roles 4262 */ 4263 public function test_get_profile_roles() { 4264 global $DB; 4265 $this->resetAfterTest(); 4266 4267 $course = $this->getDataGenerator()->create_course(); 4268 $coursecontext = context_course::instance($course->id); 4269 4270 // Assign a student role. 4271 $studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); 4272 $user1 = $this->getDataGenerator()->create_user(); 4273 role_assign($studentrole->id, $user1->id, $coursecontext); 4274 4275 // Assign an editing teacher role. 4276 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 4277 $user2 = $this->getDataGenerator()->create_user(); 4278 role_assign($teacherrole->id, $user2->id, $coursecontext); 4279 4280 // Create a custom role that can be assigned at course level, but don't assign it yet. 4281 create_role('Custom role', 'customrole', 'Custom course role'); 4282 $customrole = $DB->get_record('role', array('shortname' => 'customrole'), '*', MUST_EXIST); 4283 set_role_contextlevels($customrole->id, [CONTEXT_COURSE]); 4284 core_role_set_assign_allowed($teacherrole->id, $customrole->id); // Allow teacher to assign the role in the course. 4285 4286 // Set the site policy 'profileroles' to show student, teacher and non-editing teacher roles (i.e. not the custom role). 4287 $neteacherrole = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST); 4288 set_config('profileroles', "{$studentrole->id}, {$teacherrole->id}, {$neteacherrole->id}"); 4289 4290 // A student in the course (given they can't assign roles) should see those roles which are: 4291 // - listed in the 'profileroles' site policy AND 4292 // - are assigned in the course context (or parent contexts). 4293 // In this case, the non-editing teacher role is not assigned and should not be returned. 4294 $expected = [ 4295 $teacherrole->id => (object) [ 4296 'id' => $teacherrole->id, 4297 'name' => '', 4298 'shortname' => $teacherrole->shortname, 4299 'sortorder' => $teacherrole->sortorder, 4300 'coursealias' => null 4301 ], 4302 $studentrole->id => (object) [ 4303 'id' => $studentrole->id, 4304 'name' => '', 4305 'shortname' => $studentrole->shortname, 4306 'sortorder' => $studentrole->sortorder, 4307 'coursealias' => null 4308 ] 4309 ]; 4310 $this->setUser($user1); 4311 $this->assertEquals($expected, get_profile_roles($coursecontext)); 4312 4313 // An editing teacher should also see only 2 roles at this stage as only 2 roles are assigned: 'teacher' and 'student'. 4314 $this->setUser($user2); 4315 $this->assertEquals($expected, get_profile_roles($coursecontext)); 4316 4317 // Assign a custom role in the course. 4318 $user3 = $this->getDataGenerator()->create_user(); 4319 role_assign($customrole->id, $user3->id, $coursecontext); 4320 4321 // Confirm that the teacher can see the custom role now that it's assigned. 4322 $expectedteacher = [ 4323 $teacherrole->id => (object) [ 4324 'id' => $teacherrole->id, 4325 'name' => '', 4326 'shortname' => $teacherrole->shortname, 4327 'sortorder' => $teacherrole->sortorder, 4328 'coursealias' => null 4329 ], 4330 $studentrole->id => (object) [ 4331 'id' => $studentrole->id, 4332 'name' => '', 4333 'shortname' => $studentrole->shortname, 4334 'sortorder' => $studentrole->sortorder, 4335 'coursealias' => null 4336 ], 4337 $customrole->id => (object) [ 4338 'id' => $customrole->id, 4339 'name' => 'Custom role', 4340 'shortname' => $customrole->shortname, 4341 'sortorder' => $customrole->sortorder, 4342 'coursealias' => null 4343 ] 4344 ]; 4345 $this->setUser($user2); 4346 $this->assertEquals($expectedteacher, get_profile_roles($coursecontext)); 4347 4348 // And that the student can't, because the role isn't included in the 'profileroles' site policy. 4349 $expectedstudent = [ 4350 $teacherrole->id => (object) [ 4351 'id' => $teacherrole->id, 4352 'name' => '', 4353 'shortname' => $teacherrole->shortname, 4354 'sortorder' => $teacherrole->sortorder, 4355 'coursealias' => null 4356 ], 4357 $studentrole->id => (object) [ 4358 'id' => $studentrole->id, 4359 'name' => '', 4360 'shortname' => $studentrole->shortname, 4361 'sortorder' => $studentrole->sortorder, 4362 'coursealias' => null 4363 ] 4364 ]; 4365 $this->setUser($user1); 4366 $this->assertEquals($expectedstudent, get_profile_roles($coursecontext)); 4367 4368 // If we have no roles listed in the site policy, the teacher should be able to see the assigned roles. 4369 $expectedteacher = [ 4370 $studentrole->id => (object) [ 4371 'id' => $studentrole->id, 4372 'name' => '', 4373 'shortname' => $studentrole->shortname, 4374 'sortorder' => $studentrole->sortorder, 4375 'coursealias' => null 4376 ], 4377 $customrole->id => (object) [ 4378 'id' => $customrole->id, 4379 'name' => 'Custom role', 4380 'shortname' => $customrole->shortname, 4381 'sortorder' => $customrole->sortorder, 4382 'coursealias' => null 4383 ], 4384 $teacherrole->id => (object) [ 4385 'id' => $teacherrole->id, 4386 'name' => '', 4387 'shortname' => $teacherrole->shortname, 4388 'sortorder' => $teacherrole->sortorder, 4389 'coursealias' => null 4390 ], 4391 ]; 4392 set_config('profileroles', ""); 4393 $this->setUser($user2); 4394 $this->assertEquals($expectedteacher, get_profile_roles($coursecontext)); 4395 } 4396 4397 /** 4398 * Data provider for is_parent_of context checks. 4399 * 4400 * @return array 4401 */ 4402 public function is_parent_of_provider(): array { 4403 $provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array { 4404 return [ 4405 "includeself: true; {$desc}" => [ 4406 $contextpath, 4407 $testpath, 4408 true, 4409 $expected, 4410 ], 4411 "includeself: false; {$desc}" => [ 4412 $contextpath, 4413 $testpath, 4414 false, 4415 $expected, 4416 ], 4417 ]; 4418 }; 4419 4420 return array_merge( 4421 [ 4422 'includeself: true, testing self' => [ 4423 '/1/4/17/291/1001/17105', 4424 '/1/4/17/291/1001/17105', 4425 true, 4426 true, 4427 ], 4428 'includeself: false, testing self' => [ 4429 '/1/4/17/291/1001/17105', 4430 '/1/4/17/291/1001/17105', 4431 false, 4432 false, 4433 ], 4434 ], 4435 $provideboth( 4436 'testing parent', 4437 '/1/4/17/291/1001/17105', 4438 '/1/4/17/291/1001', 4439 false 4440 ), 4441 $provideboth( 4442 'testing child', 4443 '/1/4/17/291/1001', 4444 '/1/4/17/291/1001/17105', 4445 true 4446 ), 4447 $provideboth( 4448 'testing grandchild', 4449 '/1', 4450 '/1/4/17/291/1001/17105', 4451 true 4452 ) 4453 ); 4454 } 4455 4456 /** 4457 * Ensure that the is_parent_of() function works as anticipated. 4458 * 4459 * @dataProvider is_parent_of_provider 4460 * @covers \context::is_parent_of 4461 * @covers \context_block::is_parent_of 4462 * @covers \context_course::is_parent_of 4463 * @covers \context_coursecat::is_parent_of 4464 * @covers \context_module::is_parent_of 4465 * @covers \context_system::is_parent_of 4466 * @covers \context_user::is_parent_of 4467 * @param string $contextpath The path of the context being compared with 4468 * @param string $testpath The path of the context being compared 4469 * @param bool $testself Whether to check the current context 4470 * @param bool $expected The expected result 4471 */ 4472 public function test_is_parent_of(string $contextpath, string $testpath, bool $testself, bool $expected): void { 4473 $context = $this->getMockBuilder(\context::class) 4474 ->disableOriginalConstructor() 4475 ->onlyMethods([ 4476 'get_url', 4477 'get_capabilities', 4478 ]) 4479 ->getMock(); 4480 4481 $rcp = new ReflectionProperty($context, '_path'); 4482 $rcp->setAccessible(true); 4483 $rcp->setValue($context, $contextpath); 4484 4485 $comparisoncontext = $this->getMockBuilder(\context::class) 4486 ->disableOriginalConstructor() 4487 ->onlyMethods([ 4488 'get_url', 4489 'get_capabilities', 4490 ]) 4491 ->getMock(); 4492 4493 $rcp = new ReflectionProperty($comparisoncontext, '_path'); 4494 $rcp->setAccessible(true); 4495 $rcp->setValue($comparisoncontext, $testpath); 4496 4497 $this->assertEquals($expected, $context->is_parent_of($comparisoncontext, $testself)); 4498 } 4499 4500 /** 4501 * Data provider for is_child_of context checks. 4502 * 4503 * @return array 4504 */ 4505 public function is_child_of_provider(): array { 4506 $provideboth = function(string $desc, string $contextpath, string $testpath, bool $expected): array { 4507 return [ 4508 "includeself: true; {$desc}" => [ 4509 $contextpath, 4510 $testpath, 4511 true, 4512 $expected, 4513 ], 4514 "includeself: false; {$desc}" => [ 4515 $contextpath, 4516 $testpath, 4517 false, 4518 $expected, 4519 ], 4520 ]; 4521 }; 4522 4523 return array_merge( 4524 [ 4525 'includeself: true, testing self' => [ 4526 '/1/4/17/291/1001/17105', 4527 '/1/4/17/291/1001/17105', 4528 true, 4529 true, 4530 ], 4531 'includeself: false, testing self' => [ 4532 '/1/4/17/291/1001/17105', 4533 '/1/4/17/291/1001/17105', 4534 false, 4535 false, 4536 ], 4537 ], 4538 $provideboth( 4539 'testing child', 4540 '/1/4/17/291/1001/17105', 4541 '/1/4/17/291/1001', 4542 true 4543 ), 4544 $provideboth( 4545 'testing parent', 4546 '/1/4/17/291/1001', 4547 '/1/4/17/291/1001/17105', 4548 false 4549 ), 4550 $provideboth( 4551 'testing grandchild', 4552 '/1/4/17/291/1001/17105', 4553 '/1', 4554 true 4555 ), 4556 $provideboth( 4557 'testing grandparent', 4558 '/1', 4559 '/1/4/17/291/1001/17105', 4560 false 4561 ) 4562 ); 4563 } 4564 4565 /** 4566 * Ensure that the is_child_of() function works as anticipated. 4567 * 4568 * @dataProvider is_child_of_provider 4569 * @covers \context::is_child_of 4570 * @covers \context_block::is_child_of 4571 * @covers \context_course::is_child_of 4572 * @covers \context_coursecat::is_child_of 4573 * @covers \context_module::is_child_of 4574 * @covers \context_system::is_child_of 4575 * @covers \context_user::is_child_of 4576 * @param string $contextpath The path of the context being compared with 4577 * @param string $testpath The path of the context being compared 4578 * @param bool $testself Whether to check the current context 4579 * @param bool $expected The expected result 4580 */ 4581 public function test_is_child_of(string $contextpath, string $testpath, bool $testself, bool $expected): void { 4582 $context = $this->getMockBuilder(\context::class) 4583 ->disableOriginalConstructor() 4584 ->onlyMethods([ 4585 'get_url', 4586 'get_capabilities', 4587 ]) 4588 ->getMock(); 4589 4590 $rcp = new ReflectionProperty($context, '_path'); 4591 $rcp->setAccessible(true); 4592 $rcp->setValue($context, $contextpath); 4593 4594 $comparisoncontext = $this->getMockBuilder(\context::class) 4595 ->disableOriginalConstructor() 4596 ->onlyMethods([ 4597 'get_url', 4598 'get_capabilities', 4599 ]) 4600 ->getMock(); 4601 4602 $rcp = new ReflectionProperty($comparisoncontext, '_path'); 4603 $rcp->setAccessible(true); 4604 $rcp->setValue($comparisoncontext, $testpath); 4605 4606 $this->assertEquals($expected, $context->is_child_of($comparisoncontext, $testself)); 4607 } 4608 4609 /** 4610 * Ensure that the get_parent_contexts() function limits the number of queries it performs. 4611 * 4612 * @covers ::get_parent_contexts 4613 */ 4614 public function test_get_parent_contexts_preload() { 4615 global $DB; 4616 4617 $this->resetAfterTest(); 4618 4619 /* 4620 * Given the following data structure: 4621 * System 4622 * - Category 4623 * --- Category 4624 * ----- Category 4625 * ------- Category 4626 * --------- Course 4627 * ----------- Activity (Forum) 4628 */ 4629 4630 $contexts = []; 4631 4632 $cat1 = $this->getDataGenerator()->create_category(); 4633 $cat2 = $this->getDataGenerator()->create_category(['parent' => $cat1->id]); 4634 $cat3 = $this->getDataGenerator()->create_category(['parent' => $cat2->id]); 4635 $cat4 = $this->getDataGenerator()->create_category(['parent' => $cat3->id]); 4636 $course = $this->getDataGenerator()->create_course(['category' => $cat4->id]); 4637 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); 4638 4639 $modcontext = context_module::instance($forum->cmid); 4640 4641 context_helper::reset_caches(); 4642 4643 // There should only be a single DB query. 4644 $predbqueries = $DB->perf_get_reads(); 4645 4646 $parents = $modcontext->get_parent_contexts(); 4647 // Note: For some databases There is one read, plus one FETCH, plus one CLOSE. 4648 // These all show as reads, when there has actually only been a single query. 4649 $this->assertLessThanOrEqual(3, $DB->perf_get_reads() - $predbqueries); 4650 } 4651 4652 /** 4653 * Ensure that get_with_capability_sql and get_with_capability_join respect context locking. 4654 * 4655 * @covers ::get_with_capability_join 4656 * @covers ::get_with_capability_sql 4657 */ 4658 public function test_get_with_capability_sql_locked() { 4659 global $DB; 4660 4661 $this->resetAfterTest(); 4662 4663 $generator = $this->getDataGenerator(); 4664 4665 $cat1 = $generator->create_category(); 4666 $cat2 = $generator->create_category(); 4667 $cat1course1 = $generator->create_course(['category' => $cat1->id]); 4668 $cat1course1forum = $generator->create_module('forum', ['course' => $cat1course1]); 4669 4670 $contexts = (object) [ 4671 'system' => \context_system::instance(), 4672 'cat1' => \context_coursecat::instance($cat1->id), 4673 'cat2' => \context_coursecat::instance($cat2->id), 4674 'cat1course1' => \context_course::instance($cat1course1->id), 4675 'cat1course1forum' => \context_module::instance($cat1course1forum->cmid), 4676 ]; 4677 4678 // Test with the 'mod/forum:startdiscussion' capability. 4679 $caput = 'mod/forum:startdiscussion'; 4680 4681 // Create a test user. 4682 $uut = $generator->create_and_enrol($cat1course1, 'teacher'); 4683 4684 // Initially the user will be returned by get_users_by_capability. 4685 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4686 $users = $DB->get_records_sql($sql, $params); 4687 $this->assertArrayHasKey($uut->id, $users); 4688 4689 // Freezing the forum will remove the user. 4690 set_config('contextlocking', 1); 4691 $contexts->cat1course1forum->set_locked(true); 4692 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4693 $users = $DB->get_records_sql($sql, $params); 4694 $this->assertArrayNotHasKey($uut->id, $users); 4695 4696 // But not if context locking is disabled. 4697 set_config('contextlocking', 0); 4698 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4699 $users = $DB->get_records_sql($sql, $params); 4700 $this->assertArrayHasKey($uut->id, $users); 4701 4702 $contexts->cat1course1forum->set_locked(false); 4703 4704 // Freezing the course will have the same effect. 4705 set_config('contextlocking', 1); 4706 $contexts->cat1course1->set_locked(true); 4707 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4708 $users = $DB->get_records_sql($sql, $params); 4709 $this->assertArrayNotHasKey($uut->id, $users); 4710 4711 // But not if context locking is disabled. 4712 set_config('contextlocking', 0); 4713 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4714 $users = $DB->get_records_sql($sql, $params); 4715 $this->assertArrayHasKey($uut->id, $users); 4716 4717 $contexts->cat1course1->set_locked(false); 4718 4719 // Freezing the category will have the same effect. 4720 set_config('contextlocking', 1); 4721 $contexts->cat1->set_locked(true); 4722 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4723 $users = $DB->get_records_sql($sql, $params); 4724 $this->assertArrayNotHasKey($uut->id, $users); 4725 4726 // But not if context locking is disabled. 4727 set_config('contextlocking', 0); 4728 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4729 $users = $DB->get_records_sql($sql, $params); 4730 $this->assertArrayHasKey($uut->id, $users); 4731 4732 $contexts->cat1->set_locked(false); 4733 4734 // Freezing an unrelated category will have no effect. 4735 set_config('contextlocking', 1); 4736 $contexts->cat2->set_locked(true); 4737 list($sql, $params) = get_with_capability_sql($contexts->cat1course1forum, $caput); 4738 $users = $DB->get_records_sql($sql, $params); 4739 $this->assertArrayHasKey($uut->id, $users); 4740 } 4741 4742 /** 4743 * Ensure that get_users_by_capability respects context freezing. 4744 * 4745 * @covers ::get_users_by_capability 4746 */ 4747 public function test_get_users_by_capability_locked() { 4748 $this->resetAfterTest(); 4749 4750 $generator = $this->getDataGenerator(); 4751 4752 $cat1 = $generator->create_category(); 4753 $cat2 = $generator->create_category(); 4754 $cat1course1 = $generator->create_course(['category' => $cat1->id]); 4755 $cat1course1forum = $generator->create_module('forum', ['course' => $cat1course1]); 4756 4757 $contexts = (object) [ 4758 'system' => \context_system::instance(), 4759 'cat1' => \context_coursecat::instance($cat1->id), 4760 'cat2' => \context_coursecat::instance($cat2->id), 4761 'cat1course1' => \context_course::instance($cat1course1->id), 4762 'cat1course1forum' => \context_module::instance($cat1course1forum->cmid), 4763 ]; 4764 4765 // Test with the 'mod/forum:startdiscussion' capability. 4766 $caput = 'mod/forum:startdiscussion'; 4767 4768 // Create a test user. 4769 $uut = $generator->create_and_enrol($cat1course1, 'teacher'); 4770 4771 // Initially the user will be returned by get_users_by_capability. 4772 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4773 $this->assertArrayHasKey($uut->id, $users); 4774 4775 // Freezing the forum will remove the user. 4776 set_config('contextlocking', 1); 4777 $contexts->cat1course1forum->set_locked(true); 4778 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4779 $this->assertArrayNotHasKey($uut->id, $users); 4780 4781 // But not if context locking is disabled. 4782 set_config('contextlocking', 0); 4783 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4784 $this->assertArrayHasKey($uut->id, $users); 4785 4786 $contexts->cat1course1forum->set_locked(false); 4787 4788 // Freezing the course will have the same effect. 4789 set_config('contextlocking', 1); 4790 $contexts->cat1course1->set_locked(true); 4791 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4792 $this->assertArrayNotHasKey($uut->id, $users); 4793 4794 // But not if context locking is disabled. 4795 set_config('contextlocking', 0); 4796 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4797 $this->assertArrayHasKey($uut->id, $users); 4798 4799 $contexts->cat1course1->set_locked(false); 4800 4801 // Freezing the category will have the same effect. 4802 set_config('contextlocking', 1); 4803 $contexts->cat1->set_locked(true); 4804 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4805 $this->assertArrayNotHasKey($uut->id, $users); 4806 4807 // But not if context locking is disabled. 4808 set_config('contextlocking', 0); 4809 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4810 $this->assertArrayHasKey($uut->id, $users); 4811 4812 $contexts->cat1->set_locked(false); 4813 4814 // Freezing an unrelated category will have no effect. 4815 set_config('contextlocking', 1); 4816 $contexts->cat2->set_locked(true); 4817 $users = get_users_by_capability($contexts->cat1course1forum, $caput); 4818 $this->assertArrayHasKey($uut->id, $users); 4819 } 4820 4821 /** 4822 * Test require_all_capabilities. 4823 * 4824 * @covers ::require_all_capabilities 4825 */ 4826 public function test_require_all_capabilities() { 4827 global $DB; 4828 4829 $this->resetAfterTest(); 4830 4831 $course = $this->getDataGenerator()->create_course(); 4832 $coursecontext = context_course::instance($course->id); 4833 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST); 4834 $teacher = $this->getDataGenerator()->create_user(); 4835 role_assign($teacherrole->id, $teacher->id, $coursecontext); 4836 4837 // Note: Here are used default capabilities, the full test is in permission evaluation bellow, 4838 // use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user. 4839 $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupsection'))); 4840 $this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse'))); 4841 4842 $sca = array('moodle/backup:backupsection', 'moodle/backup:backupcourse'); 4843 4844 $this->setUser($teacher); 4845 require_all_capabilities($sca, $coursecontext); 4846 require_all_capabilities($sca, $coursecontext, $teacher); 4847 4848 // Guest users should not have any of these perms. 4849 $this->setUser(0); 4850 $this->expectException(\required_capability_exception::class); 4851 require_all_capabilities($sca, $coursecontext); 4852 } 4853 4854 /** 4855 * Test get_navigation_filter_context. 4856 * 4857 * @covers ::get_navigation_filter_context 4858 */ 4859 public function test_get_navigation_filter_context() { 4860 $this->resetAfterTest(); 4861 $course = $this->getDataGenerator()->create_course(); 4862 set_config('filternavigationwithsystemcontext', 0); 4863 // First test passed values are returned if disabled. 4864 $this->assertNull(context_helper::get_navigation_filter_context(null)); 4865 $coursecontext = context_course::instance($course->id); 4866 $filtercontext = context_helper::get_navigation_filter_context($coursecontext); 4867 $this->assertEquals($coursecontext->id, $filtercontext->id); 4868 4869 // Now test that any input returns system context if enabled. 4870 set_config('filternavigationwithsystemcontext', 1); 4871 $filtercontext = context_helper::get_navigation_filter_context(null); 4872 $this->assertInstanceOf('\context_system', $filtercontext); 4873 $filtercontext = context_helper::get_navigation_filter_context($coursecontext); 4874 $this->assertInstanceOf('\context_system', $filtercontext); 4875 } 4876 } 4877 4878 /** 4879 * Context caching fixture 4880 */ 4881 class context_inspection extends context_helper { 4882 public static function test_context_cache_size() { 4883 return self::$cache_count; 4884 } 4885 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body