Search moodle.org's
Developer Documentation

See Release Notes

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

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Auth e-mail external API
  19   *
  20   * @package    auth_email
  21   * @category   external
  22   * @copyright  2016 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 3.2
  25   */
  26  
  27  use core_external\external_api;
  28  use core_external\external_format_value;
  29  use core_external\external_function_parameters;
  30  use core_external\external_multiple_structure;
  31  use core_external\external_single_structure;
  32  use core_external\external_value;
  33  use core_external\external_warnings;
  34  
  35  defined('MOODLE_INTERNAL') || die;
  36  
  37  require_once($CFG->libdir . '/authlib.php');
  38  require_once($CFG->dirroot . '/user/editlib.php');
  39  require_once($CFG->dirroot . '/user/profile/lib.php');
  40  
  41  /**
  42   * Auth e-mail external functions
  43   *
  44   * @package    auth_email
  45   * @category   external
  46   * @copyright  2016 Juan Leyva <juan@moodle.com>
  47   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  48   * @since      Moodle 3.2
  49   */
  50  class auth_email_external extends external_api {
  51  
  52      /**
  53       * Check if registration is enabled in this site.
  54       *
  55       * @throws moodle_exception
  56       * @since Moodle 3.2
  57       */
  58      protected static function check_signup_enabled() {
  59          global $CFG;
  60  
  61          if (empty($CFG->registerauth) or $CFG->registerauth != 'email') {
  62              throw new moodle_exception('registrationdisabled', 'error');
  63          }
  64      }
  65  
  66      /**
  67       * Describes the parameters for get_signup_settings.
  68       *
  69       * @return external_function_parameters
  70       * @since Moodle 3.2
  71       */
  72      public static function get_signup_settings_parameters() {
  73          return new external_function_parameters(array());
  74      }
  75  
  76      /**
  77       * Get the signup required settings and profile fields.
  78       *
  79       * @return array settings and possible warnings
  80       * @since Moodle 3.2
  81       * @throws moodle_exception
  82       */
  83      public static function get_signup_settings() {
  84          global $CFG, $PAGE;
  85  
  86          $context = context_system::instance();
  87          // We need this to make work the format text functions.
  88          $PAGE->set_context($context);
  89  
  90          self::check_signup_enabled();
  91  
  92          $result = array();
  93          $result['namefields'] = useredit_get_required_name_fields();
  94  
  95          if (!empty($CFG->passwordpolicy)) {
  96              $result['passwordpolicy'] = print_password_policy();
  97          }
  98          $manager = new \core_privacy\local\sitepolicy\manager();
  99          if ($sitepolicy = $manager->get_embed_url()) {
 100              $result['sitepolicy'] = $sitepolicy->out(false);
 101          }
 102          if (!empty($CFG->sitepolicyhandler)) {
 103              $result['sitepolicyhandler'] = $CFG->sitepolicyhandler;
 104          }
 105          if (!empty($CFG->defaultcity)) {
 106              $result['defaultcity'] = $CFG->defaultcity;
 107          }
 108          if (!empty($CFG->country)) {
 109              $result['country'] = $CFG->country;
 110          }
 111  
 112          if ($fields = profile_get_signup_fields()) {
 113              $result['profilefields'] = array();
 114              foreach ($fields as $field) {
 115                  $fielddata = $field->object->get_field_config_for_external();
 116                  $fielddata['categoryname'] = \core_external\util::format_string($field->categoryname, $context->id);
 117                  $fielddata['name'] = \core_external\util::format_string($fielddata['name'], $context->id);
 118                  list($fielddata['defaultdata'], $fielddata['defaultdataformat']) =
 119                      \core_external\util::format_text($fielddata['defaultdata'], $fielddata['defaultdataformat'], $context->id);
 120  
 121                  $result['profilefields'][] = $fielddata;
 122              }
 123          }
 124  
 125          if (signup_captcha_enabled()) {
 126              // With reCAPTCHA v2 the captcha will be rendered by the mobile client using just the publickey.
 127              $result['recaptchapublickey'] = $CFG->recaptchapublickey;
 128          }
 129  
 130          $result['warnings'] = array();
 131          return $result;
 132      }
 133  
 134      /**
 135       * Describes the get_signup_settings return value.
 136       *
 137       * @return external_single_structure
 138       * @since Moodle 3.2
 139       */
 140      public static function get_signup_settings_returns() {
 141  
 142          return new external_single_structure(
 143              array(
 144                  'namefields' => new external_multiple_structure(
 145                       new external_value(PARAM_NOTAGS, 'The order of the name fields')
 146                  ),
 147                  'passwordpolicy' => new external_value(PARAM_RAW, 'Password policy', VALUE_OPTIONAL),
 148                  'sitepolicy' => new external_value(PARAM_RAW, 'Site policy', VALUE_OPTIONAL),
 149                  'sitepolicyhandler' => new external_value(PARAM_PLUGIN, 'Site policy handler', VALUE_OPTIONAL),
 150                  'defaultcity' => new external_value(PARAM_NOTAGS, 'Default city', VALUE_OPTIONAL),
 151                  'country' => new external_value(PARAM_ALPHA, 'Default country', VALUE_OPTIONAL),
 152                  'profilefields' => new external_multiple_structure(
 153                      new external_single_structure(
 154                          array(
 155                              'id' => new external_value(PARAM_INT, 'Profile field id', VALUE_OPTIONAL),
 156                              'shortname' => new external_value(PARAM_ALPHANUMEXT, 'Profile field shortname', VALUE_OPTIONAL),
 157                              'name' => new external_value(PARAM_RAW, 'Profield field name', VALUE_OPTIONAL),
 158                              'datatype' => new external_value(PARAM_ALPHANUMEXT, 'Profield field datatype', VALUE_OPTIONAL),
 159                              'description' => new external_value(PARAM_RAW, 'Profield field description', VALUE_OPTIONAL),
 160                              'descriptionformat' => new external_format_value('description'),
 161                              'categoryid' => new external_value(PARAM_INT, 'Profield field category id', VALUE_OPTIONAL),
 162                              'categoryname' => new external_value(PARAM_RAW, 'Profield field category name', VALUE_OPTIONAL),
 163                              'sortorder' => new external_value(PARAM_INT, 'Profield field sort order', VALUE_OPTIONAL),
 164                              'required' => new external_value(PARAM_INT, 'Profield field required', VALUE_OPTIONAL),
 165                              'locked' => new external_value(PARAM_INT, 'Profield field locked', VALUE_OPTIONAL),
 166                              'visible' => new external_value(PARAM_INT, 'Profield field visible', VALUE_OPTIONAL),
 167                              'forceunique' => new external_value(PARAM_INT, 'Profield field unique', VALUE_OPTIONAL),
 168                              'signup' => new external_value(PARAM_INT, 'Profield field in signup form', VALUE_OPTIONAL),
 169                              'defaultdata' => new external_value(PARAM_RAW, 'Profield field default data', VALUE_OPTIONAL),
 170                              'defaultdataformat' => new external_format_value('defaultdata'),
 171                              'param1' => new external_value(PARAM_RAW, 'Profield field settings', VALUE_OPTIONAL),
 172                              'param2' => new external_value(PARAM_RAW, 'Profield field settings', VALUE_OPTIONAL),
 173                              'param3' => new external_value(PARAM_RAW, 'Profield field settings', VALUE_OPTIONAL),
 174                              'param4' => new external_value(PARAM_RAW, 'Profield field settings', VALUE_OPTIONAL),
 175                              'param5' => new external_value(PARAM_RAW, 'Profield field settings', VALUE_OPTIONAL),
 176                          )
 177                      ), 'Required profile fields', VALUE_OPTIONAL
 178                  ),
 179                  'recaptchapublickey' => new external_value(PARAM_RAW, 'Recaptcha public key', VALUE_OPTIONAL),
 180                  'recaptchachallengehash' => new external_value(PARAM_RAW, 'Recaptcha challenge hash', VALUE_OPTIONAL),
 181                  'recaptchachallengeimage' => new external_value(PARAM_URL, 'Recaptcha challenge noscript image', VALUE_OPTIONAL),
 182                  'recaptchachallengejs' => new external_value(PARAM_URL, 'Recaptcha challenge js url', VALUE_OPTIONAL),
 183                  'warnings'  => new external_warnings(),
 184              )
 185          );
 186      }
 187  
 188      /**
 189       * Describes the parameters for signup_user.
 190       *
 191       * @return external_function_parameters
 192       * @since Moodle 3.2
 193       */
 194      public static function signup_user_parameters() {
 195          return new external_function_parameters(
 196              array(
 197                  'username' => new external_value(core_user::get_property_type('username'), 'Username'),
 198                  'password' => new external_value(core_user::get_property_type('password'), 'Plain text password'),
 199                  'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'),
 200                  'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'),
 201                  'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'),
 202                  'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_DEFAULT, ''),
 203                  'country' => new external_value(core_user::get_property_type('country'), 'Home country code', VALUE_DEFAULT, ''),
 204                  'recaptchachallengehash' => new external_value(PARAM_RAW, 'Recaptcha challenge hash', VALUE_DEFAULT, ''),
 205                  'recaptcharesponse' => new external_value(PARAM_NOTAGS, 'Recaptcha response', VALUE_DEFAULT, ''),
 206                  'customprofilefields' => new external_multiple_structure(
 207                      new external_single_structure(
 208                          array(
 209                              'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
 210                              'name'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
 211                              'value' => new external_value(PARAM_RAW, 'Custom field value, can be an encoded json if required')
 212                          )
 213                      ), 'User custom fields (also known as user profile fields)', VALUE_DEFAULT, array()
 214                  ),
 215                  'redirect' => new external_value(PARAM_LOCALURL, 'Redirect the user to this site url after confirmation.',
 216                                                      VALUE_DEFAULT, ''),
 217              )
 218          );
 219      }
 220  
 221      /**
 222       * Get the signup required settings and profile fields.
 223       *
 224       * @param  string $username               username
 225       * @param  string $password               plain text password
 226       * @param  string $firstname              the first name(s) of the user
 227       * @param  string $lastname               the family name of the user
 228       * @param  string $email                  a valid and unique email address
 229       * @param  string $city                   home city of the user
 230       * @param  string $country                home country code
 231       * @param  string $recaptchachallengehash recaptcha challenge hash
 232       * @param  string $recaptcharesponse      recaptcha response
 233       * @param  array  $customprofilefields    user custom fields (also known as user profile fields)
 234       * @param  string $redirect               Site url to redirect the user after confirmation
 235       * @return array settings and possible warnings
 236       * @since Moodle 3.2
 237       * @throws moodle_exception
 238       * @throws invalid_parameter_exception
 239       */
 240      public static function signup_user($username, $password, $firstname, $lastname, $email, $city = '', $country = '',
 241                                          $recaptchachallengehash = '', $recaptcharesponse = '', $customprofilefields = array(),
 242                                          $redirect = '') {
 243          global $CFG, $PAGE;
 244  
 245          $warnings = array();
 246          $params = self::validate_parameters(
 247              self::signup_user_parameters(),
 248              array(
 249                  'username' => $username,
 250                  'password' => $password,
 251                  'firstname' => $firstname,
 252                  'lastname' => $lastname,
 253                  'email' => $email,
 254                  'city' => $city,
 255                  'country' => $country,
 256                  'recaptchachallengehash' => $recaptchachallengehash,
 257                  'recaptcharesponse' => $recaptcharesponse,
 258                  'customprofilefields' => $customprofilefields,
 259                  'redirect' => $redirect,
 260              )
 261          );
 262  
 263          // We need this to make work the format text functions.
 264          $context = context_system::instance();
 265          $PAGE->set_context($context);
 266  
 267          self::check_signup_enabled();
 268  
 269          // Validate profile fields param types.
 270          $allowedfields = profile_get_signup_fields();
 271          $fieldproperties = array();
 272          $fieldsrequired = array();
 273          foreach ($allowedfields as $field) {
 274              $fieldproperties[$field->object->inputname] = $field->object->get_field_properties();
 275              if ($field->object->is_required()) {
 276                  $fieldsrequired[$field->object->inputname] = true;
 277              }
 278          }
 279  
 280          foreach ($params['customprofilefields'] as $profilefield) {
 281              if (!array_key_exists($profilefield['name'], $fieldproperties)) {
 282                  throw new invalid_parameter_exception('Invalid field' . $profilefield['name']);
 283              }
 284              list($type, $allownull) = $fieldproperties[$profilefield['name']];
 285              validate_param($profilefield['value'], $type, $allownull);
 286              // Remove from the potential required list.
 287              if (isset($fieldsrequired[$profilefield['name']])) {
 288                  unset($fieldsrequired[$profilefield['name']]);
 289              }
 290          }
 291          if (!empty($fieldsrequired)) {
 292              throw new invalid_parameter_exception('Missing required parameters: ' . implode(',', array_keys($fieldsrequired)));
 293          }
 294  
 295          // Validate the data sent.
 296          $data = $params;
 297          $data['email2'] = $data['email'];
 298          // Force policy agreed if a site policy is set. The client is responsible of implementing the interface check.
 299          $manager = new \core_privacy\local\sitepolicy\manager();
 300          if ($manager->is_defined()) {
 301              $data['policyagreed'] = 1;
 302          }
 303          unset($data['recaptcharesponse']);
 304          unset($data['customprofilefields']);
 305          // Add profile fields data.
 306          foreach ($params['customprofilefields'] as $profilefield) {
 307              // First, check if the value is a json (some profile fields like text area uses an array for sending data).
 308              $datadecoded = json_decode($profilefield['value'], true);
 309              if (is_array($datadecoded) && (json_last_error() == JSON_ERROR_NONE)) {
 310                  $data[$profilefield['name']] = $datadecoded;
 311              } else {
 312                  $data[$profilefield['name']] = $profilefield['value'];
 313              }
 314          }
 315  
 316          $errors = signup_validate_data($data, array());
 317  
 318          // Validate recaptcha.
 319          if (signup_captcha_enabled()) {
 320              require_once($CFG->libdir . '/recaptchalib_v2.php');
 321              $response = recaptcha_check_response(RECAPTCHA_VERIFY_URL, $CFG->recaptchaprivatekey,
 322                                                   getremoteaddr(), $params['recaptcharesponse']);
 323              if (!$response['isvalid']) {
 324                  $errors['recaptcharesponse'] = $response['error'];
 325              }
 326          }
 327  
 328          if (!empty($errors)) {
 329              foreach ($errors as $itemname => $message) {
 330                  $warnings[] = array(
 331                      'item' => $itemname,
 332                      'itemid' => 0,
 333                      'warningcode' => 'fielderror',
 334                      'message' => s($message)
 335                  );
 336              }
 337              $result = array(
 338                  'success' => false,
 339                  'warnings' => $warnings,
 340              );
 341          } else {
 342              // Save the user.
 343              $user = signup_setup_new_user((object) $data);
 344  
 345              $authplugin = get_auth_plugin('email');
 346  
 347              // Check if we should redirect the user once the user is confirmed.
 348              $confirmationurl = null;
 349              if (!empty($params['redirect'])) {
 350                  // Pass via moodle_url to fix thinks like admin links.
 351                  $redirect = new moodle_url($params['redirect']);
 352  
 353                  $confirmationurl = new moodle_url('/login/confirm.php', array('redirect' => $redirect->out()));
 354              }
 355              $authplugin->user_signup_with_confirmation($user, false, $confirmationurl);
 356  
 357              $result = array(
 358                  'success' => true,
 359                  'warnings' => array(),
 360              );
 361          }
 362          return $result;
 363      }
 364  
 365      /**
 366       * Describes the signup_user return value.
 367       *
 368       * @return external_single_structure
 369       * @since Moodle 3.2
 370       */
 371      public static function signup_user_returns() {
 372  
 373          return new external_single_structure(
 374              array(
 375                  'success' => new external_value(PARAM_BOOL, 'True if the user was created false otherwise'),
 376                  'warnings'  => new external_warnings(),
 377              )
 378          );
 379      }
 380  }