summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2011-05-22 09:08:19 -0400
committerDries Buytaert <dries@buytaert.net>2011-05-22 09:08:19 -0400
commit5dbe8aa92b9e746962b43f9a24b10d7f9616d232 (patch)
tree3a99baca5a9f27bb3914ef9ee082eb4d2d6d84fc /modules
parentc88f2e0188e48f4c4adfb101d70733a0b27d4685 (diff)
parent267b9c435c5d7266f4ce674b84d95bfe1f5a0920 (diff)
downloadbrdo-5dbe8aa92b9e746962b43f9a24b10d7f9616d232.tar.gz
brdo-5dbe8aa92b9e746962b43f9a24b10d7f9616d232.tar.bz2
Merge branch '7.x' of git.drupal.org:project/drupal into 7.x
Diffstat (limited to 'modules')
-rw-r--r--modules/comment/comment.module8
-rw-r--r--modules/field/field.attach.inc25
-rw-r--r--modules/node/node.install34
-rw-r--r--modules/node/node.module10
-rw-r--r--modules/openid/openid.module10
-rw-r--r--modules/openid/openid.test12
-rw-r--r--modules/simpletest/tests/database_test.test21
-rw-r--r--modules/simpletest/tests/menu.test58
-rw-r--r--modules/system/system.api.php24
-rw-r--r--modules/taxonomy/taxonomy.module4
-rw-r--r--modules/user/user.module119
11 files changed, 204 insertions, 121 deletions
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 836f2fed8..499adc847 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -986,8 +986,8 @@ function comment_build_content($comment, $node, $view_mode = 'full', $langcode =
$comment->content = array();
// Build fields content.
- field_attach_prepare_view('comment', array($comment->cid => $comment), $view_mode);
- entity_prepare_view('comment', array($comment->cid => $comment));
+ field_attach_prepare_view('comment', array($comment->cid => $comment), $view_mode, $langcode);
+ entity_prepare_view('comment', array($comment->cid => $comment), $langcode);
$comment->content += field_attach_view('comment', $comment, $view_mode, $langcode);
$comment->content['links'] = array(
@@ -1089,8 +1089,8 @@ function comment_links($comment, $node) {
* An array in the format expected by drupal_render().
*/
function comment_view_multiple($comments, $node, $view_mode = 'full', $weight = 0, $langcode = NULL) {
- field_attach_prepare_view('comment', $comments, $view_mode);
- entity_prepare_view('comment', $comments);
+ field_attach_prepare_view('comment', $comments, $view_mode, $langcode);
+ entity_prepare_view('comment', $comments, $langcode);
$build = array(
'#sorted' => TRUE,
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index 82aabc054..3b15c76c8 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -257,9 +257,9 @@ function _field_invoke($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $opti
* - 'deleted': If TRUE, the function will operate on deleted fields
* as well as non-deleted fields. If unset or FALSE, only
* non-deleted fields are operated on.
- * - 'language': A language code or an array of language codes keyed by field
- * name. It will be used to narrow down to a single value the available
- * languages to act on.
+ * - 'language': A language code or an array of arrays of language codes keyed
+ * by entity id and field name. It will be used to narrow down to a single
+ * value the available languages to act on.
*
* @return
* An array of returned values keyed by entity id.
@@ -311,7 +311,8 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
// Unless a language suggestion is provided we iterate on all the
// available languages.
$available_languages = field_available_languages($entity_type, $field);
- $languages = _field_language_suggestion($available_languages, $options['language'], $field_name);
+ $language = !empty($options['language'][$id]) ? $options['language'][$id] : $options['language'];
+ $languages = _field_language_suggestion($available_languages, $language, $field_name);
foreach ($languages as $langcode) {
$grouped_items[$field_id][$langcode][$id] = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
}
@@ -1074,8 +1075,13 @@ function field_attach_delete_revision($entity_type, $entity) {
* An array of entities, keyed by entity id.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
+ * @param $langcode
+ * (Optional) The language the field values are to be shown in. If no language
+ * is provided the current language is used.
*/
-function field_attach_prepare_view($entity_type, $entities, $view_mode) {
+function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcode = NULL) {
+ $options = array('language' => array());
+
// To ensure hooks are only run once per entity, only process items without
// the _field_view_prepared flag.
// @todo: resolve this more generally for both entity and field level hooks.
@@ -1085,17 +1091,22 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode) {
// Add this entity to the items to be prepared.
$prepare[$id] = $entity;
+ // Determine the actual language to display for each field, given the
+ // languages available in the field data.
+ $options['language'][$id] = field_language($entity_type, $entity, NULL, $langcode);
+
// Mark this item as prepared.
$entity->_field_view_prepared = TRUE;
}
}
+ $null = NULL;
// First let the field types do their preparation.
- _field_invoke_multiple('prepare_view', $entity_type, $prepare);
+ _field_invoke_multiple('prepare_view', $entity_type, $prepare, $null, $null, $options);
// Then let the formatters do their own specific massaging.
// field_default_prepare_view() takes care of dispatching to the correct
// formatters according to the display settings for the view mode.
- _field_invoke_multiple_default('prepare_view', $entity_type, $prepare, $view_mode);
+ _field_invoke_multiple_default('prepare_view', $entity_type, $prepare, $view_mode, $null, $options);
}
/**
diff --git a/modules/node/node.install b/modules/node/node.install
index c5378dc85..14290e3ad 100644
--- a/modules/node/node.install
+++ b/modules/node/node.install
@@ -469,7 +469,26 @@ function node_update_dependencies() {
* @ingroup update-api-6.x-to-7.x
*/
function _update_7000_node_get_types() {
- return db_query('SELECT * FROM {node_type}')->fetchAllAssoc('type', PDO::FETCH_OBJ);
+ $node_types = db_query('SELECT * FROM {node_type}')->fetchAllAssoc('type', PDO::FETCH_OBJ);
+
+ // Create default settings for orphan nodes.
+ $all_types = db_query('SELECT DISTINCT type FROM {node}')->fetchCol();
+ $extra_types = array_diff($all_types, array_keys($node_types));
+
+ foreach ($extra_types as $type) {
+ $type_object = new stdClass;
+ $type_object->type = $type;
+
+ // In Drupal 6, whether you have a body field or not is a flag in the node
+ // type table. If it's enabled, nodes may or may not have an empty string
+ // for the bodies. As we can't detect what this setting should be in
+ // Drupal 7 without access to the Drupal 6 node type settings, we assume
+ // the default, which is to enable the body field.
+ $type_object->has_body = 1;
+ $type_object->body_label = 'Body';
+ $node_types[$type_object->type] = $type_object;
+ }
+ return $node_types;
}
/**
@@ -600,19 +619,6 @@ function node_update_7006(&$sandbox) {
// Get node type info, specifically the body field settings.
$node_types = _update_7000_node_get_types();
- // Create default settings for orphan nodes.
- $extra_types = db_query('SELECT DISTINCT type FROM {node} WHERE type NOT IN (:types)', array(':types' => array_keys($node_types)))->fetchCol();
- foreach ($extra_types as $type) {
- $type_object = new stdClass;
- $type_object->type = $type;
- // Always create a body. Querying node_revisions for a non-empty body
- // would skip creating body fields for types that have a body but
- // the nodes of that type so far had empty bodies.
- $type_object->has_body = 1;
- $type_object->body_label = 'Body';
- $node_types[$type_object->type] = $type_object;
- }
-
// Add body field instances for existing node types.
foreach ($node_types as $node_type) {
if ($node_type->has_body) {
diff --git a/modules/node/node.module b/modules/node/node.module
index 4f079edd7..524a57fa7 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -1350,15 +1350,15 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
if (node_hook($node, 'view')) {
- $node = node_invoke($node, 'view', $view_mode);
+ $node = node_invoke($node, 'view', $view_mode, $langcode);
}
// Build fields content.
// In case of a multiple view, node_view_multiple() already ran the
// 'prepare_view' step. An internal flag prevents the operation from running
// twice.
- field_attach_prepare_view('node', array($node->nid => $node), $view_mode);
- entity_prepare_view('node', array($node->nid => $node));
+ field_attach_prepare_view('node', array($node->nid => $node), $view_mode, $langcode);
+ entity_prepare_view('node', array($node->nid => $node), $langcode);
$node->content += field_attach_view('node', $node, $view_mode, $langcode);
// Always display a read more link on teasers because we have no way
@@ -2513,8 +2513,8 @@ function node_feed($nids = FALSE, $channel = array()) {
* An array in the format expected by drupal_render().
*/
function node_view_multiple($nodes, $view_mode = 'teaser', $weight = 0, $langcode = NULL) {
- field_attach_prepare_view('node', $nodes, $view_mode);
- entity_prepare_view('node', $nodes);
+ field_attach_prepare_view('node', $nodes, $view_mode, $langcode);
+ entity_prepare_view('node', $nodes, $langcode);
$build = array();
foreach ($nodes as $node) {
$build['nodes'][$node->nid] = node_view($node, $view_mode, $langcode);
diff --git a/modules/openid/openid.module b/modules/openid/openid.module
index 6d4b1d7ff..7673de886 100644
--- a/modules/openid/openid.module
+++ b/modules/openid/openid.module
@@ -341,18 +341,14 @@ function openid_complete($response = array()) {
$response['openid.claimed_id'] = $service['claimed_id'];
}
elseif ($service['version'] == 2) {
- // Returned Claimed Identifier could contain unique fragment
- // identifier to allow identifier recycling so we need to preserve
- // it in the response.
- $response_claimed_id = openid_normalize($response['openid.claimed_id']);
-
+ $response['openid.claimed_id'] = openid_normalize($response['openid.claimed_id']);
// OpenID Authentication, section 11.2:
// If the returned Claimed Identifier is different from the one sent
// to the OpenID Provider, we need to do discovery on the returned
// identififer to make sure that the provider is authorized to
// respond on behalf of this.
- if ($response_claimed_id != $claimed_id) {
- $services = openid_discovery($response_claimed_id);
+ if ($response['openid.claimed_id'] != $claimed_id) {
+ $services = openid_discovery($response['openid.claimed_id']);
$uris = array();
foreach ($services as $discovered_service) {
if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) {
diff --git a/modules/openid/openid.test b/modules/openid/openid.test
index 09632ba14..202a8355e 100644
--- a/modules/openid/openid.test
+++ b/modules/openid/openid.test
@@ -89,12 +89,12 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
// Identifier is the URL of an XRDS document containing an OP Identifier
// Element. The Relying Party sends the special value
// "http://specs.openid.net/auth/2.0/identifier_select" as Claimed
- // Identifier. The OpenID Provider responds with the actual identifier
- // including the fragment.
- $identity = url('openid-test/yadis/xrds/dummy-user', array('absolute' => TRUE, 'fragment' => $this->randomName()));
- // Tell openid_test.module to respond with this identifier. We test if
- // openid_complete() processes it right.
- variable_set('openid_test_response', array('openid.claimed_id' => $identity));
+ // Identifier. The OpenID Provider responds with the actual identifier.
+ $identity = url('openid-test/yadis/xrds/dummy-user', array('absolute' => TRUE));
+ // Tell openid_test.module to respond with this identifier. The URL scheme
+ // is stripped in order to test that the returned identifier is normalized in
+ // openid_complete().
+ variable_set('openid_test_response', array('openid.claimed_id' => preg_replace('@^https?://@', '', $identity)));
$this->addIdentity(url('openid-test/yadis/xrds/server', array('absolute' => TRUE)), 2, 'http://specs.openid.net/auth/2.0/identifier_select', $identity);
variable_set('openid_test_response', array());
diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test
index 231355ceb..c22d1fc5d 100644
--- a/modules/simpletest/tests/database_test.test
+++ b/modules/simpletest/tests/database_test.test
@@ -1325,6 +1325,27 @@ class DatabaseSelectTestCase extends DatabaseTestCase {
}
/**
+ * Test query COMMENT system against vulnerabilities.
+ */
+ function testVulnerableComment() {
+ $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $query = (string)$query;
+ $expected = "/* Testing query comments SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
+
+ $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
+ $this->assertEqual($query, $expected, t('The flattened query contains the sanitised comment string.'));
+ }
+
+ /**
* Test basic conditionals on SELECT statements.
*/
function testSimpleSelectConditional() {
diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test
index 2578bebc0..5642fcee0 100644
--- a/modules/simpletest/tests/menu.test
+++ b/modules/simpletest/tests/menu.test
@@ -857,6 +857,64 @@ class MenuTreeDataTestCase extends DrupalUnitTestCase {
}
/**
+ * Menu tree output related tests.
+ */
+class MenuTreeOutputTestCase extends DrupalWebTestCase {
+ /**
+ * Dummy link structure acceptable for menu_tree_output().
+ */
+ var $tree_data = array(
+ '1'=> array(
+ 'link' => array( 'menu_name' => 'main-menu', 'mlid' => 1, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a', 'localized_options' => array('attributes' => array('title' =>'')) ),
+ 'below' => array(
+ '2' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 2, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')) ),
+ 'below' => array(
+ '3' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 3, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')) ),
+ 'below' => array() ),
+ '4' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 4, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')) ),
+ 'below' => array() )
+ )
+ )
+ )
+ ),
+ '5' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 5, 'hidden'=>1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access'=>1, 'href' => 'e', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ),
+ '6' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 6, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access'=>0, 'href' => 'f', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ),
+ '7' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 7, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access'=>1, 'href' => 'g', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) )
+ );
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Menu tree output',
+ 'description' => 'Tests menu tree output functions.',
+ 'group' => 'Menu',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ /**
+ * Validate the generation of a proper menu tree output.
+ */
+ function testMenuTreeData() {
+ $output = menu_tree_output($this->tree_data);
+
+ // Validate that the - in main-menu is changed into an underscore
+ $this->assertEqual( $output['1']['#theme'], 'menu_link__main_menu', t('Hyphen is changed to a dash on menu_link'));
+ $this->assertEqual( $output['#theme_wrappers'][0], 'menu_tree__main_menu', t('Hyphen is changed to a dash on menu_tree wrapper'));
+ // Looking for child items in the data
+ $this->assertEqual( $output['1']['#below']['2']['#href'], 'a/b', t('Checking the href on a child item'));
+ $this->assertTrue( in_array('active-trail',$output['1']['#below']['2']['#attributes']['class']) , t('Checking the active trail class'));
+ // Validate that the hidden and no access items are missing
+ $this->assertFalse( isset($output['5']), t('Hidden item should be missing'));
+ $this->assertFalse( isset($output['6']), t('False access should be missing'));
+ // Item 7 is after a couple hidden items. Just to make sure that 5 and 6 are skipped and 7 still included
+ $this->assertTrue( isset($output['7']), t('Item after hidden items is present'));
+ }
+}
+
+/**
* Menu breadcrumbs related tests.
*/
class MenuBreadcrumbTestCase extends DrupalWebTestCase {
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index 1b295b4f4..c7db6f1dd 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -87,16 +87,16 @@ function hook_hook_info_alter(&$hooks) {
* - uri callback: A function taking an entity as argument and returning the
* uri elements of the entity, e.g. 'path' and 'options'. The actual entity
* uri can be constructed by passing these elements to url().
- * - label callback: (optional) A function taking an entity as argument and
- * returning the label of the entity. The entity label is the main string
- * associated with an entity; for example, the title of a node or the
- * subject of a comment. If there is an entity object property that defines
- * the label, use the 'label' element of the 'entity keys' return
- * value component to provide this information (see below). If more complex
- * logic is needed to determine the label of an entity, you can instead
- * specify a callback function here, which will be called to determine the
- * entity label. See also the entity_label() function, which implements this
- * logic.
+ * - label callback: (optional) A function taking an entity and an entity type
+ * as arguments and returning the label of the entity. The entity label is
+ * the main string associated with an entity; for example, the title of a
+ * node or the subject of a comment. If there is an entity object property
+ * that defines the label, use the 'label' element of the 'entity keys'
+ * return value component to provide this information (see below). If more
+ * complex logic is needed to determine the label of an entity, you can
+ * instead specify a callback function here, which will be called to
+ * determine the entity label. See also the entity_label() function, which
+ * implements this logic.
* - fieldable: Set to TRUE if you want your entity type to accept fields
* being attached to it.
* - translation: An associative array of modules registered as field
@@ -502,8 +502,10 @@ function hook_admin_paths_alter(&$paths) {
* The entities keyed by entity ID.
* @param $type
* The type of entities being loaded (i.e. node, user, comment).
+ * @param $langcode
+ * The language to display the entity in.
*/
-function hook_entity_prepare_view($entities, $type) {
+function hook_entity_prepare_view($entities, $type, $langcode) {
// Load a specific node into the user object for later theming.
if ($type == 'user') {
$nodes = mymodule_get_user_nodes(array_keys($entities));
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index 5fb41e61f..50d2fd608 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -682,8 +682,8 @@ function taxonomy_term_view($term, $view_mode = 'full', $langcode = NULL) {
$langcode = $GLOBALS['language_content']->language;
}
- field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode);
- entity_prepare_view('taxonomy_term', array($term->tid => $term));
+ field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode);
+ entity_prepare_view('taxonomy_term', array($term->tid => $term), $langcode);
$build = array(
'#theme' => 'taxonomy_term',
diff --git a/modules/user/user.module b/modules/user/user.module
index 204155a4c..90d313b10 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -442,27 +442,18 @@ function user_save($account, $edit = array(), $category = 'account') {
user_module_invoke('presave', $edit, $account, $category);
// Invoke presave operations of Field Attach API and Entity API. Those APIs
- // require a fully-fledged (and updated) entity object, so $edit is not
- // necessarily sufficient, as it technically contains submitted form values
- // only. Therefore, we need to clone $account into a new object and copy any
- // new property values of $edit into it.
- $account_updated = clone $account;
+ // 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_updated->$key = $value;
- }
- field_attach_presave('user', $account_updated);
- module_invoke_all('entity_presave', $account_updated, 'user');
- // Update $edit with any changes modules might have applied to the account.
- foreach ($account_updated as $key => $value) {
- if (!property_exists($account, $key) || $value !== $account->$key) {
- $edit[$key] = $value;
- }
+ $account->$key = $value;
}
+ field_attach_presave('user', $account);
+ module_invoke_all('entity_presave', $account, 'user');
if (is_object($account) && !$account->is_new) {
// Process picture uploads.
- if (!$delete_previous_picture = empty($edit['picture']->fid)) {
- $picture = $edit['picture'];
+ 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) {
@@ -475,26 +466,23 @@ function user_save($account, $edit = array(), $category = 'account') {
// Move the temporary file into the final location.
if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
- $delete_previous_picture = TRUE;
$picture->status = FILE_STATUS_PERMANENT;
- $edit['picture'] = file_save($picture);
+ $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);
+ }
}
-
- // Delete the previous picture if it was deleted or replaced.
- if ($delete_previous_picture && !empty($account->picture->fid)) {
- file_usage_delete($account->picture, 'user', 'user', $account->uid);
- file_delete($account->picture);
- }
-
- $edit['picture'] = empty($edit['picture']->fid) ? 0 : $edit['picture']->fid;
+ $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid;
// Do not allow 'uid' to be changed.
- $edit['uid'] = $account->uid;
+ $account->uid = $account->original->uid;
// Save changes to the user table.
- $success = drupal_write_record('users', $edit, 'uid');
+ $success = drupal_write_record('users', $account, 'uid');
if ($success === FALSE) {
// The query failed - better to abort the save than risk further
// data loss.
@@ -502,13 +490,13 @@ function user_save($account, $edit = array(), $category = 'account') {
}
// Reload user roles if provided.
- if (isset($edit['roles']) && is_array($edit['roles'])) {
+ 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($edit['roles']) as $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,
@@ -520,13 +508,13 @@ function user_save($account, $edit = array(), $category = 'account') {
}
// Delete a blocked user's sessions to kick them if they are online.
- if (isset($edit['status']) && $edit['status'] == 0) {
+ 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 (!empty($edit['pass'])) {
+ if ($account->pass != $account->original->pass) {
drupal_session_destroy_uid($account->uid);
if ($account->uid == $GLOBALS['user']->uid) {
drupal_session_regenerate();
@@ -534,60 +522,56 @@ function user_save($account, $edit = array(), $category = 'account') {
}
// Save Field data.
- $entity = (object) $edit;
- field_attach_update('user', $entity);
-
- // Refresh user object.
- $user = user_load($account->uid, TRUE);
- // Make the original, unchanged user account available to update hooks.
- if (isset($account->original)) {
- $user->original = $account->original;
- }
+ field_attach_update('user', $account);
// Send emails after we have the new user object.
- if (isset($edit['status']) && $edit['status'] != $account->status) {
+ if ($account->status != $account->original->status) {
// The user's status is changing; conditionally send notification email.
- $op = $edit['status'] == 1 ? 'status_activated' : 'status_blocked';
- _user_mail_notify($op, $user);
+ $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
+ _user_mail_notify($op, $account);
}
- user_module_invoke('update', $edit, $user, $category);
- module_invoke_all('entity_update', $user, 'user');
- unset($user->original);
+ // 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, $category);
+ 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($edit['uid'])) {
- $edit['uid'] = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
+ 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($edit['created'])) {
- $edit['created'] = REQUEST_TIME;
+ if (!isset($account->created)) {
+ $account->created = REQUEST_TIME;
}
- $success = drupal_write_record('users', $edit);
+ $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;
}
- // Build a stub user object.
- $user = (object) $edit;
- $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+ // Make sure $account is properly initialized.
+ $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
- field_attach_insert('user', $user);
-
- user_module_invoke('insert', $edit, $user, $category);
- module_invoke_all('entity_insert', $user, 'user');
+ field_attach_insert('user', $account);
+ $edit = (array) $account;
+ user_module_invoke('insert', $edit, $account, $category);
+ module_invoke_all('entity_insert', $account, 'user');
// Save user roles.
- if (isset($edit['roles']) && is_array($edit['roles'])) {
+ if (count($account->roles) > 1) {
$query = db_insert('users_roles')->fields(array('uid', 'rid'));
- foreach (array_keys($edit['roles']) as $rid) {
+ foreach (array_keys($account->roles) as $rid) {
if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
$query->values(array(
- 'uid' => $edit['uid'],
+ 'uid' => $account->uid,
'rid' => $rid,
));
}
@@ -595,8 +579,13 @@ function user_save($account, $edit = array(), $category = 'account') {
$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 $user;
+ return $account;
}
catch (Exception $e) {
$transaction->rollback();
@@ -2523,8 +2512,8 @@ function user_build_content($account, $view_mode = 'full', $langcode = NULL) {
$account->content = array();
// Build fields content.
- field_attach_prepare_view('user', array($account->uid => $account), $view_mode);
- entity_prepare_view('user', array($account->uid => $account));
+ field_attach_prepare_view('user', array($account->uid => $account), $view_mode, $langcode);
+ entity_prepare_view('user', array($account->uid => $account), $langcode);
$account->content += field_attach_view('user', $account, $view_mode, $langcode);
// Populate $account->content with a render() array.