See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 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 namespace core; 18 19 /** 20 * Test core_user class. 21 * 22 * @package core 23 * @copyright 2013 Rajesh Taneja <rajesh@moodle.com> 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 class user_test extends \advanced_testcase { 27 28 /** 29 * Setup test data. 30 */ 31 protected function setUp(): void { 32 $this->resetAfterTest(true); 33 } 34 35 public function test_get_user() { 36 global $CFG; 37 38 39 // Create user and try fetach it with api. 40 $user = $this->getDataGenerator()->create_user(); 41 $this->assertEquals($user, \core_user::get_user($user->id, '*', MUST_EXIST)); 42 43 // Test noreply user. 44 $CFG->noreplyuserid = null; 45 $noreplyuser = \core_user::get_noreply_user(); 46 $this->assertEquals(1, $noreplyuser->emailstop); 47 $this->assertFalse(\core_user::is_real_user($noreplyuser->id)); 48 $this->assertEquals($CFG->noreplyaddress, $noreplyuser->email); 49 $this->assertEquals(get_string('noreplyname'), $noreplyuser->firstname); 50 51 // Set user as noreply user and make sure noreply propery is set. 52 \core_user::reset_internal_users(); 53 $CFG->noreplyuserid = $user->id; 54 $noreplyuser = \core_user::get_noreply_user(); 55 $this->assertEquals(1, $noreplyuser->emailstop); 56 $this->assertTrue(\core_user::is_real_user($noreplyuser->id)); 57 58 // Test support user. 59 \core_user::reset_internal_users(); 60 $CFG->supportemail = null; 61 $CFG->noreplyuserid = null; 62 $supportuser = \core_user::get_support_user(); 63 $adminuser = get_admin(); 64 $this->assertEquals($adminuser, $supportuser); 65 $this->assertTrue(\core_user::is_real_user($supportuser->id)); 66 67 // When supportemail is set. 68 \core_user::reset_internal_users(); 69 $CFG->supportemail = 'test@example.com'; 70 $supportuser = \core_user::get_support_user(); 71 $this->assertEquals(\core_user::SUPPORT_USER, $supportuser->id); 72 $this->assertFalse(\core_user::is_real_user($supportuser->id)); 73 74 // Set user as support user and make sure noreply propery is set. 75 \core_user::reset_internal_users(); 76 $CFG->supportuserid = $user->id; 77 $supportuser = \core_user::get_support_user(); 78 $this->assertEquals($user, $supportuser); 79 $this->assertTrue(\core_user::is_real_user($supportuser->id)); 80 } 81 82 /** 83 * Test get_user_by_username method. 84 */ 85 public function test_get_user_by_username() { 86 $record = array(); 87 $record['username'] = 'johndoe'; 88 $record['email'] = 'johndoe@example.com'; 89 $record['timecreated'] = time(); 90 91 // Create a default user for the test. 92 $userexpected = $this->getDataGenerator()->create_user($record); 93 94 // Assert that the returned user is the espected one. 95 $this->assertEquals($userexpected, \core_user::get_user_by_username('johndoe')); 96 97 // Assert that a subset of fields is correctly returned. 98 $this->assertEquals((object) $record, \core_user::get_user_by_username('johndoe', 'username,email,timecreated')); 99 100 // Assert that a user with a different mnethostid will no be returned. 101 $this->assertFalse(\core_user::get_user_by_username('johndoe', 'username,email,timecreated', 2)); 102 103 // Create a new user from a different host. 104 $record['mnethostid'] = 2; 105 $userexpected2 = $this->getDataGenerator()->create_user($record); 106 107 // Assert that the new user is returned when specified the correct mnethostid. 108 $this->assertEquals($userexpected2, \core_user::get_user_by_username('johndoe', '*', 2)); 109 110 // Assert that a user not in the db return false. 111 $this->assertFalse(\core_user::get_user_by_username('janedoe')); 112 } 113 114 public function test_search() { 115 global $DB; 116 117 self::init_search_tests(); 118 119 // Set up three courses for test. 120 $generator = $this->getDataGenerator(); 121 $course1 = $generator->create_course(); 122 $course2 = $generator->create_course(); 123 $course3 = $generator->create_course(); 124 125 // Manager user in system level. 126 $manager = $generator->create_user(['firstname' => 'Manager', 'lastname' => 'Person', 127 'email' => 'x@x.x']); 128 $systemcontext = \context_system::instance(); 129 $generator->role_assign($DB->get_field('role', 'id', ['shortname' => 'manager']), 130 $manager->id, $systemcontext->id); 131 132 // Teachers in one and two courses. 133 $teacher1 = $generator->create_user(['firstname' => 'Alberto', 'lastname' => 'Unwin', 134 'email' => 'a.unwin@x.x']); 135 $generator->enrol_user($teacher1->id, $course1->id, 'teacher'); 136 $teacher2and3 = $generator->create_user(['firstname' => 'Alexandra', 'lastname' => 'Penguin', 137 'email' => 'sillypenguin@x.x']); 138 $generator->enrol_user($teacher2and3->id, $course2->id, 'teacher'); 139 $generator->enrol_user($teacher2and3->id, $course3->id, 'teacher'); 140 141 // Students in each course and some on multiple courses. 142 $student1 = $generator->create_user(['firstname' => 'Amanda', 'lastname' => 'Hodder', 143 'email' => 'hodder_a@x.x']); 144 $generator->enrol_user($student1->id, $course1->id, 'student'); 145 $student2 = $generator->create_user(['firstname' => 'Audrey', 'lastname' => 'Methuen', 146 'email' => 'audrey@x.x']); 147 $generator->enrol_user($student2->id, $course2->id, 'student'); 148 $student3 = $generator->create_user(['firstname' => 'Austin', 'lastname' => 'Bloomsbury', 149 'email' => 'a.bloomsbury@x.x']); 150 $generator->enrol_user($student3->id, $course3->id, 'student'); 151 $student1and2 = $generator->create_user(['firstname' => 'Augustus', 'lastname' => 'Random', 152 'email' => 'random@x.x']); 153 $generator->enrol_user($student1and2->id, $course1->id, 'student'); 154 $generator->enrol_user($student1and2->id, $course2->id, 'student'); 155 $studentall = $generator->create_user(['firstname' => 'Amelia', 'lastname' => 'House', 156 'email' => 'house@x.x']); 157 $generator->enrol_user($studentall->id, $course1->id, 'student'); 158 $generator->enrol_user($studentall->id, $course2->id, 'student'); 159 $generator->enrol_user($studentall->id, $course3->id, 'student'); 160 161 // Special mixed user (name does not begin with A) is a teacher in one course and student 162 // in another. 163 $mixed = $generator->create_user(['firstname' => 'Xavier', 'lastname' => 'Harper', 164 'email' => 'xh1248@x.x']); 165 $generator->enrol_user($mixed->id, $course1->id, 'student'); 166 $generator->enrol_user($mixed->id, $course3->id, 'teacher'); 167 168 // As admin user, try searching for somebody at system level by first name, checking the 169 // results. 170 $this->setAdminUser(); 171 $result = \core_user::search('Amelia'); 172 $this->assertCount(1, $result); 173 174 // Check some basic fields, and test other fields are present. 175 $this->assertEquals($studentall->id, $result[0]->id); 176 $this->assertEquals('Amelia', $result[0]->firstname); 177 $this->assertEquals('House', $result[0]->lastname); 178 $this->assertEquals('house@x.x', $result[0]->email); 179 $this->assertEquals(0, $result[0]->deleted); 180 $this->assertObjectHasAttribute('firstnamephonetic', $result[0]); 181 $this->assertObjectHasAttribute('lastnamephonetic', $result[0]); 182 $this->assertObjectHasAttribute('middlename', $result[0]); 183 $this->assertObjectHasAttribute('alternatename', $result[0]); 184 $this->assertObjectHasAttribute('imagealt', $result[0]); 185 $this->assertObjectHasAttribute('username', $result[0]); 186 187 // Now search by lastname, both names, and partials, case-insensitive. 188 $this->assertEquals($result, \core_user::search('House')); 189 $this->assertEquals($result, \core_user::search('Amelia house')); 190 $this->assertEquals($result, \core_user::search('amelI')); 191 $this->assertEquals($result, \core_user::search('hoUs')); 192 $this->assertEquals($result, \core_user::search('Amelia H')); 193 194 // Admin user can also search by email (full or partial). 195 $this->assertEquals($result, \core_user::search('house@x.x')); 196 $this->assertEquals($result, \core_user::search('hOuse@')); 197 198 // What if we just search for A? (They all begin with A except the manager.) 199 $result = \core_user::search('a'); 200 $this->assertCount(7, $result); 201 202 // Au gets us Audrey, Austin, and Augustus - in alphabetical order by surname. 203 $result = \core_user::search('au'); 204 $this->assertCount(3, $result); 205 $this->assertEquals('Austin', $result[0]->firstname); 206 $this->assertEquals('Audrey', $result[1]->firstname); 207 $this->assertEquals('Augustus', $result[2]->firstname); 208 209 // But if we search within course 2 we'll get Audrey and Augustus first. 210 $course2context = \context_course::instance($course2->id); 211 $result = \core_user::search('au', $course2context); 212 $this->assertCount(3, $result); 213 $this->assertEquals('Audrey', $result[0]->firstname); 214 $this->assertEquals('Augustus', $result[1]->firstname); 215 $this->assertEquals('Austin', $result[2]->firstname); 216 217 // Try doing a few searches as manager - we should get the same results and can still 218 // search by email too. 219 $this->setUser($manager); 220 $result = \core_user::search('a'); 221 $this->assertCount(7, $result); 222 $result = \core_user::search('au', $course2context); 223 $this->assertCount(3, $result); 224 $result = \core_user::search('house@x.x'); 225 $this->assertCount(1, $result); 226 227 // Teacher 1. No site-level permission so can't see users outside the enrolled course. 228 $this->setUser($teacher1); 229 $result = \core_user::search('au'); 230 $this->assertCount(1, $result); 231 $this->assertEquals('Augustus', $result[0]->firstname); 232 233 // Can still search by email for that user. 234 $result = \core_user::search('random@x.x'); 235 $this->assertCount(1, $result); 236 237 // Search everyone - teacher can only see four users (including themself). 238 $result = \core_user::search('a'); 239 $this->assertCount(4, $result); 240 241 // Search within course 2 - you get the same four users (which doesn't include 242 // everyone on that course) but the two on course 2 should be first. 243 $result = \core_user::search('a', $course2context); 244 $this->assertCount(4, $result); 245 $this->assertEquals('Amelia', $result[0]->firstname); 246 $this->assertEquals('Augustus', $result[1]->firstname); 247 248 // Other teacher. 249 $this->setUser($teacher2and3); 250 $result = \core_user::search('au'); 251 $this->assertCount(3, $result); 252 253 $result = \core_user::search('a'); 254 $this->assertCount(5, $result); 255 256 // Student can only see users on course 3. 257 $this->setUser($student3); 258 $result = \core_user::search('a'); 259 $this->assertCount(3, $result); 260 261 $result = \core_user::search('au'); 262 $this->assertCount(1, $result); 263 $this->assertEquals('Austin', $result[0]->firstname); 264 265 // Student cannot search by email. 266 $result = \core_user::search('a.bloomsbury@x.x'); 267 $this->assertCount(0, $result); 268 269 // Student on all courses can see all the A users. 270 $this->setUser($studentall); 271 $result = \core_user::search('a'); 272 $this->assertCount(7, $result); 273 274 // Mixed user can see users on courses 1 and 3. 275 $this->setUser($mixed); 276 $result = \core_user::search('a'); 277 $this->assertCount(6, $result); 278 279 // Mixed user can search by email for students on course 3 but not on course 1. 280 $result = \core_user::search('hodder_a@x.x'); 281 $this->assertCount(0, $result); 282 $result = \core_user::search('house@x.x'); 283 $this->assertCount(1, $result); 284 } 285 286 /** 287 * Tests the search() function with limits on the number to return. 288 */ 289 public function test_search_with_count() { 290 self::init_search_tests(); 291 $generator = $this->getDataGenerator(); 292 $course = $generator->create_course(); 293 294 // Check default limit (30). 295 for ($i = 0; $i < 31; $i++) { 296 $student = $generator->create_user(['firstname' => 'Guy', 'lastname' => 'Xxx' . $i, 297 'email' => 'xxx@x.x']); 298 $generator->enrol_user($student->id, $course->id, 'student'); 299 } 300 $this->setAdminUser(); 301 $result = \core_user::search('Guy'); 302 $this->assertCount(30, $result); 303 304 // Check a small limit. 305 $result = \core_user::search('Guy', null, 10); 306 $this->assertCount(10, $result); 307 308 // Check no limit. 309 $result = \core_user::search('Guy', null, 0); 310 $this->assertCount(31, $result); 311 } 312 313 /** 314 * When course is in separate groups mode and user is a student, they can't see people who 315 * are not in the same group. This is checked by the user profile permission thing and not 316 * currently by the original query. 317 */ 318 public function test_search_group_permissions() { 319 global $DB; 320 321 self::init_search_tests(); 322 323 // Create one user to do the searching. 324 $generator = $this->getDataGenerator(); 325 $course = $generator->create_course(['groupmode' => SEPARATEGROUPS]); 326 $searcher = $generator->create_user(['firstname' => 'Searchy', 'lastname' => 'Sam', 327 'email' => 'xxx@x.x']); 328 $generator->enrol_user($searcher->id, $course->id, 'student'); 329 $group = $generator->create_group(['courseid' => $course->id]); 330 groups_add_member($group, $searcher); 331 332 // Create a large number of people so that we have to make multiple database reads. 333 $targets = []; 334 for ($i = 0; $i < 50; $i++) { 335 $student = $generator->create_user(['firstname' => 'Guy', 'lastname' => 'Xxx' . $i, 336 'email' => 'xxx@x.x']); 337 $generator->enrol_user($student->id, $course->id, 'student'); 338 $targets[] = $student; 339 } 340 341 // The first and last people are in the same group. 342 groups_add_member($group, $targets[0]); 343 groups_add_member($group, $targets[49]); 344 345 // As searcher, we only find the 2 in the same group. 346 $this->setUser($searcher); 347 $result = \core_user::search('Guy'); 348 $this->assertCount(2, $result); 349 350 // If we change the course to visible groups though, we get the max number. 351 $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $course->id]); 352 $result = \core_user::search('Guy'); 353 $this->assertCount(30, $result); 354 } 355 356 /** 357 * When course is in separate groups mode and user is a student, they can't see people who 358 * are not in the same group. This is checked by the user profile permission thing and not 359 * currently by the original query. 360 */ 361 public function test_search_deleted_users() { 362 self::init_search_tests(); 363 364 // Create one user to do the searching. 365 $generator = $this->getDataGenerator(); 366 $course = $generator->create_course(); 367 $searcher = $generator->create_user(['firstname' => 'Searchy', 'lastname' => 'Sam', 368 'email' => 'xxx@x.x']); 369 $generator->enrol_user($searcher->id, $course->id, 'student'); 370 371 // Create another two users to search for. 372 $student1 = $generator->create_user(['firstname' => 'Amelia', 'lastname' => 'Aardvark']); 373 $student2 = $generator->create_user(['firstname' => 'Amelia', 'lastname' => 'Beetle']); 374 $generator->enrol_user($student1->id, $course->id, 'student'); 375 $generator->enrol_user($student2->id, $course->id, 'student'); 376 377 // As searcher, we find both users. 378 $this->setUser($searcher); 379 $result = \core_user::search('Amelia'); 380 $this->assertCount(2, $result); 381 382 // What if one is deleted? 383 delete_user($student1); 384 $result = \core_user::search('Amelia'); 385 $this->assertCount(1, $result); 386 $this->assertEquals('Beetle', $result[0]->lastname); 387 388 // Delete the other, for good measure. 389 delete_user($student2); 390 $result = \core_user::search('Amelia'); 391 $this->assertCount(0, $result); 392 } 393 394 /** 395 * Carries out standard setup for the search test functions. 396 */ 397 protected static function init_search_tests() { 398 global $DB; 399 400 // For all existing users, set their name and email to something stupid so we don't 401 // accidentally find one, confusing the test counts. 402 $DB->set_field('user', 'firstname', 'Zaphod'); 403 $DB->set_field('user', 'lastname', 'Beeblebrox'); 404 $DB->set_field('user', 'email', 'zaphod@beeblebrox.example.org'); 405 406 // This is the default value, but let's set it just to be certain in case it changes later. 407 // It affects what fields admin (and other users with the viewuseridentity permission) can 408 // search in addition to the name. 409 set_config('showuseridentity', 'email'); 410 } 411 412 /** 413 * Test require_active_user 414 */ 415 public function test_require_active_user() { 416 global $DB; 417 418 // Create a default user for the test. 419 $userexpected = $this->getDataGenerator()->create_user(); 420 421 // Simple case, all good. 422 \core_user::require_active_user($userexpected, true, true); 423 424 // Set user not confirmed. 425 $DB->set_field('user', 'confirmed', 0, array('id' => $userexpected->id)); 426 try { 427 \core_user::require_active_user($userexpected); 428 } catch (\moodle_exception $e) { 429 $this->assertEquals('usernotconfirmed', $e->errorcode); 430 } 431 $DB->set_field('user', 'confirmed', 1, array('id' => $userexpected->id)); 432 433 // Set nologin auth method. 434 $DB->set_field('user', 'auth', 'nologin', array('id' => $userexpected->id)); 435 try { 436 \core_user::require_active_user($userexpected, false, true); 437 } catch (\moodle_exception $e) { 438 $this->assertEquals('suspended', $e->errorcode); 439 } 440 // Check no exceptions are thrown if we don't specify to check suspended. 441 \core_user::require_active_user($userexpected); 442 $DB->set_field('user', 'auth', 'manual', array('id' => $userexpected->id)); 443 444 // Set user suspended. 445 $DB->set_field('user', 'suspended', 1, array('id' => $userexpected->id)); 446 try { 447 \core_user::require_active_user($userexpected, true); 448 } catch (\moodle_exception $e) { 449 $this->assertEquals('suspended', $e->errorcode); 450 } 451 // Check no exceptions are thrown if we don't specify to check suspended. 452 \core_user::require_active_user($userexpected); 453 454 // Delete user. 455 delete_user($userexpected); 456 try { 457 \core_user::require_active_user($userexpected); 458 } catch (\moodle_exception $e) { 459 $this->assertEquals('userdeleted', $e->errorcode); 460 } 461 462 // Use a not real user. 463 $noreplyuser = \core_user::get_noreply_user(); 464 try { 465 \core_user::require_active_user($noreplyuser, true); 466 } catch (\moodle_exception $e) { 467 $this->assertEquals('invaliduser', $e->errorcode); 468 } 469 470 // Get the guest user. 471 $guestuser = $DB->get_record('user', array('username' => 'guest')); 472 try { 473 \core_user::require_active_user($guestuser, true); 474 } catch (\moodle_exception $e) { 475 $this->assertEquals('guestsarenotallowed', $e->errorcode); 476 } 477 478 } 479 480 /** 481 * Test get_property_definition() method. 482 */ 483 public function test_get_property_definition() { 484 // Try to get a existing property. 485 $properties = \core_user::get_property_definition('id'); 486 $this->assertEquals($properties['type'], PARAM_INT); 487 $properties = \core_user::get_property_definition('username'); 488 $this->assertEquals($properties['type'], PARAM_USERNAME); 489 490 // Invalid property. 491 try { 492 \core_user::get_property_definition('fullname'); 493 } catch (\coding_exception $e) { 494 $this->assertMatchesRegularExpression('/Invalid property requested./', $e->getMessage()); 495 } 496 497 // Empty parameter. 498 try { 499 \core_user::get_property_definition(''); 500 } catch (\coding_exception $e) { 501 $this->assertMatchesRegularExpression('/Invalid property requested./', $e->getMessage()); 502 } 503 } 504 505 /** 506 * Test validate() method. 507 */ 508 public function test_validate() { 509 510 // Create user with just with username and firstname. 511 $record = array('username' => 's10', 'firstname' => 'Bebe Stevens'); 512 $validation = \core_user::validate((object)$record); 513 514 // Validate the user, should return true as the user data is correct. 515 $this->assertTrue($validation); 516 517 // Create user with incorrect data (invalid country and theme). 518 $record = array('username' => 's1', 'firstname' => 'Eric Cartman', 'country' => 'UU', 'theme' => 'beise'); 519 520 // Should return an array with 2 errors. 521 $validation = \core_user::validate((object)$record); 522 $this->assertArrayHasKey('country', $validation); 523 $this->assertArrayHasKey('theme', $validation); 524 $this->assertCount(2, $validation); 525 526 // Create user with malicious data (xss). 527 $record = array('username' => 's3', 'firstname' => 'Kyle<script>alert(1);<script> Broflovski'); 528 529 // Should return an array with 1 error. 530 $validation = \core_user::validate((object)$record); 531 $this->assertCount(1, $validation); 532 $this->assertArrayHasKey('firstname', $validation); 533 } 534 535 /** 536 * Test clean_data() method. 537 */ 538 public function test_clean_data() { 539 $this->resetAfterTest(false); 540 541 $user = new \stdClass(); 542 $user->firstname = 'John <script>alert(1)</script> Doe'; 543 $user->username = 'john%#&~%*_doe'; 544 $user->email = ' john@testing.com '; 545 $user->deleted = 'no'; 546 $user->description = '<b>A description <script>alert(123);</script>about myself.</b>'; 547 $usercleaned = \core_user::clean_data($user); 548 549 // Expected results. 550 $this->assertEquals('John alert(1) Doe', $usercleaned->firstname); 551 $this->assertEquals('john@testing.com', $usercleaned->email); 552 $this->assertEquals(0, $usercleaned->deleted); 553 $this->assertEquals('<b>A description <script>alert(123);</script>about myself.</b>', $user->description); 554 $this->assertEquals('john_doe', $user->username); 555 556 // Try to clean an invalid property (userfullname). 557 $user->userfullname = 'John Doe'; 558 \core_user::clean_data($user); 559 $this->assertDebuggingCalled("The property 'userfullname' could not be cleaned."); 560 } 561 562 /** 563 * Test clean_field() method. 564 */ 565 public function test_clean_field() { 566 567 // Create a 'malicious' user object/ 568 $user = new \stdClass(); 569 $user->firstname = 'John <script>alert(1)</script> Doe'; 570 $user->username = 'john%#&~%*_doe'; 571 $user->email = ' john@testing.com '; 572 $user->deleted = 'no'; 573 $user->description = '<b>A description <script>alert(123);</script>about myself.</b>'; 574 $user->userfullname = 'John Doe'; 575 576 // Expected results. 577 $this->assertEquals('John alert(1) Doe', \core_user::clean_field($user->firstname, 'firstname')); 578 $this->assertEquals('john_doe', \core_user::clean_field($user->username, 'username')); 579 $this->assertEquals('john@testing.com', \core_user::clean_field($user->email, 'email')); 580 $this->assertEquals(0, \core_user::clean_field($user->deleted, 'deleted')); 581 $this->assertEquals('<b>A description <script>alert(123);</script>about myself.</b>', \core_user::clean_field($user->description, 'description')); 582 583 // Try to clean an invalid property (fullname). 584 \core_user::clean_field($user->userfullname, 'fullname'); 585 $this->assertDebuggingCalled("The property 'fullname' could not be cleaned."); 586 } 587 588 /** 589 * Test get_property_type() method. 590 */ 591 public function test_get_property_type() { 592 593 // Fetch valid properties and verify if the type is correct. 594 $type = \core_user::get_property_type('username'); 595 $this->assertEquals(PARAM_USERNAME, $type); 596 $type = \core_user::get_property_type('email'); 597 $this->assertEquals(PARAM_RAW_TRIMMED, $type); 598 $type = \core_user::get_property_type('timezone'); 599 $this->assertEquals(PARAM_TIMEZONE, $type); 600 601 // Try to fetch type of a non-existent properties. 602 $nonexistingproperty = 'userfullname'; 603 $this->expectException('coding_exception'); 604 $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty); 605 \core_user::get_property_type($nonexistingproperty); 606 $nonexistingproperty = 'mobilenumber'; 607 $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty); 608 \core_user::get_property_type($nonexistingproperty); 609 } 610 611 /** 612 * Test get_property_null() method. 613 */ 614 public function test_get_property_null() { 615 // Fetch valid properties and verify if it is NULL_ALLOWED or NULL_NOT_ALLOWED. 616 $property = \core_user::get_property_null('username'); 617 $this->assertEquals(NULL_NOT_ALLOWED, $property); 618 $property = \core_user::get_property_null('password'); 619 $this->assertEquals(NULL_NOT_ALLOWED, $property); 620 $property = \core_user::get_property_null('imagealt'); 621 $this->assertEquals(NULL_ALLOWED, $property); 622 $property = \core_user::get_property_null('middlename'); 623 $this->assertEquals(NULL_ALLOWED, $property); 624 625 // Try to fetch type of a non-existent properties. 626 $nonexistingproperty = 'lastnamefonetic'; 627 $this->expectException('coding_exception'); 628 $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty); 629 \core_user::get_property_null($nonexistingproperty); 630 $nonexistingproperty = 'midlename'; 631 $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty); 632 \core_user::get_property_null($nonexistingproperty); 633 } 634 635 /** 636 * Test get_property_choices() method. 637 */ 638 public function test_get_property_choices() { 639 640 // Test against country property choices. 641 $choices = \core_user::get_property_choices('country'); 642 $this->assertArrayHasKey('AU', $choices); 643 $this->assertArrayHasKey('BR', $choices); 644 $this->assertArrayNotHasKey('WW', $choices); 645 $this->assertArrayNotHasKey('TX', $choices); 646 647 // Test against lang property choices. 648 $choices = \core_user::get_property_choices('lang'); 649 $this->assertArrayHasKey('en', $choices); 650 $this->assertArrayNotHasKey('ww', $choices); 651 $this->assertArrayNotHasKey('yy', $choices); 652 653 // Test against theme property choices. 654 $choices = \core_user::get_property_choices('theme'); 655 $this->assertArrayHasKey('boost', $choices); 656 $this->assertArrayHasKey('classic', $choices); 657 $this->assertArrayNotHasKey('unknowntheme', $choices); 658 $this->assertArrayNotHasKey('wrongtheme', $choices); 659 660 // Try to fetch type of a non-existent properties. 661 $nonexistingproperty = 'language'; 662 $this->expectException('coding_exception'); 663 $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty); 664 \core_user::get_property_null($nonexistingproperty); 665 $nonexistingproperty = 'coutries'; 666 $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty); 667 \core_user::get_property_null($nonexistingproperty); 668 } 669 670 /** 671 * Test get_property_default(). 672 */ 673 public function test_get_property_default() { 674 global $CFG; 675 $this->resetAfterTest(); 676 677 $country = \core_user::get_property_default('country'); 678 $this->assertEquals($CFG->country, $country); 679 set_config('country', 'AU'); 680 \core_user::reset_caches(); 681 $country = \core_user::get_property_default('country'); 682 $this->assertEquals($CFG->country, $country); 683 684 $lang = \core_user::get_property_default('lang'); 685 $this->assertEquals($CFG->lang, $lang); 686 set_config('lang', 'en'); 687 $lang = \core_user::get_property_default('lang'); 688 $this->assertEquals($CFG->lang, $lang); 689 690 $this->setTimezone('Europe/London', 'Pacific/Auckland'); 691 \core_user::reset_caches(); 692 $timezone = \core_user::get_property_default('timezone'); 693 $this->assertEquals('Europe/London', $timezone); 694 $this->setTimezone('99', 'Pacific/Auckland'); 695 \core_user::reset_caches(); 696 $timezone = \core_user::get_property_default('timezone'); 697 $this->assertEquals('Pacific/Auckland', $timezone); 698 699 $this->expectException(\coding_exception::class); 700 $this->expectExceptionMessage('Invalid property requested, or the property does not has a default value.'); 701 \core_user::get_property_default('firstname'); 702 } 703 704 /** 705 * Ensure that the noreply user is not cached. 706 */ 707 public function test_get_noreply_user() { 708 global $CFG; 709 710 // Create a new fake language 'xx' with the 'noreplyname'. 711 $langfolder = $CFG->dataroot . '/lang/xx'; 712 check_dir_exists($langfolder); 713 $langconfig = "<?php\n\defined('MOODLE_INTERNAL') || die();"; 714 file_put_contents($langfolder . '/langconfig.php', $langconfig); 715 $langconfig = "<?php\n\$string['noreplyname'] = 'XXX';"; 716 file_put_contents($langfolder . '/moodle.php', $langconfig); 717 718 $CFG->lang='en'; 719 $enuser = \core_user::get_noreply_user(); 720 721 $CFG->lang='xx'; 722 $xxuser = \core_user::get_noreply_user(); 723 724 $this->assertNotEquals($enuser, $xxuser); 725 } 726 727 /** 728 * Test is_real_user method. 729 */ 730 public function test_is_real_user() { 731 global $CFG, $USER; 732 733 // Real users are real users. 734 $auser = $this->getDataGenerator()->create_user(); 735 $guest = guest_user(); 736 $this->assertTrue(\core_user::is_real_user($auser->id)); 737 $this->assertTrue(\core_user::is_real_user($auser->id, true)); 738 $this->assertTrue(\core_user::is_real_user($guest->id)); 739 $this->assertTrue(\core_user::is_real_user($guest->id, true)); 740 741 // Non-logged in users are not real users. 742 $this->assertSame(0, $USER->id, 'The non-logged in user should have an ID of 0.'); 743 $this->assertFalse(\core_user::is_real_user($USER->id)); 744 $this->assertFalse(\core_user::is_real_user($USER->id, true)); 745 746 // Other types of logged in users are real users. 747 $this->setAdminUser(); 748 $this->assertTrue(\core_user::is_real_user($USER->id)); 749 $this->assertTrue(\core_user::is_real_user($USER->id, true)); 750 $this->setGuestUser(); 751 $this->assertTrue(\core_user::is_real_user($USER->id)); 752 $this->assertTrue(\core_user::is_real_user($USER->id, true)); 753 $this->setUser($auser); 754 $this->assertTrue(\core_user::is_real_user($USER->id)); 755 $this->assertTrue(\core_user::is_real_user($USER->id, true)); 756 757 // Fake accounts are not real users. 758 $CFG->noreplyuserid = null; 759 $this->assertFalse(\core_user::is_real_user(\core_user::get_noreply_user()->id)); 760 $this->assertFalse(\core_user::is_real_user(\core_user::get_noreply_user()->id, true)); 761 $CFG->supportuserid = null; 762 $CFG->supportemail = 'test@example.com'; 763 $this->assertFalse(\core_user::is_real_user(\core_user::get_support_user()->id)); 764 $this->assertFalse(\core_user::is_real_user(\core_user::get_support_user()->id, true)); 765 } 766 767 /** 768 * Tests for the {@see \core_user::awaiting_action()} method. 769 */ 770 public function test_awaiting_action() { 771 global $CFG, $DB, $USER; 772 773 $guest = \core_user::get_user($CFG->siteguest); 774 $student = $this->getDataGenerator()->create_user(); 775 $teacher = $this->getDataGenerator()->create_user(); 776 $manager = $this->getDataGenerator()->create_user(); 777 $admin = get_admin(); 778 779 $this->getDataGenerator()->role_assign($DB->get_field('role', 'id', ['shortname' => 'manager']), 780 $manager->id, \context_system::instance()->id); 781 782 // Scenario: Guests required to agree to site policy. 783 $this->assertFalse(\core_user::awaiting_action($guest)); 784 785 $CFG->sitepolicyguest = 'https://example.com'; 786 $this->assertTrue(\core_user::awaiting_action($guest)); 787 788 $guest->policyagreed = 1; 789 $this->assertFalse(\core_user::awaiting_action($guest)); 790 791 // Scenario: Student required to fill their profile. 792 $this->assertFalse(\core_user::awaiting_action($student)); 793 794 $student->firstname = ''; 795 $this->assertTrue(\core_user::awaiting_action($student)); 796 797 $student->firstname = 'Alice'; 798 $this->assertFalse(\core_user::awaiting_action($student)); 799 800 // Scenario: Teacher force to change their password. 801 $this->assertFalse(\core_user::awaiting_action($teacher)); 802 803 set_user_preference('auth_forcepasswordchange', 1, $teacher); 804 $this->assertTrue(\core_user::awaiting_action($teacher)); 805 806 unset_user_preference('auth_forcepasswordchange', $teacher); 807 $this->assertFalse(\core_user::awaiting_action($teacher)); 808 809 // Scenario: Admins do not need to agree to the policy but others do. 810 $this->assertFalse(\core_user::awaiting_action($admin)); 811 $this->assertFalse(\core_user::awaiting_action($manager)); 812 $CFG->sitepolicy = 'https://example.com'; 813 $this->assertFalse(\core_user::awaiting_action($admin)); 814 $this->assertTrue(\core_user::awaiting_action($manager)); 815 } 816 817 /** 818 * Test that user with Letter avatar respect language preference. 819 * 820 * @param array $userdata 821 * @param string $fullnameconfig 822 * @param string $expected 823 * @return void 824 * @covers \core_user::get_initials 825 * @dataProvider user_name_provider 826 */ 827 public function test_get_initials(array $userdata, string $fullnameconfig, string $expected): void { 828 $this->resetAfterTest(); 829 // Create a user. 830 $page = new \moodle_page(); 831 $page->set_url('/user/profile.php'); 832 $page->set_context(\context_system::instance()); 833 $renderer = $page->get_renderer('core'); 834 $user1 = 835 $this->getDataGenerator()->create_user( 836 array_merge( 837 ['picture' => 0, 'email' => 'user1@example.com'], 838 $userdata 839 ) 840 ); 841 set_config('fullnamedisplay', $fullnameconfig); 842 $initials = \core_user::get_initials($user1); 843 $this->assertEquals($expected, $initials); 844 } 845 846 /** 847 * Provider of user configuration for testing initials rendering 848 * 849 * @return array[] 850 */ 851 public static function user_name_provider(): array { 852 return [ 853 'simple user' => [ 854 'user' => ['firstname' => 'first', 'lastname' => 'last'], 855 'fullnamedisplay' => 'language', 856 'expected' => 'fl', 857 ], 858 'simple user with lastname firstname in language settings' => [ 859 'user' => ['firstname' => 'first', 'lastname' => 'last'], 860 'fullnamedisplay' => 'lastname firstname', 861 'expected' => 'lf', 862 ], 863 'simple user with no surname' => [ 864 'user' => ['firstname' => '', 'lastname' => 'L'], 865 'fullnamedisplay' => 'language', 866 'expected' => 'L', 867 ], 868 'simple user with a middle name' => [ 869 'user' => ['firstname' => 'f', 'lastname' => 'l', 'middlename' => 'm'], 870 'fullnamedisplay' => 'middlename lastname', 871 'expected' => 'ml', 872 ], 873 'user with a middle name & fullnamedisplay contains 3 names' => [ 874 'user' => ['firstname' => 'first', 'lastname' => 'last', 'middlename' => 'middle'], 875 'fullnamedisplay' => 'firstname middlename lastname', 876 'expected' => 'fl', 877 ], 878 'simple user with a namefield consisting of one element' => [ 879 'user' => ['firstname' => 'first', 'lastname' => 'last'], 880 'fullnamedisplay' => 'lastname', 881 'expected' => 'l', 882 ], 883 ]; 884 } 885 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body