Search moodle.org's
Developer Documentation

  • 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 37 and 311] [Versions 38 and 311] [Versions 39 and 311]

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