diff --git a/auth/email/classes/external.php b/auth/email/classes/external.php index b7c46c7a85c..6a1b3876d48 100644 --- a/auth/email/classes/external.php +++ b/auth/email/classes/external.php @@ -175,4 +175,178 @@ class auth_email_external extends external_api { ) ); } + + /** + * Describes the parameters for signup_user. + * + * @return external_external_function_parameters + * @since Moodle 3.2 + */ + public static function signup_user_parameters() { + return new external_function_parameters( + array( + 'username' => new external_value(core_user::get_property_type('username'), 'Username'), + 'password' => new external_value(core_user::get_property_type('password'), 'Plain text password'), + 'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'), + 'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'), + 'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'), + 'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_DEFAULT, ''), + 'country' => new external_value(core_user::get_property_type('country'), 'Home country code', VALUE_DEFAULT, ''), + 'recaptchachallengehash' => new external_value(PARAM_RAW, 'Recaptcha challenge hash', VALUE_DEFAULT, ''), + 'recaptcharesponse' => new external_value(PARAM_NOTAGS, 'Recaptcha response', VALUE_DEFAULT, ''), + 'customprofilefields' => new external_multiple_structure( + new external_single_structure( + array( + 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'), + 'name' => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'), + 'value' => new external_value(PARAM_RAW, 'Custom field value, can be an encoded json if required') + ) + ), 'User custom fields (also known as user profile fields)', VALUE_DEFAULT, array() + ) + ) + ); + } + + /** + * Get the signup required settings and profile fields. + * + * @param string $username username + * @param string $password plain text password + * @param string $firstname the first name(s) of the user + * @param string $lastname the family name of the user + * @param string $email a valid and unique email address + * @param string $city home city of the user + * @param string $country home country code + * @param string $recaptchachallengehash recaptcha challenge hash + * @param string $recaptcharesponse recaptcha response + * @param array $customprofilefields user custom fields (also known as user profile fields) + * @return array settings and possible warnings + * @since Moodle 3.2 + * @throws moodle_exception + * @throws invalid_parameter_exception + */ + public static function signup_user($username, $password, $firstname, $lastname, $email, $city = '', $country = '', + $recaptchachallengehash = '', $recaptcharesponse = '', $customprofilefields = array()) { + global $CFG, $PAGE; + + $warnings = array(); + $params = self::validate_parameters( + self::signup_user_parameters(), + array( + 'username' => $username, + 'password' => $password, + 'firstname' => $firstname, + 'lastname' => $lastname, + 'email' => $email, + 'city' => $city, + 'country' => $country, + 'recaptchachallengehash' => $recaptchachallengehash, + 'recaptcharesponse' => $recaptcharesponse, + 'customprofilefields' => $customprofilefields, + ) + ); + + // We need this to make work the format text functions. + $context = context_system::instance(); + $PAGE->set_context($context); + + self::check_signup_enabled(); + + // Validate profile fields param types. + $allowedfields = profile_get_signup_fields(); + $fieldproperties = array(); + $fieldsrequired = array(); + foreach ($allowedfields as $field) { + $fieldproperties[$field->object->inputname] = $field->object->get_field_properties(); + if ($field->object->is_required()) { + $fieldsrequired[$field->object->inputname] = true; + } + } + + foreach ($params['customprofilefields'] as $profilefield) { + if (!array_key_exists($profilefield['name'], $fieldproperties)) { + throw new invalid_parameter_exception('Invalid field' . $profilefield['name']); + } + list($type, $allownull) = $fieldproperties[$profilefield['name']]; + validate_param($profilefield['value'], $type, $allownull); + // Remove from the potential required list. + if (isset($fieldsrequired[$profilefield['name']])) { + unset($fieldsrequired[$profilefield['name']]); + } + } + if (!empty($fieldsrequired)) { + throw new invalid_parameter_exception('Missing required parameters: ' . implode(',', array_keys($fieldsrequired))); + } + + // Validate the data sent. + $data = $params; + $data['email2'] = $data['email']; + unset($data['recaptcharesponse']); + unset($data['customprofilefields']); + // Add profile fields data. + foreach ($params['customprofilefields'] as $profilefield) { + // First, check if the value is a json (some profile fields like text area uses an array for sending data). + $datadecoded = json_decode($profilefield['value'], true); + if (is_array($datadecoded) && (json_last_error() == JSON_ERROR_NONE)) { + $data[$profilefield['name']] = $datadecoded; + } else { + $data[$profilefield['name']] = $profilefield['value']; + } + } + + $errors = signup_validate_data($data, array()); + + // Validate recaptcha. + if (signup_captcha_enabled()) { + require_once($CFG->libdir . '/recaptchalib.php'); + $response = recaptcha_check_answer($CFG->recaptchaprivatekey, getremoteaddr(), $params['recaptchachallengehash'], + $params['recaptcharesponse'], true); + if (!$response->is_valid) { + $errors['recaptcharesponse'] = $response->error; + } + } + + if (!empty($errors)) { + foreach ($errors as $itemname => $message) { + $warnings[] = array( + 'item' => $itemname, + 'itemid' => 0, + 'warningcode' => 'fielderror', + 'message' => s($message) + ); + } + $result = array( + 'success' => false, + 'warnings' => $warnings, + ); + } else { + // Save the user. + $user = signup_setup_new_user((object) $data); + + $authplugin = get_auth_plugin('email'); + $authplugin->user_signup($user, false); + + $result = array( + 'success' => true, + 'warnings' => array(), + ); + } + return $result; + } + + /** + * Describes the signup_user return value. + * + * @return external_single_structure + * @since Moodle 3.2 + */ + public static function signup_user_returns() { + + return new external_single_structure( + array( + 'success' => new external_value(PARAM_BOOL, 'True if the user was created false otherwise'), + 'warnings' => new external_warnings(), + ) + ); + } } diff --git a/auth/email/db/services.php b/auth/email/db/services.php index 2978a87a3c4..033bdab6b0c 100644 --- a/auth/email/db/services.php +++ b/auth/email/db/services.php @@ -34,5 +34,13 @@ $functions = array( 'ajax' => true, 'loginrequired' => false, ), + 'auth_email_signup_user' => array( + 'classname' => 'auth_email_external', + 'methodname' => 'signup_user', + 'description' => 'Adds a new user (pendingto be confirmed) in the site.', + 'type' => 'write', + 'ajax' => true, + 'loginrequired' => false, + ), ); diff --git a/auth/email/tests/external_test.php b/auth/email/tests/external_test.php index d8b20b55c0f..75dbe79e6f3 100644 --- a/auth/email/tests/external_test.php +++ b/auth/email/tests/external_test.php @@ -50,9 +50,12 @@ class auth_email_external_testcase extends externallib_advanced_testcase { $CFG->registerauth = 'email'; $categoryid = $DB->insert_record('user_info_category', array('name' => 'Cat 1', 'sortorder' => 1)); - $field = $DB->insert_record('user_info_field', array( + $this->field1 = $DB->insert_record('user_info_field', array( 'shortname' => 'frogname', 'name' => 'Name of frog', 'categoryid' => $categoryid, - 'datatype' => 'text', 'signup' => 1, 'visible' => 1)); + 'datatype' => 'text', 'signup' => 1, 'visible' => 1, 'required' => 1)); + $this->field2 = $DB->insert_record('user_info_field', array( + 'shortname' => 'sometext', 'name' => 'Some text in textarea', 'categoryid' => $categoryid, + 'datatype' => 'textarea', 'signup' => 1, 'visible' => 1, 'required' => 1)); } public function test_get_signup_settings() { @@ -73,8 +76,75 @@ class auth_email_external_testcase extends externallib_advanced_testcase { $this->assertEquals(print_password_policy(), $result['passwordpolicy']); $this->assertNotContains('recaptchachallengehash', $result); $this->assertNotContains('recaptchachallengeimage', $result); - $this->assertCount(1, $result['profilefields']); + $this->assertCount(2, $result['profilefields']); $this->assertEquals('text', $result['profilefields'][0]['datatype']); + $this->assertEquals('textarea', $result['profilefields'][1]['datatype']); } + public function test_signup_user() { + global $DB; + + $username = 'pepe'; + $password = 'abcdefAª.ªª!!3'; + $firstname = 'Pepe'; + $lastname = 'Pérez'; + $email = 'myemail@no.zbc'; + $city = 'Bcn'; + $country = 'ES'; + $customprofilefields = array( + array( + 'type' => 'text', + 'name' => 'profile_field_frogname', + 'value' => 'random text', + ), + array( + 'type' => 'textarea', + 'name' => 'profile_field_sometext', + 'value' => json_encode( + array( + 'text' => 'blah blah', + 'format' => FORMAT_HTML + ) + ), + ) + ); + + // Create new user. + $result = auth_email_external::signup_user($username, $password, $firstname, $lastname, $email, $city, $country, + '', '', $customprofilefields); + $result = external_api::clean_returnvalue(auth_email_external::signup_user_returns(), $result); + $this->assertTrue($result['success']); + $this->assertEmpty($result['warnings']); + $user = $DB->get_record('user', array('username' => $username)); + $this->assertEquals($firstname, $user->firstname); + $this->assertEquals($lastname, $user->lastname); + $this->assertEquals($email, $user->email); + $this->assertEquals($city, $user->city); + $this->assertEquals($country, $user->country); + $this->assertEquals(0, $user->confirmed); + $this->assertEquals(current_language(), $user->lang); + $this->assertEquals('email', $user->auth); + $infofield = $DB->get_record('user_info_data', array('userid' => $user->id, 'fieldid' => $this->field1)); + $this->assertEquals($customprofilefields[0]['value'], $infofield->data); + $infofield = $DB->get_record('user_info_data', array('userid' => $user->id, 'fieldid' => $this->field2)); + $this->assertEquals(json_decode($customprofilefields[1]['value'])->text, $infofield->data); + + // Try to create a user with the same username, email and password. We ommit also the profile fields. + $password = 'abc'; + $result = auth_email_external::signup_user($username, $password, $firstname, $lastname, $email, $city, $country, + '', '', $customprofilefields); + $result = external_api::clean_returnvalue(auth_email_external::signup_user_returns(), $result); + $this->assertFalse($result['success']); + $this->assertCount(3, $result['warnings']); + $expectederrors = array('username', 'email', 'password'); + $finalerrors = []; + foreach ($result['warnings'] as $warning) { + $finalerrors[] = $warning['item']; + } + $this->assertEquals($expectederrors, $finalerrors); + + // Do not pass the required profile fields. + $this->expectException('invalid_parameter_exception'); + $result = auth_email_external::signup_user($username, $password, $firstname, $lastname, $email, $city, $country); + } } diff --git a/auth/email/version.php b/auth/email/version.php index 75de5270287..90ca9870a75 100644 --- a/auth/email/version.php +++ b/auth/email/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2016052301; // The current plugin version (Date: YYYYMMDDXX) +$plugin->version = 2016052302; // The current plugin version (Date: YYYYMMDDXX) $plugin->requires = 2016051900; // Requires this Moodle version $plugin->component = 'auth_email'; // Full name of the plugin (used for diagnostics)