Search moodle.org's
Developer Documentation

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