Search moodle.org's
Developer Documentation

See Release Notes

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

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

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