diff --git a/includes/form.inc b/includes/form.inc index 53d958b574b..282f3a1b1d0 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -33,8 +33,8 @@ * form for modules automatically. For example: * * @code - * // Display the user registration form. - * $output = drupal_get_form('user_register'); + * // Display the user registration form. + * $output = drupal_get_form('user_register_form'); * @endcode * * Forms can also be built and submitted programmatically without any user input @@ -389,7 +389,7 @@ function form_set_cache($form_build_id, $form, $form_state) { * $form_state['values']['mail'] = 'robouser@example.com'; * $form_state['values']['pass'] = 'password'; * $form_state['values']['op'] = t('Create new account'); - * drupal_form_submit('user_register', $form_state); + * drupal_form_submit('user_register_form', $form_state); * * // Create a new node * $form_state = array(); diff --git a/modules/block/block.module b/modules/block/block.module index 8db09eb8134..06aa7717801 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -425,42 +425,33 @@ function block_form_user_profile_form_alter(&$form, &$form_state) { $account = $form['#user']; $rids = array_keys($account->roles); $result = db_query("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.status = 1 AND b.custom <> 0 AND (r.rid IN (:rids) OR r.rid IS NULL) ORDER BY b.weight, b.module", array(':rids' => $rids)); - $form['block'] = array( - '#type' => 'fieldset', - '#title' => t('Personalize blocks'), - '#description' => t('Blocks consist of content or information that complements the main content of the page. Enable or disable optional blocks using the checkboxes below.'), - '#weight' => 3, - '#collapsible' => TRUE, - '#tree' => TRUE - ); + + $blocks = array(); foreach ($result as $block) { $data = module_invoke($block->module, 'block_info'); if ($data[$block->delta]['info']) { - $return = TRUE; - $form['block'][$block->module][$block->delta] = array( + $blocks[$block->module][$block->delta] = array( '#type' => 'checkbox', '#title' => check_plain($data[$block->delta]['info']), '#default_value' => isset($account->block[$block->module][$block->delta]) ? $account->block[$block->module][$block->delta] : ($block->custom == 1), ); } } - - if (!isset($return)) { - $form['block']['#access'] = FALSE; + // Only display the fieldset if there are any personalizable blocks. + if ($blocks) { + $form['block'] = array( + '#type' => 'fieldset', + '#title' => t('Personalize blocks'), + '#description' => t('Blocks consist of content or information that complements the main content of the page. Enable or disable optional blocks using the checkboxes below.'), + '#weight' => 3, + '#collapsible' => TRUE, + '#tree' => TRUE, + ); + $form['block'] += $blocks; } } } -/** - * Implement hook_user_validate(). - */ -function block_user_validate(&$edit, $account, $category) { - if (empty($edit['block'])) { - $edit['block'] = array(); - } - return $edit; -} - /** * Implement hook_form_FORM_ID_alter(). */ diff --git a/modules/locale/locale.module b/modules/locale/locale.module index e73eaf87bfe..18dc0a5a1b1 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -338,7 +338,7 @@ function locale_form_alter(&$form, &$form_state, $form_id) { if (variable_get('language_count', 1) > 1) { // Display language selector when either creating a user on the admin // interface or editing a user account. - if (($form_id == 'user_register' && user_access('administer users')) || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account')) { + if (($form_id == 'user_register_form' && user_access('administer users')) || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account')) { locale_language_selector_form($form, $form_state, $form['#user']); } } diff --git a/modules/openid/openid.module b/modules/openid/openid.module index 697f4db5f99..d910e4dfaef 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -129,7 +129,7 @@ function _openid_user_login_form_alter(&$form, &$form_state) { * * Adds OpenID login to the login forms. */ -function openid_form_user_register_alter(&$form, &$form_state) { +function openid_form_user_register_form_alter(&$form, &$form_state) { if (isset($_SESSION['openid']['values'])) { // We were unable to auto-register a new user. Prefill the registration // form with the values we have. @@ -437,9 +437,9 @@ function openid_authentication($response) { $form_state['values']['pass'] = user_password(); $form_state['values']['status'] = variable_get('user_register', 1) == 1; $form_state['values']['response'] = $response; - $form = drupal_retrieve_form('user_register', $form_state); - drupal_prepare_form('user_register', $form, $form_state); - drupal_validate_form('user_register', $form, $form_state); + $form = drupal_retrieve_form('user_register_form', $form_state); + drupal_prepare_form('user_register_form', $form, $form_state); + drupal_validate_form('user_register_form', $form, $form_state); if (form_get_errors()) { // We were unable to register a valid new user, redirect to standard // user/register and prefill with the values we received. @@ -452,7 +452,7 @@ function openid_authentication($response) { } else { unset($form_state['values']['response']); - $account = user_save('', $form_state['values']); + $account = user_save(drupal_anonymous_user(), $form_state['values']); // Terminate if an error occurred during user_save(). if (!$account) { drupal_set_message(t("Error saving user account."), 'error'); diff --git a/modules/profile/profile.module b/modules/profile/profile.module index 2ff780480a0..aac2c8105d7 100644 --- a/modules/profile/profile.module +++ b/modules/profile/profile.module @@ -210,17 +210,19 @@ function profile_block_view($delta = '') { } /** - * Implement hook_user_update(). + * Implement hook_user_presave(). */ -function profile_user_update(&$edit, $account, $category) { - return profile_save_profile($edit, $account, $category); +function profile_user_presave(&$edit, $account, $category) { + if ($account->uid) { + profile_save_profile($edit, $account, $category); + } } /** * Implement hook_user_insert(). */ function profile_user_insert(&$edit, $account, $category) { - return profile_save_profile($edit, $account, $category, TRUE); + profile_save_profile($edit, $account, $category, TRUE); } /** @@ -368,25 +370,23 @@ function _profile_form_explanation($field) { * Implement hook_form_alter(). */ function profile_form_alter(&$form, &$form_state, $form_id) { - if ($form_id == 'user_register' || $form_id == 'user_profile_form') { - $register = ($form['#user']->uid > 0 ? FALSE : TRUE); - $form = array_merge($form, profile_form_profile($form['#user'], $form['#user_category'], $register)); + if (!($form_id == 'user_register_form' || $form_id == 'user_profile_form')) { + return; } -} - -function profile_form_profile($account, $category, $register = FALSE) { - $result = _profile_get_fields($category, $register); + $form['#validate'][] = 'profile_user_form_validate'; + $account = $form['#user']; + $result = _profile_get_fields($form['#user_category'], $form['#user_category'] == 'register'); $weight = 1; - $fields = array(); foreach ($result as $field) { $category = $field->category; - if (!isset($fields[$category])) { - $fields[$category] = array('#type' => 'fieldset', '#title' => check_plain($category), '#weight' => $weight++); + if (!isset($form[$category])) { + $form[$category] = array('#type' => 'fieldset', '#title' => check_plain($category), '#weight' => $weight++); } switch ($field->type) { case 'textfield': case 'url': - $fields[$category][$field->name] = array('#type' => 'textfield', + $form[$category][$field->name] = array( + '#type' => 'textfield', '#title' => check_plain($field->title), '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '', '#maxlength' => 255, @@ -394,33 +394,40 @@ function profile_form_profile($account, $category, $register = FALSE) { '#required' => $field->required, ); if ($field->autocomplete) { - $fields[$category][$field->name]['#autocomplete_path'] = "profile/autocomplete/" . $field->fid; + $form[$category][$field->name]['#autocomplete_path'] = "profile/autocomplete/" . $field->fid; } break; + case 'textarea': - $fields[$category][$field->name] = array('#type' => 'textarea', + $form[$category][$field->name] = array( + '#type' => 'textarea', '#title' => check_plain($field->title), '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '', '#description' => _profile_form_explanation($field), '#required' => $field->required, ); break; + case 'list': - $fields[$category][$field->name] = array('#type' => 'textarea', + $form[$category][$field->name] = array( + '#type' => 'textarea', '#title' => check_plain($field->title), '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '', '#description' => _profile_form_explanation($field), '#required' => $field->required, ); break; + case 'checkbox': - $fields[$category][$field->name] = array('#type' => 'checkbox', + $form[$category][$field->name] = array( + '#type' => 'checkbox', '#title' => check_plain($field->title), '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '', '#description' => _profile_form_explanation($field), '#required' => $field->required, ); break; + case 'selection': $options = $field->required ? array() : array('--'); $lines = preg_split("/[\n\r]/", $field->options); @@ -429,7 +436,8 @@ function profile_form_profile($account, $category, $register = FALSE) { $options[$line] = $line; } } - $fields[$category][$field->name] = array('#type' => 'select', + $form[$category][$field->name] = array( + '#type' => 'select', '#title' => check_plain($field->title), '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '', '#options' => $options, @@ -437,8 +445,10 @@ function profile_form_profile($account, $category, $register = FALSE) { '#required' => $field->required, ); break; + case 'date': - $fields[$category][$field->name] = array('#type' => 'date', + $form[$category][$field->name] = array( + '#type' => 'date', '#title' => check_plain($field->title), '#default_value' => isset($account->{$field->name}) ? $account->{$field->name} : '', '#description' => _profile_form_explanation($field), @@ -447,7 +457,6 @@ function profile_form_profile($account, $category, $register = FALSE) { break; } } - return $fields; } /** @@ -461,25 +470,24 @@ function _profile_update_user_fields($fields, $account) { } /** - * Implement hook_user_validate(). + * Form validation handler for the user register/profile form. + * + * @see profile_form_alter() */ -function profile_user_validate(&$edit, $account, $category) { - $result = _profile_get_fields($category); +function profile_user_form_validate($form, &$form_state) { + $result = _profile_get_fields($form['#user_category'], $form['#user_category'] == 'register'); foreach ($result as $field) { - if ($edit[$field->name]) { - if ($field->type == 'url') { - if (!valid_url($edit[$field->name], TRUE)) { - form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => $field->title))); - } + if (!empty($form_state['values'][$field->name])) { + if ($field->type == 'url' && !valid_url($form_state['values'][$field->name], TRUE)) { + form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => $field->title))); } } elseif ($field->required && !user_access('administer users')) { form_set_error($field->name, t('The field %field is required.', array('%field' => $field->title))); } } - - return $edit; } + /** * Implement hook_user_categories(). */ diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index ca7502fb7db..66154ec7ed3 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -866,7 +866,7 @@ class DrupalWebTestCase extends DrupalTestCase { $edit['pass'] = user_password(); $edit['status'] = 1; - $account = user_save('', $edit); + $account = user_save(drupal_anonymous_user(), $edit); $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login')); if (empty($account->uid)) { diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 8a055d65fde..13a71ec5e25 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -555,7 +555,7 @@ function hook_form_alter(&$form, &$form_state, $form_id) { */ function hook_form_FORM_ID_alter(&$form, &$form_state) { // Modification for the form with the given form ID goes here. For example, if - // FORM_ID is "user_register" this code would run only on the user + // FORM_ID is "user_register_form" this code would run only on the user // registration form. // Add a checkbox to registration form about agreeing to terms of use. diff --git a/modules/system/system.module b/modules/system/system.module index 14d5833bc41..23db12b5602 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -1492,7 +1492,7 @@ function system_form_user_profile_form_alter(&$form, &$form_state) { /** * Implement hook_form_FORM_ID_alter(). */ -function system_form_user_register_alter(&$form, &$form_state) { +function system_form_user_register_form_alter(&$form, &$form_state) { if (variable_get('configurable_timezones', 1)) { if (variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT) == DRUPAL_USER_TIMEZONE_SELECT) { system_user_timezone($form, $form_state); diff --git a/modules/trigger/tests/trigger_test.module b/modules/trigger/tests/trigger_test.module index 4793ad05f22..028f64b7d90 100644 --- a/modules/trigger/tests/trigger_test.module +++ b/modules/trigger/tests/trigger_test.module @@ -35,6 +35,7 @@ function trigger_test_action_info() { 'comment_insert', 'comment_update', 'comment_delete', + 'user_presave', 'user_insert', 'user_update', 'user_delete', diff --git a/modules/trigger/trigger.module b/modules/trigger/trigger.module index 7c831ebe44c..9742509bb30 100644 --- a/modules/trigger/trigger.module +++ b/modules/trigger/trigger.module @@ -136,11 +136,14 @@ function trigger_trigger_info() { ), ), 'user' => array( + 'user_presave' => array( + 'label' => t('When either creating a new user account or updating an existing'), + ), 'user_insert' => array( - 'label' => t('After a user account has been created'), + 'label' => t('After creating a new user account'), ), 'user_update' => array( - 'label' => t("After a user's profile has been updated"), + 'label' => t('After updating a user account'), ), 'user_delete' => array( 'label' => t('After a user has been deleted'), @@ -469,6 +472,13 @@ function trigger_user_logout($account) { _trigger_user('user_logout', $edit = NULL, $account); } +/** + * Implement hook_user_presave(). + */ +function trigger_user_presave(&$edit, $account, $category) { + _trigger_user('user_presave', $edit, $account, $category); +} + /** * Implement hook_user_insert(). */ diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc index 5bc88acefe6..d265b79c8ce 100644 --- a/modules/user/user.admin.inc +++ b/modules/user/user.admin.inc @@ -12,7 +12,7 @@ function user_admin($callback_arg = '') { switch ($op) { case t('Create new account'): case 'create': - $build['user_register'] = drupal_get_form('user_register'); + $build['user_register'] = drupal_get_form('user_register_form'); break; default: if (!empty($_POST['accounts']) && isset($_POST['operation']) && ($_POST['operation'] == 'cancel')) { diff --git a/modules/user/user.api.php b/modules/user/user.api.php index 82895e1828e..aca316ac22e 100644 --- a/modules/user/user.api.php +++ b/modules/user/user.api.php @@ -178,28 +178,6 @@ function hook_user_operations() { return $operations; } -/** - * The user object has been updated and changed. - * - * Use this if (probably along with 'insert') if you want to reuse some - * information from the user object. - * - * @param &$edit - * The array of form values submitted by the user. - * @param $account - * The user object on which the operation is performed. - * @param $category - * The active category of user information being edited. - */ -function hook_user_after_update(&$edit, $account, $category) { - db_insert('user_changes') - ->fields(array( - 'uid' => $account->uid, - 'changed' => time(), - )) - ->execute(); -} - /** * Retrieve a list of all user setting/information categories. * @@ -218,7 +196,36 @@ function hook_user_categories() { } /** - * The user account is being added. + * A user account is about to be created or updated. + * + * This hook is primarily intended for modules that want to store properties in + * the serialized {users}.data column, which is automatically loaded whenever a + * user account object is loaded, and the module needs to prepare the stored + * data in some way. + * The module should save its custom additions to the user object into the + * database and set the saved fields to NULL in $edit. + * + * @param &$edit + * The array of form values submitted by the user. + * @param $account + * The user object on which the operation is performed. + * @param $category + * The active category of user information being edited. + * + * @see hook_user_insert() + * @see hook_user_update() + */ +function hook_user_presave(&$edit, $account, $category) { + // Make sure that our form value 'mymodule_foo' is stored as 'mymodule_bar'. + if (isset($edit['mymodule_foo'])) { + $edit['mymodule_bar'] = $edit['mymodule_foo']; + // Inform user_save() to ignore the value of our property. + $edit['mymodule_foo'] = NULL; + } +} + +/** + * A user account was created. * * The module should save its custom additions to the user object into the * database and set the saved fields to NULL in $edit. @@ -229,6 +236,9 @@ function hook_user_categories() { * The user object on which the operation is being performed. * @param $category * The active category of user information being edited. + * + * @see hook_user_presave() + * @see hook_user_update() */ function hook_user_insert(&$edit, $account, $category) { db_insert('mytable') @@ -237,9 +247,35 @@ function hook_user_insert(&$edit, $account, $category) { 'uid' => $account->uid, )) ->execute(); + // Inform user_save() to ignore the value of our property. $edit['myfield'] = NULL; } +/** + * A user account was updated. + * + * Modules may use this hook to update their user data in a custom storage + * after a user account has been updated. + * + * @param &$edit + * The array of form values submitted by the user. + * @param $account + * The user object on which the operation is performed. + * @param $category + * The active category of user information being edited. + * + * @see hook_user_presave() + * @see hook_user_insert() + */ +function hook_user_update(&$edit, $account, $category) { + db_insert('user_changes') + ->fields(array( + 'uid' => $account->uid, + 'changed' => time(), + )) + ->execute(); +} + /** * The user just logged in. * @@ -270,75 +306,6 @@ function hook_user_logout($account) { ->execute(); } -/** - * Modify the account before it gets saved. - * - * @param &$edit - * The array of form values submitted by the user. - * @param $account - * The user object on which the operation is performed. - * @param $category - * The active category of user information being edited. - */ -function hook_user_submit(&$edit, $account, $category) { - if ($category == 'account') { - if (!empty($edit['picture_upload'])) { - $edit['picture'] = $edit['picture_upload']; - } - // Delete picture if requested, and if no replacement picture was given. - elseif (!empty($edit['picture_delete'])) { - $edit['picture'] = NULL; - } - // Remove these values so they don't end up serialized in the data field. - $edit['picture_upload'] = NULL; - $edit['picture_delete'] = NULL; - - if (isset($edit['roles'])) { - $edit['roles'] = array_filter($edit['roles']); - } - } -} - -/** - * The user account is being changed. - * - * The module should save its custom additions to the user object into the - * database and set the saved fields to NULL in $edit. - * - * @param &$edit - * The array of form values submitted by the user. - * @param $account - * The user object on which the operation is performed. - * @param $category - * The active category of user information being edited. - */ -function hook_user_update(&$edit, $account, $category) { - db_update('mytable') - ->fields(array('myfield' => $edit['myfield'])) - ->condition('uid', $account->uid) - ->execute(); - $edit['myfield'] = NULL; -} - -/** - * The user account is about to be modified. - * - * The module should validate its custom additions to the user object, - * registering errors as necessary. - * - * @param &$edit - * The array of form values submitted by the user. - * @param $account - * The user object on which the operation is being performed. - * @param $category - * The active category of user information being edited. - */ -function hook_user_validate(&$edit, $account, $category) { - if ($category == 'mymodule' && empty($edit['myfield'])) { - form_set_error('myfield', t('Myfield is required.')); - } -} - /** * The user's account information is being displayed. * diff --git a/modules/user/user.module b/modules/user/user.module index 5fcb39e93b0..1e0afc92336 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -336,8 +336,10 @@ function user_save($account, $edit = array(), $category = 'account') { if (!isset($account->is_new)) { $account->is_new = empty($account->uid); } + + user_module_invoke('presave', $edit, $account, $category); + if (is_object($account) && !$account->is_new) { - user_module_invoke('update', $edit, $account, $category); $data = unserialize(db_query('SELECT data FROM {users} WHERE uid = :uid', array(':uid' => $account->uid))->fetchField()); // Consider users edited by an administrator as logged in, if they haven't // already, so anonymous users can view the profile (if allowed). @@ -384,15 +386,10 @@ function user_save($account, $edit = array(), $category = 'account') { $edit['uid'] = $account->uid; // Save changes to the user table. $success = drupal_write_record('users', $edit, 'uid'); - if (!$success) { + if ($success === FALSE) { // The query failed - better to abort the save than risk further // data loss. - - // TODO: Fields change: I think this is a bug. If no columns in - // the user table are changed, drupal_write_record returns - // FALSE because rowCount() (rows changed) is 0. However, - // non-users data may have been changed, e.g. fields. - // return FALSE; + return FALSE; } // If the picture changed or was unset, remove the old one. This step needs @@ -448,7 +445,7 @@ function user_save($account, $edit = array(), $category = 'account') { _user_mail_notify($op, $user); } - user_module_invoke('after_update', $edit, $user, $category); + user_module_invoke('update', $edit, $user, $category); } else { // Allow 'created' to be set by the caller. @@ -463,7 +460,7 @@ function user_save($account, $edit = array(), $category = 'account') { $edit['mail'] = trim($edit['mail']); $success = drupal_write_record('users', $edit); - if (!$success) { + if ($success === FALSE) { // On a failed INSERT some other existing user's uid may be returned. // We must abort to avoid overwriting their account. return FALSE; @@ -856,40 +853,184 @@ function user_user_view($account) { } /** - * Implement hook_user_validate(). + * Helper function to add default user account fields to user registration and edit form. */ -function user_user_validate(&$edit, $account, $category) { - if ($category == 'account') { - $uid = isset($account->uid) ? $account->uid : FALSE; - // Validate the username when: new user account; or user is editing own account and can change username; or an admin user. - if (!$uid || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || user_access('administer users')) { - if ($error = user_validate_name($edit['name'])) { +function user_account_form(&$form, &$form_state) { + global $user; + + $account = $form['#user']; + $register = ($form['#user']->uid > 0 ? FALSE : TRUE); + + _user_password_dynamic_validation(); + $admin = user_access('administer users'); + + $form['#validate'][] = 'user_account_form_validate'; + + // Account information. + $form['account'] = array( + '#type' => 'fieldset', + '#title' => t('Account information'), + '#weight' => -10, + ); + // Only show name field on registration form or user can change own username. + $form['account']['name'] = array( + '#type' => 'textfield', + '#title' => t('Username'), + '#maxlength' => USERNAME_MAX_LENGTH, + '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'), + '#required' => TRUE, + '#attributes' => array('class' => array('username')), + '#default_value' => (!$register ? $account->name : ''), + '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin), + ); + + $form['account']['mail'] = array( + '#type' => 'textfield', + '#title' => t('E-mail address'), + '#maxlength' => EMAIL_MAX_LENGTH, + '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'), + '#required' => TRUE, + '#default_value' => (!$register ? $account->mail : ''), + ); + + // Display password field only for existing users or when user is allowed to + // assign a password during registration. + if (!$register) { + $form['account']['pass'] = array( + '#type' => 'password_confirm', + '#size' => 25, + '#description' => t('To change the current user password, enter the new password in both fields.'), + ); + } + elseif (!variable_get('user_email_verification', TRUE) || $admin) { + $form['account']['pass'] = array( + '#type' => 'password_confirm', + '#size' => 25, + '#description' => t('Provide a password for the new account in both fields.'), + '#required' => TRUE, + ); + } + + if ($admin) { + $status = (isset($account->status) ? $account->status : 1); + } + else { + $status = (variable_get('user_register', 1) == 1); + } + $form['account']['status'] = array( + '#type' => 'radios', + '#title' => t('Status'), + '#default_value' => $status, + '#options' => array(t('Blocked'), t('Active')), + '#access' => $admin, + ); + + $roles = user_roles(TRUE); + // The disabled checkbox subelement for the 'authenticated user' role + // must be generated separately and added to the checkboxes element, + // because of a limitation in Form API not supporting a single disabled + // checkbox within a set of checkboxes. + // @todo This should be solved more elegantly. See issue #119038. + $checkbox_authenticated = array( + '#type' => 'checkbox', + '#title' => $roles[DRUPAL_AUTHENTICATED_RID], + '#default_value' => TRUE, + '#disabled' => TRUE, + ); + unset($roles[DRUPAL_AUTHENTICATED_RID]); + $form['account']['roles'] = array( + '#type' => 'checkboxes', + '#title' => t('Roles'), + '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()), + '#options' => $roles, + '#access' => $roles && user_access('administer permissions'), + DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated, + ); + + $form['account']['notify'] = array( + '#type' => 'checkbox', + '#title' => t('Notify user of new account'), + '#access' => $register && $admin, + ); + + // Signature. + $form['signature_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Signature settings'), + '#weight' => 1, + '#access' => (!$register && variable_get('user_signatures', 0)), + ); + $form['signature_settings']['signature'] = array( + '#type' => 'textarea', + '#title' => t('Signature'), + '#default_value' => isset($account->signature) ? $account->signature : '', + '#description' => t('Your signature will be publicly displayed at the end of your comments.'), + ); + + // Picture/avatar. + $form['picture'] = array( + '#type' => 'fieldset', + '#title' => t('Picture'), + '#weight' => 1, + '#access' => (!$register && variable_get('user_pictures', 0)), + ); + $form['picture']['picture'] = array( + '#type' => 'value', + '#value' => isset($account->picture) ? $account->picture : NULL, + ); + $form['picture']['picture_current'] = array( + '#markup' => theme('user_picture', array('account' => $account)), + ); + $form['picture']['picture_delete'] = array( + '#type' => 'checkbox', + '#title' => t('Delete picture'), + '#access' => !empty($account->picture->fid), + '#description' => t('Check this box to delete your current picture.'), + ); + $form['picture']['picture_upload'] = array( + '#type' => 'file', + '#title' => t('Upload picture'), + '#size' => 48, + '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions pixels and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) . ' ' . variable_get('user_picture_guidelines', ''), + ); + $form['#validate'][] = 'user_validate_picture'; +} + +/** + * Form validation handler for user_account_form(). + */ +function user_account_form_validate($form, &$form_state) { + if ($form['#user_category'] == 'account' || $form['#user_category'] == 'register') { + $account = $form['#user']; + // Validate new or changing username. + if (isset($form_state['values']['name'])) { + if ($error = user_validate_name($form_state['values']['name'])) { form_set_error('name', $error); } - elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(name) = LOWER(:name)", 0, 1, array(':uid' => $uid, ':name' => $edit['name']))->fetchField()) { - form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name']))); + elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(name) = LOWER(:name)", 0, 1, array(':uid' => $account->uid, ':name' => $form_state['values']['name']))->fetchField()) { + form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name']))); } } // Validate the e-mail address, and check if it is taken by an existing user. - if ($error = user_validate_mail($edit['mail'])) { + if ($error = user_validate_mail($form_state['values']['mail'])) { form_set_error('mail', $error); } - elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(mail) = LOWER(:mail)", 0, 1, array(':uid' => $uid, ':mail' => $edit['mail']))->fetchField()) { + elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(mail) = LOWER(:mail)", 0, 1, array(':uid' => $account->uid, ':mail' => $form_state['values']['mail']))->fetchField()) { // Format error message dependent on whether the user is logged in or not. if ($GLOBALS['user']->uid) { - form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $edit['mail']))); + form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail']))); } else { - form_set_error('mail', t('The e-mail address %email is already registered. Have you forgotten your password?', array('%email' => $edit['mail'], '@password' => url('user/password')))); + form_set_error('mail', t('The e-mail address %email is already registered. Have you forgotten your password?', array('%email' => $form_state['values']['mail'], '@password' => url('user/password')))); } } // Make sure the signature isn't longer than the size of the database field. // Signatures are disabled by default, so make sure it exists first. - if (isset($edit['signature'])) { + if (isset($form_state['values']['signature'])) { $user_schema = drupal_get_schema('users'); - if (strlen($edit['signature']) > $user_schema['fields']['signature']['length']) { + if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) { form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length']))); } } @@ -897,10 +1038,10 @@ function user_user_validate(&$edit, $account, $category) { } /** - * Implement hook_user_submit(). + * Implement hook_user_presave(). */ -function user_user_submit(&$edit, $account, $category) { - if ($category == 'account') { +function user_user_presave(&$edit, $account, $category) { + if ($category == 'account' || $category == 'register') { if (!empty($edit['picture_upload'])) { $edit['picture'] = $edit['picture_upload']; } @@ -912,6 +1053,7 @@ function user_user_submit(&$edit, $account, $category) { $edit['picture_upload'] = NULL; $edit['picture_delete'] = NULL; + // Prepare user roles. if (isset($edit['roles'])) { $edit['roles'] = array_filter($edit['roles']); } @@ -1235,7 +1377,7 @@ function user_menu() { $items['user/register'] = array( 'title' => 'Create new account', 'page callback' => 'drupal_get_form', - 'page arguments' => array('user_register'), + 'page arguments' => array('user_register_form'), 'access callback' => 'user_register_access', 'type' => MENU_LOCAL_TASK, ); @@ -1376,8 +1518,10 @@ function user_menu() { $items['user/%user/edit'] = array( 'title' => 'Edit', - 'page callback' => 'user_edit', - 'page arguments' => array(1), + 'title callback' => 'user_page_title', + 'title arguments' => array(1), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('user_profile_form', 1), 'access callback' => 'user_edit_access', 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, @@ -1397,8 +1541,8 @@ function user_menu() { $items['user/%user_category/edit/' . $category['name']] = array( 'title callback' => 'check_plain', 'title arguments' => array($category['title']), - 'page callback' => 'user_edit', - 'page arguments' => array(1, 3), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('user_profile_form', 1, 3), 'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access', 'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1), 'type' => MENU_LOCAL_TASK, @@ -1760,7 +1904,7 @@ function user_external_login_register($name, $module) { 'status' => 1, 'access' => REQUEST_TIME ); - $account = user_save('', $userinfo); + $account = user_save(drupal_anonymous_user(), $userinfo); // Terminate if an error occurred during user_save(). if (!$account) { drupal_set_message(t("Error saving user account."), 'error'); @@ -1794,142 +1938,6 @@ function user_pass_rehash($password, $timestamp, $login) { return md5($timestamp . $password . $login); } -function user_edit_form(&$form, &$form_state) { - global $user; - - $account = $form['#user']; - $register = ($form['#user']->uid > 0 ? FALSE : TRUE); - - _user_password_dynamic_validation(); - $admin = user_access('administer users'); - - // Account information: - $form['account'] = array( - '#type' => 'fieldset', - '#title' => t('Account information'), - '#weight' => -10, - ); - // Only show name field when: registration page; or user is editing own - // account and can change username; or an admin user. - if ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin) { - $form['account']['name'] = array( - '#type' => 'textfield', - '#title' => t('Username'), - '#maxlength' => USERNAME_MAX_LENGTH, - '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'), - '#required' => TRUE, - '#attributes' => array('class' => array('username')), - ); - if (!$register) { - $form['account']['name']['#default_value'] = $account->name; - } - } - $form['account']['mail'] = array( - '#type' => 'textfield', - '#title' => t('E-mail address'), - '#maxlength' => EMAIL_MAX_LENGTH, - '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'), - '#required' => TRUE, - ); - if (!$register) { - $form['account']['mail']['#default_value'] = $account->mail; - } - if (!$register) { - $form['account']['pass'] = array( - '#type' => 'password_confirm', - '#description' => t('To change the current user password, enter the new password in both fields.'), - '#size' => 25, - ); - } - elseif (!variable_get('user_email_verification', TRUE) || $admin) { - $form['account']['pass'] = array( - '#type' => 'password_confirm', - '#description' => t('Provide a password for the new account in both fields.'), - '#required' => TRUE, - '#size' => 25, - ); - } - if ($admin) { - $form['account']['status'] = array( - '#type' => 'radios', - '#title' => t('Status'), - '#default_value' => isset($account->status) ? $account->status : 1, - '#options' => array(t('Blocked'), t('Active')) - ); - } - if (user_access('administer permissions')) { - $roles = user_roles(TRUE); - - // The disabled checkbox subelement for the 'authenticated user' role - // must be generated separately and added to the checkboxes element, - // because of a limitation in D6 FormAPI not supporting a single disabled - // checkbox within a set of checkboxes. - // TODO: This should be solved more elegantly. See issue #119038. - $checkbox_authenticated = array( - '#type' => 'checkbox', - '#title' => $roles[DRUPAL_AUTHENTICATED_RID], - '#default_value' => TRUE, - '#disabled' => TRUE, - ); - - unset($roles[DRUPAL_AUTHENTICATED_RID]); - if ($roles) { - $form['account']['roles'] = array( - '#type' => 'checkboxes', - '#title' => t('Roles'), - '#default_value' => isset($account->roles) ? array_keys($account->roles) : array(), - '#options' => $roles, - DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated, - ); - } - } - - // Signature: - if (variable_get('user_signatures', 0) && module_exists('comment') && !$register) { - $form['signature_settings'] = array( - '#type' => 'fieldset', - '#title' => t('Signature settings'), - '#weight' => 1, - ); - $form['signature_settings']['signature'] = array( - '#type' => 'textarea', - '#title' => t('Signature'), - '#default_value' => isset($account->signature) ? $account->signature : '', - '#description' => t('Your signature will be publicly displayed at the end of your comments.'), - ); - } - - // Picture/avatar: - if (variable_get('user_pictures', 0) && !$register) { - $form['picture'] = array( - '#type' => 'fieldset', - '#title' => t('Picture'), - '#weight' => 1, - ); - $form['picture']['picture'] = array( - '#type' => 'value', - '#value' => isset($account->picture) ? $account->picture : NULL, - ); - $form['picture']['picture_current'] = array( - '#markup' => theme('user_picture', array('account' => $account)), - ); - $form['picture']['picture_delete'] = array( - '#type' => 'checkbox', - '#title' => t('Delete picture'), - '#access' => !empty($account->picture->fid), - '#description' => t('Check this box to delete your current picture.'), - ); - $form['picture']['picture_upload'] = array( - '#type' => 'file', - '#title' => t('Upload picture'), - '#size' => 48, - '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions pixels and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) . ' ' . variable_get('user_picture_guidelines', ''), - ); - $form['#validate'][] = 'user_profile_form_validate'; - $form['#validate'][] = 'user_validate_picture'; - } -} - /** * Cancel a user account. * @@ -3034,104 +3042,15 @@ function user_block_user_action(&$object, $context = array()) { } /** - * Submit handler for the user registration form. - * - * This function is shared by the installation form and the normal registration form, - * which is why it can't be in the user.pages.inc file. - */ -function user_register_submit($form, &$form_state) { - global $base_url; - $admin = user_access('administer users'); - - $mail = $form_state['values']['mail']; - $name = $form_state['values']['name']; - if (!variable_get('user_email_verification', TRUE) || $admin) { - $pass = $form_state['values']['pass']; - } - else { - $pass = user_password(); - }; - $notify = isset($form_state['values']['notify']) ? $form_state['values']['notify'] : NULL; - $from = variable_get('site_mail', ini_get('sendmail_from')); - if (isset($form_state['values']['roles'])) { - // Remove unset roles. - $roles = array_filter($form_state['values']['roles']); - } - else { - $roles = array(); - } - - if (!$admin && array_intersect(array_keys($form_state['values']), array('uid', 'roles', 'init', 'session', 'status'))) { - watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING); - $form_state['redirect'] = 'user/register'; - return; - } - // The unset below is needed to prevent these form values from being saved as - // user data. - unset($form_state['values']['form_token'], $form_state['values']['submit'], $form_state['values']['op'], $form_state['values']['notify'], $form_state['values']['form_id'], $form_state['values']['affiliates'], $form_state['values']['destination'], $form_state['values']['form_build_id']); - - $merge_data = array('pass' => $pass, 'init' => $mail, 'roles' => $roles); - if (!$admin) { - // Set the user's status because it was not displayed in the form. - $merge_data['status'] = variable_get('user_register', 1) == 1; - } - $account = user_save('', array_merge($form_state['values'], $merge_data)); - // Terminate if an error occurred during user_save(). - if (!$account) { - drupal_set_message(t("Error saving user account."), 'error'); - $form_state['redirect'] = ''; - return; - } - $form_state['user'] = $account; - - watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit')); - - // Add plain text password into user account to generate mail tokens. - $account->password = $pass; - if ($admin && !$notify) { - drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name))); - } - elseif (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) { - // No e-mail verification is required, create new user account, and login - // user immediately. - _user_mail_notify('register_no_approval_required', $account); - $form_state['uid'] = $account->uid; - user_login_submit(array(), $form_state); - drupal_set_message(t('Registration successful. You are now logged in.')); - $form_state['redirect'] = ''; - return; - } - elseif ($account->status || $notify) { - // Create new user account, no administrator approval required. - $op = $notify ? 'register_admin_created' : 'register_no_approval_required'; - _user_mail_notify($op, $account); - if ($notify) { - drupal_set_message(t('Password and further instructions have been e-mailed to the new user %name.', array('@url' => url("user/$account->uid"), '%name' => $account->name))); - } - else { - drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.')); - $form_state['redirect'] = ''; - return; - } - } - else { - // Create new user account, administrator approval required. - _user_mail_notify('register_pending_approval', $account); - drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, a welcome message with further instructions has been sent to your e-mail address.')); - $form_state['redirect'] = ''; - return; - - } -} - -/** - * Form builder; The user registration form. + * Form builder; the user registration form. * * @ingroup forms - * @see user_register_validate() + * @see user_account_form() + * @see user_account_form_validate() + * @see user_account_form_submit() * @see user_register_submit() */ -function user_register($form, &$form_state) { +function user_register_form($form, &$form_state) { global $user; $admin = user_access('administer users'); @@ -3145,12 +3064,9 @@ function user_register($form, &$form_state) { $form['#user_category'] = 'register'; // Start with the default user account fields. - user_edit_form($form, $form_state); + user_account_form($form, $form_state); + if ($admin) { - $form['account']['notify'] = array( - '#type' => 'checkbox', - '#title' => t('Notify user of new account') - ); // Redirect back to page which initiated the create request; // usually admin/people/create. $form_state['redirect'] = $_GET['q']; @@ -3164,14 +3080,89 @@ function user_register($form, &$form_state) { $form['account']['#type'] = 'markup'; } - $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30); - $form['#validate'][] = 'user_register_validate'; + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Create new account'), + '#weight' => 30, + ); + + // Add the final user registration form submit handler. + $form['#submit'][] = 'user_register_submit'; return $form; } -function user_register_validate($form, &$form_state) { - user_module_invoke('validate', $form_state['values'], $form_state['values'], 'account'); +/** + * Submit handler for the user registration form. + * + * This function is shared by the installation form and the normal registration form, + * which is why it can't be in the user.pages.inc file. + * + * @see user_register_form() + */ +function user_register_submit($form, &$form_state) { + $admin = user_access('administer users'); + + if (!variable_get('user_email_verification', TRUE) || $admin) { + $pass = $form_state['values']['pass']; + } + else { + $pass = user_password(); + } + $notify = !empty($form_state['values']['notify']); + + // The unset below is needed to prevent these form values from being saved as + // user data. + unset($form_state['values']['form_token'], $form_state['values']['submit'], $form_state['values']['op'], $form_state['values']['notify'], $form_state['values']['form_id'], $form_state['values']['affiliates'], $form_state['values']['destination'], $form_state['values']['form_build_id']); + + $form_state['values']['pass'] = $pass; + $form_state['values']['init'] = $form_state['values']['mail']; + + $account = $form['#user']; + $account = user_save($account, $form_state['values']); + // Terminate if an error occurred during user_save(). + if (!$account) { + drupal_set_message(t("Error saving user account."), 'error'); + $form_state['redirect'] = ''; + return; + } + $form_state['user'] = $account; + + watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit')); + + // Add plain text password into user account to generate mail tokens. + $account->password = $pass; + + // New administrative account without notification. + if ($admin && !$notify) { + drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name))); + } + // No e-mail verification required; login user immediately. + elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) { + _user_mail_notify('register_no_approval_required', $account); + $form_state['uid'] = $account->uid; + user_login_submit(array(), $form_state); + drupal_set_message(t('Registration successful. You are now logged in.')); + $form_state['redirect'] = ''; + } + // No administrator approval required. + elseif ($account->status || $notify) { + $op = $notify ? 'register_admin_created' : 'register_no_approval_required'; + _user_mail_notify($op, $account); + if ($notify) { + drupal_set_message(t('Password and further instructions have been e-mailed to the new user %name.', array('@url' => url("user/$account->uid"), '%name' => $account->name))); + } + else { + drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.')); + $form_state['redirect'] = ''; + } + } + // Administrator approval required. + else { + _user_mail_notify('register_pending_approval', $account); + drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, a welcome message with further instructions has been sent to your e-mail address.')); + $form_state['redirect'] = ''; + } } /** diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 8bbb41a5314..fc88e395a90 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -209,18 +209,13 @@ function template_preprocess_user_profile_category(&$variables) { } } -/** - * Menu callback; Present the form to edit a given user or profile category. - */ -function user_edit($account, $category = 'account') { - drupal_set_title($account->name); - return drupal_get_form('user_profile_form', $account, $category); -} - /** * Form builder; edit a user account or one of their profile categories. * * @ingroup forms + * @see user_account_form() + * @see user_account_form_validate() + * @see user_account_form_submit() * @see user_profile_form_validate() * @see user_profile_form_submit() * @see user_cancel_confirm_form_submit() @@ -232,23 +227,31 @@ function user_profile_form($form, &$form_state, $account, $category = 'account') $form['#user_category'] = $category; if ($category == 'account') { - user_edit_form($form, $form_state); + user_account_form($form, $form_state); } // Attach field widgets. field_attach_form('user', $account, $form, $form_state); - $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 30); - - if (($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')) { + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#weight' => 30, + ); + if ($category == 'account') { $form['cancel'] = array( '#type' => 'submit', '#value' => t('Cancel account'), '#weight' => 31, '#submit' => array('user_edit_cancel_submit'), + '#access' => ($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users'), ); } + $form['#validate'][] = 'user_profile_form_validate'; + // Add the final user profile form submit handler. + $form['#submit'][] = 'user_profile_form_submit'; + return $form; } @@ -258,14 +261,6 @@ function user_profile_form($form, &$form_state, $account, $category = 'account') function user_profile_form_validate($form, &$form_state) { $edit = (object)$form_state['values']; field_attach_form_validate('user', $edit, $form, $form_state); - $edit = (array)$edit; - user_module_invoke('validate', $edit, $form['#user'], $form['#user_category']); - // Validate input to ensure that non-privileged users can't alter protected data. - if ((!user_access('administer users') && array_intersect(array_keys($edit), array('uid', 'init', 'session'))) || (!user_access('administer permissions') && isset($form_state['values']['roles']))) { - watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING); - // set this to a value type field - form_set_error('category', t('Detected malicious attempt to alter protected user fields.')); - } } /** @@ -279,7 +274,7 @@ function user_profile_form_submit($form, &$form_state) { $edit = (object)$form_state['values']; field_attach_submit('user', $edit, $form, $form_state); $edit = (array)$edit; - user_module_invoke('submit', $edit, $account, $category); + user_save($account, $edit, $category); // Clear the page cache because pages can contain usernames and/or profile information: diff --git a/modules/user/user.test b/modules/user/user.test index 14b7c407c2b..186a45fedbd 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -1224,7 +1224,7 @@ class UserSaveTestCase extends DrupalWebTestCase { 'pass' => user_password(), 'status' => 1, ); - $user_by_return = user_save('', $user); + $user_by_return = user_save(drupal_anonymous_user(), $user); $this->assertTrue($user_by_return, t('Loading user by return of user_save().')); // Test if created user exists.