Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   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   * 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  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * User class to access user details.
  29   *
  30   * @todo       move api's from user/lib.php and deprecate old ones.
  31   * @package    core
  32   * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
  33   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class core_user {
  36      /**
  37       * No reply user id.
  38       */
  39      const NOREPLY_USER = -10;
  40  
  41      /**
  42       * Support user id.
  43       */
  44      const SUPPORT_USER = -20;
  45  
  46      /**
  47       * Hide email address from everyone.
  48       */
  49      const MAILDISPLAY_HIDE = 0;
  50  
  51      /**
  52       * Display email address to everyone.
  53       */
  54      const MAILDISPLAY_EVERYONE = 1;
  55  
  56      /**
  57       * Display email address to course members only.
  58       */
  59      const MAILDISPLAY_COURSE_MEMBERS_ONLY = 2;
  60  
  61      /**
  62       * List of fields that can be synched/locked during authentication.
  63       */
  64      const AUTHSYNCFIELDS = [
  65          'firstname',
  66          'lastname',
  67          'email',
  68          'city',
  69          'country',
  70          'lang',
  71          'description',
  72          'url',
  73          'idnumber',
  74          'institution',
  75          'department',
  76          'phone1',
  77          'phone2',
  78          'address',
  79          'firstnamephonetic',
  80          'lastnamephonetic',
  81          'middlename',
  82          'alternatename'
  83      ];
  84  
  85      /** @var int Indicates that user profile view should be prevented */
  86      const VIEWPROFILE_PREVENT = -1;
  87      /** @var int Indicates that user profile view should not be prevented */
  88      const VIEWPROFILE_DO_NOT_PREVENT = 0;
  89      /** @var int Indicates that user profile view should be allowed even if Moodle would prevent it */
  90      const VIEWPROFILE_FORCE_ALLOW = 1;
  91  
  92      /** @var stdClass keep record of noreply user */
  93      public static $noreplyuser = false;
  94  
  95      /** @var stdClass keep record of support user */
  96      public static $supportuser = false;
  97  
  98      /** @var array store user fields properties cache. */
  99      protected static $propertiescache = null;
 100  
 101      /** @var array store user preferences cache. */
 102      protected static $preferencescache = null;
 103  
 104      /**
 105       * Return user object from db or create noreply or support user,
 106       * if userid matches corse_user::NOREPLY_USER or corse_user::SUPPORT_USER
 107       * respectively. If userid is not found, then return false.
 108       *
 109       * @param int $userid user id
 110       * @param string $fields A comma separated list of user fields to be returned, support and noreply user
 111       *                       will not be filtered by this.
 112       * @param int $strictness IGNORE_MISSING means compatible mode, false returned if user not found, debug message if more found;
 113       *                        IGNORE_MULTIPLE means return first user, ignore multiple user records found(not recommended);
 114       *                        MUST_EXIST means throw an exception if no user record or multiple records found.
 115       * @return stdClass|bool user record if found, else false.
 116       * @throws dml_exception if user record not found and respective $strictness is set.
 117       */
 118      public static function get_user($userid, $fields = '*', $strictness = IGNORE_MISSING) {
 119          global $DB;
 120  
 121          // If noreply user then create fake record and return.
 122          switch ($userid) {
 123              case self::NOREPLY_USER:
 124                  return self::get_noreply_user();
 125                  break;
 126              case self::SUPPORT_USER:
 127                  return self::get_support_user();
 128                  break;
 129              default:
 130                  return $DB->get_record('user', array('id' => $userid), $fields, $strictness);
 131          }
 132      }
 133  
 134      /**
 135       * Return user object from db based on their email.
 136       *
 137       * @param string $email The email of the user searched.
 138       * @param string $fields A comma separated list of user fields to be returned, support and noreply user.
 139       * @param int $mnethostid The id of the remote host.
 140       * @param int $strictness IGNORE_MISSING means compatible mode, false returned if user not found, debug message if more found;
 141       *                        IGNORE_MULTIPLE means return first user, ignore multiple user records found(not recommended);
 142       *                        MUST_EXIST means throw an exception if no user record or multiple records found.
 143       * @return stdClass|bool user record if found, else false.
 144       * @throws dml_exception if user record not found and respective $strictness is set.
 145       */
 146      public static function get_user_by_email($email, $fields = '*', $mnethostid = null, $strictness = IGNORE_MISSING) {
 147          global $DB, $CFG;
 148  
 149          // Because we use the username as the search criteria, we must also restrict our search based on mnet host.
 150          if (empty($mnethostid)) {
 151              // If empty, we restrict to local users.
 152              $mnethostid = $CFG->mnet_localhost_id;
 153          }
 154  
 155          return $DB->get_record('user', array('email' => $email, 'mnethostid' => $mnethostid), $fields, $strictness);
 156      }
 157  
 158      /**
 159       * Return user object from db based on their username.
 160       *
 161       * @param string $username The username of the user searched.
 162       * @param string $fields A comma separated list of user fields to be returned, support and noreply user.
 163       * @param int $mnethostid The id of the remote host.
 164       * @param int $strictness IGNORE_MISSING means compatible mode, false returned if user not found, debug message if more found;
 165       *                        IGNORE_MULTIPLE means return first user, ignore multiple user records found(not recommended);
 166       *                        MUST_EXIST means throw an exception if no user record or multiple records found.
 167       * @return stdClass|bool user record if found, else false.
 168       * @throws dml_exception if user record not found and respective $strictness is set.
 169       */
 170      public static function get_user_by_username($username, $fields = '*', $mnethostid = null, $strictness = IGNORE_MISSING) {
 171          global $DB, $CFG;
 172  
 173          // Because we use the username as the search criteria, we must also restrict our search based on mnet host.
 174          if (empty($mnethostid)) {
 175              // If empty, we restrict to local users.
 176              $mnethostid = $CFG->mnet_localhost_id;
 177          }
 178  
 179          return $DB->get_record('user', array('username' => $username, 'mnethostid' => $mnethostid), $fields, $strictness);
 180      }
 181  
 182      /**
 183       * Searches for users by name, possibly within a specified context, with current user's access.
 184       *
 185       * Deciding which users to search is complicated because it relies on user permissions;
 186       * ideally, we shouldn't show names if you aren't allowed to see their profile. The permissions
 187       * for seeing profile are really complicated.
 188       *
 189       * Even if search is restricted to a course, it's possible that other people might have
 190       * been able to contribute within the course (e.g. they were enrolled before and not now;
 191       * or people with system-level roles) so if the user has permission we do want to include
 192       * everyone. However, if there are multiple results then we prioritise the ones who are
 193       * enrolled in the course.
 194       *
 195       * If you have moodle/user:viewdetails at system level, you can search everyone.
 196       * Otherwise we check which courses you *do* have that permission and search everyone who is
 197       * enrolled on those courses.
 198       *
 199       * Normally you can only search the user's name. If you have the moodle/site:viewuseridentity
 200       * capability then we also let you search the fields which are listed as identity fields in
 201       * the 'showuseridentity' config option. For example, this might include the user's ID number
 202       * or email.
 203       *
 204       * The $max parameter controls the maximum number of users returned. If users are restricted
 205       * from view for some reason, multiple runs of the main query might be made; the $querylimit
 206       * parameter allows this to be restricted. Both parameters can be zero to remove limits.
 207       *
 208       * The returned user objects include id, username, all fields required for user pictures, and
 209       * user identity fields.
 210       *
 211       * @param string $query Search query text
 212       * @param \context_course|null $coursecontext Course context or null if system-wide
 213       * @param int $max Max number of users to return, default 30 (zero = no limit)
 214       * @param int $querylimit Max number of database queries, default 5 (zero = no limit)
 215       * @return array Array of user objects with limited fields
 216       */
 217      public static function search($query, \context_course $coursecontext = null,
 218              $max = 30, $querylimit = 5) {
 219          global $CFG, $DB;
 220          require_once($CFG->dirroot . '/user/lib.php');
 221  
 222          // Allow limits to be turned off.
 223          if (!$max) {
 224              $max = PHP_INT_MAX;
 225          }
 226          if (!$querylimit) {
 227              $querylimit = PHP_INT_MAX;
 228          }
 229  
 230          // Check permission to view profiles at each context.
 231          $systemcontext = \context_system::instance();
 232          $viewsystem = has_capability('moodle/user:viewdetails', $systemcontext);
 233          if ($viewsystem) {
 234              $userquery = 'SELECT id FROM {user}';
 235              $userparams = [];
 236          }
 237          if (!$viewsystem) {
 238              list($userquery, $userparams) = self::get_enrolled_sql_on_courses_with_capability(
 239                      'moodle/user:viewdetails');
 240              if (!$userquery) {
 241                  // No permissions anywhere, return nothing.
 242                  return [];
 243              }
 244          }
 245  
 246          // Start building the WHERE clause based on name.
 247          list ($where, $whereparams) = users_search_sql($query, 'u', false);
 248  
 249          // We allow users to search with extra identity fields (as well as name) but only if they
 250          // have the permission to display those identity fields.
 251          $extrasql = '';
 252          $extraparams = [];
 253  
 254          if (empty($CFG->showuseridentity)) {
 255              // Explode gives wrong result with empty string.
 256              $extra = [];
 257          } else {
 258              $extra = explode(',', $CFG->showuseridentity);
 259          }
 260  
 261          // We need the username just to skip guests.
 262          $extrafieldlist = $extra;
 263          if (!in_array('username', $extra)) {
 264              $extrafieldlist[] = 'username';
 265          }
 266          // The deleted flag will always be false because users_search_sql excludes deleted users,
 267          // but it must be present or it causes PHP warnings in some functions below.
 268          if (!in_array('deleted', $extra)) {
 269              $extrafieldlist[] = 'deleted';
 270          }
 271          $selectfields = \user_picture::fields('u',
 272                  array_merge(get_all_user_name_fields(), $extrafieldlist));
 273  
 274          $index = 1;
 275          foreach ($extra as $fieldname) {
 276              if ($extrasql) {
 277                  $extrasql .= ' OR ';
 278              }
 279              $extrasql .= $DB->sql_like('u.' . $fieldname, ':extra' . $index, false);
 280              $extraparams['extra' . $index] = $query . '%';
 281              $index++;
 282          }
 283  
 284          $identitysystem = has_capability('moodle/site:viewuseridentity', $systemcontext);
 285          $usingshowidentity = false;
 286          if ($identitysystem) {
 287              // They have permission everywhere so just add the extra query to the normal query.
 288              $where .= ' OR ' . $extrasql;
 289              $whereparams = array_merge($whereparams, $extraparams);
 290          } else {
 291              // Get all courses where user can view full user identity.
 292              list($sql, $params) = self::get_enrolled_sql_on_courses_with_capability(
 293                      'moodle/site:viewuseridentity');
 294              if ($sql) {
 295                  // Join that with the user query to get an extra field indicating if we can.
 296                  $userquery = "
 297                          SELECT innerusers.id, COUNT(identityusers.id) AS showidentity
 298                            FROM ($userquery) innerusers
 299                       LEFT JOIN ($sql) identityusers ON identityusers.id = innerusers.id
 300                        GROUP BY innerusers.id";
 301                  $userparams = array_merge($userparams, $params);
 302                  $usingshowidentity = true;
 303  
 304                  // Query on the extra fields only in those places.
 305                  $where .= ' OR (users.showidentity > 0 AND (' . $extrasql . '))';
 306                  $whereparams = array_merge($whereparams, $extraparams);
 307              }
 308          }
 309  
 310          // Default order is just name order. But if searching within a course then we show users
 311          // within the course first.
 312          list ($order, $orderparams) = users_order_by_sql('u', $query, $systemcontext);
 313          if ($coursecontext) {
 314              list ($sql, $params) = get_enrolled_sql($coursecontext);
 315              $mainfield = 'innerusers2.id';
 316              if ($usingshowidentity) {
 317                  $mainfield .= ', innerusers2.showidentity';
 318              }
 319              $userquery = "
 320                      SELECT $mainfield, COUNT(courseusers.id) AS incourse
 321                        FROM ($userquery) innerusers2
 322                   LEFT JOIN ($sql) courseusers ON courseusers.id = innerusers2.id
 323                    GROUP BY $mainfield";
 324              $userparams = array_merge($userparams, $params);
 325  
 326              $order = 'incourse DESC, ' . $order;
 327          }
 328  
 329          // Get result (first 30 rows only) from database. Take a couple spare in case we have to
 330          // drop some.
 331          $result = [];
 332          $got = 0;
 333          $pos = 0;
 334          $readcount = $max + 2;
 335          for ($i = 0; $i < $querylimit; $i++) {
 336              $rawresult = $DB->get_records_sql("
 337                      SELECT $selectfields
 338                        FROM ($userquery) users
 339                        JOIN {user} u ON u.id = users.id
 340                       WHERE $where
 341                    ORDER BY $order", array_merge($userparams, $whereparams, $orderparams),
 342                      $pos, $readcount);
 343              foreach ($rawresult as $user) {
 344                  // Skip guest.
 345                  if ($user->username === 'guest') {
 346                      continue;
 347                  }
 348                  // Check user can really view profile (there are per-user cases where this could
 349                  // be different for some reason, this is the same check used by the profile view pages
 350                  // to double-check that it is OK).
 351                  if (!user_can_view_profile($user)) {
 352                      continue;
 353                  }
 354                  $result[] = $user;
 355                  $got++;
 356                  if ($got >= $max) {
 357                      break;
 358                  }
 359              }
 360  
 361              if ($got >= $max) {
 362                  // All necessary results obtained.
 363                  break;
 364              }
 365              if (count($rawresult) < $readcount) {
 366                  // No more results from database.
 367                  break;
 368              }
 369              $pos += $readcount;
 370          }
 371  
 372          return $result;
 373      }
 374  
 375      /**
 376       * Gets an SQL query that lists all enrolled user ids on any course where the current
 377       * user has the specified capability. Helper function used for searching users.
 378       *
 379       * @param string $capability Required capability
 380       * @return array Array containing SQL and params, or two nulls if there are no courses
 381       */
 382      protected static function get_enrolled_sql_on_courses_with_capability($capability) {
 383          // Get all courses where user have the capability.
 384          $courses = get_user_capability_course($capability, null, true,
 385                  implode(',', array_values(context_helper::get_preload_record_columns('ctx'))));
 386          if (!$courses) {
 387              return [null, null];
 388          }
 389  
 390          // Loop around all courses getting the SQL for enrolled users. Note: This query could
 391          // probably be more efficient (without the union) if get_enrolled_sql had a way to
 392          // pass an array of courseids, but it doesn't.
 393          $unionsql = '';
 394          $unionparams = [];
 395          foreach ($courses as $course) {
 396              // Get SQL to list user ids enrolled in this course.
 397              \context_helper::preload_from_record($course);
 398              list ($sql, $params) = get_enrolled_sql(\context_course::instance($course->id));
 399  
 400              // Combine to a big union query.
 401              if ($unionsql) {
 402                  $unionsql .= ' UNION ';
 403              }
 404              $unionsql .= $sql;
 405              $unionparams = array_merge($unionparams, $params);
 406          }
 407  
 408          return [$unionsql, $unionparams];
 409      }
 410  
 411      /**
 412       * Helper function to return dummy noreply user record.
 413       *
 414       * @return stdClass
 415       */
 416      protected static function get_dummy_user_record() {
 417          global $CFG;
 418  
 419          $dummyuser = new stdClass();
 420          $dummyuser->id = self::NOREPLY_USER;
 421          $dummyuser->email = $CFG->noreplyaddress;
 422          $dummyuser->firstname = get_string('noreplyname');
 423          $dummyuser->username = 'noreply';
 424          $dummyuser->lastname = '';
 425          $dummyuser->confirmed = 1;
 426          $dummyuser->suspended = 0;
 427          $dummyuser->deleted = 0;
 428          $dummyuser->picture = 0;
 429          $dummyuser->auth = 'manual';
 430          $dummyuser->firstnamephonetic = '';
 431          $dummyuser->lastnamephonetic = '';
 432          $dummyuser->middlename = '';
 433          $dummyuser->alternatename = '';
 434          $dummyuser->imagealt = '';
 435          return $dummyuser;
 436      }
 437  
 438      /**
 439       * Return noreply user record, this is currently used in messaging
 440       * system only for sending messages from noreply email.
 441       * It will return record of $CFG->noreplyuserid if set else return dummy
 442       * user object with hard-coded $user->emailstop = 1 so noreply can be sent to user.
 443       *
 444       * @return stdClass user record.
 445       */
 446      public static function get_noreply_user() {
 447          global $CFG;
 448  
 449          if (!empty(self::$noreplyuser)) {
 450              return self::$noreplyuser;
 451          }
 452  
 453          // If noreply user is set then use it, else create one.
 454          if (!empty($CFG->noreplyuserid)) {
 455              self::$noreplyuser = self::get_user($CFG->noreplyuserid);
 456              self::$noreplyuser->emailstop = 1; // Force msg stop for this user.
 457              return self::$noreplyuser;
 458          } else {
 459              // Do not cache the dummy user record to avoid language internationalization issues.
 460              $noreplyuser = self::get_dummy_user_record();
 461              $noreplyuser->maildisplay = '1'; // Show to all.
 462              $noreplyuser->emailstop = 1;
 463              return $noreplyuser;
 464          }
 465      }
 466  
 467      /**
 468       * Return support user record, this is currently used in messaging
 469       * system only for sending messages to support email.
 470       * $CFG->supportuserid is set then returns user record
 471       * $CFG->supportemail is set then return dummy record with $CFG->supportemail
 472       * else return admin user record with hard-coded $user->emailstop = 0, so user
 473       * gets support message.
 474       *
 475       * @return stdClass user record.
 476       */
 477      public static function get_support_user() {
 478          global $CFG;
 479  
 480          if (!empty(self::$supportuser)) {
 481              return self::$supportuser;
 482          }
 483  
 484          // If custom support user is set then use it, else if supportemail is set then use it, else use noreply.
 485          if (!empty($CFG->supportuserid)) {
 486              self::$supportuser = self::get_user($CFG->supportuserid, '*', MUST_EXIST);
 487          } else if (empty(self::$supportuser) && !empty($CFG->supportemail)) {
 488              // Try sending it to support email if support user is not set.
 489              $supportuser = self::get_dummy_user_record();
 490              $supportuser->id = self::SUPPORT_USER;
 491              $supportuser->email = $CFG->supportemail;
 492              if ($CFG->supportname) {
 493                  $supportuser->firstname = $CFG->supportname;
 494              }
 495              $supportuser->username = 'support';
 496              $supportuser->maildisplay = '1'; // Show to all.
 497              // Unset emailstop to make sure support message is sent.
 498              $supportuser->emailstop = 0;
 499              return $supportuser;
 500          }
 501  
 502          // Send support msg to admin user if nothing is set above.
 503          if (empty(self::$supportuser)) {
 504              self::$supportuser = get_admin();
 505          }
 506  
 507          // Unset emailstop to make sure support message is sent.
 508          self::$supportuser->emailstop = 0;
 509          return self::$supportuser;
 510      }
 511  
 512      /**
 513       * Reset self::$noreplyuser and self::$supportuser.
 514       * This is only used by phpunit, and there is no other use case for this function.
 515       * Please don't use it outside phpunit.
 516       */
 517      public static function reset_internal_users() {
 518          if (PHPUNIT_TEST) {
 519              self::$noreplyuser = false;
 520              self::$supportuser = false;
 521          } else {
 522              debugging('reset_internal_users() should not be used outside phpunit.', DEBUG_DEVELOPER);
 523          }
 524      }
 525  
 526      /**
 527       * Return true if user id is greater than 0 and alternatively check db.
 528       *
 529       * @param int $userid user id.
 530       * @param bool $checkdb if true userid will be checked in db. By default it's false, and
 531       *                      userid is compared with 0 for performance.
 532       * @return bool true is real user else false.
 533       */
 534      public static function is_real_user($userid, $checkdb = false) {
 535          global $DB;
 536  
 537          if ($userid <= 0) {
 538              return false;
 539          }
 540          if ($checkdb) {
 541              return $DB->record_exists('user', array('id' => $userid));
 542          } else {
 543              return true;
 544          }
 545      }
 546  
 547      /**
 548       * Check if the given user is an active user in the site.
 549       *
 550       * @param  stdClass  $user         user object
 551       * @param  boolean $checksuspended whether to check if the user has the account suspended
 552       * @param  boolean $checknologin   whether to check if the user uses the nologin auth method
 553       * @throws moodle_exception
 554       * @since  Moodle 3.0
 555       */
 556      public static function require_active_user($user, $checksuspended = false, $checknologin = false) {
 557  
 558          if (!self::is_real_user($user->id)) {
 559              throw new moodle_exception('invaliduser', 'error');
 560          }
 561  
 562          if ($user->deleted) {
 563              throw new moodle_exception('userdeleted');
 564          }
 565  
 566          if (empty($user->confirmed)) {
 567              throw new moodle_exception('usernotconfirmed', 'moodle', '', $user->username);
 568          }
 569  
 570          if (isguestuser($user)) {
 571              throw new moodle_exception('guestsarenotallowed', 'error');
 572          }
 573  
 574          if ($checksuspended and $user->suspended) {
 575              throw new moodle_exception('suspended', 'auth');
 576          }
 577  
 578          if ($checknologin and $user->auth == 'nologin') {
 579              throw new moodle_exception('suspended', 'auth');
 580          }
 581      }
 582  
 583      /**
 584       * Updates the provided users profile picture based upon the expected fields returned from the edit or edit_advanced forms.
 585       *
 586       * @param stdClass $usernew An object that contains some information about the user being updated
 587       * @param array $filemanageroptions
 588       * @return bool True if the user was updated, false if it stayed the same.
 589       */
 590      public static function update_picture(stdClass $usernew, $filemanageroptions = array()) {
 591          global $CFG, $DB;
 592          require_once("$CFG->libdir/gdlib.php");
 593  
 594          $context = context_user::instance($usernew->id, MUST_EXIST);
 595          $user = core_user::get_user($usernew->id, 'id, picture', MUST_EXIST);
 596  
 597          $newpicture = $user->picture;
 598          // Get file_storage to process files.
 599          $fs = get_file_storage();
 600          if (!empty($usernew->deletepicture)) {
 601              // The user has chosen to delete the selected users picture.
 602              $fs->delete_area_files($context->id, 'user', 'icon'); // Drop all images in area.
 603              $newpicture = 0;
 604          }
 605  
 606          // Save newly uploaded file, this will avoid context mismatch for newly created users.
 607          if (!isset($usernew->imagefile)) {
 608              $usernew->imagefile = 0;
 609          }
 610          file_save_draft_area_files($usernew->imagefile, $context->id, 'user', 'newicon', 0, $filemanageroptions);
 611          if (($iconfiles = $fs->get_area_files($context->id, 'user', 'newicon')) && count($iconfiles) == 2) {
 612              // Get file which was uploaded in draft area.
 613              foreach ($iconfiles as $file) {
 614                  if (!$file->is_directory()) {
 615                      break;
 616                  }
 617              }
 618              // Copy file to temporary location and the send it for processing icon.
 619              if ($iconfile = $file->copy_content_to_temp()) {
 620                  // There is a new image that has been uploaded.
 621                  // Process the new image and set the user to make use of it.
 622                  // NOTE: Uploaded images always take over Gravatar.
 623                  $newpicture = (int)process_new_icon($context, 'user', 'icon', 0, $iconfile);
 624                  // Delete temporary file.
 625                  @unlink($iconfile);
 626                  // Remove uploaded file.
 627                  $fs->delete_area_files($context->id, 'user', 'newicon');
 628              } else {
 629                  // Something went wrong while creating temp file.
 630                  // Remove uploaded file.
 631                  $fs->delete_area_files($context->id, 'user', 'newicon');
 632                  return false;
 633              }
 634          }
 635  
 636          if ($newpicture != $user->picture) {
 637              $DB->set_field('user', 'picture', $newpicture, array('id' => $user->id));
 638              return true;
 639          } else {
 640              return false;
 641          }
 642      }
 643  
 644  
 645  
 646      /**
 647       * Definition of user profile fields and the expected parameter type for data validation.
 648       *
 649       * array(
 650       *     'property_name' => array(       // The user property to be checked. Should match the field on the user table.
 651       *          'null' => NULL_ALLOWED,    // Defaults to NULL_NOT_ALLOWED. Takes NULL_NOT_ALLOWED or NULL_ALLOWED.
 652       *          'type' => PARAM_TYPE,      // Expected parameter type of the user field.
 653       *          'choices' => array(1, 2..) // An array of accepted values of the user field.
 654       *          'default' => $CFG->setting // An default value for the field.
 655       *     )
 656       * )
 657       *
 658       * The fields choices and default are optional.
 659       *
 660       * @return void
 661       */
 662      protected static function fill_properties_cache() {
 663          global $CFG, $SESSION;
 664          if (self::$propertiescache !== null) {
 665              return;
 666          }
 667  
 668          // Array of user fields properties and expected parameters.
 669          // Every new field on the user table should be added here otherwise it won't be validated.
 670          $fields = array();
 671          $fields['id'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 672          $fields['auth'] = array('type' => PARAM_AUTH, 'null' => NULL_NOT_ALLOWED);
 673          $fields['confirmed'] = array('type' => PARAM_BOOL, 'null' => NULL_NOT_ALLOWED);
 674          $fields['policyagreed'] = array('type' => PARAM_BOOL, 'null' => NULL_NOT_ALLOWED);
 675          $fields['deleted'] = array('type' => PARAM_BOOL, 'null' => NULL_NOT_ALLOWED);
 676          $fields['suspended'] = array('type' => PARAM_BOOL, 'null' => NULL_NOT_ALLOWED);
 677          $fields['mnethostid'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 678          $fields['username'] = array('type' => PARAM_USERNAME, 'null' => NULL_NOT_ALLOWED);
 679          $fields['password'] = array('type' => PARAM_RAW, 'null' => NULL_NOT_ALLOWED);
 680          $fields['idnumber'] = array('type' => PARAM_RAW, 'null' => NULL_NOT_ALLOWED);
 681          $fields['firstname'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 682          $fields['lastname'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 683          $fields['surname'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 684          $fields['email'] = array('type' => PARAM_RAW_TRIMMED, 'null' => NULL_NOT_ALLOWED);
 685          $fields['emailstop'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 0);
 686          $fields['icq'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 687          $fields['skype'] = array('type' => PARAM_NOTAGS, 'null' => NULL_ALLOWED);
 688          $fields['aim'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 689          $fields['yahoo'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 690          $fields['msn'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 691          $fields['phone1'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 692          $fields['phone2'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 693          $fields['institution'] = array('type' => PARAM_TEXT, 'null' => NULL_NOT_ALLOWED);
 694          $fields['department'] = array('type' => PARAM_TEXT, 'null' => NULL_NOT_ALLOWED);
 695          $fields['address'] = array('type' => PARAM_TEXT, 'null' => NULL_NOT_ALLOWED);
 696          $fields['city'] = array('type' => PARAM_TEXT, 'null' => NULL_NOT_ALLOWED, 'default' => $CFG->defaultcity);
 697          $fields['country'] = array('type' => PARAM_ALPHA, 'null' => NULL_NOT_ALLOWED, 'default' => $CFG->country,
 698                  'choices' => array_merge(array('' => ''), get_string_manager()->get_list_of_countries(true, true)));
 699          $fields['lang'] = array('type' => PARAM_LANG, 'null' => NULL_NOT_ALLOWED,
 700                  'default' => (!empty($CFG->autolangusercreation) && !empty($SESSION->lang)) ? $SESSION->lang : $CFG->lang,
 701                  'choices' => array_merge(array('' => ''), get_string_manager()->get_list_of_translations(false)));
 702          $fields['calendartype'] = array('type' => PARAM_PLUGIN, 'null' => NULL_NOT_ALLOWED, 'default' => $CFG->calendartype,
 703                  'choices' => array_merge(array('' => ''), \core_calendar\type_factory::get_list_of_calendar_types()));
 704          $fields['theme'] = array('type' => PARAM_THEME, 'null' => NULL_NOT_ALLOWED,
 705                  'default' => theme_config::DEFAULT_THEME, 'choices' => array_merge(array('' => ''), get_list_of_themes()));
 706          $fields['timezone'] = array('type' => PARAM_TIMEZONE, 'null' => NULL_NOT_ALLOWED,
 707                  'default' => core_date::get_server_timezone()); // Must not use choices here: timezones can come and go.
 708          $fields['firstaccess'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 709          $fields['lastaccess'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 710          $fields['lastlogin'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 711          $fields['currentlogin'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 712          $fields['lastip'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED);
 713          $fields['secret'] = array('type' => PARAM_ALPHANUM, 'null' => NULL_NOT_ALLOWED);
 714          $fields['picture'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 715          $fields['url'] = array('type' => PARAM_URL, 'null' => NULL_NOT_ALLOWED);
 716          $fields['description'] = array('type' => PARAM_RAW, 'null' => NULL_ALLOWED);
 717          $fields['descriptionformat'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 718          $fields['mailformat'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED,
 719                  'default' => $CFG->defaultpreference_mailformat);
 720          $fields['maildigest'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED,
 721                  'default' => $CFG->defaultpreference_maildigest);
 722          $fields['maildisplay'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED,
 723                  'default' => $CFG->defaultpreference_maildisplay);
 724          $fields['autosubscribe'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED,
 725                  'default' => $CFG->defaultpreference_autosubscribe);
 726          $fields['trackforums'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED,
 727                  'default' => $CFG->defaultpreference_trackforums);
 728          $fields['timecreated'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 729          $fields['timemodified'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 730          $fields['trustbitmask'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED);
 731          $fields['imagealt'] = array('type' => PARAM_TEXT, 'null' => NULL_ALLOWED);
 732          $fields['lastnamephonetic'] = array('type' => PARAM_NOTAGS, 'null' => NULL_ALLOWED);
 733          $fields['firstnamephonetic'] = array('type' => PARAM_NOTAGS, 'null' => NULL_ALLOWED);
 734          $fields['middlename'] = array('type' => PARAM_NOTAGS, 'null' => NULL_ALLOWED);
 735          $fields['alternatename'] = array('type' => PARAM_NOTAGS, 'null' => NULL_ALLOWED);
 736  
 737          self::$propertiescache = $fields;
 738      }
 739  
 740      /**
 741       * Get properties of a user field.
 742       *
 743       * @param string $property property name to be retrieved.
 744       * @throws coding_exception if the requested property name is invalid.
 745       * @return array the property definition.
 746       */
 747      public static function get_property_definition($property) {
 748  
 749          self::fill_properties_cache();
 750  
 751          if (!array_key_exists($property, self::$propertiescache)) {
 752              throw new coding_exception('Invalid property requested.');
 753          }
 754  
 755          return self::$propertiescache[$property];
 756      }
 757  
 758      /**
 759       * Validate user data.
 760       *
 761       * This method just validates each user field and return an array of errors. It doesn't clean the data,
 762       * the methods clean() and clean_field() should be used for this purpose.
 763       *
 764       * @param stdClass|array $data user data object or array to be validated.
 765       * @return array|true $errors array of errors found on the user object, true if the validation passed.
 766       */
 767      public static function validate($data) {
 768          // Get all user profile fields definition.
 769          self::fill_properties_cache();
 770  
 771          foreach ($data as $property => $value) {
 772              try {
 773                  if (isset(self::$propertiescache[$property])) {
 774                      validate_param($value, self::$propertiescache[$property]['type'], self::$propertiescache[$property]['null']);
 775                  }
 776                  // Check that the value is part of a list of allowed values.
 777                  if (!empty(self::$propertiescache[$property]['choices']) &&
 778                          !isset(self::$propertiescache[$property]['choices'][$value])) {
 779                      throw new invalid_parameter_exception($value);
 780                  }
 781              } catch (invalid_parameter_exception $e) {
 782                  $errors[$property] = $e->getMessage();
 783              }
 784          }
 785  
 786          return empty($errors) ? true : $errors;
 787      }
 788  
 789      /**
 790       * Clean the properties cache.
 791       *
 792       * During unit tests we need to be able to reset all caches so that each new test starts in a known state.
 793       * Intended for use only for testing, phpunit calls this before every test.
 794       */
 795      public static function reset_caches() {
 796          self::$propertiescache = null;
 797      }
 798  
 799      /**
 800       * Clean the user data.
 801       *
 802       * @param stdClass|array $user the user data to be validated against properties definition.
 803       * @return stdClass $user the cleaned user data.
 804       */
 805      public static function clean_data($user) {
 806          if (empty($user)) {
 807              return $user;
 808          }
 809  
 810          foreach ($user as $field => $value) {
 811              // Get the property parameter type and do the cleaning.
 812              try {
 813                  $user->$field = core_user::clean_field($value, $field);
 814              } catch (coding_exception $e) {
 815                  debugging("The property '$field' could not be cleaned.", DEBUG_DEVELOPER);
 816              }
 817          }
 818  
 819          return $user;
 820      }
 821  
 822      /**
 823       * Clean a specific user field.
 824       *
 825       * @param string $data the user field data to be cleaned.
 826       * @param string $field the user field name on the property definition cache.
 827       * @return string the cleaned user data.
 828       */
 829      public static function clean_field($data, $field) {
 830          if (empty($data) || empty($field)) {
 831              return $data;
 832          }
 833  
 834          try {
 835              $type = core_user::get_property_type($field);
 836  
 837              if (isset(self::$propertiescache[$field]['choices'])) {
 838                  if (!array_key_exists($data, self::$propertiescache[$field]['choices'])) {
 839                      if (isset(self::$propertiescache[$field]['default'])) {
 840                          $data = self::$propertiescache[$field]['default'];
 841                      } else {
 842                          $data = '';
 843                      }
 844                  } else {
 845                      return $data;
 846                  }
 847              } else {
 848                  $data = clean_param($data, $type);
 849              }
 850          } catch (coding_exception $e) {
 851              debugging("The property '$field' could not be cleaned.", DEBUG_DEVELOPER);
 852          }
 853  
 854          return $data;
 855      }
 856  
 857      /**
 858       * Get the parameter type of the property.
 859       *
 860       * @param string $property property name to be retrieved.
 861       * @throws coding_exception if the requested property name is invalid.
 862       * @return int the property parameter type.
 863       */
 864      public static function get_property_type($property) {
 865  
 866          self::fill_properties_cache();
 867  
 868          if (!array_key_exists($property, self::$propertiescache)) {
 869              throw new coding_exception('Invalid property requested: ' . $property);
 870          }
 871  
 872          return self::$propertiescache[$property]['type'];
 873      }
 874  
 875      /**
 876       * Discover if the property is NULL_ALLOWED or NULL_NOT_ALLOWED.
 877       *
 878       * @param string $property property name to be retrieved.
 879       * @throws coding_exception if the requested property name is invalid.
 880       * @return bool true if the property is NULL_ALLOWED, false otherwise.
 881       */
 882      public static function get_property_null($property) {
 883  
 884          self::fill_properties_cache();
 885  
 886          if (!array_key_exists($property, self::$propertiescache)) {
 887              throw new coding_exception('Invalid property requested: ' . $property);
 888          }
 889  
 890          return self::$propertiescache[$property]['null'];
 891      }
 892  
 893      /**
 894       * Get the choices of the property.
 895       *
 896       * This is a helper method to validate a value against a list of acceptable choices.
 897       * For instance: country, language, themes and etc.
 898       *
 899       * @param string $property property name to be retrieved.
 900       * @throws coding_exception if the requested property name is invalid or if it does not has a list of choices.
 901       * @return array the property parameter type.
 902       */
 903      public static function get_property_choices($property) {
 904  
 905          self::fill_properties_cache();
 906  
 907          if (!array_key_exists($property, self::$propertiescache) && !array_key_exists('choices',
 908                  self::$propertiescache[$property])) {
 909  
 910              throw new coding_exception('Invalid property requested, or the property does not has a list of choices.');
 911          }
 912  
 913          return self::$propertiescache[$property]['choices'];
 914      }
 915  
 916      /**
 917       * Get the property default.
 918       *
 919       * This method gets the default value of a field (if exists).
 920       *
 921       * @param string $property property name to be retrieved.
 922       * @throws coding_exception if the requested property name is invalid or if it does not has a default value.
 923       * @return string the property default value.
 924       */
 925      public static function get_property_default($property) {
 926  
 927          self::fill_properties_cache();
 928  
 929          if (!array_key_exists($property, self::$propertiescache) || !isset(self::$propertiescache[$property]['default'])) {
 930              throw new coding_exception('Invalid property requested, or the property does not has a default value.');
 931          }
 932  
 933          return self::$propertiescache[$property]['default'];
 934      }
 935  
 936      /**
 937       * Definition of updateable user preferences and rules for data and access validation.
 938       *
 939       * array(
 940       *     'preferencename' => array(      // Either exact preference name or a regular expression.
 941       *          'null' => NULL_ALLOWED,    // Defaults to NULL_NOT_ALLOWED. Takes NULL_NOT_ALLOWED or NULL_ALLOWED.
 942       *          'type' => PARAM_TYPE,      // Expected parameter type of the user field - mandatory
 943       *          'choices' => array(1, 2..) // An array of accepted values of the user field - optional
 944       *          'default' => $CFG->setting // An default value for the field - optional
 945       *          'isregex' => false/true    // Whether the name of the preference is a regular expression (default false).
 946       *          'permissioncallback' => callable // Function accepting arguments ($user, $preferencename) that checks if current user
 947       *                                     // is allowed to modify this preference for given user.
 948       *                                     // If not specified core_user::default_preference_permission_check() will be assumed.
 949       *          'cleancallback' => callable // Custom callback for cleaning value if something more difficult than just type/choices is needed
 950       *                                     // accepts arguments ($value, $preferencename)
 951       *     )
 952       * )
 953       *
 954       * @return void
 955       */
 956      protected static function fill_preferences_cache() {
 957          if (self::$preferencescache !== null) {
 958              return;
 959          }
 960  
 961          // Array of user preferences and expected types/values.
 962          // Every preference that can be updated directly by user should be added here.
 963          $preferences = array();
 964          $preferences['auth_forcepasswordchange'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'choices' => array(0, 1),
 965              'permissioncallback' => function($user, $preferencename) {
 966                  global $USER;
 967                  $systemcontext = context_system::instance();
 968                  return ($USER->id != $user->id && (has_capability('moodle/user:update', $systemcontext) ||
 969                          ($user->timecreated > time() - 10 && has_capability('moodle/user:create', $systemcontext))));
 970              });
 971          $preferences['usemodchooser'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 1,
 972              'choices' => array(0, 1));
 973          $preferences['forum_markasreadonnotification'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 1,
 974              'choices' => array(0, 1));
 975          $preferences['htmleditor'] = array('type' => PARAM_NOTAGS, 'null' => NULL_ALLOWED,
 976              'cleancallback' => function($value, $preferencename) {
 977                  if (empty($value) || !array_key_exists($value, core_component::get_plugin_list('editor'))) {
 978                      return null;
 979                  }
 980                  return $value;
 981              });
 982          $preferences['badgeprivacysetting'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 1,
 983              'choices' => array(0, 1), 'permissioncallback' => function($user, $preferencename) {
 984                  global $CFG, $USER;
 985                  return !empty($CFG->enablebadges) && $user->id == $USER->id;
 986              });
 987          $preferences['blogpagesize'] = array('type' => PARAM_INT, 'null' => NULL_NOT_ALLOWED, 'default' => 10,
 988              'permissioncallback' => function($user, $preferencename) {
 989                  global $USER;
 990                  return $USER->id == $user->id && has_capability('moodle/blog:view', context_system::instance());
 991              });
 992          $preferences['user_home_page_preference'] = array('type' => PARAM_INT, 'null' => NULL_ALLOWED, 'default' => HOMEPAGE_MY,
 993              'choices' => array(HOMEPAGE_SITE, HOMEPAGE_MY),
 994              'permissioncallback' => function ($user, $preferencename) {
 995                  global $CFG;
 996                  return (!empty($CFG->defaulthomepage) && ($CFG->defaulthomepage == HOMEPAGE_USER));
 997              }
 998          );
 999  
1000          // Core components that may want to define their preferences.
1001          // List of core components implementing callback is hardcoded here for performance reasons.
1002          // TODO MDL-58184 cache list of core components implementing a function.
1003          $corecomponents = ['core_message', 'core_calendar', 'core_contentbank'];
1004          foreach ($corecomponents as $component) {
1005              if (($pluginpreferences = component_callback($component, 'user_preferences')) && is_array($pluginpreferences)) {
1006                  $preferences += $pluginpreferences;
1007              }
1008          }
1009  
1010          // Plugins that may define their preferences.
1011          if ($pluginsfunction = get_plugins_with_function('user_preferences')) {
1012              foreach ($pluginsfunction as $plugintype => $plugins) {
1013                  foreach ($plugins as $function) {
1014                      if (($pluginpreferences = call_user_func($function)) && is_array($pluginpreferences)) {
1015                          $preferences += $pluginpreferences;
1016                      }
1017                  }
1018              }
1019          }
1020  
1021          self::$preferencescache = $preferences;
1022      }
1023  
1024      /**
1025       * Retrieves the preference definition
1026       *
1027       * @param string $preferencename
1028       * @return array
1029       */
1030      protected static function get_preference_definition($preferencename) {
1031          self::fill_preferences_cache();
1032  
1033          foreach (self::$preferencescache as $key => $preference) {
1034              if (empty($preference['isregex'])) {
1035                  if ($key === $preferencename) {
1036                      return $preference;
1037                  }
1038              } else {
1039                  if (preg_match($key, $preferencename)) {
1040                      return $preference;
1041                  }
1042              }
1043          }
1044  
1045          throw new coding_exception('Invalid preference requested.');
1046      }
1047  
1048      /**
1049       * Default callback used for checking if current user is allowed to change permission of user $user
1050       *
1051       * @param stdClass $user
1052       * @param string $preferencename
1053       * @return bool
1054       */
1055      protected static function default_preference_permission_check($user, $preferencename) {
1056          global $USER;
1057          if (is_mnet_remote_user($user)) {
1058              // Can't edit MNET user.
1059              return false;
1060          }
1061  
1062          if ($user->id == $USER->id) {
1063              // Editing own profile.
1064              $systemcontext = context_system::instance();
1065              return has_capability('moodle/user:editownprofile', $systemcontext);
1066          } else  {
1067              // Teachers, parents, etc.
1068              $personalcontext = context_user::instance($user->id);
1069              if (!has_capability('moodle/user:editprofile', $personalcontext)) {
1070                  return false;
1071              }
1072              if (is_siteadmin($user->id) and !is_siteadmin($USER)) {
1073                  // Only admins may edit other admins.
1074                  return false;
1075              }
1076              return true;
1077          }
1078      }
1079  
1080      /**
1081       * Can current user edit preference of this/another user
1082       *
1083       * @param string $preferencename
1084       * @param stdClass $user
1085       * @return bool
1086       */
1087      public static function can_edit_preference($preferencename, $user) {
1088          if (!isloggedin() || isguestuser()) {
1089              // Guests can not edit anything.
1090              return false;
1091          }
1092  
1093          try {
1094              $definition = self::get_preference_definition($preferencename);
1095          } catch (coding_exception $e) {
1096              return false;
1097          }
1098  
1099          if ($user->deleted || !context_user::instance($user->id, IGNORE_MISSING)) {
1100              // User is deleted.
1101              return false;
1102          }
1103  
1104          if (isset($definition['permissioncallback'])) {
1105              $callback = $definition['permissioncallback'];
1106              if (is_callable($callback)) {
1107                  return call_user_func_array($callback, [$user, $preferencename]);
1108              } else {
1109                  throw new coding_exception('Permission callback for preference ' . s($preferencename) . ' is not callable');
1110                  return false;
1111              }
1112          } else {
1113              return self::default_preference_permission_check($user, $preferencename);
1114          }
1115      }
1116  
1117      /**
1118       * Clean value of a user preference
1119       *
1120       * @param string $value the user preference value to be cleaned.
1121       * @param string $preferencename the user preference name
1122       * @return string the cleaned preference value
1123       */
1124      public static function clean_preference($value, $preferencename) {
1125  
1126          $definition = self::get_preference_definition($preferencename);
1127  
1128          if (isset($definition['type']) && $value !== null) {
1129              $value = clean_param($value, $definition['type']);
1130          }
1131  
1132          if (isset($definition['cleancallback'])) {
1133              $callback = $definition['cleancallback'];
1134              if (is_callable($callback)) {
1135                  return $callback($value, $preferencename);
1136              } else {
1137                  throw new coding_exception('Clean callback for preference ' . s($preferencename) . ' is not callable');
1138              }
1139          } else if ($value === null && (!isset($definition['null']) || $definition['null'] == NULL_ALLOWED)) {
1140              return null;
1141          } else if (isset($definition['choices'])) {
1142              if (!in_array($value, $definition['choices'])) {
1143                  if (isset($definition['default'])) {
1144                      return $definition['default'];
1145                  } else {
1146                      $first = reset($definition['choices']);
1147                      return $first;
1148                  }
1149              } else {
1150                  return $value;
1151              }
1152          } else {
1153              if ($value === null) {
1154                  return isset($definition['default']) ? $definition['default'] : '';
1155              }
1156              return $value;
1157          }
1158      }
1159  
1160  }