Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  use core_external\external_description;
  18  use core_external\external_value;
  19  use core_external\external_format_value;
  20  use core_external\external_single_structure;
  21  use core_external\external_multiple_structure;
  22  use core_external\external_function_parameters;
  23  use core_external\external_warnings;
  24  
  25  /**
  26   * User external functions
  27   *
  28   * @package    core_user
  29   * @category   external
  30   * @copyright  2011 Jerome Mouneyrac
  31   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   * @since Moodle 2.2
  33   */
  34  class core_user_external extends \core_external\external_api {
  35  
  36      /**
  37       * Returns description of method parameters
  38       *
  39       * @return external_function_parameters
  40       * @since Moodle 2.2
  41       */
  42      public static function create_users_parameters() {
  43          global $CFG;
  44          $userfields = [
  45              'createpassword' => new external_value(PARAM_BOOL, 'True if password should be created and mailed to user.',
  46                  VALUE_OPTIONAL),
  47              // General.
  48              'username' => new external_value(core_user::get_property_type('username'),
  49                  'Username policy is defined in Moodle security config.'),
  50              'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc',
  51                  VALUE_DEFAULT, 'manual', core_user::get_property_null('auth')),
  52              'password' => new external_value(core_user::get_property_type('password'),
  53                  'Plain text password consisting of any characters', VALUE_OPTIONAL),
  54              'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'),
  55              'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'),
  56              'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'),
  57              'maildisplay' => new external_value(core_user::get_property_type('maildisplay'), 'Email visibility', VALUE_OPTIONAL),
  58              'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
  59              'country' => new external_value(core_user::get_property_type('country'),
  60                  'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
  61              'timezone' => new external_value(core_user::get_property_type('timezone'),
  62                  'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
  63              'description' => new external_value(core_user::get_property_type('description'), 'User profile description, no HTML',
  64                  VALUE_OPTIONAL),
  65              // Additional names.
  66              'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'),
  67                  'The first name(s) phonetically of the user', VALUE_OPTIONAL),
  68              'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'),
  69                  'The family name phonetically of the user', VALUE_OPTIONAL),
  70              'middlename' => new external_value(core_user::get_property_type('middlename'), 'The middle name of the user',
  71                  VALUE_OPTIONAL),
  72              'alternatename' => new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user',
  73                  VALUE_OPTIONAL),
  74              // Interests.
  75              'interests' => new external_value(PARAM_TEXT, 'User interests (separated by commas)', VALUE_OPTIONAL),
  76              // Optional.
  77              'idnumber' => new external_value(core_user::get_property_type('idnumber'),
  78                  'An arbitrary ID code number perhaps from the institution', VALUE_DEFAULT, ''),
  79              'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL),
  80              'department' => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL),
  81              'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL),
  82              'phone2' => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL),
  83              'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
  84              // Other user preferences stored in the user table.
  85              'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server',
  86                  VALUE_DEFAULT, core_user::get_property_default('lang'), core_user::get_property_null('lang')),
  87              'calendartype' => new external_value(core_user::get_property_type('calendartype'),
  88                  'Calendar type such as "gregorian", must exist on server', VALUE_DEFAULT, $CFG->calendartype, VALUE_OPTIONAL),
  89              'theme' => new external_value(core_user::get_property_type('theme'),
  90                  'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
  91              'mailformat' => new external_value(core_user::get_property_type('mailformat'),
  92                  'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
  93              // Custom user profile fields.
  94              'customfields' => new external_multiple_structure(
  95                  new external_single_structure(
  96                      [
  97                          'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
  98                          'value' => new external_value(PARAM_RAW, 'The value of the custom field')
  99                      ]
 100                  ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
 101              // User preferences.
 102              'preferences' => new external_multiple_structure(
 103              new external_single_structure(
 104                  [
 105                      'type'  => new external_value(PARAM_RAW, 'The name of the preference'),
 106                      'value' => new external_value(PARAM_RAW, 'The value of the preference')
 107                  ]
 108              ), 'User preferences', VALUE_OPTIONAL),
 109          ];
 110          return new external_function_parameters(
 111              [
 112                  'users' => new external_multiple_structure(
 113                      new external_single_structure($userfields)
 114                  )
 115              ]
 116          );
 117      }
 118  
 119      /**
 120       * Create one or more users.
 121       *
 122       * @throws invalid_parameter_exception
 123       * @param array $users An array of users to create.
 124       * @return array An array of arrays
 125       * @since Moodle 2.2
 126       */
 127      public static function create_users($users) {
 128          global $CFG, $DB;
 129          require_once($CFG->dirroot."/lib/weblib.php");
 130          require_once($CFG->dirroot."/user/lib.php");
 131          require_once($CFG->dirroot."/user/editlib.php");
 132          require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
 133  
 134          // Ensure the current user is allowed to run this function.
 135          $context = context_system::instance();
 136          self::validate_context($context);
 137          require_capability('moodle/user:create', $context);
 138  
 139          // Do basic automatic PARAM checks on incoming data, using params description.
 140          // If any problems are found then exceptions are thrown with helpful error messages.
 141          $params = self::validate_parameters(self::create_users_parameters(), array('users' => $users));
 142  
 143          $availableauths  = core_component::get_plugin_list('auth');
 144          unset($availableauths['mnet']);       // These would need mnethostid too.
 145          unset($availableauths['webservice']); // We do not want new webservice users for now.
 146  
 147          $availablethemes = core_component::get_plugin_list('theme');
 148          $availablelangs  = get_string_manager()->get_list_of_translations();
 149  
 150          $transaction = $DB->start_delegated_transaction();
 151  
 152          $userids = array();
 153          foreach ($params['users'] as $user) {
 154              // Make sure that the username, firstname and lastname are not blank.
 155              foreach (array('username', 'firstname', 'lastname') as $fieldname) {
 156                  if (trim($user[$fieldname]) === '') {
 157                      throw new invalid_parameter_exception('The field '.$fieldname.' cannot be blank');
 158                  }
 159              }
 160  
 161              // Make sure that the username doesn't already exist.
 162              if ($DB->record_exists('user', array('username' => $user['username'], 'mnethostid' => $CFG->mnet_localhost_id))) {
 163                  throw new invalid_parameter_exception('Username already exists: '.$user['username']);
 164              }
 165  
 166              // Make sure auth is valid.
 167              if (empty($availableauths[$user['auth']])) {
 168                  throw new invalid_parameter_exception('Invalid authentication type: '.$user['auth']);
 169              }
 170  
 171              // Make sure lang is valid.
 172              if (empty($availablelangs[$user['lang']])) {
 173                  throw new invalid_parameter_exception('Invalid language code: '.$user['lang']);
 174              }
 175  
 176              // Make sure lang is valid.
 177              if (!empty($user['theme']) && empty($availablethemes[$user['theme']])) { // Theme is VALUE_OPTIONAL,
 178                                                                                       // so no default value
 179                                                                                       // We need to test if the client sent it
 180                                                                                       // => !empty($user['theme']).
 181                  throw new invalid_parameter_exception('Invalid theme: '.$user['theme']);
 182              }
 183  
 184              // Make sure we have a password or have to create one.
 185              $authplugin = get_auth_plugin($user['auth']);
 186              if ($authplugin->is_internal() && empty($user['password']) && empty($user['createpassword'])) {
 187                  throw new invalid_parameter_exception('Invalid password: you must provide a password, or set createpassword.');
 188              }
 189  
 190              $user['confirmed'] = true;
 191              $user['mnethostid'] = $CFG->mnet_localhost_id;
 192  
 193              // Start of user info validation.
 194              // Make sure we validate current user info as handled by current GUI. See user/editadvanced_form.php func validation().
 195              if (!validate_email($user['email'])) {
 196                  throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
 197              } else if (empty($CFG->allowaccountssameemail)) {
 198                  // Make a case-insensitive query for the given email address.
 199                  $select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid';
 200                  $params = array(
 201                      'email' => $user['email'],
 202                      'mnethostid' => $user['mnethostid']
 203                  );
 204                  // If there are other user(s) that already have the same email, throw an error.
 205                  if ($DB->record_exists_select('user', $select, $params)) {
 206                      throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
 207                  }
 208              }
 209              // End of user info validation.
 210  
 211              $createpassword = !empty($user['createpassword']);
 212              unset($user['createpassword']);
 213              $updatepassword = false;
 214              if ($authplugin->is_internal()) {
 215                  if ($createpassword) {
 216                      $user['password'] = '';
 217                  } else {
 218                      $updatepassword = true;
 219                  }
 220              } else {
 221                  $user['password'] = AUTH_PASSWORD_NOT_CACHED;
 222              }
 223  
 224              // Create the user data now!
 225              $user['id'] = user_create_user($user, $updatepassword, false);
 226  
 227              $userobject = (object)$user;
 228  
 229              // Set user interests.
 230              if (!empty($user['interests'])) {
 231                  $trimmedinterests = array_map('trim', explode(',', $user['interests']));
 232                  $interests = array_filter($trimmedinterests, function($value) {
 233                      return !empty($value);
 234                  });
 235                  useredit_update_interests($userobject, $interests);
 236              }
 237  
 238              // Custom fields.
 239              if (!empty($user['customfields'])) {
 240                  foreach ($user['customfields'] as $customfield) {
 241                      // Profile_save_data() saves profile file it's expecting a user with the correct id,
 242                      // and custom field to be named profile_field_"shortname".
 243                      $user["profile_field_".$customfield['type']] = $customfield['value'];
 244                  }
 245                  profile_save_data((object) $user);
 246              }
 247  
 248              if ($createpassword) {
 249                  setnew_password_and_mail($userobject);
 250                  unset_user_preference('create_password', $userobject);
 251                  set_user_preference('auth_forcepasswordchange', 1, $userobject);
 252              }
 253  
 254              // Trigger event.
 255              \core\event\user_created::create_from_userid($user['id'])->trigger();
 256  
 257              // Preferences.
 258              if (!empty($user['preferences'])) {
 259                  $userpref = (object)$user;
 260                  foreach ($user['preferences'] as $preference) {
 261                      $userpref->{'preference_'.$preference['type']} = $preference['value'];
 262                  }
 263                  useredit_update_user_preference($userpref);
 264              }
 265  
 266              $userids[] = array('id' => $user['id'], 'username' => $user['username']);
 267          }
 268  
 269          $transaction->allow_commit();
 270  
 271          return $userids;
 272      }
 273  
 274      /**
 275       * Returns description of method result value
 276       *
 277       * @return external_description
 278       * @since Moodle 2.2
 279       */
 280      public static function create_users_returns() {
 281          return new external_multiple_structure(
 282              new external_single_structure(
 283                  array(
 284                      'id'       => new external_value(core_user::get_property_type('id'), 'user id'),
 285                      'username' => new external_value(core_user::get_property_type('username'), 'user name'),
 286                  )
 287              )
 288          );
 289      }
 290  
 291  
 292      /**
 293       * Returns description of method parameters
 294       *
 295       * @return external_function_parameters
 296       * @since Moodle 2.2
 297       */
 298      public static function delete_users_parameters() {
 299          return new external_function_parameters(
 300              array(
 301                  'userids' => new external_multiple_structure(new external_value(core_user::get_property_type('id'), 'user ID')),
 302              )
 303          );
 304      }
 305  
 306      /**
 307       * Delete users
 308       *
 309       * @throws moodle_exception
 310       * @param array $userids
 311       * @return null
 312       * @since Moodle 2.2
 313       */
 314      public static function delete_users($userids) {
 315          global $CFG, $DB, $USER;
 316          require_once($CFG->dirroot."/user/lib.php");
 317  
 318          // Ensure the current user is allowed to run this function.
 319          $context = context_system::instance();
 320          require_capability('moodle/user:delete', $context);
 321          self::validate_context($context);
 322  
 323          $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids));
 324  
 325          $transaction = $DB->start_delegated_transaction();
 326  
 327          foreach ($params['userids'] as $userid) {
 328              $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST);
 329              // Must not allow deleting of admins or self!!!
 330              if (is_siteadmin($user)) {
 331                  throw new moodle_exception('useradminodelete', 'error');
 332              }
 333              if ($USER->id == $user->id) {
 334                  throw new moodle_exception('usernotdeletederror', 'error');
 335              }
 336              user_delete_user($user);
 337          }
 338  
 339          $transaction->allow_commit();
 340  
 341          return null;
 342      }
 343  
 344      /**
 345       * Returns description of method result value
 346       *
 347       * @return null
 348       * @since Moodle 2.2
 349       */
 350      public static function delete_users_returns() {
 351          return null;
 352      }
 353  
 354      /**
 355       * Returns description of method parameters.
 356       *
 357       * @return external_function_parameters
 358       * @since Moodle 3.2
 359       */
 360      public static function update_user_preferences_parameters() {
 361          return new external_function_parameters(
 362              array(
 363                  'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0),
 364                  'emailstop' => new external_value(core_user::get_property_type('emailstop'),
 365                      'Enable or disable notifications for this user', VALUE_DEFAULT, null),
 366                  'preferences' => new external_multiple_structure(
 367                      new external_single_structure(
 368                          array(
 369                              'type'  => new external_value(PARAM_RAW, 'The name of the preference'),
 370                              'value' => new external_value(PARAM_RAW, 'The value of the preference, do not set this field if you
 371                                  want to remove (unset) the current value.', VALUE_DEFAULT, null),
 372                          )
 373                      ), 'User preferences', VALUE_DEFAULT, array()
 374                  )
 375              )
 376          );
 377      }
 378  
 379      /**
 380       * Update the user's preferences.
 381       *
 382       * @param int $userid
 383       * @param bool|null $emailstop
 384       * @param array $preferences
 385       * @return null
 386       * @since Moodle 3.2
 387       */
 388      public static function update_user_preferences($userid = 0, $emailstop = null, $preferences = array()) {
 389          global $USER, $CFG;
 390  
 391          require_once($CFG->dirroot . '/user/lib.php');
 392          require_once($CFG->dirroot . '/user/editlib.php');
 393          require_once($CFG->dirroot . '/message/lib.php');
 394  
 395          if (empty($userid)) {
 396              $userid = $USER->id;
 397          }
 398  
 399          $systemcontext = context_system::instance();
 400          self::validate_context($systemcontext);
 401          $params = array(
 402              'userid' => $userid,
 403              'emailstop' => $emailstop,
 404              'preferences' => $preferences
 405          );
 406          $params = self::validate_parameters(self::update_user_preferences_parameters(), $params);
 407          $preferences = $params['preferences'];
 408  
 409          // Preferences.
 410          if (!empty($preferences)) {
 411              $userpref = ['id' => $userid];
 412              foreach ($preferences as $preference) {
 413  
 414                  /*
 415                   * Rename user message provider preferences to avoid orphan settings on old app versions.
 416                   * @todo Remove this "translation" block on MDL-73284.
 417                   */
 418                  if (preg_match('/message_provider_.*_loggedin/', $preference['type']) ||
 419                          preg_match('/message_provider_.*_loggedoff/', $preference['type'])) {
 420                      $nameparts = explode('_', $preference['type']);
 421                      array_pop($nameparts);
 422                      $preference['type'] = implode('_', $nameparts).'_enabled';
 423                  }
 424  
 425                  $userpref['preference_' . $preference['type']] = $preference['value'];
 426              }
 427              useredit_update_user_preference($userpref);
 428          }
 429  
 430          // Check if they want to update the email.
 431          if ($emailstop !== null) {
 432              $otheruser = ($userid == $USER->id) ? $USER : core_user::get_user($userid, '*', MUST_EXIST);
 433              core_user::require_active_user($otheruser);
 434              if (core_message_can_edit_message_profile($otheruser) && $otheruser->emailstop != $emailstop) {
 435                  $user = new stdClass();
 436                  $user->id = $userid;
 437                  $user->emailstop = $emailstop;
 438                  user_update_user($user);
 439  
 440                  // Update the $USER if we should.
 441                  if ($userid == $USER->id) {
 442                      $USER->emailstop = $emailstop;
 443                  }
 444              }
 445          }
 446  
 447          return null;
 448      }
 449  
 450      /**
 451       * Returns description of method result value
 452       *
 453       * @return null
 454       * @since Moodle 3.2
 455       */
 456      public static function update_user_preferences_returns() {
 457          return null;
 458      }
 459  
 460      /**
 461       * Returns description of method parameters
 462       *
 463       * @return external_function_parameters
 464       * @since Moodle 2.2
 465       */
 466      public static function update_users_parameters() {
 467          $userfields = [
 468              'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'),
 469              // General.
 470              'username' => new external_value(core_user::get_property_type('username'),
 471                  'Username policy is defined in Moodle security config.', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 472              'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc',
 473                  VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 474              'suspended' => new external_value(core_user::get_property_type('suspended'),
 475                  'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL),
 476              'password' => new external_value(core_user::get_property_type('password'),
 477                  'Plain text password consisting of any characters', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 478              'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user',
 479                  VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 480              'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user',
 481                  VALUE_OPTIONAL),
 482              'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address', VALUE_OPTIONAL,
 483                  '', NULL_NOT_ALLOWED),
 484              'maildisplay' => new external_value(core_user::get_property_type('maildisplay'), 'Email visibility', VALUE_OPTIONAL),
 485              'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
 486              'country' => new external_value(core_user::get_property_type('country'),
 487                  'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
 488              'timezone' => new external_value(core_user::get_property_type('timezone'),
 489                  'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
 490              'description' => new external_value(core_user::get_property_type('description'), 'User profile description, no HTML',
 491                  VALUE_OPTIONAL),
 492              // User picture.
 493              'userpicture' => new external_value(PARAM_INT,
 494                  'The itemid where the new user picture has been uploaded to, 0 to delete', VALUE_OPTIONAL),
 495              // Additional names.
 496              'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'),
 497                  'The first name(s) phonetically of the user', VALUE_OPTIONAL),
 498              'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'),
 499                  'The family name phonetically of the user', VALUE_OPTIONAL),
 500              'middlename' => new external_value(core_user::get_property_type('middlename'), 'The middle name of the user',
 501                  VALUE_OPTIONAL),
 502              'alternatename' => new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user',
 503                  VALUE_OPTIONAL),
 504              // Interests.
 505              'interests' => new external_value(PARAM_TEXT, 'User interests (separated by commas)', VALUE_OPTIONAL),
 506              // Optional.
 507              'idnumber' => new external_value(core_user::get_property_type('idnumber'),
 508                  'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
 509              'institution' => new external_value(core_user::get_property_type('institution'), 'Institution', VALUE_OPTIONAL),
 510              'department' => new external_value(core_user::get_property_type('department'), 'Department', VALUE_OPTIONAL),
 511              'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone', VALUE_OPTIONAL),
 512              'phone2' => new external_value(core_user::get_property_type('phone2'), 'Mobile phone', VALUE_OPTIONAL),
 513              'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
 514              // Other user preferences stored in the user table.
 515              'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server',
 516                  VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 517              'calendartype' => new external_value(core_user::get_property_type('calendartype'),
 518                  'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
 519              'theme' => new external_value(core_user::get_property_type('theme'),
 520                  'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
 521              'mailformat' => new external_value(core_user::get_property_type('mailformat'),
 522                  'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
 523              // Custom user profile fields.
 524              'customfields' => new external_multiple_structure(
 525                  new external_single_structure(
 526                      [
 527                          'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
 528                          'value' => new external_value(PARAM_RAW, 'The value of the custom field')
 529                      ]
 530                  ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
 531              // User preferences.
 532              'preferences' => new external_multiple_structure(
 533                  new external_single_structure(
 534                      [
 535                          'type'  => new external_value(PARAM_RAW, 'The name of the preference'),
 536                          'value' => new external_value(PARAM_RAW, 'The value of the preference')
 537                      ]
 538                  ), 'User preferences', VALUE_OPTIONAL),
 539          ];
 540          return new external_function_parameters(
 541              [
 542                  'users' => new external_multiple_structure(
 543                      new external_single_structure($userfields)
 544                  )
 545              ]
 546          );
 547      }
 548  
 549      /**
 550       * Update users
 551       *
 552       * @param array $users
 553       * @return null
 554       * @since Moodle 2.2
 555       */
 556      public static function update_users($users) {
 557          global $CFG, $DB, $USER;
 558          require_once($CFG->dirroot."/user/lib.php");
 559          require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
 560          require_once($CFG->dirroot.'/user/editlib.php');
 561  
 562          // Ensure the current user is allowed to run this function.
 563          $context = context_system::instance();
 564          require_capability('moodle/user:update', $context);
 565          self::validate_context($context);
 566  
 567          $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users));
 568  
 569          $filemanageroptions = array('maxbytes' => $CFG->maxbytes,
 570                  'subdirs'        => 0,
 571                  'maxfiles'       => 1,
 572                  'accepted_types' => 'optimised_image');
 573  
 574          $warnings = array();
 575          foreach ($params['users'] as $user) {
 576              // Catch any exception while updating a user and return it as a warning.
 577              try {
 578                  $transaction = $DB->start_delegated_transaction();
 579  
 580                  // First check the user exists.
 581                  if (!$existinguser = core_user::get_user($user['id'])) {
 582                      throw new moodle_exception('invaliduserid', '', '', null,
 583                              'Invalid user ID');
 584                  }
 585                  // Check if we are trying to update an admin.
 586                  if ($existinguser->id != $USER->id and is_siteadmin($existinguser) and !is_siteadmin($USER)) {
 587                      throw new moodle_exception('usernotupdatedadmin', '', '', null,
 588                              'Cannot update admin accounts');
 589                  }
 590                  // Other checks (deleted, remote or guest users).
 591                  if ($existinguser->deleted) {
 592                      throw new moodle_exception('usernotupdateddeleted', '', '', null,
 593                              'User is a deleted user');
 594                  }
 595                  if (is_mnet_remote_user($existinguser)) {
 596                      throw new moodle_exception('usernotupdatedremote', '', '', null,
 597                              'User is a remote user');
 598                  }
 599                  if (isguestuser($existinguser->id)) {
 600                      throw new moodle_exception('usernotupdatedguest', '', '', null,
 601                              'Cannot update guest account');
 602                  }
 603                  // Check duplicated emails.
 604                  if (isset($user['email']) && $user['email'] !== $existinguser->email) {
 605                      if (!validate_email($user['email'])) {
 606                          throw new moodle_exception('useremailinvalid', '', '', null,
 607                                  'Invalid email address');
 608                      } else if (empty($CFG->allowaccountssameemail)) {
 609                          // Make a case-insensitive query for the given email address
 610                          // and make sure to exclude the user being updated.
 611                          $select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid AND id <> :userid';
 612                          $params = array(
 613                              'email' => $user['email'],
 614                              'mnethostid' => $CFG->mnet_localhost_id,
 615                              'userid' => $user['id']
 616                          );
 617                          // Skip if there are other user(s) that already have the same email.
 618                          if ($DB->record_exists_select('user', $select, $params)) {
 619                              throw new moodle_exception('useremailduplicate', '', '', null,
 620                                      'Duplicate email address');
 621                          }
 622                      }
 623                  }
 624  
 625                  user_update_user($user, true, false);
 626  
 627                  $userobject = (object)$user;
 628  
 629                  // Update user picture if it was specified for this user.
 630                  if (empty($CFG->disableuserimages) && isset($user['userpicture'])) {
 631                      $userobject->deletepicture = null;
 632  
 633                      if ($user['userpicture'] == 0) {
 634                          $userobject->deletepicture = true;
 635                      } else {
 636                          $userobject->imagefile = $user['userpicture'];
 637                      }
 638  
 639                      core_user::update_picture($userobject, $filemanageroptions);
 640                  }
 641  
 642                  // Update user interests.
 643                  if (!empty($user['interests'])) {
 644                      $trimmedinterests = array_map('trim', explode(',', $user['interests']));
 645                      $interests = array_filter($trimmedinterests, function($value) {
 646                          return !empty($value);
 647                      });
 648                      useredit_update_interests($userobject, $interests);
 649                  }
 650  
 651                  // Update user custom fields.
 652                  if (!empty($user['customfields'])) {
 653  
 654                      foreach ($user['customfields'] as $customfield) {
 655                          // Profile_save_data() saves profile file it's expecting a user with the correct id,
 656                          // and custom field to be named profile_field_"shortname".
 657                          $user["profile_field_".$customfield['type']] = $customfield['value'];
 658                      }
 659                      profile_save_data((object) $user);
 660                  }
 661  
 662                  // Trigger event.
 663                  \core\event\user_updated::create_from_userid($user['id'])->trigger();
 664  
 665                  // Preferences.
 666                  if (!empty($user['preferences'])) {
 667                      $userpref = clone($existinguser);
 668                      foreach ($user['preferences'] as $preference) {
 669                          $userpref->{'preference_'.$preference['type']} = $preference['value'];
 670                      }
 671                      useredit_update_user_preference($userpref);
 672                  }
 673                  if (isset($user['suspended']) and $user['suspended']) {
 674                      \core\session\manager::kill_user_sessions($user['id']);
 675                  }
 676  
 677                  $transaction->allow_commit();
 678              } catch (Exception $e) {
 679                  try {
 680                      $transaction->rollback($e);
 681                  } catch (Exception $e) {
 682                      $warning = [];
 683                      $warning['item'] = 'user';
 684                      $warning['itemid'] = $user['id'];
 685                      if ($e instanceof moodle_exception) {
 686                          $warning['warningcode'] = $e->errorcode;
 687                      } else {
 688                          $warning['warningcode'] = $e->getCode();
 689                      }
 690                      $warning['message'] = $e->getMessage();
 691                      $warnings[] = $warning;
 692                  }
 693              }
 694          }
 695  
 696          return ['warnings' => $warnings];
 697      }
 698  
 699      /**
 700       * Returns description of method result value
 701       *
 702       * @return external_description
 703       * @since Moodle 2.2
 704       */
 705      public static function update_users_returns() {
 706          return new external_single_structure(
 707              array(
 708                  'warnings' => new external_warnings()
 709              )
 710          );
 711      }
 712  
 713      /**
 714       * Returns description of method parameters
 715       *
 716       * @return external_function_parameters
 717       * @since Moodle 2.4
 718       */
 719      public static function get_users_by_field_parameters() {
 720          return new external_function_parameters(
 721              array(
 722                  'field' => new external_value(PARAM_ALPHA, 'the search field can be
 723                      \'id\' or \'idnumber\' or \'username\' or \'email\''),
 724                  'values' => new external_multiple_structure(
 725                          new external_value(PARAM_RAW, 'the value to match'))
 726              )
 727          );
 728      }
 729  
 730      /**
 731       * Get user information for a unique field.
 732       *
 733       * @throws coding_exception
 734       * @throws invalid_parameter_exception
 735       * @param string $field
 736       * @param array $values
 737       * @return array An array of arrays containg user profiles.
 738       * @since Moodle 2.4
 739       */
 740      public static function get_users_by_field($field, $values) {
 741          global $CFG, $USER, $DB;
 742          require_once($CFG->dirroot . "/user/lib.php");
 743  
 744          $params = self::validate_parameters(self::get_users_by_field_parameters(),
 745                  array('field' => $field, 'values' => $values));
 746  
 747          // This array will keep all the users that are allowed to be searched,
 748          // according to the current user's privileges.
 749          $cleanedvalues = array();
 750  
 751          switch ($field) {
 752              case 'id':
 753                  $paramtype = core_user::get_property_type('id');
 754                  break;
 755              case 'idnumber':
 756                  $paramtype = core_user::get_property_type('idnumber');
 757                  break;
 758              case 'username':
 759                  $paramtype = core_user::get_property_type('username');
 760                  break;
 761              case 'email':
 762                  $paramtype = core_user::get_property_type('email');
 763                  break;
 764              default:
 765                  throw new coding_exception('invalid field parameter',
 766                          'The search field \'' . $field . '\' is not supported, look at the web service documentation');
 767          }
 768  
 769          // Clean the values.
 770          foreach ($values as $value) {
 771              $cleanedvalue = clean_param($value, $paramtype);
 772              if ( $value != $cleanedvalue) {
 773                  throw new invalid_parameter_exception('The field \'' . $field .
 774                          '\' value is invalid: ' . $value . '(cleaned value: '.$cleanedvalue.')');
 775              }
 776              $cleanedvalues[] = $cleanedvalue;
 777          }
 778  
 779          // Retrieve the users.
 780          $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
 781  
 782          $context = context_system::instance();
 783          self::validate_context($context);
 784  
 785          // Finally retrieve each users information.
 786          $returnedusers = array();
 787          foreach ($users as $user) {
 788              $userdetails = user_get_user_details_courses($user);
 789  
 790              // Return the user only if the searched field is returned.
 791              // Otherwise it means that the $USER was not allowed to search the returned user.
 792              if (!empty($userdetails) and !empty($userdetails[$field])) {
 793                  $returnedusers[] = $userdetails;
 794              }
 795          }
 796  
 797          return $returnedusers;
 798      }
 799  
 800      /**
 801       * Returns description of method result value
 802       *
 803       * @return external_multiple_structure
 804       * @since Moodle 2.4
 805       */
 806      public static function get_users_by_field_returns() {
 807          return new external_multiple_structure(self::user_description());
 808      }
 809  
 810  
 811      /**
 812       * Returns description of get_users() parameters.
 813       *
 814       * @return external_function_parameters
 815       * @since Moodle 2.5
 816       */
 817      public static function get_users_parameters() {
 818          return new external_function_parameters(
 819              array(
 820                  'criteria' => new external_multiple_structure(
 821                      new external_single_structure(
 822                          array(
 823                              'key' => new external_value(PARAM_ALPHA, 'the user column to search, expected keys (value format) are:
 824                                  "id" (int) matching user id,
 825                                  "lastname" (string) user last name (Note: you can use % for searching but it may be considerably slower!),
 826                                  "firstname" (string) user first name (Note: you can use % for searching but it may be considerably slower!),
 827                                  "idnumber" (string) matching user idnumber,
 828                                  "username" (string) matching user username,
 829                                  "email" (string) user email (Note: you can use % for searching but it may be considerably slower!),
 830                                  "auth" (string) matching user auth plugin'),
 831                              'value' => new external_value(PARAM_RAW, 'the value to search')
 832                          )
 833                      ), 'the key/value pairs to be considered in user search. Values can not be empty.
 834                          Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
 835                          key occurences are forbidden.
 836                          The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
 837                          the search is still executed on the valid criterias.
 838                          You can search without criteria, but the function is not designed for it.
 839                          It could very slow or timeout. The function is designed to search some specific users.'
 840                  )
 841              )
 842          );
 843      }
 844  
 845      /**
 846       * Retrieve matching user.
 847       *
 848       * @throws moodle_exception
 849       * @param array $criteria the allowed array keys are id/lastname/firstname/idnumber/username/email/auth.
 850       * @return array An array of arrays containing user profiles.
 851       * @since Moodle 2.5
 852       */
 853      public static function get_users($criteria = array()) {
 854          global $CFG, $USER, $DB;
 855  
 856          require_once($CFG->dirroot . "/user/lib.php");
 857  
 858          $params = self::validate_parameters(self::get_users_parameters(),
 859                  array('criteria' => $criteria));
 860  
 861          // Validate the criteria and retrieve the users.
 862          $users = array();
 863          $warnings = array();
 864          $sqlparams = array();
 865          $usedkeys = array();
 866  
 867          // Do not retrieve deleted users.
 868          $sql = ' deleted = 0';
 869  
 870          foreach ($params['criteria'] as $criteriaindex => $criteria) {
 871  
 872              // Check that the criteria has never been used.
 873              if (array_key_exists($criteria['key'], $usedkeys)) {
 874                  throw new moodle_exception('keyalreadyset', '', '', null, 'The key ' . $criteria['key'] . ' can only be sent once');
 875              } else {
 876                  $usedkeys[$criteria['key']] = true;
 877              }
 878  
 879              $invalidcriteria = false;
 880              // Clean the parameters.
 881              $paramtype = PARAM_RAW;
 882              switch ($criteria['key']) {
 883                  case 'id':
 884                      $paramtype = core_user::get_property_type('id');
 885                      break;
 886                  case 'idnumber':
 887                      $paramtype = core_user::get_property_type('idnumber');
 888                      break;
 889                  case 'username':
 890                      $paramtype = core_user::get_property_type('username');
 891                      break;
 892                  case 'email':
 893                      // We use PARAM_RAW to allow searches with %.
 894                      $paramtype = core_user::get_property_type('email');
 895                      break;
 896                  case 'auth':
 897                      $paramtype = core_user::get_property_type('auth');
 898                      break;
 899                  case 'lastname':
 900                  case 'firstname':
 901                      $paramtype = core_user::get_property_type('firstname');
 902                      break;
 903                  default:
 904                      // Send back a warning that this search key is not supported in this version.
 905                      // This warning will make the function extandable without breaking clients.
 906                      $warnings[] = array(
 907                          'item' => $criteria['key'],
 908                          'warningcode' => 'invalidfieldparameter',
 909                          'message' =>
 910                              'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation'
 911                      );
 912                      // Do not add this invalid criteria to the created SQL request.
 913                      $invalidcriteria = true;
 914                      unset($params['criteria'][$criteriaindex]);
 915                      break;
 916              }
 917  
 918              if (!$invalidcriteria) {
 919                  $cleanedvalue = clean_param($criteria['value'], $paramtype);
 920  
 921                  $sql .= ' AND ';
 922  
 923                  // Create the SQL.
 924                  switch ($criteria['key']) {
 925                      case 'id':
 926                      case 'idnumber':
 927                      case 'username':
 928                      case 'auth':
 929                          $sql .= $criteria['key'] . ' = :' . $criteria['key'];
 930                          $sqlparams[$criteria['key']] = $cleanedvalue;
 931                          break;
 932                      case 'email':
 933                      case 'lastname':
 934                      case 'firstname':
 935                          $sql .= $DB->sql_like($criteria['key'], ':' . $criteria['key'], false);
 936                          $sqlparams[$criteria['key']] = $cleanedvalue;
 937                          break;
 938                      default:
 939                          break;
 940                  }
 941              }
 942          }
 943  
 944          $users = $DB->get_records_select('user', $sql, $sqlparams, 'id ASC');
 945  
 946          // Finally retrieve each users information.
 947          $returnedusers = array();
 948          foreach ($users as $user) {
 949              $userdetails = user_get_user_details_courses($user);
 950  
 951              // Return the user only if all the searched fields are returned.
 952              // Otherwise it means that the $USER was not allowed to search the returned user.
 953              if (!empty($userdetails)) {
 954                  $validuser = true;
 955  
 956                  foreach ($params['criteria'] as $criteria) {
 957                      if (empty($userdetails[$criteria['key']])) {
 958                          $validuser = false;
 959                      }
 960                  }
 961  
 962                  if ($validuser) {
 963                      $returnedusers[] = $userdetails;
 964                  }
 965              }
 966          }
 967  
 968          return array('users' => $returnedusers, 'warnings' => $warnings);
 969      }
 970  
 971      /**
 972       * Returns description of get_users result value.
 973       *
 974       * @return external_description
 975       * @since Moodle 2.5
 976       */
 977      public static function get_users_returns() {
 978          return new external_single_structure(
 979              array('users' => new external_multiple_structure(
 980                                  self::user_description()
 981                               ),
 982                    'warnings' => new external_warnings('always set to \'key\'', 'faulty key name')
 983              )
 984          );
 985      }
 986  
 987      /**
 988       * Returns description of method parameters
 989       *
 990       * @return external_function_parameters
 991       * @since Moodle 2.2
 992       */
 993      public static function get_course_user_profiles_parameters() {
 994          return new external_function_parameters(
 995              array(
 996                  'userlist' => new external_multiple_structure(
 997                      new external_single_structure(
 998                          array(
 999                              'userid'    => new external_value(core_user::get_property_type('id'), 'userid'),
1000                              'courseid'    => new external_value(PARAM_INT, 'courseid'),
1001                          )
1002                      )
1003                  )
1004              )
1005          );
1006      }
1007  
1008      /**
1009       * Get course participant's details
1010       *
1011       * @param array $userlist  array of user ids and according course ids
1012       * @return array An array of arrays describing course participants
1013       * @since Moodle 2.2
1014       */
1015      public static function get_course_user_profiles($userlist) {
1016          global $CFG, $USER, $DB;
1017          require_once($CFG->dirroot . "/user/lib.php");
1018          $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist));
1019  
1020          $userids = array();
1021          $courseids = array();
1022          foreach ($params['userlist'] as $value) {
1023              $userids[] = $value['userid'];
1024              $courseids[$value['userid']] = $value['courseid'];
1025          }
1026  
1027          // Cache all courses.
1028          $courses = array();
1029          list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
1030          $cselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1031          $cjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
1032          $params['contextlevel'] = CONTEXT_COURSE;
1033          $coursesql = "SELECT c.* $cselect
1034                          FROM {course} c $cjoin
1035                         WHERE c.id $sqlcourseids";
1036          $rs = $DB->get_recordset_sql($coursesql, $params);
1037          foreach ($rs as $course) {
1038              // Adding course contexts to cache.
1039              context_helper::preload_from_record($course);
1040              // Cache courses.
1041              $courses[$course->id] = $course;
1042          }
1043          $rs->close();
1044  
1045          list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
1046          $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1047          $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
1048          $params['contextlevel'] = CONTEXT_USER;
1049          $usersql = "SELECT u.* $uselect
1050                        FROM {user} u $ujoin
1051                       WHERE u.id $sqluserids";
1052          $users = $DB->get_recordset_sql($usersql, $params);
1053          $result = array();
1054          foreach ($users as $user) {
1055              if (!empty($user->deleted)) {
1056                  continue;
1057              }
1058              context_helper::preload_from_record($user);
1059              $course = $courses[$courseids[$user->id]];
1060              $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
1061              self::validate_context($context);
1062              if ($userarray = user_get_user_details($user, $course)) {
1063                  $result[] = $userarray;
1064              }
1065          }
1066  
1067          $users->close();
1068  
1069          return $result;
1070      }
1071  
1072      /**
1073       * Returns description of method result value
1074       *
1075       * @return external_description
1076       * @since Moodle 2.2
1077       */
1078      public static function get_course_user_profiles_returns() {
1079          $additionalfields = array(
1080              'groups' => new external_multiple_structure(
1081                  new external_single_structure(
1082                      array(
1083                          'id'  => new external_value(PARAM_INT, 'group id'),
1084                          'name' => new external_value(PARAM_RAW, 'group name'),
1085                          'description' => new external_value(PARAM_RAW, 'group description'),
1086                          'descriptionformat' => new external_format_value('description'),
1087                      )
1088                  ), 'user groups', VALUE_OPTIONAL),
1089              'roles' => new external_multiple_structure(
1090                  new external_single_structure(
1091                      array(
1092                          'roleid'       => new external_value(PARAM_INT, 'role id'),
1093                          'name'         => new external_value(PARAM_RAW, 'role name'),
1094                          'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
1095                          'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
1096                      )
1097                  ), 'user roles', VALUE_OPTIONAL),
1098              'enrolledcourses' => new external_multiple_structure(
1099                  new external_single_structure(
1100                      array(
1101                          'id'  => new external_value(PARAM_INT, 'Id of the course'),
1102                          'fullname'  => new external_value(PARAM_RAW, 'Fullname of the course'),
1103                          'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
1104                      )
1105                  ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
1106          );
1107  
1108          return new external_multiple_structure(self::user_description($additionalfields));
1109      }
1110  
1111      /**
1112       * Create user return value description.
1113       *
1114       * @param array $additionalfields some additional field
1115       * @return external_description
1116       */
1117      public static function user_description($additionalfields = array()) {
1118          $userfields = array(
1119              'id'    => new external_value(core_user::get_property_type('id'), 'ID of the user'),
1120              'username'    => new external_value(core_user::get_property_type('username'), 'The username', VALUE_OPTIONAL),
1121              'firstname'   => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL),
1122              'lastname'    => new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL),
1123              'fullname'    => new external_value(core_user::get_property_type('firstname'), 'The fullname of the user'),
1124              'email'       => new external_value(core_user::get_property_type('email'), 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
1125              'address'     => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
1126              'phone1'      => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL),
1127              'phone2'      => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL),
1128              'department'  => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL),
1129              'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL),
1130              'idnumber'    => new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
1131              'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
1132              'firstaccess' => new external_value(core_user::get_property_type('firstaccess'), 'first access to the site (0 if never)', VALUE_OPTIONAL),
1133              'lastaccess'  => new external_value(core_user::get_property_type('lastaccess'), 'last access to the site (0 if never)', VALUE_OPTIONAL),
1134              'auth'        => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_OPTIONAL),
1135              'suspended'   => new external_value(core_user::get_property_type('suspended'), 'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL),
1136              'confirmed'   => new external_value(core_user::get_property_type('confirmed'), 'Active user: 1 if confirmed, 0 otherwise', VALUE_OPTIONAL),
1137              'lang'        => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_OPTIONAL),
1138              'calendartype' => new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL),
1139              'theme'       => new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
1140              'timezone'    => new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
1141              'mailformat'  => new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
1142              'description' => new external_value(core_user::get_property_type('description'), 'User profile description', VALUE_OPTIONAL),
1143              'descriptionformat' => new external_format_value(core_user::get_property_type('descriptionformat'), VALUE_OPTIONAL),
1144              'city'        => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
1145              'country'     => new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
1146              'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
1147              'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
1148              'customfields' => new external_multiple_structure(
1149                  new external_single_structure(
1150                      array(
1151                          'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
1152                          'value' => new external_value(PARAM_RAW, 'The value of the custom field (as stored in the database)'),
1153                          'displayvalue' => new external_value(PARAM_RAW, 'The value of the custom field for display',
1154                              VALUE_OPTIONAL),
1155                          'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
1156                          'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
1157                      )
1158                  ), 'User custom fields (also known as user profile fields)', VALUE_OPTIONAL),
1159              'preferences' => new external_multiple_structure(
1160                  new external_single_structure(
1161                      array(
1162                          'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
1163                          'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1164                      )
1165              ), 'Users preferences', VALUE_OPTIONAL)
1166          );
1167          if (!empty($additionalfields)) {
1168              $userfields = array_merge($userfields, $additionalfields);
1169          }
1170          return new external_single_structure($userfields);
1171      }
1172  
1173      /**
1174       * Returns description of method parameters
1175       *
1176       * @return external_function_parameters
1177       * @since Moodle 2.6
1178       */
1179      public static function add_user_private_files_parameters() {
1180          return new external_function_parameters(
1181              array(
1182                  'draftid' => new external_value(PARAM_INT, 'draft area id')
1183              )
1184          );
1185      }
1186  
1187      /**
1188       * Copy files from a draft area to users private files area.
1189       *
1190       * @throws invalid_parameter_exception
1191       * @throws moodle_exception
1192       * @param int $draftid Id of a draft area containing files.
1193       * @return array An array of warnings
1194       * @since Moodle 2.6
1195       */
1196      public static function add_user_private_files($draftid) {
1197          global $CFG, $USER;
1198          require_once($CFG->libdir . "/filelib.php");
1199  
1200          $params = self::validate_parameters(self::add_user_private_files_parameters(), array('draftid' => $draftid));
1201  
1202          if (isguestuser()) {
1203              throw new invalid_parameter_exception('Guest users cannot upload files');
1204          }
1205  
1206          $context = context_user::instance($USER->id);
1207          require_capability('moodle/user:manageownfiles', $context);
1208  
1209          $maxbytes = $CFG->userquota;
1210          $maxareabytes = $CFG->userquota;
1211          if (has_capability('moodle/user:ignoreuserquota', $context)) {
1212              $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
1213              $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
1214          } else {
1215              // Get current used space for this user (private files only).
1216              $fileareainfo = file_get_file_area_info($context->id, 'user', 'private');
1217              $usedspace = $fileareainfo['filesize_without_references'];
1218  
1219              // Get the total size of the new files we want to add to private files.
1220              $newfilesinfo = file_get_draft_area_info($params['draftid']);
1221  
1222              if (($newfilesinfo['filesize_without_references'] + $usedspace) > $maxareabytes) {
1223                  throw new moodle_exception('maxareabytes');
1224              }
1225          }
1226  
1227          $options = array('subdirs' => 1,
1228                           'maxbytes' => $maxbytes,
1229                           'maxfiles' => -1,
1230                           'areamaxbytes' => $maxareabytes);
1231  
1232          file_merge_files_from_draft_area_into_filearea($draftid, $context->id, 'user', 'private', 0, $options);
1233  
1234          return null;
1235      }
1236  
1237      /**
1238       * Returns description of method result value
1239       *
1240       * @return external_description
1241       * @since Moodle 2.2
1242       */
1243      public static function add_user_private_files_returns() {
1244          return null;
1245      }
1246  
1247      /**
1248       * Returns description of method parameters.
1249       *
1250       * @return external_function_parameters
1251       * @since Moodle 2.6
1252       */
1253      public static function add_user_device_parameters() {
1254          return new external_function_parameters(
1255              array(
1256                  'appid'     => new external_value(PARAM_NOTAGS, 'the app id, usually something like com.moodle.moodlemobile'),
1257                  'name'      => new external_value(PARAM_NOTAGS, 'the device name, \'occam\' or \'iPhone\' etc.'),
1258                  'model'     => new external_value(PARAM_NOTAGS, 'the device model \'Nexus4\' or \'iPad1,1\' etc.'),
1259                  'platform'  => new external_value(PARAM_NOTAGS, 'the device platform \'iOS\' or \'Android\' etc.'),
1260                  'version'   => new external_value(PARAM_NOTAGS, 'the device version \'6.1.2\' or \'4.2.2\' etc.'),
1261                  'pushid'    => new external_value(PARAM_RAW, 'the device PUSH token/key/identifier/registration id'),
1262                  'uuid'      => new external_value(PARAM_RAW, 'the device UUID'),
1263                  'publickey' => new external_value(PARAM_RAW, 'the app generated public key', VALUE_DEFAULT, null),
1264              )
1265          );
1266      }
1267  
1268      /**
1269       * Add a user device in Moodle database (for PUSH notifications usually).
1270       *
1271       * @throws moodle_exception
1272       * @param string $appid The app id, usually something like com.moodle.moodlemobile.
1273       * @param string $name The device name, occam or iPhone etc.
1274       * @param string $model The device model Nexus4 or iPad1.1 etc.
1275       * @param string $platform The device platform iOs or Android etc.
1276       * @param string $version The device version 6.1.2 or 4.2.2 etc.
1277       * @param string $pushid The device PUSH token/key/identifier/registration id.
1278       * @param string $uuid The device UUID.
1279       * @param string $publickey The app generated public key
1280       * @return array List of possible warnings.
1281       * @since Moodle 2.6
1282       */
1283      public static function add_user_device($appid, $name, $model, $platform, $version, $pushid, $uuid, $publickey = null) {
1284          global $CFG, $USER, $DB;
1285          require_once($CFG->dirroot . "/user/lib.php");
1286  
1287          $params = self::validate_parameters(self::add_user_device_parameters(),
1288                  array('appid' => $appid,
1289                        'name' => $name,
1290                        'model' => $model,
1291                        'platform' => $platform,
1292                        'version' => $version,
1293                        'pushid' => $pushid,
1294                        'uuid' => $uuid,
1295                        'publickey' => $publickey,
1296                        ));
1297  
1298          $warnings = array();
1299  
1300          // Prevent duplicate keys for users.
1301          if ($DB->get_record('user_devices', array('pushid' => $params['pushid'], 'userid' => $USER->id))) {
1302              $warnings['warning'][] = array(
1303                  'item' => $params['pushid'],
1304                  'warningcode' => 'existingkeyforthisuser',
1305                  'message' => 'This key is already stored for this user'
1306              );
1307              return $warnings;
1308          }
1309  
1310          // Notice that we can have multiple devices because previously it was allowed to have repeated ones.
1311          // Since we don't have a clear way to decide which one is the more appropiate, we update all.
1312          if ($userdevices = $DB->get_records('user_devices', array('uuid' => $params['uuid'],
1313                  'appid' => $params['appid'], 'userid' => $USER->id))) {
1314  
1315              foreach ($userdevices as $userdevice) {
1316                  $userdevice->version    = $params['version'];   // Maybe the user upgraded the device.
1317                  $userdevice->pushid     = $params['pushid'];
1318                  $userdevice->publickey  = $params['publickey'];
1319                  $userdevice->timemodified  = time();
1320                  $DB->update_record('user_devices', $userdevice);
1321              }
1322  
1323          } else {
1324              $userdevice = new stdclass;
1325              $userdevice->userid     = $USER->id;
1326              $userdevice->appid      = $params['appid'];
1327              $userdevice->name       = $params['name'];
1328              $userdevice->model      = $params['model'];
1329              $userdevice->platform   = $params['platform'];
1330              $userdevice->version    = $params['version'];
1331              $userdevice->pushid     = $params['pushid'];
1332              $userdevice->uuid       = $params['uuid'];
1333              $userdevice->publickey  = $params['publickey'];
1334              $userdevice->timecreated  = time();
1335              $userdevice->timemodified = $userdevice->timecreated;
1336  
1337              if (!$DB->insert_record('user_devices', $userdevice)) {
1338                  throw new moodle_exception("There was a problem saving in the database the device with key: " . $params['pushid']);
1339              }
1340          }
1341  
1342          return $warnings;
1343      }
1344  
1345      /**
1346       * Returns description of method result value.
1347       *
1348       * @return external_multiple_structure
1349       * @since Moodle 2.6
1350       */
1351      public static function add_user_device_returns() {
1352          return new external_multiple_structure(
1353             new external_warnings()
1354          );
1355      }
1356  
1357      /**
1358       * Returns description of method parameters.
1359       *
1360       * @return external_function_parameters
1361       * @since Moodle 2.9
1362       */
1363      public static function remove_user_device_parameters() {
1364          return new external_function_parameters(
1365              array(
1366                  'uuid'  => new external_value(PARAM_RAW, 'the device UUID'),
1367                  'appid' => new external_value(PARAM_NOTAGS,
1368                                                  'the app id, if empty devices matching the UUID for the user will be removed',
1369                                                  VALUE_DEFAULT, ''),
1370              )
1371          );
1372      }
1373  
1374      /**
1375       * Remove a user device from the Moodle database (for PUSH notifications usually).
1376       *
1377       * @param string $uuid The device UUID.
1378       * @param string $appid The app id, opitonal parameter. If empty all the devices fmatching the UUID or the user will be removed.
1379       * @return array List of possible warnings and removal status.
1380       * @since Moodle 2.9
1381       */
1382      public static function remove_user_device($uuid, $appid = "") {
1383          global $CFG;
1384          require_once($CFG->dirroot . "/user/lib.php");
1385  
1386          $params = self::validate_parameters(self::remove_user_device_parameters(), array('uuid' => $uuid, 'appid' => $appid));
1387  
1388          $context = context_system::instance();
1389          self::validate_context($context);
1390  
1391          // Warnings array, it can be empty at the end but is mandatory.
1392          $warnings = array();
1393  
1394          $removed = user_remove_user_device($params['uuid'], $params['appid']);
1395  
1396          if (!$removed) {
1397              $warnings[] = array(
1398                  'item' => $params['uuid'],
1399                  'warningcode' => 'devicedoesnotexist',
1400                  'message' => 'The device doesn\'t exists in the database'
1401              );
1402          }
1403  
1404          $result = array(
1405              'removed' => $removed,
1406              'warnings' => $warnings
1407          );
1408  
1409          return $result;
1410      }
1411  
1412      /**
1413       * Returns description of method result value.
1414       *
1415       * @return external_multiple_structure
1416       * @since Moodle 2.9
1417       */
1418      public static function remove_user_device_returns() {
1419          return new external_single_structure(
1420              array(
1421                  'removed' => new external_value(PARAM_BOOL, 'True if removed, false if not removed because it doesn\'t exists'),
1422                  'warnings' => new external_warnings(),
1423              )
1424          );
1425      }
1426  
1427      /**
1428       * Returns description of method parameters
1429       *
1430       * @return external_function_parameters
1431       * @since Moodle 2.9
1432       */
1433      public static function view_user_list_parameters() {
1434          return new external_function_parameters(
1435              array(
1436                  'courseid' => new external_value(PARAM_INT, 'id of the course, 0 for site')
1437              )
1438          );
1439      }
1440  
1441      /**
1442       * Trigger the user_list_viewed event.
1443       *
1444       * @param int $courseid id of course
1445       * @return array of warnings and status result
1446       * @since Moodle 2.9
1447       * @throws moodle_exception
1448       */
1449      public static function view_user_list($courseid) {
1450          global $CFG;
1451          require_once($CFG->dirroot . "/user/lib.php");
1452          require_once($CFG->dirroot . '/course/lib.php');
1453  
1454          $params = self::validate_parameters(self::view_user_list_parameters(),
1455                                              array(
1456                                                  'courseid' => $courseid
1457                                              ));
1458  
1459          $warnings = array();
1460  
1461          if (empty($params['courseid'])) {
1462              $params['courseid'] = SITEID;
1463          }
1464  
1465          $course = get_course($params['courseid']);
1466  
1467          if ($course->id == SITEID) {
1468              $context = context_system::instance();
1469          } else {
1470              $context = context_course::instance($course->id);
1471          }
1472          self::validate_context($context);
1473  
1474          course_require_view_participants($context);
1475  
1476          user_list_view($course, $context);
1477  
1478          $result = array();
1479          $result['status'] = true;
1480          $result['warnings'] = $warnings;
1481          return $result;
1482      }
1483  
1484      /**
1485       * Returns description of method result value
1486       *
1487       * @return external_description
1488       * @since Moodle 2.9
1489       */
1490      public static function view_user_list_returns() {
1491          return new external_single_structure(
1492              array(
1493                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1494                  'warnings' => new external_warnings()
1495              )
1496          );
1497      }
1498  
1499      /**
1500       * Returns description of method parameters
1501       *
1502       * @return external_function_parameters
1503       * @since Moodle 2.9
1504       */
1505      public static function view_user_profile_parameters() {
1506          return new external_function_parameters(
1507              array(
1508                  'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
1509                  'courseid' => new external_value(PARAM_INT, 'id of the course, default site course', VALUE_DEFAULT, 0)
1510              )
1511          );
1512      }
1513  
1514      /**
1515       * Trigger the user profile viewed event.
1516       *
1517       * @param int $userid id of user
1518       * @param int $courseid id of course
1519       * @return array of warnings and status result
1520       * @since Moodle 2.9
1521       * @throws moodle_exception
1522       */
1523      public static function view_user_profile($userid, $courseid = 0) {
1524          global $CFG, $USER;
1525          require_once($CFG->dirroot . "/user/profile/lib.php");
1526  
1527          $params = self::validate_parameters(self::view_user_profile_parameters(),
1528                                              array(
1529                                                  'userid' => $userid,
1530                                                  'courseid' => $courseid
1531                                              ));
1532  
1533          $warnings = array();
1534  
1535          if (empty($params['userid'])) {
1536              $params['userid'] = $USER->id;
1537          }
1538  
1539          if (empty($params['courseid'])) {
1540              $params['courseid'] = SITEID;
1541          }
1542  
1543          $course = get_course($params['courseid']);
1544          $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1545          core_user::require_active_user($user);
1546  
1547          if ($course->id == SITEID) {
1548              $coursecontext = context_system::instance();;
1549          } else {
1550              $coursecontext = context_course::instance($course->id);
1551          }
1552          self::validate_context($coursecontext);
1553  
1554          $currentuser = $USER->id == $user->id;
1555          $usercontext = context_user::instance($user->id);
1556  
1557          if (!$currentuser and
1558                  !has_capability('moodle/user:viewdetails', $coursecontext) and
1559                  !has_capability('moodle/user:viewdetails', $usercontext)) {
1560              throw new moodle_exception('cannotviewprofile');
1561          }
1562  
1563          // Case like user/profile.php.
1564          if ($course->id == SITEID) {
1565              profile_view($user, $usercontext);
1566          } else {
1567              // Case like user/view.php.
1568              if (!$currentuser and !can_access_course($course, $user, '', true)) {
1569                  throw new moodle_exception('notenrolledprofile');
1570              }
1571  
1572              profile_view($user, $coursecontext, $course);
1573          }
1574  
1575          $result = array();
1576          $result['status'] = true;
1577          $result['warnings'] = $warnings;
1578          return $result;
1579      }
1580  
1581      /**
1582       * Returns description of method result value
1583       *
1584       * @return external_description
1585       * @since Moodle 2.9
1586       */
1587      public static function view_user_profile_returns() {
1588          return new external_single_structure(
1589              array(
1590                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1591                  'warnings' => new external_warnings()
1592              )
1593          );
1594      }
1595  
1596      /**
1597       * Returns description of method parameters
1598       *
1599       * @return external_function_parameters
1600       * @since Moodle 3.2
1601       */
1602      public static function get_user_preferences_parameters() {
1603          return new external_function_parameters(
1604              array(
1605                  'name' => new external_value(PARAM_RAW, 'preference name, empty for all', VALUE_DEFAULT, ''),
1606                  'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0)
1607              )
1608          );
1609      }
1610  
1611      /**
1612       * Return user preferences.
1613       *
1614       * @param string $name preference name, empty for all
1615       * @param int $userid id of the user, 0 for current user
1616       * @return array of warnings and preferences
1617       * @since Moodle 3.2
1618       * @throws moodle_exception
1619       */
1620      public static function get_user_preferences($name = '', $userid = 0) {
1621          global $USER;
1622  
1623          $params = self::validate_parameters(self::get_user_preferences_parameters(),
1624                                              array(
1625                                                  'name' => $name,
1626                                                  'userid' => $userid
1627                                              ));
1628          $preferences = array();
1629          $warnings = array();
1630  
1631          $context = context_system::instance();
1632          self::validate_context($context);
1633  
1634          if (empty($params['name'])) {
1635              $name = null;
1636          }
1637          if (empty($params['userid'])) {
1638              $user = null;
1639          } else {
1640              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1641              core_user::require_active_user($user);
1642              if ($user->id != $USER->id) {
1643                  // Only admins can retrieve other users preferences.
1644                  require_capability('moodle/site:config', $context);
1645              }
1646          }
1647  
1648          $userpreferences = get_user_preferences($name, null, $user);
1649          // Check if we received just one preference.
1650          if (!is_array($userpreferences)) {
1651              $userpreferences = array($name => $userpreferences);
1652          }
1653  
1654          foreach ($userpreferences as $name => $value) {
1655              $preferences[] = array(
1656                  'name' => $name,
1657                  'value' => $value,
1658              );
1659          }
1660  
1661          $result = array();
1662          $result['preferences'] = $preferences;
1663          $result['warnings'] = $warnings;
1664          return $result;
1665      }
1666  
1667      /**
1668       * Returns description of method result value
1669       *
1670       * @return external_description
1671       * @since Moodle 3.2
1672       */
1673      public static function get_user_preferences_returns() {
1674          return new external_single_structure(
1675              array(
1676                  'preferences' => new external_multiple_structure(
1677                      new external_single_structure(
1678                          array(
1679                              'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1680                              'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1681                          )
1682                      ),
1683                      'User custom fields (also known as user profile fields)'
1684                  ),
1685                  'warnings' => new external_warnings()
1686              )
1687          );
1688      }
1689  
1690      /**
1691       * Returns description of method parameters
1692       *
1693       * @return external_function_parameters
1694       * @since Moodle 3.2
1695       */
1696      public static function update_picture_parameters() {
1697          return new external_function_parameters(
1698              array(
1699                  'draftitemid' => new external_value(PARAM_INT, 'Id of the user draft file to use as image'),
1700                  'delete' => new external_value(PARAM_BOOL, 'If we should delete the user picture', VALUE_DEFAULT, false),
1701                  'userid' => new external_value(PARAM_INT, 'Id of the user, 0 for current user', VALUE_DEFAULT, 0)
1702              )
1703          );
1704      }
1705  
1706      /**
1707       * Update or delete the user picture in the site
1708       *
1709       * @param  int  $draftitemid id of the user draft file to use as image
1710       * @param  bool $delete      if we should delete the user picture
1711       * @param  int $userid       id of the user, 0 for current user
1712       * @return array warnings and success status
1713       * @since Moodle 3.2
1714       * @throws moodle_exception
1715       */
1716      public static function update_picture($draftitemid, $delete = false, $userid = 0) {
1717          global $CFG, $USER, $PAGE;
1718  
1719          $params = self::validate_parameters(
1720              self::update_picture_parameters(),
1721              array(
1722                  'draftitemid' => $draftitemid,
1723                  'delete' => $delete,
1724                  'userid' => $userid
1725              )
1726          );
1727  
1728          $context = context_system::instance();
1729          self::validate_context($context);
1730  
1731          if (!empty($CFG->disableuserimages)) {
1732              throw new moodle_exception('userimagesdisabled', 'admin');
1733          }
1734  
1735          if (empty($params['userid']) or $params['userid'] == $USER->id) {
1736              $user = $USER;
1737              require_capability('moodle/user:editownprofile', $context);
1738          } else {
1739              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1740              core_user::require_active_user($user);
1741              $personalcontext = context_user::instance($user->id);
1742  
1743              require_capability('moodle/user:editprofile', $personalcontext);
1744              if (is_siteadmin($user) and !is_siteadmin($USER)) {  // Only admins may edit other admins.
1745                  throw new moodle_exception('useradmineditadmin');
1746              }
1747          }
1748  
1749          // Load the appropriate auth plugin.
1750          $userauth = get_auth_plugin($user->auth);
1751          if (is_mnet_remote_user($user) or !$userauth->can_edit_profile() or $userauth->edit_profile_url()) {
1752              throw new moodle_exception('noprofileedit', 'auth');
1753          }
1754  
1755          $filemanageroptions = array(
1756              'maxbytes' => $CFG->maxbytes,
1757              'subdirs' => 0,
1758              'maxfiles' => 1,
1759              'accepted_types' => 'optimised_image'
1760          );
1761          $user->deletepicture = $params['delete'];
1762          $user->imagefile = $params['draftitemid'];
1763          $success = core_user::update_picture($user, $filemanageroptions);
1764  
1765          $result = array(
1766              'success' => $success,
1767              'warnings' => array(),
1768          );
1769          if ($success) {
1770              $userpicture = new user_picture(core_user::get_user($user->id));
1771              $userpicture->size = 1; // Size f1.
1772              $result['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
1773          }
1774          return $result;
1775      }
1776  
1777      /**
1778       * Returns description of method result value
1779       *
1780       * @return external_description
1781       * @since Moodle 3.2
1782       */
1783      public static function update_picture_returns() {
1784          return new external_single_structure(
1785              array(
1786                  'success' => new external_value(PARAM_BOOL, 'True if the image was updated, false otherwise.'),
1787                  'profileimageurl' => new external_value(PARAM_URL, 'New profile user image url', VALUE_OPTIONAL),
1788                  'warnings' => new external_warnings()
1789              )
1790          );
1791      }
1792  
1793      /**
1794       * Returns description of method parameters
1795       *
1796       * @return external_function_parameters
1797       * @since Moodle 3.2
1798       */
1799      public static function set_user_preferences_parameters() {
1800          return new external_function_parameters(
1801              array(
1802                  'preferences' => new external_multiple_structure(
1803                      new external_single_structure(
1804                          array(
1805                              'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1806                              'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1807                              'userid' => new external_value(PARAM_INT, 'Id of the user to set the preference'),
1808                          )
1809                      )
1810                  )
1811              )
1812          );
1813      }
1814  
1815      /**
1816       * Set user preferences.
1817       *
1818       * @param array $preferences list of preferences including name, value and userid
1819       * @return array of warnings and preferences saved
1820       * @since Moodle 3.2
1821       * @throws moodle_exception
1822       */
1823      public static function set_user_preferences($preferences) {
1824          global $USER;
1825  
1826          $params = self::validate_parameters(self::set_user_preferences_parameters(), array('preferences' => $preferences));
1827          $warnings = array();
1828          $saved = array();
1829  
1830          $context = context_system::instance();
1831          self::validate_context($context);
1832  
1833          $userscache = array();
1834          foreach ($params['preferences'] as $pref) {
1835              // Check to which user set the preference.
1836              if (!empty($userscache[$pref['userid']])) {
1837                  $user = $userscache[$pref['userid']];
1838              } else {
1839                  try {
1840                      $user = core_user::get_user($pref['userid'], '*', MUST_EXIST);
1841                      core_user::require_active_user($user);
1842                      $userscache[$pref['userid']] = $user;
1843                  } catch (Exception $e) {
1844                      $warnings[] = array(
1845                          'item' => 'user',
1846                          'itemid' => $pref['userid'],
1847                          'warningcode' => 'invaliduser',
1848                          'message' => $e->getMessage()
1849                      );
1850                      continue;
1851                  }
1852              }
1853  
1854              try {
1855                  if (core_user::can_edit_preference($pref['name'], $user)) {
1856                      $value = core_user::clean_preference($pref['value'], $pref['name']);
1857                      set_user_preference($pref['name'], $value, $user->id);
1858                      $saved[] = array(
1859                          'name' => $pref['name'],
1860                          'userid' => $user->id,
1861                      );
1862                  } else {
1863                      $warnings[] = array(
1864                          'item' => 'user',
1865                          'itemid' => $user->id,
1866                          'warningcode' => 'nopermission',
1867                          'message' => 'You are not allowed to change the preference '.s($pref['name']).' for user '.$user->id
1868                      );
1869                  }
1870              } catch (Exception $e) {
1871                  $warnings[] = array(
1872                      'item' => 'user',
1873                      'itemid' => $user->id,
1874                      'warningcode' => 'errorsavingpreference',
1875                      'message' => $e->getMessage()
1876                  );
1877              }
1878          }
1879  
1880          $result = array();
1881          $result['saved'] = $saved;
1882          $result['warnings'] = $warnings;
1883          return $result;
1884      }
1885  
1886      /**
1887       * Returns description of method result value
1888       *
1889       * @return external_description
1890       * @since Moodle 3.2
1891       */
1892      public static function set_user_preferences_returns() {
1893          return new external_single_structure(
1894              array(
1895                  'saved' => new external_multiple_structure(
1896                      new external_single_structure(
1897                          array(
1898                              'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1899                              'userid' => new external_value(PARAM_INT, 'The user the preference was set for'),
1900                          )
1901                      ), 'Preferences saved'
1902                  ),
1903                  'warnings' => new external_warnings()
1904              )
1905          );
1906      }
1907  
1908      /**
1909       * Returns description of method parameters.
1910       *
1911       * @return external_function_parameters
1912       * @since Moodle 3.2
1913       */
1914      public static function agree_site_policy_parameters() {
1915          return new external_function_parameters(array());
1916      }
1917  
1918      /**
1919       * Agree the site policy for the current user.
1920       *
1921       * @return array of warnings and status result
1922       * @since Moodle 3.2
1923       * @throws moodle_exception
1924       */
1925      public static function agree_site_policy() {
1926          global $CFG, $DB, $USER;
1927  
1928          $warnings = array();
1929  
1930          $context = context_system::instance();
1931          try {
1932              // We expect an exception here since the user didn't agree the site policy yet.
1933              self::validate_context($context);
1934          } catch (Exception $e) {
1935              // We are expecting only a sitepolicynotagreed exception.
1936              if (!($e instanceof moodle_exception) or $e->errorcode != 'sitepolicynotagreed') {
1937                  // In case we receive a different exception, throw it.
1938                  throw $e;
1939              }
1940          }
1941  
1942          $manager = new \core_privacy\local\sitepolicy\manager();
1943          if (!empty($USER->policyagreed)) {
1944              $status = false;
1945              $warnings[] = array(
1946                  'item' => 'user',
1947                  'itemid' => $USER->id,
1948                  'warningcode' => 'alreadyagreed',
1949                  'message' => 'The user already agreed the site policy.'
1950              );
1951          } else if (!$manager->is_defined()) {
1952              $status = false;
1953              $warnings[] = array(
1954                  'item' => 'user',
1955                  'itemid' => $USER->id,
1956                  'warningcode' => 'nositepolicy',
1957                  'message' => 'The site does not have a site policy configured.'
1958              );
1959          } else {
1960              $status = $manager->accept();
1961          }
1962  
1963          $result = array();
1964          $result['status'] = $status;
1965          $result['warnings'] = $warnings;
1966          return $result;
1967      }
1968  
1969      /**
1970       * Returns description of method result value.
1971       *
1972       * @return external_description
1973       * @since Moodle 3.2
1974       */
1975      public static function agree_site_policy_returns() {
1976          return new external_single_structure(
1977              array(
1978                  'status' => new external_value(PARAM_BOOL, 'Status: true only if we set the policyagreed to 1 for the user'),
1979                  'warnings' => new external_warnings()
1980              )
1981          );
1982      }
1983  
1984      /**
1985       * Returns description of method parameters.
1986       *
1987       * @return external_function_parameters
1988       * @since Moodle 3.4
1989       */
1990      public static function get_private_files_info_parameters() {
1991          return new external_function_parameters(
1992              array(
1993                  'userid' => new external_value(PARAM_INT, 'Id of the user, default to current user.', VALUE_DEFAULT, 0)
1994              )
1995          );
1996      }
1997  
1998      /**
1999       * Returns general information about files in the user private files area.
2000       *
2001       * @param int $userid Id of the user, default to current user.
2002       * @return array of warnings and file area information
2003       * @since Moodle 3.4
2004       * @throws moodle_exception
2005       */
2006      public static function get_private_files_info($userid = 0) {
2007          global $CFG, $USER;
2008          require_once($CFG->libdir . '/filelib.php');
2009  
2010          $params = self::validate_parameters(self::get_private_files_info_parameters(), array('userid' => $userid));
2011          $warnings = array();
2012  
2013          $context = context_system::instance();
2014          self::validate_context($context);
2015  
2016          if (empty($params['userid']) || $params['userid'] == $USER->id) {
2017              $usercontext = context_user::instance($USER->id);
2018              require_capability('moodle/user:manageownfiles', $usercontext);
2019          } else {
2020              $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2021              core_user::require_active_user($user);
2022              // Only admins can retrieve other users information.
2023              require_capability('moodle/site:config', $context);
2024              $usercontext = context_user::instance($user->id);
2025          }
2026  
2027          $fileareainfo = file_get_file_area_info($usercontext->id, 'user', 'private');
2028  
2029          $result = array();
2030          $result['filecount'] = $fileareainfo['filecount'];
2031          $result['foldercount'] = $fileareainfo['foldercount'];
2032          $result['filesize'] = $fileareainfo['filesize'];
2033          $result['filesizewithoutreferences'] = $fileareainfo['filesize_without_references'];
2034          $result['warnings'] = $warnings;
2035          return $result;
2036      }
2037  
2038      /**
2039       * Returns description of method result value.
2040       *
2041       * @return external_description
2042       * @since Moodle 3.4
2043       */
2044      public static function get_private_files_info_returns() {
2045          return new external_single_structure(
2046              array(
2047                  'filecount' => new external_value(PARAM_INT, 'Number of files in the area.'),
2048                  'foldercount' => new external_value(PARAM_INT, 'Number of folders in the area.'),
2049                  'filesize' => new external_value(PARAM_INT, 'Total size of the files in the area.'),
2050                  'filesizewithoutreferences' => new external_value(PARAM_INT, 'Total size of the area excluding file references'),
2051                  'warnings' => new external_warnings()
2052              )
2053          );
2054      }
2055  }