diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index b1d3b50..fc3460b 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1612,7 +1612,7 @@ function install_configure_form($form, &$form_state, &$install_state) {
drupal_add_library('system', 'drupal.timezone');
// We add these strings as settings because JavaScript translation does not
// work during installation.
- drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail'))), 'setting');
+ drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-user-mail'))), 'setting');
// Add JS to show / hide the 'Email administrator about site updates' elements
drupal_add_js('jQuery(function () { Drupal.hideEmailAdministratorCheckbox() });', 'inline');
@@ -1879,7 +1879,8 @@ function _install_configure_form($form, &$form_state, &$install_state) {
);
$form['admin_account']['account']['#tree'] = TRUE;
- $form['admin_account']['account']['name'] = array('#type' => 'textfield',
+ $form['admin_account']['account']['username'] = array(
+ '#type' => 'textfield',
'#title' => st('Username'),
'#maxlength' => USERNAME_MAX_LENGTH,
'#description' => st('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
@@ -1893,6 +1894,7 @@ function _install_configure_form($form, &$form_state, &$install_state) {
'#title' => st('E-mail address'),
'#required' => TRUE,
'#weight' => -5,
+ '#parents' => array('user', 'mail'),
);
$form['admin_account']['account']['pass'] = array(
'#type' => 'password_confirm',
@@ -1960,8 +1962,8 @@ function _install_configure_form($form, &$form_state, &$install_state) {
* @see install_configure_form_submit()
*/
function install_configure_form_validate($form, &$form_state) {
- if ($error = user_validate_name($form_state['values']['account']['name'])) {
- form_error($form['admin_account']['account']['name'], $error);
+ if ($error = account_validate_name($form_state['values']['account']['username'])) {
+ form_error($form['admin_account']['account']['username'], $error);
}
}
@@ -1971,8 +1973,6 @@ function install_configure_form_validate($form, &$form_state) {
* @see install_configure_form_validate()
*/
function install_configure_form_submit($form, &$form_state) {
- global $user;
-
config('system.site')
->set('name', $form_state['values']['site_name'])
->set('mail', $form_state['values']['site_mail'])
@@ -1988,20 +1988,30 @@ function install_configure_form_submit($form, &$form_state) {
// Add the site maintenance account's email address to the list of
// addresses to be notified when updates are available, if selected.
if ($form_state['values']['update_status_module'][2]) {
- config('update.settings')->set('notification.emails', array($form_state['values']['account']['mail']))->save();
+ config('update.settings')->set('notification.emails', array($form_state['values']['user']['mail']))->save();
}
}
// We precreated user 1 with placeholder values. Let's save the real values.
- $account = user_load(1);
- $account->init = $account->mail = $form_state['values']['account']['mail'];
- $account->roles = !empty($account->roles) ? $account->roles : array();
- $account->status = 1;
- $account->timezone = $form_state['values']['date_default_timezone'];
- $account->pass = $form_state['values']['account']['pass'];
- $account->name = $form_state['values']['account']['name'];
+ $user = user_load(1);
+ $user->mail = $form_state['values']['user']['mail'];
+ $user->roles = !empty($account->roles) ? $account->roles : array();
+ $user->status = 1;
+ $user->timezone = $form_state['values']['date_default_timezone'];
+ $user->save();
+
+ $account = entity_create('account', array(
+ 'uid' => $user->id(),
+ 'username' => $form_state['values']['account']['username'],
+ 'pass' => $form_state['values']['account']['pass'],
+ 'created' => REQUEST_TIME,
+ 'init' => $form_state['values']['user']['mail'],
+ ));
$account->save();
+
// Load global $user and perform final login tasks.
+ unset($user);
+ global $user;
$user = user_load(1);
user_login_finalize();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 400d885..3e6f842 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -388,27 +388,33 @@ protected function drupalCreateUser(array $permissions = array()) {
}
}
- // Create a user assigned to that role.
- $edit = array();
- $edit['name'] = $this->randomName();
- $edit['mail'] = $edit['name'] . '@example.com';
- $edit['pass'] = user_password();
- $edit['status'] = 1;
+ // Create a user and account assigned to that role.
+ $account_values = array();
+ $account_values['username'] = $this->randomName();
+ $account_values['pass'] = account_password();
+
+ $user_values = array();
+ $user_values['mail'] = $account_values['username'] . '@example.com';
+ $user_values['status'] = 1;
if ($rid) {
- $edit['roles'] = array($rid => $rid);
+ $user_values['roles'] = array($rid => $rid);
}
+ $user = entity_create('user', $user_values);
+ $user->save();
- $account = entity_create('user', $edit);
+ $account_values['uid'] = $user->uid;
+ $account = entity_create('account', $account_values);
$account->save();
- $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
+ $this->assertTrue(!empty($account->uid), t('User created with name %username and pass %pass', array('%username' => $account_values['username'], '%pass' => $account_values['pass'])), t('User login'));
if (empty($account->uid)) {
return FALSE;
}
// Add the raw password so that we can log in as this user.
- $account->pass_raw = $edit['pass'];
- return $account;
+ $user->username = $account_values['username'];
+ $user->pass_raw = $account_values['pass'];
+ return $user;
}
/**
@@ -531,14 +537,14 @@ protected function drupalLogin($user) {
}
$edit = array(
- 'name' => $user->name,
+ 'username' => $user->username,
'pass' => $user->pass_raw
);
$this->drupalPost('user', $edit, t('Log in'));
// If a "log out" link appears on the page, it is almost certainly because
// the login was successful.
- $pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $user->name)), t('User login'));
+ $pass = $this->assertLink(t('Log out'), 0, t('User %username successfully logged in.', array('%username' => $user->username)), t('User login'));
if ($pass) {
$this->loggedInUser = $user;
@@ -562,7 +568,7 @@ protected function drupalLogout() {
// screen.
$this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
$this->assertResponse(200, t('User was logged out.'));
- $pass = $this->assertField('name', t('Username field found.'), t('Logout'));
+ $pass = $this->assertField('username', t('Username field found.'), t('Logout'));
$pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
if ($pass) {
@@ -656,13 +662,15 @@ protected function setUp() {
'site_name' => 'Drupal',
'site_mail' => 'simpletest@example.com',
'account' => array(
- 'name' => $this->root_user->name,
- 'mail' => $this->root_user->mail,
+ 'username' => $this->root_user->name,
'pass' => array(
'pass1' => $this->root_user->pass_raw,
'pass2' => $this->root_user->pass_raw,
),
),
+ 'user' => array(
+ 'mail' => $this->root_user->mail,
+ ),
// form_type_checkboxes_value() requires NULL instead of FALSE values
// for programmatic form submissions to disable a checkbox.
'update_status_module' => array(
diff --git a/core/modules/user/lib/Drupal/user/Account.php b/core/modules/user/lib/Drupal/user/Account.php
new file mode 100644
index 0000000..910864c
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Account.php
@@ -0,0 +1,72 @@
+aid;
+ }
+}
\ No newline at end of file
diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
index b79e983..547eb80 100644
--- a/core/modules/user/lib/Drupal/user/AccountFormController.php
+++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
@@ -32,31 +32,19 @@ public function form(array $form, array &$form_state, EntityInterface $account)
'#weight' => -10,
);
- // Only show name field on registration form or user can change own username.
- $form['account']['name'] = array(
+ // Only show username field on registration form or user can change own username.
+ $form['account']['username'] = 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'), 'autocomplete' => 'off'),
- '#default_value' => (!$register ? $account->name : ''),
+ '#default_value' => (!$register ? $account->username : ''),
'#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
'#weight' => -10,
);
- // The mail field is NOT required if account originally had no mail set
- // and the user performing the edit has 'administer users' permission.
- // This allows users without e-mail address to be edited and deleted.
- $form['account']['mail'] = array(
- '#type' => 'email',
- '#title' => t('E-mail address'),
- '#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' => !(empty($account->mail) && user_access('administer users')),
- '#default_value' => (!$register ? $account->mail : ''),
- '#attributes' => array('autocomplete' => 'off'),
- );
-
// Display password field only for existing users or when user is allowed to
// assign a password during registration.
if (!$register) {
@@ -75,10 +63,9 @@ public function form(array $form, array &$form_state, EntityInterface $account)
// The user may only change their own password without their current
// password if they logged in via a one-time login link.
if (!$pass_reset) {
- $protected_values['mail'] = $form['account']['mail']['#title'];
$protected_values['pass'] = t('Password');
$request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
- $current_pass_description = t('Required if you want to change the %mail or %pass below. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new));
+ $current_pass_description = t('Required if you want to change the %pass below. !request_new.', array('%pass' => $protected_values['pass'], '!request_new' => $request_new));
}
// The user must enter their current password to change to a new one.
@@ -118,205 +105,43 @@ public function form(array $form, array &$form_state, EntityInterface $account)
$status = $register ? $config->get('register') == USER_REGISTER_VISITORS : $account->status;
}
- $form['account']['status'] = array(
- '#type' => 'radios',
- '#title' => t('Status'),
- '#default_value' => $status,
- '#options' => array(t('Blocked'), t('Active')),
- '#access' => $admin,
- );
-
- $roles = array_map('check_plain', 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 && $config->get('signatures')),
- );
-
- $form['signature_settings']['signature'] = array(
- '#type' => 'text_format',
- '#title' => t('Signature'),
- '#default_value' => isset($account->signature) ? $account->signature : '',
- '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
- '#format' => isset($account->signature_format) ? $account->signature_format : NULL,
- );
-
- // 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. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')),
- );
-
- $form['#validate'][] = 'user_validate_picture';
-
- $user_preferred_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account);
-
- $user_preferred_admin_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account, 'admin');
-
- // Is default the interface language?
- include_once DRUPAL_ROOT . '/core/includes/language.inc';
- $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_SELECTED;
- $form['language'] = array(
- '#type' => language_multilingual() ? 'fieldset' : 'container',
- '#title' => t('Language settings'),
- // Display language selector when either creating a user on the admin
- // interface or editing a user account.
- '#access' => !$register || user_access('administer users'),
- );
-
- $form['language']['preferred_langcode'] = array(
- '#type' => 'language_select',
- '#title' => t('Site language'),
- '#languages' => LANGUAGE_CONFIGURABLE,
- '#default_value' => $user_preferred_langcode,
- '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."),
- );
-
- $form['language']['preferred_admin_langcode'] = array(
- '#type' => 'language_select',
- '#title' => t('Administration pages language'),
- '#languages' => LANGUAGE_CONFIGURABLE,
- '#default_value' => $user_preferred_admin_langcode,
- '#access' => user_access('access administration pages', $account),
- );
-
- // User entities contain both a langcode property (for identifying the
- // language of the entity data) and a preferred_langcode property (see
- // above). Rather than provide a UI forcing the user to choose both
- // separately, assume that the user profile data is in the user's preferred
- // language. This element provides that synchronization. For use-cases where
- // this synchronization is not desired, a module can alter or remove this
- // element.
- $form['language']['langcode'] = array(
- '#type' => 'value',
- '#value_callback' => '_user_language_selector_langcode_value',
- // For the synchronization to work, this element must have a larger weight
- // than the preferred_langcode element. Set a large weight here in case
- // a module alters the weight of the other element.
- '#weight' => 100,
- );
-
return parent::form($form, $form_state, $account);
}
/**
- * Overrides Drupal\Core\Entity\EntityFormController::submit().
+ * Overrides Drupal\Core\Entity\EntityFormController::validate().
*/
public function validate(array $form, array &$form_state) {
parent::validate($form, $form_state);
$account = $this->getEntity($form_state);
// 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);
+ if (isset($form_state['values']['username'])) {
+ if ($error = account_validate_name($form_state['values']['username'])) {
+ form_set_error('username', $error);
}
// Cast the user ID as an integer. It might have been set to NULL, which
// could lead to unexpected results.
else {
- $name_taken = (bool) db_select('users')
- ->fields('users', array('uid'))
+ $name_taken = (bool) db_select('account')
+ ->fields('account', array('uid'))
->condition('uid', (int) $account->uid, '<>')
- ->condition('name', db_like($form_state['values']['name']), 'LIKE')
+ ->condition('username', db_like($form_state['values']['username']), 'LIKE')
->range(0, 1)
->execute()
->fetchField();
if ($name_taken) {
- form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
+ form_set_error('username', t('The username %username is already taken.', array('%username' => $form_state['values']['username'])));
}
}
}
-
- $mail = $form_state['values']['mail'];
-
- if (!empty($mail)) {
- $mail_taken = (bool) db_select('users')
- ->fields('users', array('uid'))
- ->condition('uid', (int) $account->uid, '<>')
- ->condition('mail', db_like($mail), 'LIKE')
- ->range(0, 1)
- ->execute()
- ->fetchField();
-
- if ($mail_taken) {
- // 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' => $mail)));
- }
- else {
- form_set_error('mail', t('The e-mail address %email is already registered. Have you forgotten your password?', array('%email' => $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($form_state['values']['signature'])) {
- // Move text format for user signature into 'signature_format'.
- $form_state['values']['signature_format'] = $form_state['values']['signature']['format'];
- // Move text value for user signature into 'signature'.
- $form_state['values']['signature'] = $form_state['values']['signature']['value'];
-
- $user_schema = drupal_get_schema('users');
- 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'])));
- }
- }
}
+
}
diff --git a/core/modules/user/lib/Drupal/user/AccountStorageController.php b/core/modules/user/lib/Drupal/user/AccountStorageController.php
new file mode 100644
index 0000000..86f797b
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/AccountStorageController.php
@@ -0,0 +1,86 @@
+isNew() || (!empty($entity->pass) && $entity->pass != $entity->original->pass)) {
+ // Allow alternate password hashing schemes.
+ require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/includes/password.inc');
+ $entity->pass = user_hash_password(trim($entity->pass));
+ // Abort if the hashing failed and returned FALSE.
+ if (!$entity->pass) {
+ throw new EntityMalformedException('The entity does not have a password.');
+ }
+ }
+
+ if (!$entity->isNew()) {
+ // If the password is empty, that means it was not changed, so use the
+ // original password.
+ if (empty($entity->pass)) {
+ $entity->pass = $entity->original->pass;
+ }
+ }
+
+ if (empty($entity->uid)) {
+ // Create a User for this Account.
+ $user = entity_create('user', array(
+ 'mail' => $entity->init,
+ 'created' => REQUEST_TIME,
+ 'status' => 1,
+ ));
+ $user->save();
+ $entity->uid = $user->uid;
+ }
+ }
+
+ /**
+ * Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
+ */
+ protected function postSave(EntityInterface $entity, $update) {
+ if ($update) {
+ // If the username was changed and the user entity nice name was set as
+ // the username, update it.
+ if ($entity->original->username != $entity->username &&
+ $entity->original->username == $user->name) {
+ $user = entity_load('user', $entity->uid);
+ $user->name = $entity->username;
+ $user->save();
+ }
+
+ // If the password has been changed, delete all open sessions for the
+ // user and recreate the current one.
+ if ($entity->pass != $entity->original->pass) {
+ drupal_session_destroy_uid($entity->uid);
+ if ($entity->uid == $GLOBALS['user']->uid) {
+ drupal_session_regenerate();
+ }
+ }
+ }
+ else {
+ $user = entity_load('user', $entity->uid);
+ $user->name = $entity->username;
+ $user->save();
+ }
+ }
+}
diff --git a/core/modules/user/lib/Drupal/user/RegisterFormController.php b/core/modules/user/lib/Drupal/user/RegisterFormController.php
index 6e4a260..4cd8b74 100644
--- a/core/modules/user/lib/Drupal/user/RegisterFormController.php
+++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php
@@ -20,6 +20,8 @@ class RegisterFormController extends AccountFormController {
public function form(array $form, array &$form_state, EntityInterface $account) {
global $user;
+ $language_interface = language(LANGUAGE_TYPE_INTERFACE);
+ $register = empty($account->uid);
$admin = user_access('administer users');
// Pass access information to the submit handler. Running an access check
@@ -41,6 +43,15 @@ public function form(array $form, array &$form_state, EntityInterface $account)
// Start with the default user account fields.
$form = parent::form($form, $form_state, $account);
+ $form['account']['init'] = array(
+ '#type' => 'email',
+ '#title' => t('E-mail address'),
+ '#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' => !(empty($account->init) && user_access('administer users')),
+ '#default_value' => (!$register ? $account->init : ''),
+ '#attributes' => array('autocomplete' => 'off'),
+ );
+
// Attach field widgets, and hide the ones where the 'user_register_form'
// setting is not on.
field_attach_form('user', $account, $form, $form_state);
@@ -78,14 +89,13 @@ public function submit(array $form, array &$form_state) {
$pass = $form_state['values']['pass'];
}
else {
- $pass = user_password();
+ $pass = account_password();
}
// Remove unneeded values.
form_state_values_clean($form_state);
$form_state['values']['pass'] = $pass;
- $form_state['values']['init'] = $form_state['values']['mail'];
parent::submit($form, $form_state);
}
@@ -101,6 +111,10 @@ public function save(array $form, array &$form_state) {
$account->save();
+ // Get our user entity and store our pass against it.
+ $user = user_load($account->uid);
+ $user->pass = $account->pass;
+
// Terminate if an error occurred while saving the account.
if ($status =! SAVED_NEW) {
drupal_set_message(t("Error saving user account."), 'error');
@@ -110,7 +124,7 @@ public function save(array $form, array &$form_state) {
$form_state['user'] = $account;
$form_state['values']['uid'] = $account->uid;
- 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'));
+ watchdog('user', 'New user: %username (%email).', array('%name' => $form_state['values']['username'], '%email' => $form_state['values']['init']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
// Add plain text password into user account to generate mail tokens.
$account->password = $pass;
@@ -118,11 +132,11 @@ public function save(array $form, array &$form_state) {
// New administrative account without notification.
$uri = $account->uri();
if ($admin && !$notify) {
- drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ drupal_set_message(t('Created a new user account for %username. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%username' => $account->username)));
}
// No e-mail verification required; log in user immediately.
elseif (!$admin && !config('user.settings')->get('verify_mail') && $account->status) {
- _user_mail_notify('register_no_approval_required', $account);
+ _user_mail_notify('register_no_approval_required', $user);
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
drupal_set_message(t('Registration successful. You are now logged in.'));
@@ -130,14 +144,14 @@ public function save(array $form, array &$form_state) {
}
// No administrator approval required.
elseif ($account->status || $notify) {
- if (empty($account->mail) && $notify) {
- drupal_set_message(t('The new user %name was created without an email address, so no welcome message was sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ if (empty($account->init) && $notify) {
+ drupal_set_message(t('The new user %username was created without an email address, so no welcome message was sent.', array('@url' => url($uri['path'], $uri['options']), '%username' => $account->username)));
}
else {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
- _user_mail_notify($op, $account);
+ _user_mail_notify($op, $user);
if ($notify) {
- drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %username.', array('@url' => url($uri['path'], $uri['options']), '%username' => $account->username)));
}
else {
drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
@@ -147,7 +161,7 @@ public function save(array $form, array &$form_state) {
}
// Administrator approval required.
else {
- _user_mail_notify('register_pending_approval', $account);
+ _user_mail_notify('register_pending_approval', $user);
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/core/modules/user/lib/Drupal/user/SettingsFormController.php b/core/modules/user/lib/Drupal/user/SettingsFormController.php
new file mode 100644
index 0000000..c65d5c4
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/SettingsFormController.php
@@ -0,0 +1,62 @@
+getEntity($form_state);
+
+ // @todo Actually the cancel action can be assimilated to the delete one: we
+ // should alter it instead of providing a new one.
+ unset($element['delete']);
+
+ $element['cancel'] = array(
+ '#type' => 'submit',
+ '#value' => t('Cancel account'),
+ '#submit' => array('user_edit_cancel_submit'),
+ '#access' => $account->uid > 1 && (($account->uid == $GLOBALS['user']->uid && user_access('cancel account')) || user_access('administer users')),
+ );
+
+ return $element;
+ }
+
+ /**
+ * Overrides Drupal\Core\Entity\EntityFormController::submit().
+ */
+ public function submit(array $form, array &$form_state) {
+ // @todo Consider moving this into the parent method.
+ // Remove unneeded values.
+ form_state_values_clean($form_state);
+ parent::submit($form, $form_state);
+ }
+
+ /**
+ * Overrides Drupal\Core\Entity\EntityFormController::save().
+ */
+ public function save(array $form, array &$form_state) {
+ $account = $this->getEntity($form_state);
+ $account->save();
+ $form_state['values']['uid'] = $account->id();
+
+ // Clear the page cache because pages can contain usernames and/or profile
+ // information:
+ cache_invalidate(array('content' => TRUE));
+
+ drupal_set_message(t('The changes have been saved.'));
+ }
+}
diff --git a/core/modules/user/lib/Drupal/user/User.php b/core/modules/user/lib/Drupal/user/User.php
index 2217588..63178ba 100644
--- a/core/modules/user/lib/Drupal/user/User.php
+++ b/core/modules/user/lib/Drupal/user/User.php
@@ -36,13 +36,6 @@ class User extends Entity {
public $name = '';
/**
- * The user's password (hashed).
- *
- * @var string
- */
- public $pass;
-
- /**
* The user's email address.
*
* @var string
@@ -136,13 +129,6 @@ class User extends Entity {
public $picture = 0;
/**
- * The email address used for initial account creation.
- *
- * @var string
- */
- public $init = '';
-
- /**
* The user's roles.
*
* @var array
@@ -151,8 +137,15 @@ class User extends Entity {
/**
* Implements Drupal\Core\Entity\EntityInterface::id().
+ *
+ * To help other systems integrate with the anonymous user API we will save
+ * the user as soon as an ID is requested for it so we can keep track of it.
*/
public function id() {
+ if (!empty($this->enforceIsNew) || !$this->uid) {
+ $this->save();
+ }
+
return $this->uid;
}
}
diff --git a/core/modules/user/lib/Drupal/user/UserFormController.php b/core/modules/user/lib/Drupal/user/UserFormController.php
new file mode 100644
index 0000000..6b250f3
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserFormController.php
@@ -0,0 +1,240 @@
+uid);
+ $admin = user_access('administer users');
+
+ // User information.
+ $form['user'] = array(
+ '#type' => 'container',
+ '#weight' => -10,
+ );
+
+ if ($admin) {
+ $status = isset($account->status) ? $account->status : 1;
+ }
+ else {
+ $status = $register ? $config->get('register') == USER_REGISTER_VISITORS : $account->status;
+ }
+
+ // The mail field is NOT required if account originally had no mail set
+ // and the user performing the edit has 'administer users' permission.
+ // This allows users without e-mail address to be edited and deleted.
+ // We need to retrieve the user and store the mail against the account for
+ // the protected values validation to work.
+ $form['user']['mail'] = array(
+ '#type' => 'email',
+ '#title' => t('E-mail address'),
+ '#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' => !(empty($account->mail) && user_access('administer users')),
+ '#default_value' => (!$register ? $account->mail : ''),
+ '#attributes' => array('autocomplete' => 'off'),
+ );
+
+ // Display password field only for existing users or when user is allowed to
+ // assign a password during registration.
+ if (!$register && $account_account = account_uid_load($account->uid)) {
+ $protected_values = array();
+ $current_pass_description = '';
+
+ // The user may only change their own password without their current
+ // password if they logged in via a one-time login link.
+ $protected_values['mail'] = $form['user']['mail']['#title'];
+ $current_pass_description = t('Required if you want to change the %mail below..', array('%mail' => $protected_values['mail']));
+
+ // The user must enter their current password to change to a new one.
+ if ($user->uid == $account->uid) {
+ $form['user']['current_pass_required_values'] = array(
+ '#type' => 'value',
+ '#value' => $protected_values,
+ );
+
+ $form['user']['current_pass'] = array(
+ '#type' => 'password',
+ '#title' => t('Current password'),
+ '#size' => 25,
+ '#access' => !empty($protected_values),
+ '#description' => $current_pass_description,
+ '#weight' => -5,
+ '#attributes' => array('autocomplete' => 'off'),
+ );
+
+ $account->pass = $account_account->pass;
+ $form_state['user'] = $account;
+ $form['#validate'][] = 'user_validate_current_pass';
+ }
+ }
+
+ $form['user']['status'] = array(
+ '#type' => 'radios',
+ '#title' => t('Status'),
+ '#default_value' => $status,
+ '#options' => array(t('Blocked'), t('Active')),
+ '#access' => $admin,
+ );
+
+ $roles = array_map('check_plain', 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['user']['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,
+ );
+
+ // Signature.
+ $form['signature_settings'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Signature settings'),
+ '#weight' => 1,
+ '#access' => (!$register && $config->get('signatures')),
+ );
+
+ $form['signature_settings']['signature'] = array(
+ '#type' => 'text_format',
+ '#title' => t('Signature'),
+ '#default_value' => isset($account->signature) ? $account->signature : '',
+ '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
+ '#format' => isset($account->signature_format) ? $account->signature_format : NULL,
+ );
+
+ // 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. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')),
+ );
+
+ $form['#validate'][] = 'user_validate_picture';
+
+ $user_preferred_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account);
+
+ $user_preferred_admin_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account, 'admin');
+
+ // Is default the interface language?
+ include_once DRUPAL_ROOT . '/core/includes/language.inc';
+ $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_SELECTED;
+ $form['language'] = array(
+ '#type' => language_multilingual() ? 'fieldset' : 'container',
+ '#title' => t('Language settings'),
+ // Display language selector when either creating a user on the admin
+ // interface or editing a user account.
+ '#access' => !$register || user_access('administer users'),
+ );
+
+ $form['language']['preferred_langcode'] = array(
+ '#type' => 'language_select',
+ '#title' => t('Site language'),
+ '#languages' => LANGUAGE_CONFIGURABLE,
+ '#default_value' => $user_preferred_langcode,
+ '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."),
+ );
+
+ $form['language']['preferred_admin_langcode'] = array(
+ '#type' => 'language_select',
+ '#title' => t('Administration pages language'),
+ '#languages' => LANGUAGE_CONFIGURABLE,
+ '#default_value' => $user_preferred_admin_langcode,
+ '#access' => user_access('access administration pages', $account),
+ );
+
+ // User entities contain both a langcode property (for identifying the
+ // language of the entity data) and a preferred_langcode property (see
+ // above). Rather than provide a UI forcing the user to choose both
+ // separately, assume that the user profile data is in the user's preferred
+ // language. This element provides that synchronization. For use-cases where
+ // this synchronization is not desired, a module can alter or remove this
+ // element.
+ $form['language']['langcode'] = array(
+ '#type' => 'value',
+ '#value_callback' => '_user_language_selector_langcode_value',
+ // For the synchronization to work, this element must have a larger weight
+ // than the preferred_langcode element. Set a large weight here in case
+ // a module alters the weight of the other element.
+ '#weight' => 100,
+ );
+
+ return parent::form($form, $form_state, $account);
+ }
+
+ /**
+ * Overrides Drupal\Core\Entity\EntityFormController::submit().
+ */
+ public function validate(array $form, array &$form_state) {
+ parent::validate($form, $form_state);
+
+ // 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($form_state['values']['signature'])) {
+ // Move text format for user signature into 'signature_format'.
+ $form_state['values']['signature_format'] = $form_state['values']['signature']['format'];
+ // Move text value for user signature into 'signature'.
+ $form_state['values']['signature'] = $form_state['values']['signature']['value'];
+
+ $user_schema = drupal_get_schema('users');
+ 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'])));
+ }
+ }
+ }
+}
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 2660300..b71c009 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -87,16 +87,6 @@ public function save(EntityInterface $entity) {
* Overrides Drupal\Core\Entity\DatabaseStorageController::preSave().
*/
protected function preSave(EntityInterface $entity) {
- // Update the user password if it has changed.
- if ($entity->isNew() || (!empty($entity->pass) && $entity->pass != $entity->original->pass)) {
- // Allow alternate password hashing schemes.
- require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/includes/password.inc');
- $entity->pass = user_hash_password(trim($entity->pass));
- // Abort if the hashing failed and returned FALSE.
- if (!$entity->pass) {
- throw new EntityMalformedException('The entity does not have a password.');
- }
- }
if (!empty($entity->picture_upload)) {
$entity->picture = $entity->picture_upload;
@@ -136,12 +126,6 @@ protected function preSave(EntityInterface $entity) {
}
}
$entity->picture = empty($entity->picture->fid) ? 0 : $entity->picture->fid;
-
- // If the password is empty, that means it was not changed, so use the
- // original password.
- if (empty($entity->pass)) {
- $entity->pass = $entity->original->pass;
- }
}
// Prepare user roles.
@@ -163,15 +147,6 @@ protected function preSave(EntityInterface $entity) {
protected function postSave(EntityInterface $entity, $update) {
if ($update) {
- // If the password has been changed, delete all open sessions for the
- // user and recreate the current one.
- if ($entity->pass != $entity->original->pass) {
- drupal_session_destroy_uid($entity->uid);
- if ($entity->uid == $GLOBALS['user']->uid) {
- drupal_session_regenerate();
- }
- }
-
// Remove roles that are no longer enabled for the user.
$entity->roles = array_filter($entity->roles);
@@ -232,5 +207,8 @@ protected function postDelete($entities) {
db_delete('authmap')
->condition('uid', array_keys($entities), 'IN')
->execute();
+ db_delete('account')
+ ->condition('uid', array_keys($entities), 'IN')
+ ->execute();
}
}
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 396f4c0..8c217cc 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -11,7 +11,7 @@ function user_admin($callback_arg = '') {
switch ($op) {
case t('Create new account'):
case 'create':
- $account = entity_create('user', array());
+ $account = entity_create('account', array());
$build['user_register'] = entity_get_form($account, 'register');
break;
default:
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index a099f6b..26707be 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -18,8 +18,8 @@ function user_schema() {
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
- 'description' => 'Primary Key: Unique user ID.',
'default' => 0,
+ 'description' => 'Primary Key: Unique user ID.',
),
'uuid' => array(
'description' => 'Unique Key: Universally unique identifier for this entity.',
@@ -32,7 +32,7 @@ function user_schema() {
'length' => 60,
'not null' => TRUE,
'default' => '',
- 'description' => 'Unique user name.',
+ 'description' => 'Nice name for the user.',
),
'langcode' => array(
'type' => 'varchar',
@@ -41,13 +41,6 @@ function user_schema() {
'default' => '',
'description' => "The {language}.langcode of the user's profile.",
),
- 'pass' => array(
- 'type' => 'varchar',
- 'length' => 128,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => "User's password (hashed).",
- ),
'mail' => array(
'type' => 'varchar',
'length' => 254,
@@ -126,13 +119,6 @@ function user_schema() {
'default' => 0,
'description' => "Foreign key: {file_managed}.fid of user's picture.",
),
- 'init' => array(
- 'type' => 'varchar',
- 'length' => 254,
- 'not null' => FALSE,
- 'default' => '',
- 'description' => 'E-mail address used for initial account creation.',
- ),
'data' => array(
'type' => 'blob',
'not null' => FALSE,
@@ -149,7 +135,6 @@ function user_schema() {
),
'unique keys' => array(
'uuid' => array('uuid'),
- 'name' => array('name'),
),
'primary key' => array('uid'),
'foreign keys' => array(
@@ -160,6 +145,60 @@ function user_schema() {
),
);
+ $schema['account'] = array(
+ 'description' => 'Stores account data.',
+ 'fields' => array(
+ 'aid' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: Unique account ID',
+ ),
+ 'uid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => "User's {users}.uid.",
+ ),
+ 'username' => array(
+ 'type' => 'varchar',
+ 'length' => 60,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'Unique user name.',
+ ),
+ 'pass' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => "User's password (hashed).",
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Timestamp for when user was created.',
+ ),
+ 'init' => array(
+ 'type' => 'varchar',
+ 'length' => 254,
+ 'not null' => FALSE,
+ 'default' => '',
+ 'description' => 'E-mail address used for initial account creation.',
+ ),
+ ),
+ 'indexes' => array(
+ 'created' => array('created'),
+ 'uid' => array('uid'),
+ ),
+ 'unique keys' => array(
+ 'username' => array('username'),
+ ),
+ 'primary key' => array('aid'),
+ );
+
$schema['authmap'] = array(
'description' => 'Stores distributed authentication mapping.',
'fields' => array(
@@ -599,5 +638,98 @@ function user_update_8009(&$sandbox) {
}
/**
+ * Split the users table into accounts and users and migrate users into new
+ * structure.
+ */
+function user_update_8010(&$sandbox) {
+ if (!isset($sandbox['progress'])) {
+ $schema = array(
+ 'description' => 'Stores account data.',
+ 'fields' => array(
+ 'aid' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: Unique account ID',
+ ),
+ 'uid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => "User's {users}.uid.",
+ ),
+ 'username' => array(
+ 'type' => 'varchar',
+ 'length' => 60,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'Unique user name.',
+ ),
+ 'pass' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => "User's password (hashed).",
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Timestamp for when user was created.',
+ ),
+ 'init' => array(
+ 'type' => 'varchar',
+ 'length' => 254,
+ 'not null' => FALSE,
+ 'default' => '',
+ 'description' => 'E-mail address used for initial account creation.',
+ ),
+ ),
+ 'indexes' => array(
+ 'created' => array('created'),
+ 'uid' => array('uid'),
+ ),
+ 'unique keys' => array(
+ 'username' => array('username'),
+ ),
+ 'primary key' => array('aid'),
+ );
+ db_create_table('account', $schema);
+
+ // Set up the batch.
+ $sandbox['progress'] = 0;
+ // The anonymous user doesn't need an account to we start at 1.
+ // @todo: Do we need to make a stock anonymous user anymore?
+ $sandbox['last'] = 0;
+ $sandbox['max'] = db_query('SELECT COUNT(uid) FROM {users} WHERE uid > 0')->fetchField();
+ }
+
+ $users = db_query_range('SELECT * FROM {users} WHERE uid > :uid ORDER BY uid ASC', 0, 10, array(':uid' => $sandbox['last']))->fetchAllAssoc('uid');
+
+ foreach ($users as $uid => $user) {
+ db_insert('account')
+ ->fields( array(
+ 'uid' => $user->uid,
+ 'username' => $user->name,
+ 'pass' => $user->pass,
+ 'created' => $user->created,
+ 'init' => $user->init,
+ ))
+ ->execute();
+ $sandbox['last'] = $user->uid;
+ }
+
+ $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['last'] / $sandbox['max']);
+
+ // When the migrate has finished clean up the user table.
+ if ($sandbox['#finished'] == 1) {
+ db_drop_field('users', 'pass');
+ db_drop_field('users', 'init');
+ }
+}
+
+/**
* @} End of "addtogroup updates-7.x-to-8.x".
*/
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e42002b..16e8394 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -147,8 +147,7 @@ function user_entity_info() {
'label' => t('User'),
'controller class' => 'Drupal\user\UserStorageController',
'form controller class' => array(
- 'profile' => 'Drupal\user\ProfileFormController',
- 'register' => 'Drupal\user\RegisterFormController',
+ 'settings' => 'Drupal\user\SettingsFormController',
),
'base table' => 'users',
'uri callback' => 'user_uri',
@@ -175,11 +174,54 @@ function user_entity_info() {
),
),
),
+ 'account' => array(
+ 'label' => t('Account'),
+ 'controller class' => 'Drupal\user\AccountStorageController',
+ 'form controller class' => array(
+ 'profile' => 'Drupal\user\ProfileFormController',
+ 'register' => 'Drupal\user\RegisterFormController',
+ ),
+ 'base table' => 'account',
+ 'uri callback' => 'account_uri',
+ 'label callback' => 'account_label',
+ 'fieldable' => FALSE,
+ 'entity class' => 'Drupal\user\Account',
+ 'entity keys' => array(
+ 'id' => 'aid',
+ ),
+ ),
);
}
/**
- * Entity URI callback.
+ * Entity URI callback for accounts.
+ */
+function account_uri($account) {
+ return array(
+ 'path' => 'user/' . $account->uid,
+ );
+}
+
+/**
+ * Entity label callback for accounts.
+ *
+ * This label callback has langcode for security reasons. The username is the
+ * visual identifier for an account.
+ *
+ * @param $entity_type
+ * The entity type.
+ * @param $entity
+ * The entity object.
+ *
+ * @return
+ * The username on the account.
+ */
+function account_label($entity_type, $entity) {
+ return $entity->username;
+}
+
+/**
+ * Entity URI callback for users.
*/
function user_uri($user) {
return array(
@@ -226,11 +268,6 @@ function user_field_info_alter(&$info) {
function user_field_extra_fields() {
$return['user']['user'] = array(
'form' => array(
- 'account' => array(
- 'label' => t('User name and password'),
- 'description' => t('User module account form elements.'),
- 'weight' => -10,
- ),
'timezone' => array(
'label' => t('Timezone'),
'description' => t('User module timezone form element.'),
@@ -340,25 +377,40 @@ function user_load_by_mail($mail) {
}
/**
- * Fetches a user object by account name.
+ * Fetches a user object by username.
*
- * @param string $name
- * String with the account's user name.
+ * @param string $username
+ * String with the account's username.
* @return object|bool
* A fully-loaded $user object upon successful user load or FALSE if user
* cannot be loaded.
*
- * @see user_load_multiple()
+ * @see user_load()
+ * @see account_load_by_name()
*/
-function user_load_by_name($name) {
- $users = entity_load_multiple_by_properties('user', array('name' => $name));
- return reset($users);
+function user_load_by_name($username) {
+ $account = account_load_by_name($username);
+ return user_load($account->uid);
+}
+
+/**
+ * Fetches an account object by username.
+ *
+ * @param string $username
+ * String with the account's username.
+ * @return object|bool
+ * A fully-loaded $account object upon successful user load or FALSE if user
+ * cannot be loaded.
+ */
+function account_load_by_name($username) {
+ $accounts = entity_load_multiple_by_properties('account', array('username' => $username));
+ return reset($accounts);
}
/**
* Verify the syntax of the given name.
*/
-function user_validate_name($name) {
+function account_validate_name($name) {
if (!$name) {
return t('You must enter a username.');
}
@@ -417,7 +469,7 @@ function user_validate_picture(&$form, &$form_state) {
/**
* Generate a random alphanumeric password.
*/
-function user_password($length = 10) {
+function account_password($length = 10) {
// This variable contains the list of allowable characters for the
// password. Note that the number 0 and the letter 'O' have been
// removed to avoid confusion between the two. The same is true
@@ -538,18 +590,19 @@ function user_access($string, $account = NULL) {
/**
* Checks for usernames blocked by user administration.
*
- * @param $name
- * A string containing a name of the user.
+ * @param $username
+ * A string containing a username of the account.
*
* @return
- * Object with property 'name' (the user name), if the user is blocked;
- * FALSE if the user is not blocked.
- */
-function user_is_blocked($name) {
- return db_select('users')
- ->fields('users', array('name'))
- ->condition('name', db_like($name), 'LIKE')
- ->condition('status', 0)
+ * Object with property 'username' (the user name), if the account is
+ * blocked; FALSE if the user is not blocked.
+ */
+function user_is_blocked($username) {
+ $query = db_select('account');
+ $query->join('users', 'users', 'users.uid = account.uid');
+ return $query->fields('account', array('username'))
+ ->condition('account.username', db_like($username), 'LIKE')
+ ->condition('users.status', 0)
->execute()->fetchObject();
}
@@ -687,12 +740,15 @@ function user_user_view($account) {
'#markup' => theme('user_picture', array('account' => $account)),
'#weight' => -10,
);
- $account->content['member_for'] = array(
- '#type' => 'item',
- '#title' => t('Member for'),
- '#markup' => format_interval(REQUEST_TIME - $account->created),
- '#weight' => 5,
- );
+ $account_account = account_uid_load($account->uid);
+ if ($account_account) {
+ $account->content['member_for'] = array(
+ '#type' => 'item',
+ '#title' => t('Member for'),
+ '#markup' => format_interval(REQUEST_TIME - $account_account->created),
+ '#weight' => 5,
+ );
+ }
}
/**
@@ -735,7 +791,7 @@ function user_login_block($form) {
$form['#id'] = 'user-login-form';
$form['#validate'] = user_login_default_validators();
$form['#submit'][] = 'user_login_submit';
- $form['name'] = array('#type' => 'textfield',
+ $form['username'] = array('#type' => 'textfield',
'#title' => t('Username'),
'#maxlength' => USERNAME_MAX_LENGTH,
'#size' => 15,
@@ -849,7 +905,7 @@ function user_block_view($delta = '') {
case 'new':
if (user_access('access content')) {
// Retrieve a list of new users who have subsequently accessed the site successfully.
- $items = db_query_range('SELECT uid, name FROM {users} WHERE status <> 0 AND access <> 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
+ $items = db_query_range('SELECT uid, name FROM {users} WHERE status <> 0 AND login <> 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
$block['subject'] = t('Who\'s new');
$block['content'] = array(
@@ -917,21 +973,21 @@ function user_preprocess_block(&$variables) {
* By default, the passed-in object's 'name' property is used if it exists, or
* else, the site-defined value for the 'anonymous' variable. However, a module
* may override this by implementing
- * hook_user_format_name_alter(&$name, $account).
+ * hook_user_format_name_alter(&$name, $user).
*
* @see hook_user_format_name_alter()
*
- * @param $account
- * The account object for the user whose name is to be formatted.
+ * @param $user
+ * The user object for the user whose name is to be formatted.
*
* @return
* An unsanitized string with the username to display. The code receiving
* this result must ensure that check_plain() is called on it before it is
* printed to the page.
*/
-function user_format_name($account) {
- $name = !empty($account->name) ? $account->name : config('user.settings')->get('anonymous');
- drupal_alter('user_format_name', $name, $account);
+function user_format_name($user) {
+ $name = !empty($user->name) ? $user->name : config('user.settings')->get('anonymous');
+ drupal_alter('user_format_name', $name, $user);
return $name;
}
@@ -1104,7 +1160,7 @@ function user_is_logged_in() {
}
function user_register() {
- $account = entity_create('user', array());
+ $account = entity_create('account', array());
return entity_get_form($account, 'register');
}
@@ -1347,12 +1403,35 @@ function user_menu() {
$items['user/%user/edit'] = array(
'title' => 'Edit',
'page callback' => 'entity_get_form',
+ 'page arguments' => array(1, 'settings'),
+ 'access callback' => 'user_edit_access',
+ 'access arguments' => array(1),
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'user.pages.inc',
+ );
+
+ $items['user/%user/edit/user'] = array(
+ 'title' => 'User',
+ 'page callback' => 'entity_get_form',
+ 'page arguments' => array(1, 'settings'),
+ 'access callback' => 'user_edit_access',
+ 'access arguments' => array(1),
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'file' => 'user.pages.inc',
+ 'weight' => 98,
+ );
+
+ $items['user/%account_uid/edit/account'] = array(
+ 'title' => 'Account',
+ 'page callback' => 'entity_get_form',
'page arguments' => array(1, 'profile'),
'access callback' => 'user_edit_access',
'access arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'file' => 'user.pages.inc',
+ 'weight' => 99,
);
+
return $items;
}
@@ -1494,6 +1573,27 @@ function user_uid_optional_to_arg($arg) {
}
/**
+ * Load either a specified or the current user account.
+ *
+ * @param $uid
+ * An optional user ID of the user to load. If not provided, the current
+ * user's ID will be used.
+ * @return
+ * A fully-loaded $user object upon successful user load, FALSE if user
+ * cannot be loaded.
+ *
+ * @see account_load()
+ * @todo rethink the naming of this in Drupal 8.
+ */
+function account_uid_load($uid = NULL) {
+ if (!isset($uid)) {
+ $uid = $GLOBALS['user']->uid;
+ }
+ $accounts = entity_load_multiple_by_properties('account', array('uid' => $uid));
+ return reset($accounts);
+}
+
+/**
* Menu item title callback for the 'user' path.
*
* Anonymous users should see a title based on the requested page, but
@@ -1585,7 +1685,7 @@ function user_login($form, &$form_state) {
}
// Display login form:
- $form['name'] = array('#type' => 'textfield',
+ $form['username'] = array('#type' => 'textfield',
'#title' => t('Username'),
'#size' => 60,
'#maxlength' => USERNAME_MAX_LENGTH,
@@ -1595,7 +1695,7 @@ function user_login($form, &$form_state) {
),
);
- $form['name']['#description'] = t('Enter your @s username.', array('@s' => config('system.site')->get('name')));
+ $form['username']['#description'] = t('Enter your @s username.', array('@s' => config('system.site')->get('name')));
$form['pass'] = array('#type' => 'password',
'#title' => t('Password'),
'#description' => t('Enter the password that accompanies your username.'),
@@ -1634,9 +1734,9 @@ function user_login_default_validators() {
* A FAPI validate handler. Sets an error if supplied username has been blocked.
*/
function user_login_name_validate($form, &$form_state) {
- if (isset($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
+ if (isset($form_state['values']['username']) && user_is_blocked($form_state['values']['username'])) {
// Blocked in user administration.
- form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
+ form_set_error('username', t('The username %username has not been activated or is blocked.', array('%username' => $form_state['values']['username'])));
}
}
@@ -1648,7 +1748,7 @@ function user_login_name_validate($form, &$form_state) {
function user_login_authenticate_validate($form, &$form_state) {
$password = trim($form_state['values']['pass']);
$flood_config = config('user.flood');
- if (!empty($form_state['values']['name']) && !empty($password)) {
+ if (!empty($form_state['values']['username']) && !empty($password)) {
// Do not allow any login from the current user's IP if the limit has been
// reached. Default is 50 failed attempts allowed in one hour. This is
// independent of the per-user limit to catch attempts from one IP to log
@@ -1658,7 +1758,7 @@ function user_login_authenticate_validate($form, &$form_state) {
$form_state['flood_control_triggered'] = 'ip';
return;
}
- $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
+ $account = db_query("SELECT * FROM {account} a LEFT JOIN {users} u ON u.uid = a.uid WHERE a.username = :username AND u.status = 1", array(':username' => $form_state['values']['username']))->fetchObject();
if ($account) {
if ($flood_config->get('uid_only')) {
// Register flood events based on the uid only, so they apply for any
@@ -1682,7 +1782,7 @@ function user_login_authenticate_validate($form, &$form_state) {
}
// We are not limited by flood control, so try to authenticate.
// Set $form_state['uid'] as a flag for user_login_final_validate().
- $form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
+ $form_state['uid'] = user_authenticate($form_state['values']['username'], $password);
}
}
@@ -1705,16 +1805,16 @@ function user_login_final_validate($form, &$form_state) {
if (isset($form_state['flood_control_triggered'])) {
if ($form_state['flood_control_triggered'] == 'user') {
- form_set_error('name', format_plural($flood_config->get('user_limit'), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or request a new password.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
+ form_set_error('username', format_plural($flood_config->get('user_limit'), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or request a new password.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
}
else {
// We did not find a uid, so the limit is IP-based.
- form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
+ form_set_error('username', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
}
}
else {
- form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password'))));
- watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
+ form_set_error('username', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password'))));
+ watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['username']));
}
}
elseif (isset($form_state['flood_control_user_identifier'])) {
@@ -1734,10 +1834,10 @@ function user_login_final_validate($form, &$form_state) {
* @return
* The user's uid on success, or FALSE on failure to authenticate.
*/
-function user_authenticate($name, $password) {
+function user_authenticate($username, $password) {
$uid = FALSE;
- if (!empty($name) && !empty($password)) {
- $account = user_load_by_name($name);
+ if (!empty($username) && !empty($password)) {
+ $account = account_load_by_name($username);
if ($account) {
// Allow alternate password hashing schemes.
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/includes/password.inc');
@@ -1838,7 +1938,7 @@ function user_external_login_register($name, $module) {
* A unique URL that provides a one-time log in for the user, from which
* they can change their password.
*/
-function user_pass_reset_url($account, $options = array()) {
+function account_pass_reset_url($account, $options = array()) {
$timestamp = REQUEST_TIME;
$langcode = isset($options['langcode']) ? $options['langcode'] : user_preferred_langcode($account);
$url_options = array('absolute' => TRUE, 'language' => language_load($langcode));
@@ -2169,7 +2269,7 @@ function _user_mail_text($key, $langcode = NULL, $variables = array()) {
*/
function user_mail_tokens(&$replacements, $data, $options) {
if (isset($data['user'])) {
- $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user'], $options);
+ $replacements['[user:one-time-login-url]'] = account_pass_reset_url($data['user'], $options);
$replacements['[user:cancel-url]'] = user_cancel_url($data['user'], $options);
}
}
diff --git a/core/modules/user/user.tokens.inc b/core/modules/user/user.tokens.inc
index bc37434..a3d02bd 100644
--- a/core/modules/user/user.tokens.inc
+++ b/core/modules/user/user.tokens.inc
@@ -11,7 +11,7 @@
function user_token_info() {
$types['user'] = array(
'name' => t('Users'),
- 'description' => t('Tokens related to individual user accounts.'),
+ 'description' => t('Tokens related to individual users.'),
'needs-data' => 'user',
);
$types['current-user'] = array(
@@ -22,23 +22,23 @@ function user_token_info() {
$user['uid'] = array(
'name' => t('User ID'),
- 'description' => t("The unique ID of the user account."),
+ 'description' => t("The unique ID of the user."),
);
$user['name'] = array(
'name' => t("Name"),
- 'description' => t("The login name of the user account."),
+ 'description' => t("The nice name of the user."),
);
$user['mail'] = array(
'name' => t("Email"),
- 'description' => t("The email address of the user account."),
+ 'description' => t("The email address of the user."),
);
$user['url'] = array(
'name' => t("URL"),
- 'description' => t("The URL of the account profile page."),
+ 'description' => t("The URL of the user profile page."),
);
$user['edit-url'] = array(
'name' => t("Edit URL"),
- 'description' => t("The URL of the account edit page."),
+ 'description' => t("The URL of the user edit page."),
);
$user['last-login'] = array(
@@ -48,13 +48,39 @@ function user_token_info() {
);
$user['created'] = array(
'name' => t("Created"),
+ 'description' => t("The date the user user was created."),
+ 'type' => 'date',
+ );
+
+ $types['account'] = array(
+ 'name' => t('Accounts'),
+ 'description' => t('Tokens related to individual user accounts.'),
+ 'needs-data' => 'account',
+ );
+ $account['aid'] = array(
+ 'name' => t('Account ID'),
+ 'description' => t("The unique ID of the user account."),
+ );
+ $account['uid'] = array(
+ 'name' => t('User ID'),
+ 'description' => t("The user ID of this account."),
+ );
+ $account['username'] = array(
+ 'name' => t("Name"),
+ 'description' => t("The login name of the user account."),
+ );
+ $account['created'] = array(
+ 'name' => t("Created"),
'description' => t("The date the user account was created."),
'type' => 'date',
);
return array(
'types' => $types,
- 'tokens' => array('user' => $user),
+ 'tokens' => array(
+ 'user' => $user,
+ 'account' => $account,
+ ),
);
}
@@ -127,5 +153,36 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
$replacements += token_generate('user', $tokens, array('user' => $account), $options);
}
+ if ($type == 'account' && !empty($data['account'])) {
+ $account = $data['account'];
+ foreach ($tokens as $name => $original) {
+ switch ($name) {
+ // Basic account information.
+ case 'aid':
+ // In the case of hook user_presave aid is not set yet.
+ $replacements[$original] = !empty($account->aid) ? $account->aid : t('not yet assigned');
+ break;
+
+ case 'uid':
+ // In the case of hook user_presave uid is not set yet.
+ $replacements[$original] = !empty($account->uid) ? $account->uid : t('not yet assigned');
+ break;
+
+ case 'username':
+ $replacements[$original] = $sanitize ? check_plain($account->username) : $account->username;
+ break;
+
+ case 'created':
+ // In the case of user_presave the created date may not yet be set.
+ $replacements[$original] = !empty($account->created) ? format_date($account->created, 'medium', '', NULL, $langcode) : t('not yet created');
+ break;
+ }
+ }
+
+ if ($registered_tokens = token_find_with_prefix($tokens, 'created')) {
+ $replacements += token_generate('date', $registered_tokens, array('date' => $account->created), $options);
+ }
+ }
+
return $replacements;
}