diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 36e4f77..125852a 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1892,8 +1892,13 @@ function install_configure_form_submit($form, &$form_state) {
// We precreated user 1 with placeholder values. Let's save the real values.
$account = user_load(1);
- $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1, 'timezone' => $form_state['values']['date_default_timezone']);
- user_save($account, array_merge($form_state['values']['account'], $merge_data));
+ $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'];
+ $account->save();
// Load global $user and perform final login tasks.
$user = user_load(1);
user_login_finalize();
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 839f86e..069e879 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -628,9 +628,9 @@ function block_form_user_profile_form_alter(&$form, &$form_state) {
/**
* Implements hook_user_presave().
*/
-function block_user_presave(&$edit, $account) {
- if (isset($edit['block'])) {
- $edit['data']['block'] = $edit['block'];
+function block_user_presave($account) {
+ if (isset($account->block)) {
+ $account->data['block'] = $account->block;
}
}
diff --git a/core/modules/block/block.test b/core/modules/block/block.test
index 65ddcce..f4a9549 100644
--- a/core/modules/block/block.test
+++ b/core/modules/block/block.test
@@ -542,8 +542,8 @@ class BlockCacheTestCase extends DrupalWebTestCase {
$this->normal_user_alt = $this->drupalCreateUser();
// Sync the roles, since drupalCreateUser() creates separate roles for
// the same permission sets.
- user_save($this->normal_user_alt, array('roles' => $this->normal_user->roles));
$this->normal_user_alt->roles = $this->normal_user->roles;
+ $this->normal_user_alt->save();
// Enable our test block.
$edit['blocks[block_test_test_cache][region]'] = 'sidebar_first';
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 89ce0bc..c695c7e 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -233,8 +233,8 @@ function contact_form_user_profile_form_alter(&$form, &$form_state) {
/**
* Implements hook_user_presave().
*/
-function contact_user_presave(&$edit, $account) {
- $edit['data']['contact'] = isset($edit['contact']) ? $edit['contact'] : variable_get('contact_default_status', 1);
+function contact_user_presave($account) {
+ $account->data['contact'] = isset($account->contact) ? $account->contact : variable_get('contact_default_status', 1);
}
/**
diff --git a/core/modules/contact/contact.test b/core/modules/contact/contact.test
index d7f26ac..490d8f8 100644
--- a/core/modules/contact/contact.test
+++ b/core/modules/contact/contact.test
@@ -374,7 +374,8 @@ class ContactPersonalTestCase extends DrupalWebTestCase {
// Re-create our contacted user as a blocked user.
$this->contact_user = $this->drupalCreateUser();
- user_save($this->contact_user, array('status' => 0));
+ $this->contact_user->status = 0;
+ $this->contact_user->save();
// Test that blocked users can still be contacted by admin.
$this->drupalGet('user/' . $this->contact_user->uid . '/contact');
diff --git a/core/modules/entity/tests/entity_crud_hook_test.test b/core/modules/entity/tests/entity_crud_hook_test.test
index be59e99..f582c74 100644
--- a/core/modules/entity/tests/entity_crud_hook_test.test
+++ b/core/modules/entity/tests/entity_crud_hook_test.test
@@ -356,16 +356,15 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
* Tests hook invocations for CRUD operations on users.
*/
public function testUserHooks() {
- $edit = array(
+ $account = entity_create('user', array(
'name' => 'Test user',
'mail' => 'test@example.com',
'created' => REQUEST_TIME,
'status' => 1,
'language' => 'en',
- );
- $account = (object) $edit;
+ ));
$_SESSION['entity_crud_hook_test'] = array();
- $account = user_save($account, $edit);
+ $account->save();
$this->assertHookMessageOrder(array(
'entity_crud_hook_test_user_presave called',
@@ -375,7 +374,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
));
$_SESSION['entity_crud_hook_test'] = array();
- $account = user_load($account->uid);
+ user_load($account->uid);
$this->assertHookMessageOrder(array(
'entity_crud_hook_test_entity_load called for type user',
@@ -383,8 +382,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
));
$_SESSION['entity_crud_hook_test'] = array();
- $edit['name'] = 'New name';
- $account = user_save($account, $edit);
+ $account->name = 'New name';
+ $account->save();
$this->assertHookMessageOrder(array(
'entity_crud_hook_test_user_presave called',
diff --git a/core/modules/file/tests/file.test b/core/modules/file/tests/file.test
index 05083fc..92b7ef3 100644
--- a/core/modules/file/tests/file.test
+++ b/core/modules/file/tests/file.test
@@ -709,9 +709,8 @@ class FileFieldRevisionTestCase extends FileFieldTestCase {
// Attach the second file to a user.
$user = $this->drupalCreateUser();
- $edit = (array) $user;
- $edit[$field_name][LANGUAGE_NOT_SPECIFIED][0] = (array) $node_file_r3;
- user_save($user, $edit);
+ $user->{$field_name}[LANGUAGE_NOT_SPECIFIED][0] = (array) $node_file_r3;
+ $user->save();
$this->drupalGet('user/' . $user->uid . '/edit');
// Delete the third revision and check that the file is not deleted yet.
diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module
index 9ba3e4b..2f35eec 100644
--- a/core/modules/openid/openid.module
+++ b/core/modules/openid/openid.module
@@ -83,15 +83,15 @@ function openid_help($path, $arg) {
/**
* Implements hook_user_insert().
*/
-function openid_user_insert(&$edit, $account) {
- if (!empty($edit['openid_claimed_id'])) {
+function openid_user_insert($account) {
+ if (!empty($account->openid_claimed_id)) {
// The user has registered after trying to log in via OpenID.
if (variable_get('user_email_verification', TRUE)) {
drupal_set_message(t('Once you have verified your e-mail address, you may log in via OpenID.'));
}
- user_set_authmaps($account, array('authname_openid' => $edit['openid_claimed_id']));
+ user_set_authmaps($account, array('authname_openid' => $account->openid_claimed_id));
unset($_SESSION['openid']);
- unset($edit['openid_claimed_id']);
+ unset($account->openid_claimed_id);
}
}
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 144c2ab..e2ecd19 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -102,9 +102,9 @@ function overlay_form_user_profile_form_alter(&$form, &$form_state) {
/**
* Implements hook_user_presave().
*/
-function overlay_user_presave(&$edit, $account) {
- if (isset($edit['overlay'])) {
- $edit['data']['overlay'] = $edit['overlay'];
+function overlay_user_presave($account) {
+ if (isset($account->overlay)) {
+ $account->data['overlay'] = $account->overlay;
}
}
@@ -311,7 +311,9 @@ function overlay_user_dismiss_message() {
return MENU_ACCESS_DENIED;
}
else {
- user_save(user_load($user->uid), array('data' => array('overlay_message_dismissed' => 1)));
+ $account = user_load($user->uid);
+ $account->data['overlay_message_dismissed'] = 1;
+ $account->save();
drupal_set_message(t('The message has been dismissed. You can change your overlay settings at any time by visiting your profile page.'));
// Destination is normally given. Go to the user profile as a fallback.
drupal_goto('user/' . $user->uid . '/edit');
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 591e8a1..b996bbe 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -1126,7 +1126,8 @@ class DrupalWebTestCase extends DrupalTestCase {
$edit['roles'] = array($rid => $rid);
}
- $account = user_save(drupal_anonymous_user(), $edit);
+ $account = entity_create('user', $edit);
+ $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'));
if (empty($account->uid)) {
@@ -1230,7 +1231,7 @@ class DrupalWebTestCase extends DrupalTestCase {
*
* @see drupalCreateUser()
*/
- protected function drupalLogin(stdClass $user) {
+ protected function drupalLogin($user) {
if ($this->loggedInUser) {
$this->drupalLogout();
}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 200a335..62ad353 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2021,15 +2021,14 @@ function system_form_user_register_form_alter(&$form, &$form_state) {
}
/**
- * Implements hook_user_insert().
+ * Implements hook_user_presave().
*/
-function system_user_presave(&$edit, $account) {
+function system_user_presave($account) {
if (variable_get('configurable_timezones', 1) && empty($account->timezone) && !variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT)) {
$account->timezone = variable_get('date_default_timezone', '');
}
}
-
/**
* Implements hook_user_login().
*/
diff --git a/core/modules/system/tests/session.test b/core/modules/system/tests/session.test
index 017a8ba..5cc8fe9 100644
--- a/core/modules/system/tests/session.test
+++ b/core/modules/system/tests/session.test
@@ -41,8 +41,8 @@ class SessionTestCase extends DrupalWebTestCase {
// Verify that the session is regenerated if a module calls exit
// in hook_user_login().
- user_save($user, array('name' => 'session_test_user'));
$user->name = 'session_test_user';
+ $user->save();
$this->drupalGet('session-test/id');
$matches = array();
preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
diff --git a/core/modules/user/lib/Drupal/user/User.php b/core/modules/user/lib/Drupal/user/User.php
new file mode 100644
index 0000000..b39d88d
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/User.php
@@ -0,0 +1,147 @@
+uid;
+ }
+}
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
new file mode 100644
index 0000000..bd0d9b9
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -0,0 +1,227 @@
+ $record) {
+ if ($record->picture) {
+ $picture_fids[] = $record->picture;
+ }
+ $queried_users[$key]->data = unserialize($record->data);
+ $queried_users[$key]->roles = array();
+ if ($record->uid) {
+ $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+ }
+ else {
+ $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
+ }
+ }
+
+ // Add any additional roles from the database.
+ $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
+ foreach ($result as $record) {
+ $queried_users[$record->uid]->roles[$record->rid] = $record->name;
+ }
+
+ // Add the full file objects for user pictures if enabled.
+ if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
+ $pictures = file_load_multiple($picture_fids);
+ foreach ($queried_users as $entity) {
+ if (!empty($entity->picture) && isset($pictures[$entity->picture])) {
+ $entity->picture = $pictures[$entity->picture];
+ }
+ }
+ }
+ // Call the default attachLoad() method. This will add fields and call
+ // hook_user_load().
+ parent::attachLoad($queried_users, $revision_id);
+ }
+
+ /**
+ * Overrides EntityDatabaseStorageController::create().
+ */
+ public function create(array $values) {
+ if (!isset($values['created'])) {
+ $values['created'] = REQUEST_TIME;
+ }
+ // Users always have the authenticated user role.
+ $values['roles'][DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+
+ return parent::create($values);
+ }
+
+ /**
+ * Overrides EntityDatabaseStorageController::save().
+ */
+ public function save(EntityInterface $entity) {
+ if (empty($entity->uid)) {
+ $entity->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
+ $entity->enforceIsNew();
+ }
+ parent::save($entity);
+ }
+
+ /**
+ * Overrides EntityDatabaseStorageController::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;
+ }
+ // Delete the picture if the submission indicates that it should be deleted
+ // and no replacement was submitted.
+ elseif (!empty($entity->picture_delete)) {
+ $entity->picture = 0;
+ file_usage_delete($entity->original->picture, 'user', 'user', $entity->uid);
+ file_delete($entity->original->picture);
+ }
+
+ if (!$entity->isNew()) {
+ // Process picture uploads.
+ if (!empty($entity->picture->fid) && (!isset($entity->original->picture->fid) || $entity->picture->fid != $entity->original->picture->fid)) {
+ $picture = $entity->picture;
+ // If the picture is a temporary file, move it to its final location
+ // and make it permanent.
+ if (!$picture->status) {
+ $info = image_get_info($picture->uri);
+ $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
+
+ // Prepare the pictures directory.
+ file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
+ $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $entity->uid . '-' . REQUEST_TIME . '.' . $info['extension']);
+
+ // Move the temporary file into the final location.
+ if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
+ $picture->status = FILE_STATUS_PERMANENT;
+ $entity->picture = file_save($picture);
+ file_usage_add($picture, 'user', 'user', $entity->uid);
+ }
+ }
+ // Delete the previous picture if it was deleted or replaced.
+ if (!empty($entity->original->picture->fid)) {
+ file_usage_delete($entity->original->picture, 'user', 'user', $entity->uid);
+ file_delete($entity->original->picture);
+ }
+ }
+ $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.
+ if (isset($entity->roles)) {
+ $entity->roles = array_filter($entity->roles);
+ }
+
+ // Move account cancellation information into $entity->data.
+ foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
+ if (isset($entity->{$key})) {
+ $entity->data[$key] = $entity->{$key};
+ }
+ }
+ }
+
+ /**
+ * Overrides EntityDatabaseStorageController::postSave().
+ */
+ 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);
+
+ // Reload user roles if provided.
+ if ($entity->roles != $entity->original->roles) {
+ db_delete('users_roles')
+ ->condition('uid', $entity->uid)
+ ->execute();
+
+ $query = db_insert('users_roles')->fields(array('uid', 'rid'));
+ foreach (array_keys($entity->roles) as $rid) {
+ if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+ $query->values(array(
+ 'uid' => $entity->uid,
+ 'rid' => $rid,
+ ));
+ }
+ }
+ $query->execute();
+ }
+
+ // If the user was blocked, delete the user's sessions to force a logout.
+ if ($entity->original->status != $entity->status && $entity->status == 0) {
+ drupal_session_destroy_uid($entity->uid);
+ }
+
+ // Send emails after we have the new user object.
+ if ($entity->status != $entity->original->status) {
+ // The user's status is changing; conditionally send notification email.
+ $op = $entity->status == 1 ? 'status_activated' : 'status_blocked';
+ _user_mail_notify($op, $entity);
+ }
+ }
+ else {
+ // Save user roles.
+ if (count($entity->roles) > 1) {
+ $query = db_insert('users_roles')->fields(array('uid', 'rid'));
+ foreach (array_keys($entity->roles) as $rid) {
+ if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+ $query->values(array(
+ 'uid' => $entity->uid,
+ 'rid' => $rid,
+ ));
+ }
+ }
+ $query->execute();
+ }
+ }
+ }
+}
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index dc16906..1b8a253 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -227,67 +227,57 @@ function hook_user_operations() {
}
/**
- * A user account is about to be created or updated.
+ * Act on a user account being inserted 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, modules may add to $edit['data'] in order
- * to have their data serialized on save.
+ * This hook is invoked before the user account is saved to the database.
+ *
+ * Modules that want to store properties in the serialized {users}.data column,
+ * which is automatically loaded whenever a user account object is loaded, may
+ * add their properties to $account->data in order to have their data serialized
+ * on save.
*
- * @param $edit
- * The array of form values submitted by the user.
* @param $account
- * The user object on which the operation is performed.
+ * The user account object.
*
* @see hook_user_insert()
* @see hook_user_update()
*/
-function hook_user_presave(&$edit, $account) {
+function hook_user_presave($account) {
// Make sure that our form value 'mymodule_foo' is stored as
// 'mymodule_bar' in the 'data' (serialized) column.
- if (isset($edit['mymodule_foo'])) {
- $edit['data']['mymodule_bar'] = $edit['mymodule_foo'];
+ if (isset($account->mymodule_foo)) {
+ $account->data['mymodule_bar'] = $account->mymodule_foo;
}
}
/**
- * A user account was created.
+ * Respond to creation of a new user account.
*
- * The module should save its custom additions to the user object into the
- * database.
- *
- * @param $edit
- * The array of form values submitted by the user.
* @param $account
- * The user object on which the operation is being performed.
+ * The user account object.
*
* @see hook_user_presave()
* @see hook_user_update()
*/
-function hook_user_insert(&$edit, $account) {
- db_insert('mytable')
+function hook_user_insert($account) {
+ db_insert('user_changes')
->fields(array(
- 'myfield' => $edit['myfield'],
'uid' => $account->uid,
+ 'created' => time(),
))
->execute();
}
/**
- * 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.
+ * Respond to updates to a user account.
*
- * @param $edit
- * The array of form values submitted by the user.
* @param $account
- * The user object on which the operation is performed.
+ * The user account object.
*
* @see hook_user_presave()
* @see hook_user_insert()
*/
-function hook_user_update(&$edit, $account) {
+function hook_user_update($account) {
db_insert('user_changes')
->fields(array(
'uid' => $account->uid,
diff --git a/core/modules/user/user.entity.inc b/core/modules/user/user.entity.inc
deleted file mode 100644
index 5549c77..0000000
--- a/core/modules/user/user.entity.inc
+++ /dev/null
@@ -1,52 +0,0 @@
- $record) {
- $picture_fids[] = $record->picture;
- $queried_users[$key]->data = unserialize($record->data);
- $queried_users[$key]->roles = array();
- if ($record->uid) {
- $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
- }
- else {
- $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
- }
- }
-
- // Add any additional roles from the database.
- $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
- foreach ($result as $record) {
- $queried_users[$record->uid]->roles[$record->rid] = $record->name;
- }
-
- // Add the full file objects for user pictures if enabled.
- if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
- $pictures = file_load_multiple($picture_fids);
- foreach ($queried_users as $account) {
- if (!empty($account->picture) && isset($pictures[$account->picture])) {
- $account->picture = $pictures[$account->picture];
- }
- else {
- $account->picture = NULL;
- }
- }
- }
- // Call the default attachLoad() method. This will add fields and call
- // hook_user_load().
- parent::attachLoad($queried_users, $revision_id);
- }
-}
diff --git a/core/modules/user/user.info b/core/modules/user/user.info
index d887352..8dad5a3 100644
--- a/core/modules/user/user.info
+++ b/core/modules/user/user.info
@@ -3,7 +3,6 @@ description = Manages the user registration and login system.
package = Core
version = VERSION
core = 8.x
-files[] = user.entity.inc
files[] = user.test
required = TRUE
configure = admin/config/people
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 545f2bf..51516df 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -83,12 +83,8 @@ function user_help($path, $arg) {
* be passed by reference.
*
* @param $type
- * A text string that controls which user hook to invoke. Valid choices are:
- * - cancel: Invokes hook_user_cancel().
- * - insert: Invokes hook_user_insert().
+ * A text string that controls which user hook to invoke. Valid choices are:
* - login: Invokes hook_user_login().
- * - presave: Invokes hook_user_presave().
- * - update: Invokes hook_user_update().
* @param $edit
* An associative array variable containing form values to be passed
* as the first parameter of the hook function.
@@ -145,14 +141,15 @@ function user_theme() {
* Implements hook_entity_info().
*/
function user_entity_info() {
- $return = array(
+ return array(
'user' => array(
'label' => t('User'),
- 'controller class' => 'UserController',
+ 'controller class' => 'Drupal\user\UserStorageController',
'base table' => 'users',
'uri callback' => 'user_uri',
'label callback' => 'user_label',
'fieldable' => TRUE,
+ 'entity class' => 'Drupal\user\User',
'entity keys' => array(
'id' => 'uid',
),
@@ -173,7 +170,6 @@ function user_entity_info() {
),
),
);
- return $return;
}
/**
@@ -360,224 +356,6 @@ function user_load_by_name($name) {
}
/**
- * Save changes to a user account or add a new user.
- *
- * @param $account
- * (optional) The user object to modify or add. If you want to modify
- * an existing user account, you will need to ensure that (a) $account
- * is an object, and (b) you have set $account->uid to the numeric
- * user ID of the user account you wish to modify. If you
- * want to create a new user account, you can set $account->is_new to
- * TRUE or omit the $account->uid field.
- * @param $edit
- * An array of fields and values to save. For example array('name'
- * => 'My name'). Key / value pairs added to the $edit['data'] will be
- * serialized and saved in the {users.data} column.
- *
- * @return
- * A fully-loaded $user object upon successful save or FALSE if the save failed.
- *
- * @todo D8: Drop $edit and fix user_save() to be consistent with others.
- */
-function user_save($account, $edit = array()) {
- $transaction = db_transaction();
- try {
- if (!empty($edit['pass'])) {
- // Allow alternate password hashing schemes.
- require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/includes/password.inc');
- $edit['pass'] = user_hash_password(trim($edit['pass']));
- // Abort if the hashing failed and returned FALSE.
- if (!$edit['pass']) {
- return FALSE;
- }
- }
- else {
- // Avoid overwriting an existing password with a blank password.
- unset($edit['pass']);
- }
-
- // Load the stored entity, if any.
- if (!empty($account->uid) && !isset($account->original)) {
- $account->original = entity_load_unchanged('user', $account->uid);
- }
-
- if (empty($account)) {
- $account = new stdClass();
- }
- if (!isset($account->is_new)) {
- $account->is_new = empty($account->uid);
- }
- // Prepopulate $edit['data'] with the current value of $account->data.
- // Modules can add to or remove from this array in hook_user_presave().
- if (!empty($account->data)) {
- $edit['data'] = !empty($edit['data']) ? array_merge($account->data, $edit['data']) : $account->data;
- }
-
- // Invoke hook_user_presave() for all modules.
- user_module_invoke('presave', $edit, $account);
-
- // Invoke presave operations of Field Attach API and Entity API. Those APIs
- // require a fully-fledged and updated entity object. Therefore, we need to
- // copy any new property values of $edit into it.
- foreach ($edit as $key => $value) {
- $account->$key = $value;
- }
- // Default the user entity language to the user's preferred language.
- if (!isset($account->langcode) && isset($account->preferred_langcode)) {
- $account->langcode = $account->preferred_langcode;
- }
- field_attach_presave('user', $account);
- module_invoke_all('entity_presave', $account, 'user');
-
- if (is_object($account) && !$account->is_new) {
- // Process picture uploads.
- if (!empty($account->picture->fid) && (!isset($account->original->picture->fid) || $account->picture->fid != $account->original->picture->fid)) {
- $picture = $account->picture;
- // If the picture is a temporary file move it to its final location and
- // make it permanent.
- if (!$picture->status) {
- $info = image_get_info($picture->uri);
- $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
-
- // Prepare the pictures directory.
- file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
- $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']);
-
- // Move the temporary file into the final location.
- if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
- $picture->status = FILE_STATUS_PERMANENT;
- $account->picture = file_save($picture);
- file_usage_add($picture, 'user', 'user', $account->uid);
- }
- }
- // Delete the previous picture if it was deleted or replaced.
- if (!empty($account->original->picture->fid)) {
- file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
- file_delete($account->original->picture);
- }
- }
- elseif (isset($edit['picture_delete']) && $edit['picture_delete']) {
- file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
- file_delete($account->original->picture);
- }
- $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid;
-
- // Do not allow 'uid' to be changed.
- $account->uid = $account->original->uid;
- // Save changes to the user table.
- $success = drupal_write_record('users', $account, 'uid');
- if ($success === FALSE) {
- // The query failed - better to abort the save than risk further
- // data loss.
- return FALSE;
- }
-
- // Reload user roles if provided.
- if ($account->roles != $account->original->roles) {
- db_delete('users_roles')
- ->condition('uid', $account->uid)
- ->execute();
-
- $query = db_insert('users_roles')->fields(array('uid', 'rid'));
- foreach (array_keys($account->roles) as $rid) {
- if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
- $query->values(array(
- 'uid' => $account->uid,
- 'rid' => $rid,
- ));
- }
- }
- $query->execute();
- }
-
- // Delete a blocked user's sessions to kick them if they are online.
- if ($account->original->status != $account->status && $account->status == 0) {
- drupal_session_destroy_uid($account->uid);
- }
-
- // If the password changed, delete all open sessions and recreate
- // the current one.
- if ($account->pass != $account->original->pass) {
- drupal_session_destroy_uid($account->uid);
- if ($account->uid == $GLOBALS['user']->uid) {
- drupal_session_regenerate();
- }
- }
-
- // Save Field data.
- field_attach_update('user', $account);
-
- // Send emails after we have the new user object.
- if ($account->status != $account->original->status) {
- // The user's status is changing; conditionally send notification email.
- $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
- _user_mail_notify($op, $account);
- }
-
- // Update $edit with any interim changes to $account.
- foreach ($account as $key => $value) {
- if (!property_exists($account->original, $key) || $value !== $account->original->$key) {
- $edit[$key] = $value;
- }
- }
- user_module_invoke('update', $edit, $account);
- module_invoke_all('entity_update', $account, 'user');
- }
- else {
- // Allow 'uid' to be set by the caller. There is no danger of writing an
- // existing user as drupal_write_record will do an INSERT.
- if (empty($account->uid)) {
- $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
- }
- // Allow 'created' to be set by the caller.
- if (!isset($account->created)) {
- $account->created = REQUEST_TIME;
- }
- $success = drupal_write_record('users', $account);
- 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;
- }
-
- // Make sure $account is properly initialized.
- $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
-
- field_attach_insert('user', $account);
- $edit = (array) $account;
- user_module_invoke('insert', $edit, $account);
- module_invoke_all('entity_insert', $account, 'user');
-
- // Save user roles.
- if (count($account->roles) > 1) {
- $query = db_insert('users_roles')->fields(array('uid', 'rid'));
- foreach (array_keys($account->roles) as $rid) {
- if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
- $query->values(array(
- 'uid' => $account->uid,
- 'rid' => $rid,
- ));
- }
- }
- $query->execute();
- }
- }
- // Clear internal properties.
- unset($account->is_new);
- unset($account->original);
- // Clear the static loading cache.
- entity_get_controller('user')->resetCache(array($account->uid));
-
- return $account;
- }
- catch (Exception $e) {
- $transaction->rollback();
- watchdog_exception('user', $e);
- throw $e;
- }
-}
-
-/**
* Verify the syntax of the given name.
*/
function user_validate_name($name) {
@@ -1194,20 +972,42 @@ function user_account_form_validate($form, &$form_state) {
if ($error = user_validate_name($form_state['values']['name'])) {
form_set_error('name', $error);
}
- elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('name', db_like($form_state['values']['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
- form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
+ // 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'))
+ ->condition('uid', (int) $account->uid, '<>')
+ ->condition('name', db_like($form_state['values']['name']), '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'])));
+ }
}
}
$mail = $form_state['values']['mail'];
- if (!empty($mail) && (bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($mail), 'LIKE')->range(0, 1)->execute()->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' => $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'))));
+ 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'))));
+ }
}
}
@@ -1226,30 +1026,6 @@ function user_account_form_validate($form, &$form_state) {
}
}
-/**
- * Implements hook_user_presave().
- */
-function user_user_presave(&$edit, $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;
- }
- // Prepare user roles.
- if (isset($edit['roles'])) {
- $edit['roles'] = array_filter($edit['roles']);
- }
-
- // Move account cancellation information into $user->data.
- foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
- if (isset($edit[$key])) {
- $edit['data'][$key] = $edit[$key];
- }
- }
-}
-
function user_login_block($form) {
$form['#action'] = url($_GET['q'], array('query' => drupal_get_destination()));
$form['#id'] = 'user-login-form';
@@ -2249,7 +2025,8 @@ function user_authenticate($name, $password) {
// Update user to new password scheme if needed.
if (user_needs_new_hash($account)) {
- user_save($account, array('pass' => $password));
+ $account->pass = $password;
+ $account->save();
}
}
}
@@ -2304,16 +2081,16 @@ function user_external_login_register($name, $module) {
$account = user_external_load($name);
if (!$account) {
// Register this new user.
- $userinfo = array(
+ $account = entity_create('user', array(
'name' => $name,
'pass' => user_password(),
'init' => $name,
'status' => 1,
'access' => REQUEST_TIME
- );
- $account = user_save(drupal_anonymous_user(), $userinfo);
- // Terminate if an error occurred during user_save().
- if (!$account) {
+ ));
+ $status = $account->save();
+ // Terminate if an error occurred while saving the account.
+ if ($status != SAVED_NEW) {
drupal_set_message(t("Error saving user account."), 'error');
return;
}
@@ -2461,7 +2238,8 @@ function _user_cancel($edit, $account, $method) {
if (!empty($edit['user_cancel_notify'])) {
_user_mail_notify('status_blocked', $account);
}
- user_save($account, array('status' => 0));
+ $account->status = 0;
+ $account->save();
drupal_set_message(t('%name has been disabled.', array('%name' => $account->name)));
watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
break;
@@ -3215,7 +2993,8 @@ function user_user_operations_unblock($accounts) {
foreach ($accounts as $account) {
// Skip unblocking user if they are already unblocked.
if ($account !== FALSE && $account->status == 0) {
- user_save($account, array('status' => 1));
+ $account->status = 1;
+ $account->save();
}
}
}
@@ -3231,7 +3010,8 @@ function user_user_operations_block($accounts) {
// For efficiency manually save the original account before applying any
// changes.
$account->original = clone $account;
- user_save($account, array('status' => 0));
+ $account->status = 0;
+ $account->save();
}
}
}
@@ -3240,8 +3020,6 @@ function user_user_operations_block($accounts) {
* Callback function for admin mass adding/deleting a user role.
*/
function user_multiple_role_edit($accounts, $operation, $rid) {
- // The role name is not necessary as user_save() will reload the user
- // object, but some modules' hook_user() may look at this first.
$role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField();
switch ($operation) {
@@ -3254,7 +3032,8 @@ function user_multiple_role_edit($accounts, $operation, $rid) {
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
- user_save($account, array('roles' => $roles));
+ $account->roles = $roles;
+ $account->save();
}
}
break;
@@ -3267,7 +3046,8 @@ function user_multiple_role_edit($accounts, $operation, $rid) {
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
- user_save($account, array('roles' => $roles));
+ $account->roles = $roles;
+ $account->save();
}
}
break;
@@ -3357,7 +3137,9 @@ function user_multiple_cancel_confirm_submit($form, &$form_state) {
if ($uid == $user->uid) {
$admin_form_state = $form_state;
unset($admin_form_state['values']['user_cancel_confirm']);
- $admin_form_state['values']['_account'] = $user;
+ // The $user global is not a complete user entity, so load the full
+ // entity.
+ $admin_form_state['values']['_account'] = user_load($user->uid);
user_cancel_confirm_form_submit(array(), $admin_form_state);
}
else {
@@ -3431,7 +3213,7 @@ function user_build_filter_query(SelectInterface $query) {
// the authenticated role. If so, then all users would be listed, and we can
// skip adding it to the filter query.
if ($key == 'permission') {
- $account = new stdClass();
+ $account = entity_create('user', array());
$account->uid = 'user_filter';
$account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
if (user_access($value, $account)) {
@@ -3677,7 +3459,8 @@ function user_block_user_action(&$entity, $context = array()) {
$uid = $GLOBALS['user']->uid;
}
$account = user_load($uid);
- $account = user_save($account, array('status' => 0));
+ $account->status = 0;
+ $account->save();
watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
}
@@ -3756,7 +3539,7 @@ function user_register_form($form, &$form_state) {
drupal_goto('user/' . $user->uid);
}
- $form['#user'] = drupal_anonymous_user();
+ $form['#user'] = entity_create('user', array());
$form['#attached']['library'][] = array('system', 'jquery.cookie');
$form['#attributes']['class'][] = 'user-info-from-cookie';
@@ -3827,14 +3610,10 @@ function user_register_submit($form, &$form_state) {
$account = $form['#user'];
entity_form_submit_build_entity('user', $account, $form, $form_state);
+ $status = $account->save();
- // Populate $edit with the properties of $account, which have been edited on
- // this form by taking over all values, which appear in the form values too.
- $edit = array_intersect_key((array) $account, $form_state['values']);
- $account = user_save($account, $edit);
-
- // Terminate if an error occurred during user_save().
- if (!$account) {
+ // Terminate if an error occurred while saving the account.
+ if ($status =! SAVED_NEW) {
drupal_set_message(t("Error saving user account."), 'error');
$form_state['redirect'] = '';
return;
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index f24849c..438fedb 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -264,19 +264,8 @@ function user_profile_form_submit($form, &$form_state) {
// Remove unneeded values.
form_state_values_clean($form_state);
- // Before updating the account entity, keep an unchanged copy for use with
- // user_save() later. This is necessary for modules implementing the user
- // hooks to be able to react on changes by comparing the values of $account
- // and $edit.
- $account_unchanged = clone $account;
-
entity_form_submit_build_entity('user', $account, $form, $form_state);
-
- // Populate $edit with the properties of $account, which have been edited on
- // this form by taking over all values, which appear in the form values too.
- $edit = array_intersect_key((array) $account, $form_state['values']);
-
- user_save($account_unchanged, $edit);
+ $account->save();
$form_state['values']['uid'] = $account->uid;
if (!empty($edit['pass'])) {
@@ -400,11 +389,9 @@ function user_cancel_confirm_form_submit($form, &$form_state) {
else {
// Store cancelling method and whether to notify the user in $account for
// user_cancel_confirm().
- $edit = array(
- 'user_cancel_method' => $form_state['values']['user_cancel_method'],
- 'user_cancel_notify' => $form_state['values']['user_cancel_notify'],
- );
- $account = user_save($account, $edit);
+ $account->user_cancel_method = $form_state['values']['user_cancel_method'];
+ $account->user_cancel_notify = $form_state['values']['user_cancel_notify'];
+ $account->save();
_user_mail_notify('cancel_confirm', $account);
drupal_set_message(t('A confirmation request to cancel your account has been sent to your e-mail address.'));
watchdog('user', 'Sent account cancellation request to %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
diff --git a/core/modules/user/user.test b/core/modules/user/user.test
index d5fa28e..c911088 100644
--- a/core/modules/user/user.test
+++ b/core/modules/user/user.test
@@ -168,7 +168,7 @@ class UserRegistrationTestCase extends DrupalWebTestCase {
$this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), t('Correct time zone field.'));
$this->assertEqual($new_user->langcode, language_default()->langcode, t('Correct language field.'));
$this->assertEqual($new_user->preferred_langcode, language_default()->langcode, t('Correct preferred language field.'));
- $this->assertEqual($new_user->picture, '', t('Correct picture field.'));
+ $this->assertEqual($new_user->picture, 0, t('Correct picture field.'));
$this->assertEqual($new_user->init, $mail, t('Correct init field.'));
}
@@ -553,7 +553,8 @@ class UserCancelTestCase extends DrupalWebTestCase {
'name' => 'user1',
'pass' => user_hash_password(trim($password)),
);
- // We cannot use user_save() here or the password would be hashed again.
+ // We cannot use $account->save() here, because this would result in the
+ // password being hashed again.
db_update('users')
->fields($account)
->condition('uid', 1)
@@ -845,8 +846,8 @@ class UserCancelTestCase extends DrupalWebTestCase {
// Create a regular user.
$account = $this->drupalCreateUser(array());
// This user has no e-mail address.
- $edit = array('mail' => '');
- $account = user_save($account, $edit);
+ $account->mail = '';
+ $account->save();
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer users'));
@@ -1174,7 +1175,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
// Load actual user data from database.
$account = user_load($this->user->uid, TRUE);
- $pic_path = isset($account->picture) ? $account->picture->uri : NULL;
+ $pic_path = !empty($account->picture) ? $account->picture->uri : NULL;
// Check if image is displayed in user's profile page.
$this->drupalGet('user');
@@ -1188,7 +1189,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
// Load actual user data from database.
$account1 = user_load($this->user->uid, TRUE);
- $this->assertNull($account1->picture, 'User object has no picture');
+ $this->assertFalse($account1->picture, 'User object has no picture');
$file = file_load($account->picture->fid);
$this->assertFalse($file, 'File is removed from database');
@@ -1204,7 +1205,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
// Load actual user data from database.
$account = user_load($this->user->uid, TRUE);
- return isset($account->picture) ? $account->picture->uri : NULL;
+ return !empty($account->picture) ? $account->picture->uri : NULL;
}
/**
@@ -1677,7 +1678,7 @@ class UserSaveTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'User save test',
- 'description' => 'Test user_save() for arbitrary new uid.',
+ 'description' => 'Test account saving for arbitrary new uid.',
'group' => 'User',
);
}
@@ -1692,16 +1693,15 @@ class UserSaveTestCase extends DrupalWebTestCase {
$test_name = $this->randomName();
// Create the base user, based on drupalCreateUser().
- $user = array(
+ $user = entity_create('user', array(
'name' => $test_name,
'uid' => $test_uid,
'mail' => $test_name . '@example.com',
- 'is_new' => TRUE,
'pass' => user_password(),
'status' => 1,
- );
- $user_by_return = user_save(drupal_anonymous_user(), $user);
- $this->assertTrue($user_by_return, t('Loading user by return of user_save().'));
+ ));
+ $user->enforceIsNew();
+ $user->save();
// Test if created user exists.
$user_by_uid = user_load($test_uid);
@@ -1843,9 +1843,9 @@ class UserEditTestCase extends DrupalWebTestCase {
// Create a regular user.
$user1 = $this->drupalCreateUser(array());
// This user has no e-mail address.
- $edit = array('mail' => '');
- $user1 = user_save($user1, $edit);
- $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
+ $user1->mail = '';
+ $user1->save();
+ $this->drupalPost("user/$user1->uid/edit", array('mail' => ''), t('Save'));
$this->assertRaw(t("The changes have been saved."));
}
}