Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.239
diff -u -p -r1.239 file.inc
--- includes/file.inc	21 Oct 2010 12:09:41 -0000	1.239
+++ includes/file.inc	11 Nov 2010 03:01:22 -0000
@@ -568,6 +568,9 @@ function file_save(stdClass $file) {
   $file->timestamp = REQUEST_TIME;
   $file->filesize = filesize($file->uri);
 
+  module_invoke_all('file_presave', $file);
+  module_invoke_all('entity_presave', $file, 'file');
+
   if (empty($file->fid)) {
     drupal_write_record('file_managed', $file);
     // Inform modules about the newly added file.
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.913
diff -u -p -r1.913 comment.module
--- modules/comment/comment.module	7 Nov 2010 21:46:09 -0000	1.913
+++ modules/comment/comment.module	11 Nov 2010 03:01:24 -0000
@@ -1433,6 +1433,7 @@ function comment_save($comment) {
 
     // Allow modules to alter the comment before saving.
     module_invoke_all('comment_presave', $comment);
+    module_invoke_all('entity_presave', $comment, 'comment');
 
     if ($comment->cid) {
       // Update the comment in the database.
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1317
diff -u -p -r1.1317 node.module
--- modules/node/node.module	9 Nov 2010 08:36:38 -0000	1.1317
+++ modules/node/node.module	11 Nov 2010 03:01:25 -0000
@@ -1035,6 +1035,7 @@ function node_save($node) {
 
     // Let modules modify the node before it is saved to the database.
     module_invoke_all('node_presave', $node);
+    module_invoke_all('entity_presave', $node, 'node');
 
     if ($node->is_new || !empty($node->revision)) {
       // When inserting either a new node or a new node revision, $node->log
Index: modules/simpletest/tests/entity_crud_hook_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/entity_crud_hook_test.module,v
retrieving revision 1.1
diff -u -p -r1.1 entity_crud_hook_test.module
--- modules/simpletest/tests/entity_crud_hook_test.module	15 Oct 2010 03:36:21 -0000	1.1
+++ modules/simpletest/tests/entity_crud_hook_test.module	11 Nov 2010 03:01:25 -0000
@@ -2,6 +2,59 @@
 // $Id: entity_crud_hook_test.module,v 1.1 2010/10/15 03:36:21 webchick Exp $
 
 //
+// Presave hooks
+//
+
+/**
+ * Implements hook_entity_presave().
+ */
+function entity_crud_hook_test_entity_presave($entity, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_presave().
+ */
+function entity_crud_hook_test_comment_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_presave().
+ */
+function entity_crud_hook_test_file_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_presave().
+ */
+function entity_crud_hook_test_node_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_presave().
+ */
+function entity_crud_hook_test_taxonomy_term_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_presave().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_presave().
+ */
+function entity_crud_hook_test_user_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+//
 // Insert hooks
 //
 
Index: modules/simpletest/tests/entity_crud_hook_test.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/entity_crud_hook_test.test,v
retrieving revision 1.1
diff -u -p -r1.1 entity_crud_hook_test.test
--- modules/simpletest/tests/entity_crud_hook_test.test	15 Oct 2010 03:36:21 -0000	1.1
+++ modules/simpletest/tests/entity_crud_hook_test.test	11 Nov 2010 03:01:25 -0000
@@ -81,6 +81,8 @@ class EntityCrudHookTestCase extends Dru
     $_SESSION['entity_crud_hook_test'] = array();
     comment_save($comment);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment');
     $this->assertHookMessage('entity_crud_hook_test_comment_insert called');
 
@@ -94,6 +96,8 @@ class EntityCrudHookTestCase extends Dru
     $comment->subject = 'New subject';
     comment_save($comment);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment');
     $this->assertHookMessage('entity_crud_hook_test_comment_update called');
 
@@ -123,6 +127,8 @@ class EntityCrudHookTestCase extends Dru
     $_SESSION['entity_crud_hook_test'] = array();
     file_save($file);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file');
     $this->assertHookMessage('entity_crud_hook_test_file_insert called');
 
@@ -136,6 +142,8 @@ class EntityCrudHookTestCase extends Dru
     $file->filename = 'new.entity_crud_hook_test.file';
     file_save($file);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_update called for type file');
     $this->assertHookMessage('entity_crud_hook_test_file_update called');
 
@@ -165,6 +173,8 @@ class EntityCrudHookTestCase extends Dru
     $_SESSION['entity_crud_hook_test'] = array();
     node_save($node);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node');
     $this->assertHookMessage('entity_crud_hook_test_node_insert called');
 
@@ -178,6 +188,8 @@ class EntityCrudHookTestCase extends Dru
     $node->title = 'New title';
     node_save($node);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_update called for type node');
     $this->assertHookMessage('entity_crud_hook_test_node_update called');
 
@@ -209,6 +221,8 @@ class EntityCrudHookTestCase extends Dru
     $_SESSION['entity_crud_hook_test'] = array();
     taxonomy_term_save($term);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term');
     $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called');
 
@@ -222,6 +236,8 @@ class EntityCrudHookTestCase extends Dru
     $term->name = 'New name';
     taxonomy_term_save($term);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term');
     $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called');
 
@@ -245,6 +261,8 @@ class EntityCrudHookTestCase extends Dru
     $_SESSION['entity_crud_hook_test'] = array();
     taxonomy_vocabulary_save($vocabulary);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary');
     $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called');
 
@@ -258,6 +276,8 @@ class EntityCrudHookTestCase extends Dru
     $vocabulary->name = 'New name';
     taxonomy_vocabulary_save($vocabulary);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary');
     $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called');
 
@@ -283,6 +303,8 @@ class EntityCrudHookTestCase extends Dru
     $_SESSION['entity_crud_hook_test'] = array();
     $account = user_save($account, $edit);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user');
     $this->assertHookMessage('entity_crud_hook_test_user_insert called');
 
@@ -296,6 +318,8 @@ class EntityCrudHookTestCase extends Dru
     $edit['name'] = 'New name';
     $account = user_save($account, $edit);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_presave called');
     $this->assertHookMessage('entity_crud_hook_test_entity_update called for type user');
     $this->assertHookMessage('entity_crud_hook_test_user_update called');
 
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.211
diff -u -p -r1.211 system.api.php
--- modules/system/system.api.php	28 Oct 2010 02:27:09 -0000	1.211
+++ modules/system/system.api.php	11 Nov 2010 03:01:26 -0000
@@ -276,6 +276,21 @@ function hook_entity_load($entities, $ty
 }
 
 /**
+ * Act on an entity before it is about to be created or updated.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $type
+ *   The type of entity being inserted (i.e. node, user, comment).
+ */
+function hook_entity_presave($entity, $type) {
+  // Insert the new entity into a fictional table of all entities.
+  $info = entity_get_info($type);
+  $id = reset(entity_extract_ids($type, $entity));
+  $entity->changed = REQUEST_TIME;
+}
+
+/**
  * Act on entities when inserted.
  *
  * @param $entity
@@ -2488,7 +2503,7 @@ function hook_file_validate(&$file) {
 }
 
 /**
- * Respond to a file being added.
+ * Act on a file being inserted or updated.
  *
  * This hook is called when a file has been added to the database. The hook
  * doesn't distinguish between files created as a result of a copy or those
@@ -2499,6 +2514,23 @@ function hook_file_validate(&$file) {
  *
  * @see file_save()
  */
+function hook_file_presave($file) {
+  // Change the file timestamp to an hour prior.
+  $file->timestamp -= 3600;
+}
+
+/**
+ * Respond to a file being added.
+ *
+ * This hook is called before a file has been added to the database. The hook
+ * doesn't distinguish between files created as a result of a copy or those
+ * created by an upload.
+ *
+ * @param $file
+ *   The file that is about to be saved.
+ *
+ * @see file_save()
+ */
 function hook_file_insert($file) {
 
 }
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.616
diff -u -p -r1.616 taxonomy.module
--- modules/taxonomy/taxonomy.module	9 Nov 2010 17:52:57 -0000	1.616
+++ modules/taxonomy/taxonomy.module	11 Nov 2010 03:01:26 -0000
@@ -388,6 +388,7 @@ function taxonomy_vocabulary_save($vocab
   }
 
   module_invoke_all('taxonomy_vocabulary_presave', $vocabulary);
+  module_invoke_all('entity_presave', $vocabulary, 'taxonomy_vocabulary');
 
   if (!empty($vocabulary->vid) && !empty($vocabulary->name)) {
     $status = drupal_write_record('taxonomy_vocabulary', $vocabulary, 'vid');
@@ -524,6 +525,7 @@ function taxonomy_term_save($term) {
 
   field_attach_presave('taxonomy_term', $term);
   module_invoke_all('taxonomy_term_presave', $term);
+  module_invoke_all('entity_presave', $term, 'taxonomy_term');
 
   if (empty($term->tid)) {
     $op = 'insert';
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1214
diff -u -p -r1.1214 user.module
--- modules/user/user.module	6 Nov 2010 23:24:33 -0000	1.1214
+++ modules/user/user.module	11 Nov 2010 03:01:26 -0000
@@ -416,6 +416,10 @@ function user_save($account, $edit = arr
     }
     user_module_invoke('presave', $edit, $account, $category);
 
+    $edit = (object) $edit;
+    module_invoke_all('entity_presave', $edit, 'user');
+    $edit = (array) $edit;
+
     if (is_object($account) && !$account->is_new) {
       // Consider users edited by an administrator as logged in, if they haven't
       // already, so anonymous users can view the profile (if allowed).
Index: modules/user/user.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v
retrieving revision 1.76
diff -u -p -r1.76 user.pages.inc
--- modules/user/user.pages.inc	25 Oct 2010 00:15:11 -0000	1.76
+++ modules/user/user.pages.inc	11 Nov 2010 03:01:27 -0000
@@ -258,6 +258,11 @@ function user_profile_form($form, &$form
   $form['#user'] = $account;
   $form['#user_category'] = $category;
 
+  $form['uid'] = array(
+    '#type' => 'value',
+    '#value' => $account->uid,
+  );
+
   if ($category == 'account') {
     user_account_form($form, $form_state);
     // Attach field widgets.

