Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core_user;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once($CFG->dirroot.'/user/lib.php');
  23  
  24  /**
  25   * Unit tests for user lib api.
  26   *
  27   * @package    core_user
  28   * @category   test
  29   * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
  30   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   */
  32  class userlib_test extends \advanced_testcase {
  33      /**
  34       * Test user_get_user_details_courses
  35       */
  36      public function test_user_get_user_details_courses() {
  37          global $DB;
  38  
  39          $this->resetAfterTest();
  40  
  41          // Create user and modify user profile.
  42          $user1 = $this->getDataGenerator()->create_user();
  43          $user2 = $this->getDataGenerator()->create_user();
  44          $user3 = $this->getDataGenerator()->create_user();
  45  
  46          $course1 = $this->getDataGenerator()->create_course();
  47          $coursecontext = \context_course::instance($course1->id);
  48          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
  49          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
  50          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
  51          role_assign($teacherrole->id, $user1->id, $coursecontext->id);
  52          role_assign($teacherrole->id, $user2->id, $coursecontext->id);
  53  
  54          accesslib_clear_all_caches_for_unit_testing();
  55  
  56          // Get user2 details as a user with super system capabilities.
  57          $result = user_get_user_details_courses($user2);
  58          $this->assertEquals($user2->id, $result['id']);
  59          $this->assertEquals(fullname($user2), $result['fullname']);
  60          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
  61  
  62          $this->setUser($user1);
  63          // Get user2 details as a user who can only see this user in a course.
  64          $result = user_get_user_details_courses($user2);
  65          $this->assertEquals($user2->id, $result['id']);
  66          $this->assertEquals(fullname($user2), $result['fullname']);
  67          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
  68  
  69          // Get user2 details as a user who doesn't share any course with user2.
  70          $this->setUser($user3);
  71          $result = user_get_user_details_courses($user2);
  72          $this->assertNull($result);
  73      }
  74  
  75      /**
  76       * Verify return when course groupmode set to 'no groups'.
  77       */
  78      public function test_user_get_user_details_courses_groupmode_nogroups() {
  79          $this->resetAfterTest();
  80  
  81          // Enrol 2 users into a course with groupmode set to 'no groups'.
  82          // Profiles should be visible.
  83          $user1 = $this->getDataGenerator()->create_user();
  84          $user2 = $this->getDataGenerator()->create_user();
  85          $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 0]);
  86          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
  87          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
  88  
  89          $this->setUser($user1);
  90          $userdetails = user_get_user_details_courses($user2);
  91          $this->assertIsArray($userdetails);
  92          $this->assertEquals($user2->id, $userdetails['id']);
  93      }
  94  
  95      /**
  96       * Verify return when course groupmode set to 'separate groups'.
  97       */
  98      public function test_user_get_user_details_courses_groupmode_separate() {
  99          $this->resetAfterTest();
 100  
 101          // Enrol 2 users into a course with groupmode set to 'separate groups'.
 102          // The users are not in any groups, so profiles should be hidden (same as if they were in separate groups).
 103          $user1 = $this->getDataGenerator()->create_user();
 104          $user2 = $this->getDataGenerator()->create_user();
 105          $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 1]);
 106          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
 107          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 108  
 109          $this->setUser($user1);
 110          $this->assertNull(user_get_user_details_courses($user2));
 111      }
 112  
 113      /**
 114       * Verify return when course groupmode set to 'visible groups'.
 115       */
 116      public function test_user_get_user_details_courses_groupmode_visible() {
 117          $this->resetAfterTest();
 118  
 119          // Enrol 2 users into a course with groupmode set to 'visible groups'.
 120          // The users are not in any groups, and profiles should be visible because of the groupmode.
 121          $user1 = $this->getDataGenerator()->create_user();
 122          $user2 = $this->getDataGenerator()->create_user();
 123          $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 2]);
 124          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
 125          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 126  
 127          $this->setUser($user1);
 128          $userdetails = user_get_user_details_courses($user2);
 129          $this->assertIsArray($userdetails);
 130          $this->assertEquals($user2->id, $userdetails['id']);
 131      }
 132  
 133      /**
 134       * Test user_update_user.
 135       */
 136      public function test_user_update_user() {
 137          global $DB;
 138  
 139          $this->resetAfterTest();
 140  
 141          // Create user and modify user profile.
 142          $user = $this->getDataGenerator()->create_user();
 143          $user->firstname = 'Test';
 144          $user->password = 'M00dLe@T';
 145  
 146          // Update user and capture event.
 147          $sink = $this->redirectEvents();
 148          user_update_user($user);
 149          $events = $sink->get_events();
 150          $sink->close();
 151          $event = array_pop($events);
 152  
 153          // Test updated value.
 154          $dbuser = $DB->get_record('user', array('id' => $user->id));
 155          $this->assertSame($user->firstname, $dbuser->firstname);
 156          $this->assertNotSame('M00dLe@T', $dbuser->password);
 157  
 158          // Test event.
 159          $this->assertInstanceOf('\core\event\user_updated', $event);
 160          $this->assertSame($user->id, $event->objectid);
 161          $this->assertSame('user_updated', $event->get_legacy_eventname());
 162          $this->assertEventLegacyData($dbuser, $event);
 163          $this->assertEquals(\context_user::instance($user->id), $event->get_context());
 164          $expectedlogdata = array(SITEID, 'user', 'update', 'view.php?id='.$user->id, '');
 165          $this->assertEventLegacyLogData($expectedlogdata, $event);
 166  
 167          // Update user with no password update.
 168          $password = $user->password = hash_internal_user_password('M00dLe@T');
 169          user_update_user($user, false);
 170          $dbuser = $DB->get_record('user', array('id' => $user->id));
 171          $this->assertSame($password, $dbuser->password);
 172  
 173          // Verify event is not triggred by user_update_user when needed.
 174          $sink = $this->redirectEvents();
 175          user_update_user($user, false, false);
 176          $events = $sink->get_events();
 177          $sink->close();
 178          $this->assertCount(0, $events);
 179  
 180          // With password, there should be 1 event.
 181          $sink = $this->redirectEvents();
 182          user_update_user($user, true, false);
 183          $events = $sink->get_events();
 184          $sink->close();
 185          $this->assertCount(1, $events);
 186          $event = array_pop($events);
 187          $this->assertInstanceOf('\core\event\user_password_updated', $event);
 188  
 189          // Test user data validation.
 190          $user->username = 'johndoe123';
 191          $user->auth = 'shibolth';
 192          $user->country = 'WW';
 193          $user->lang = 'xy';
 194          $user->theme = 'somewrongthemename';
 195          $user->timezone = '30.5';
 196          $debugmessages = $this->getDebuggingMessages();
 197          user_update_user($user, true, false);
 198          $this->assertDebuggingCalledCount(5, $debugmessages);
 199  
 200          // Now, with valid user data.
 201          $user->username = 'johndoe321';
 202          $user->auth = 'shibboleth';
 203          $user->country = 'AU';
 204          $user->lang = 'en';
 205          $user->theme = 'classic';
 206          $user->timezone = 'Australia/Perth';
 207          user_update_user($user, true, false);
 208          $this->assertDebuggingNotCalled();
 209      }
 210  
 211      /**
 212       * Test create_users.
 213       */
 214      public function test_create_users() {
 215          global $DB;
 216  
 217          $this->resetAfterTest();
 218  
 219          $user = array(
 220              'username' => 'usernametest1',
 221              'password' => 'Moodle2012!',
 222              'idnumber' => 'idnumbertest1',
 223              'firstname' => 'First Name User Test 1',
 224              'lastname' => 'Last Name User Test 1',
 225              'middlename' => 'Middle Name User Test 1',
 226              'lastnamephonetic' => '最後のお名前のテスト一号',
 227              'firstnamephonetic' => 'お名前のテスト一号',
 228              'alternatename' => 'Alternate Name User Test 1',
 229              'email' => 'usertest1@example.com',
 230              'description' => 'This is a description for user 1',
 231              'city' => 'Perth',
 232              'country' => 'AU'
 233              );
 234  
 235          // Create user and capture event.
 236          $sink = $this->redirectEvents();
 237          $user['id'] = user_create_user($user);
 238          $events = $sink->get_events();
 239          $sink->close();
 240          $event = array_pop($events);
 241  
 242          // Test user info in DB.
 243          $dbuser = $DB->get_record('user', array('id' => $user['id']));
 244          $this->assertEquals($dbuser->username, $user['username']);
 245          $this->assertEquals($dbuser->idnumber, $user['idnumber']);
 246          $this->assertEquals($dbuser->firstname, $user['firstname']);
 247          $this->assertEquals($dbuser->lastname, $user['lastname']);
 248          $this->assertEquals($dbuser->email, $user['email']);
 249          $this->assertEquals($dbuser->description, $user['description']);
 250          $this->assertEquals($dbuser->city, $user['city']);
 251          $this->assertEquals($dbuser->country, $user['country']);
 252  
 253          // Test event.
 254          $this->assertInstanceOf('\core\event\user_created', $event);
 255          $this->assertEquals($user['id'], $event->objectid);
 256          $this->assertEquals('user_created', $event->get_legacy_eventname());
 257          $this->assertEquals(\context_user::instance($user['id']), $event->get_context());
 258          $this->assertEventLegacyData($dbuser, $event);
 259          $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
 260          $this->assertEventLegacyLogData($expectedlogdata, $event);
 261  
 262          // Verify event is not triggred by user_create_user when needed.
 263          $user = array('username' => 'usernametest2'); // Create another user.
 264          $sink = $this->redirectEvents();
 265          user_create_user($user, true, false);
 266          $events = $sink->get_events();
 267          $sink->close();
 268          $this->assertCount(0, $events);
 269  
 270          // Test user data validation, first some invalid data.
 271          $user['username'] = 'johndoe123';
 272          $user['auth'] = 'shibolth';
 273          $user['country'] = 'WW';
 274          $user['lang'] = 'xy';
 275          $user['theme'] = 'somewrongthemename';
 276          $user['timezone'] = '-30.5';
 277          $debugmessages = $this->getDebuggingMessages();
 278          $user['id'] = user_create_user($user, true, false);
 279          $this->assertDebuggingCalledCount(5, $debugmessages);
 280          $dbuser = $DB->get_record('user', array('id' => $user['id']));
 281          $this->assertEquals($dbuser->country, 0);
 282          $this->assertEquals($dbuser->lang, 'en');
 283          $this->assertEquals($dbuser->timezone, '');
 284  
 285          // Now, with valid user data.
 286          $user['username'] = 'johndoe321';
 287          $user['auth'] = 'shibboleth';
 288          $user['country'] = 'AU';
 289          $user['lang'] = 'en';
 290          $user['theme'] = 'classic';
 291          $user['timezone'] = 'Australia/Perth';
 292          user_create_user($user, true, false);
 293          $this->assertDebuggingNotCalled();
 294      }
 295  
 296      /**
 297       * Test that creating users populates default values
 298       *
 299       * @covers ::user_create_user
 300       */
 301      public function test_user_create_user_default_values(): void {
 302          global $CFG;
 303  
 304          $this->resetAfterTest();
 305  
 306          // Update default values for city/country (both initially empty).
 307          set_config('defaultcity', 'Nadi');
 308          set_config('country', 'FJ');
 309  
 310          $userid = user_create_user((object) [
 311              'username' => 'newuser',
 312          ], false, false);
 313  
 314          $user = \core_user::get_user($userid);
 315          $this->assertEquals($CFG->calendartype, $user->calendartype);
 316          $this->assertEquals($CFG->defaultpreference_maildisplay, $user->maildisplay);
 317          $this->assertEquals($CFG->defaultpreference_mailformat, $user->mailformat);
 318          $this->assertEquals($CFG->defaultpreference_maildigest, $user->maildigest);
 319          $this->assertEquals($CFG->defaultpreference_autosubscribe, $user->autosubscribe);
 320          $this->assertEquals($CFG->defaultpreference_trackforums, $user->trackforums);
 321          $this->assertEquals($CFG->lang, $user->lang);
 322          $this->assertEquals($CFG->defaultcity, $user->city);
 323          $this->assertEquals($CFG->country, $user->country);
 324      }
 325  
 326      /**
 327       * Test that {@link user_create_user()} throws exception when invalid username is provided.
 328       *
 329       * @dataProvider data_create_user_invalid_username
 330       * @param string $username Invalid username
 331       * @param string $expectmessage Expected exception message
 332       */
 333      public function test_create_user_invalid_username($username, $expectmessage) {
 334          global $CFG;
 335  
 336          $this->resetAfterTest();
 337          $CFG->extendedusernamechars = false;
 338  
 339          $user = [
 340              'username' => $username,
 341          ];
 342  
 343          $this->expectException('moodle_exception');
 344          $this->expectExceptionMessage($expectmessage);
 345  
 346          user_create_user($user);
 347      }
 348  
 349      /**
 350       * Data provider for {@link self::test_create_user_invalid_username()}.
 351       *
 352       * @return array
 353       */
 354      public function data_create_user_invalid_username() {
 355          return [
 356              'empty_string' => [
 357                  '',
 358                  'The username cannot be blank',
 359              ],
 360              'only_whitespace' => [
 361                  "\t\t  \t\n ",
 362                  'The username cannot be blank',
 363              ],
 364              'lower_case' => [
 365                  'Mudrd8mz',
 366                  'The username must be in lower case',
 367              ],
 368              'extended_chars' => [
 369                  'dmudrák',
 370                  'The given username contains invalid characters',
 371              ],
 372          ];
 373      }
 374  
 375      /**
 376       * Test function user_count_login_failures().
 377       */
 378      public function test_user_count_login_failures() {
 379          $this->resetAfterTest();
 380          $user = $this->getDataGenerator()->create_user();
 381          $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
 382          for ($i = 0; $i < 10; $i++) {
 383              login_attempt_failed($user);
 384          }
 385          $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
 386          $count = user_count_login_failures($user); // Reset count.
 387          $this->assertEquals(10, $count);
 388          $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
 389  
 390          for ($i = 0; $i < 10; $i++) {
 391              login_attempt_failed($user);
 392          }
 393          $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
 394          $count = user_count_login_failures($user, false); // Do not reset count.
 395          $this->assertEquals(10, $count);
 396          $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
 397      }
 398  
 399      /**
 400       * Test function user_add_password_history().
 401       */
 402      public function test_user_add_password_history() {
 403          global $DB;
 404  
 405          $this->resetAfterTest();
 406  
 407          $user1 = $this->getDataGenerator()->create_user();
 408          $user2 = $this->getDataGenerator()->create_user();
 409          $user3 = $this->getDataGenerator()->create_user();
 410          $DB->delete_records('user_password_history', array());
 411  
 412          set_config('passwordreuselimit', 0);
 413  
 414          user_add_password_history($user1->id, 'pokus');
 415          $this->assertEquals(0, $DB->count_records('user_password_history'));
 416  
 417          // Test adding and discarding of old.
 418  
 419          set_config('passwordreuselimit', 3);
 420  
 421          user_add_password_history($user1->id, 'pokus');
 422          $this->assertEquals(1, $DB->count_records('user_password_history'));
 423          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 424  
 425          user_add_password_history($user1->id, 'pokus2');
 426          user_add_password_history($user1->id, 'pokus3');
 427          user_add_password_history($user1->id, 'pokus4');
 428          $this->assertEquals(3, $DB->count_records('user_password_history'));
 429          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 430  
 431          user_add_password_history($user2->id, 'pokus1');
 432          $this->assertEquals(4, $DB->count_records('user_password_history'));
 433          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 434          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 435  
 436          user_add_password_history($user2->id, 'pokus2');
 437          user_add_password_history($user2->id, 'pokus3');
 438          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 439  
 440          $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 441          user_add_password_history($user2->id, 'pokus4');
 442          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 443          $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 444  
 445          $removed = array_shift($ids);
 446          $added = array_pop($newids);
 447          $this->assertSame($ids, $newids);
 448          $this->assertGreaterThan($removed, $added);
 449  
 450          // Test disabling prevents changes.
 451  
 452          set_config('passwordreuselimit', 0);
 453  
 454          $this->assertEquals(6, $DB->count_records('user_password_history'));
 455  
 456          $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 457          user_add_password_history($user2->id, 'pokus5');
 458          user_add_password_history($user3->id, 'pokus1');
 459          $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 460          $this->assertSame($ids, $newids);
 461          $this->assertEquals(6, $DB->count_records('user_password_history'));
 462  
 463          set_config('passwordreuselimit', -1);
 464  
 465          $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 466          user_add_password_history($user2->id, 'pokus6');
 467          user_add_password_history($user3->id, 'pokus6');
 468          $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 469          $this->assertSame($ids, $newids);
 470          $this->assertEquals(6, $DB->count_records('user_password_history'));
 471      }
 472  
 473      /**
 474       * Test function user_add_password_history().
 475       */
 476      public function test_user_is_previously_used_password() {
 477          global $DB;
 478  
 479          $this->resetAfterTest();
 480  
 481          $user1 = $this->getDataGenerator()->create_user();
 482          $user2 = $this->getDataGenerator()->create_user();
 483          $DB->delete_records('user_password_history', array());
 484  
 485          set_config('passwordreuselimit', 0);
 486  
 487          user_add_password_history($user1->id, 'pokus');
 488          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
 489  
 490          set_config('passwordreuselimit', 3);
 491  
 492          user_add_password_history($user2->id, 'pokus1');
 493          user_add_password_history($user2->id, 'pokus2');
 494  
 495          user_add_password_history($user1->id, 'pokus1');
 496          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
 497          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 498          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
 499          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 500  
 501          user_add_password_history($user1->id, 'pokus2');
 502          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
 503          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
 504          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
 505          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 506  
 507          user_add_password_history($user1->id, 'pokus3');
 508          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
 509          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
 510          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 511          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 512  
 513          user_add_password_history($user1->id, 'pokus4');
 514          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 515          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
 516          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 517          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
 518  
 519          set_config('passwordreuselimit', 2);
 520  
 521          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 522          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 523          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 524          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
 525  
 526          set_config('passwordreuselimit', 3);
 527  
 528          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 529          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 530          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 531          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
 532  
 533          set_config('passwordreuselimit', 0);
 534  
 535          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 536          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 537          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
 538          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 539      }
 540  
 541      /**
 542       * Test that password history is deleted together with user.
 543       */
 544      public function test_delete_of_hashes_on_user_delete() {
 545          global $DB;
 546  
 547          $this->resetAfterTest();
 548  
 549          $user1 = $this->getDataGenerator()->create_user();
 550          $user2 = $this->getDataGenerator()->create_user();
 551          $DB->delete_records('user_password_history', array());
 552  
 553          set_config('passwordreuselimit', 3);
 554  
 555          user_add_password_history($user1->id, 'pokus');
 556          user_add_password_history($user2->id, 'pokus1');
 557          user_add_password_history($user2->id, 'pokus2');
 558  
 559          $this->assertEquals(3, $DB->count_records('user_password_history'));
 560          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 561          $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 562  
 563          delete_user($user2);
 564          $this->assertEquals(1, $DB->count_records('user_password_history'));
 565          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 566          $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 567      }
 568  
 569      /**
 570       * Test user_list_view function
 571       */
 572      public function test_user_list_view() {
 573  
 574          $this->resetAfterTest();
 575  
 576          // Course without sections.
 577          $course = $this->getDataGenerator()->create_course();
 578          $context = \context_course::instance($course->id);
 579  
 580          $this->setAdminUser();
 581  
 582          // Redirect events to the sink, so we can recover them later.
 583          $sink = $this->redirectEvents();
 584  
 585          user_list_view($course, $context);
 586          $events = $sink->get_events();
 587          $this->assertCount(1, $events);
 588          $event = reset($events);
 589  
 590          // Check the event details are correct.
 591          $this->assertInstanceOf('\core\event\user_list_viewed', $event);
 592          $this->assertEquals($context, $event->get_context());
 593          $this->assertEquals($course->shortname, $event->other['courseshortname']);
 594          $this->assertEquals($course->fullname, $event->other['coursefullname']);
 595  
 596      }
 597  
 598      /**
 599       * Test setting the user menu avatar size.
 600       */
 601      public function test_user_menu_custom_avatar_size() {
 602          global $PAGE;
 603          $this->resetAfterTest(true);
 604  
 605          $testsize = 100;
 606  
 607          $PAGE->set_url('/');
 608          $user = $this->getDataGenerator()->create_user();
 609          $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
 610          $avatarhtml = $opts->metadata['useravatar'];
 611  
 612          $matches = [];
 613          preg_match('/(?:.*width=")(\d*)(?:" height=")(\d*)(?:".*\/>)/', $avatarhtml, $matches);
 614          $this->assertCount(3, $matches);
 615  
 616          $this->assertEquals(intval($matches[1]), $testsize);
 617          $this->assertEquals(intval($matches[2]), $testsize);
 618      }
 619  
 620      /**
 621       * Test user_can_view_profile
 622       */
 623      public function test_user_can_view_profile() {
 624          global $DB, $CFG;
 625  
 626          $this->resetAfterTest();
 627  
 628          // Create five users.
 629          $user1 = $this->getDataGenerator()->create_user();
 630          $user2 = $this->getDataGenerator()->create_user();
 631          $user3 = $this->getDataGenerator()->create_user();
 632          $user4 = $this->getDataGenerator()->create_user();
 633          $user5 = $this->getDataGenerator()->create_user();
 634          $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
 635          $user7 = $this->getDataGenerator()->create_user();
 636          $user8 = $this->getDataGenerator()->create_user();
 637          $user8->id = 0; // Visitor.
 638  
 639          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 640          // Add the course creator role to the course contact and assign a user to that role.
 641          $CFG->coursecontact = '2';
 642          $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
 643          $this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
 644  
 645           // Create two courses.
 646          $course1 = $this->getDataGenerator()->create_course();
 647          $course2 = $this->getDataGenerator()->create_course();
 648          $coursecontext = \context_course::instance($course2->id);
 649          // Prepare another course with separate groups and groupmodeforce set to true.
 650          $record = new \stdClass();
 651          $record->groupmode = 1;
 652          $record->groupmodeforce = 1;
 653          $course3 = $this->getDataGenerator()->create_course($record);
 654          // Enrol users 1 and 2 in first course.
 655          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
 656          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
 657          // Enrol users 2 and 3 in second course.
 658          $this->getDataGenerator()->enrol_user($user2->id, $course2->id);
 659          $this->getDataGenerator()->enrol_user($user3->id, $course2->id);
 660          // Enrol users 1, 4, and 5 into course 3.
 661          $this->getDataGenerator()->enrol_user($user1->id, $course3->id);
 662          $this->getDataGenerator()->enrol_user($user4->id, $course3->id);
 663          $this->getDataGenerator()->enrol_user($user5->id, $course3->id);
 664  
 665          // User 3 should not be able to see user 1, either by passing their own course (course 2) or user 1's course (course 1).
 666          $this->setUser($user3);
 667          $this->assertFalse(user_can_view_profile($user1, $course2));
 668          $this->assertFalse(user_can_view_profile($user1, $course1));
 669  
 670          // Remove capability moodle/user:viewdetails in course 2.
 671          assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
 672          // Set current user to user 1.
 673          $this->setUser($user1);
 674          // User 1 can see User 1's profile.
 675          $this->assertTrue(user_can_view_profile($user1));
 676  
 677          $tempcfg = $CFG->forceloginforprofiles;
 678          $CFG->forceloginforprofiles = 0;
 679          // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
 680          $users = array($user1, $user2, $user3, $user4, $user5, $user7);
 681          foreach ($users as $user) {
 682              $this->assertTrue(user_can_view_profile($user));
 683          }
 684          // Restore setting.
 685          $CFG->forceloginforprofiles = $tempcfg;
 686  
 687          // User 1 can not see user 6 as they have been deleted.
 688          $this->assertFalse(user_can_view_profile($user6));
 689          // User 1 can see User 7 as they are a course contact.
 690          $this->assertTrue(user_can_view_profile($user7));
 691          // User 1 is in a course with user 2 and has the right capability - return true.
 692          $this->assertTrue(user_can_view_profile($user2));
 693          // User 1 is not in a course with user 3 - return false.
 694          $this->assertFalse(user_can_view_profile($user3));
 695  
 696          // Set current user to user 2.
 697          $this->setUser($user2);
 698          // User 2 is in a course with user 3 but does not have the right capability - return false.
 699          $this->assertFalse(user_can_view_profile($user3));
 700  
 701          // Set user 1 in one group and users 4 and 5 in another group.
 702          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
 703          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
 704          groups_add_member($group1->id, $user1->id);
 705          groups_add_member($group2->id, $user4->id);
 706          groups_add_member($group2->id, $user5->id);
 707          $this->setUser($user1);
 708          // Check that user 1 can not see user 4.
 709          $this->assertFalse(user_can_view_profile($user4));
 710          // Check that user 5 can see user 4.
 711          $this->setUser($user5);
 712          $this->assertTrue(user_can_view_profile($user4));
 713  
 714          // Test the user:viewalldetails cap check using the course creator role which, by default, can't see student profiles.
 715          $this->setUser($user7);
 716          $this->assertFalse(user_can_view_profile($user4));
 717          assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $coursecreatorrole->id, \context_system::instance()->id, true);
 718          reload_all_capabilities();
 719          $this->assertTrue(user_can_view_profile($user4));
 720          unassign_capability('moodle/user:viewalldetails', $coursecreatorrole->id, $coursecontext->id);
 721          reload_all_capabilities();
 722  
 723          $CFG->coursecontact = null;
 724  
 725          // Visitor (Not a guest user, userid=0).
 726          $CFG->forceloginforprofiles = 1;
 727          $this->setUser($user8);
 728          $this->assertFalse(user_can_view_profile($user1));
 729  
 730          // Let us test with guest user.
 731          $this->setGuestUser();
 732          $CFG->forceloginforprofiles = 1;
 733          foreach ($users as $user) {
 734              $this->assertFalse(user_can_view_profile($user));
 735          }
 736  
 737          // Even with cap, still guests should not be allowed in.
 738          $guestrole = $DB->get_records_menu('role', array('shortname' => 'guest'), 'id', 'archetype, id');
 739          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $guestrole['guest'], \context_system::instance()->id, true);
 740          reload_all_capabilities();
 741          foreach ($users as $user) {
 742              $this->assertFalse(user_can_view_profile($user));
 743          }
 744  
 745          $CFG->forceloginforprofiles = 0;
 746          foreach ($users as $user) {
 747              $this->assertTrue(user_can_view_profile($user));
 748          }
 749  
 750          // Let us test with Visitor user.
 751          $this->setUser($user8);
 752          $CFG->forceloginforprofiles = 1;
 753          foreach ($users as $user) {
 754              $this->assertFalse(user_can_view_profile($user));
 755          }
 756  
 757          $CFG->forceloginforprofiles = 0;
 758          foreach ($users as $user) {
 759              $this->assertTrue(user_can_view_profile($user));
 760          }
 761  
 762          // Testing non-shared courses where capabilities are met, using system role overrides.
 763          $CFG->forceloginforprofiles = $tempcfg;
 764          $course4 = $this->getDataGenerator()->create_course();
 765          $this->getDataGenerator()->enrol_user($user1->id, $course4->id);
 766  
 767          // Assign a manager role at the system context.
 768          $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
 769          $user9 = $this->getDataGenerator()->create_user();
 770          $this->getDataGenerator()->role_assign($managerrole->id, $user9->id);
 771  
 772          // Make sure viewalldetails and viewdetails are overridden to 'prevent' (i.e. can be overridden at a lower context).
 773          $systemcontext = \context_system::instance();
 774          assign_capability('moodle/user:viewdetails', CAP_PREVENT, $managerrole->id, $systemcontext, true);
 775          assign_capability('moodle/user:viewalldetails', CAP_PREVENT, $managerrole->id, $systemcontext, true);
 776  
 777          // And override these to 'Allow' in a specific course.
 778          $course4context = \context_course::instance($course4->id);
 779          assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $managerrole->id, $course4context, true);
 780          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $managerrole->id, $course4context, true);
 781  
 782          // The manager now shouldn't have viewdetails in the system or user context.
 783          $this->setUser($user9);
 784          $user1context = \context_user::instance($user1->id);
 785          $this->assertFalse(has_capability('moodle/user:viewdetails', $systemcontext));
 786          $this->assertFalse(has_capability('moodle/user:viewdetails', $user1context));
 787  
 788          // Confirm that user_can_view_profile() returns true for $user1 when called without $course param. It should find $course1.
 789          $this->assertTrue(user_can_view_profile($user1));
 790  
 791          // Confirm this also works when restricting scope to just that course.
 792          $this->assertTrue(user_can_view_profile($user1, $course4));
 793      }
 794  
 795      /**
 796       * Test user_get_user_details
 797       */
 798      public function test_user_get_user_details() {
 799          global $DB;
 800  
 801          $this->resetAfterTest();
 802  
 803          // Create user and modify user profile.
 804          $teacher = $this->getDataGenerator()->create_user();
 805          $student = $this->getDataGenerator()->create_user();
 806          $studentfullname = fullname($student);
 807  
 808          $course1 = $this->getDataGenerator()->create_course();
 809          $coursecontext = \context_course::instance($course1->id);
 810          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
 811          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 812          $this->getDataGenerator()->enrol_user($teacher->id, $course1->id);
 813          $this->getDataGenerator()->enrol_user($student->id, $course1->id);
 814          role_assign($teacherrole->id, $teacher->id, $coursecontext->id);
 815          role_assign($studentrole->id, $student->id, $coursecontext->id);
 816  
 817          accesslib_clear_all_caches_for_unit_testing();
 818  
 819          // Get student details as a user with super system capabilities.
 820          $result = user_get_user_details($student, $course1);
 821          $this->assertEquals($student->id, $result['id']);
 822          $this->assertEquals($studentfullname, $result['fullname']);
 823          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
 824  
 825          $this->setUser($teacher);
 826          // Get student details as a user who can only see this user in a course.
 827          $result = user_get_user_details($student, $course1);
 828          $this->assertEquals($student->id, $result['id']);
 829          $this->assertEquals($studentfullname, $result['fullname']);
 830          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
 831  
 832          // Get student details with required fields.
 833          $result = user_get_user_details($student, $course1, array('id', 'fullname'));
 834          $this->assertCount(2, $result);
 835          $this->assertEquals($student->id, $result['id']);
 836          $this->assertEquals($studentfullname, $result['fullname']);
 837  
 838          // Get exception for invalid required fields.
 839          $this->expectException('moodle_exception');
 840          $result = user_get_user_details($student, $course1, array('wrongrequiredfield'));
 841      }
 842  
 843      /**
 844       * Regression test for MDL-57840.
 845       *
 846       * Ensure the fields "auth, confirmed, idnumber, lang, theme, timezone and mailformat" are present when
 847       * calling user_get_user_details() function.
 848       */
 849      public function test_user_get_user_details_missing_fields() {
 850          global $CFG;
 851  
 852          $this->resetAfterTest(true);
 853          $this->setAdminUser(); // We need capabilities to view the data.
 854          $user = self::getDataGenerator()->create_user([
 855                                                            'auth'       => 'email',
 856                                                            'confirmed'  => '0',
 857                                                            'idnumber'   => 'someidnumber',
 858                                                            'lang'       => 'en',
 859                                                            'theme'      => $CFG->theme,
 860                                                            'timezone'   => '5',
 861                                                            'mailformat' => '0',
 862                                                        ]);
 863  
 864          // Fields that should get by default.
 865          $got = user_get_user_details($user);
 866          self::assertSame('email', $got['auth']);
 867          self::assertSame('0', $got['confirmed']);
 868          self::assertSame('someidnumber', $got['idnumber']);
 869          self::assertSame('en', $got['lang']);
 870          self::assertSame($CFG->theme, $got['theme']);
 871          self::assertSame('5', $got['timezone']);
 872          self::assertSame('0', $got['mailformat']);
 873      }
 874  }