diff --git a/core/includes/file.inc b/core/includes/file.inc
index 6e6611f..fd350f1 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -1225,6 +1225,7 @@ function file_create_filename($basename, $directory) {
  * @see file_unmanaged_delete()
  * @see file_usage_list()
  * @see file_usage_delete()
+ * @see hook_file_predelete()
  * @see hook_file_delete()
  */
 function file_delete(stdClass $file, $force = FALSE) {
@@ -1246,15 +1247,20 @@ function file_delete(stdClass $file, $force = FALSE) {
     return $references;
   }
 
-  // Let other modules clean up any references to the deleted file.
-  module_invoke_all('file_delete', $file);
-  module_invoke_all('entity_delete', $file, 'file');
+  // Let other modules clean up any references to the file prior to deletion.
+  module_invoke_all('file_predelete', $file);
+  module_invoke_all('entity_predelete', $file, 'file');
 
   // Make sure the file is deleted before removing its row from the
   // database, so UIs can still find the file in the database.
   if (file_unmanaged_delete($file->uri)) {
     db_delete('file_managed')->condition('fid', $file->fid)->execute();
     db_delete('file_usage')->condition('fid', $file->fid)->execute();
+
+    // Let other modules respond to file deletion.
+    module_invoke_all('file_delete', $file);
+    module_invoke_all('entity_delete', $file, 'file');
+
     return TRUE;
   }
   return FALSE;
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 1080984..e4c557a 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -884,9 +884,9 @@ function book_node_update($node) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function book_node_delete($node) {
+function book_node_predelete($node) {
   if (!empty($node->book['bid'])) {
     if ($node->nid == $node->book['bid']) {
       // Handle deletion of a top-level post.
diff --git a/core/modules/comment/comment.api.php b/core/modules/comment/comment.api.php
index 0591265..5401556 100644
--- a/core/modules/comment/comment.api.php
+++ b/core/modules/comment/comment.api.php
@@ -129,12 +129,37 @@ function hook_comment_unpublish($comment) {
 }
 
 /**
- * The comment is being deleted by the moderator.
+ * Act before comment deletion.
+ *
+ * This hook is invoked from comment_delete_multiple() before
+ * field_attach_delete() is called and before the comment is actually removed
+ * from the database.
  *
  * @param $comment
- *   Passes in the comment the action is being performed on.
- * @return
- *   Nothing.
+ *   The comment object for the comment that is about to be deleted.
+ *
+ * @see hook_comment_delete()
+ * @see comment_delete_multiple()
+ */
+function hook_comment_predelete($comment) {
+  // Delete a record associated with the comment in a custom table.
+  db_delete('example_comment_table')
+    ->condition('cid', $comment->cid)
+    ->execute();
+}
+
+/**
+ * Respond to comment deletion.
+ *
+ * This hook is invoked from comment_delete_multiple() after
+ * field_attach_delete() has called and after the comment has been removed from
+ * the database.
+ *
+ * @param $comment
+ *   The comment object for the comment that has been deleted.
+ *
+ * @see hook_comment_predelete()
+ * @see comment_delete_multiple()
  */
 function hook_comment_delete($comment) {
   drupal_set_message(t('Comment: @subject has been deleted', array('@subject' => $comment->subject)));
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index ae9278c..a8dc9bb 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1303,9 +1303,9 @@ function comment_node_insert($node) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function comment_node_delete($node) {
+function comment_node_predelete($node) {
   $cids = db_query('SELECT cid FROM {comment} WHERE nid = :nid', array(':nid' => $node->nid))->fetchCol();
   comment_delete_multiple($cids);
   db_delete('node_comment_statistics')
@@ -1401,9 +1401,9 @@ function comment_user_cancel($edit, $account, $method) {
 }
 
 /**
- * Implements hook_user_delete().
+ * Implements hook_user_predelete().
  */
-function comment_user_delete($account) {
+function comment_user_predelete($account) {
   $cids = db_query('SELECT c.cid FROM {comment} c WHERE uid = :uid', array(':uid' => $account->uid))->fetchCol();
   comment_delete_multiple($cids);
 }
@@ -1598,18 +1598,29 @@ function comment_delete($cid) {
  *
  * @param $cids
  *   The comment to delete.
+ *
+ * @see hook_comment_predelete()
+ * @see hook_comment_delete()
  */
 function comment_delete_multiple($cids) {
   $comments = comment_load_multiple($cids);
   if ($comments) {
     $transaction = db_transaction();
     try {
+      foreach ($comments as $comment) {
+        // Allow modules to act before the comment is deleted.
+        module_invoke_all('comment_predelete', $comment);
+        module_invoke_all('entity_predelete', $comment, 'comment');
+      }
+
       // Delete the comments.
       db_delete('comment')
         ->condition('cid', array_keys($comments), 'IN')
         ->execute();
       foreach ($comments as $comment) {
         field_attach_delete('comment', $comment);
+
+        // Allow modules to respond to the comment's deletion.
         module_invoke_all('comment_delete', $comment);
         module_invoke_all('entity_delete', $comment, 'comment');
 
diff --git a/core/modules/entity/entity.api.php b/core/modules/entity/entity.api.php
index 9b19477..0082fc0 100644
--- a/core/modules/entity/entity.api.php
+++ b/core/modules/entity/entity.api.php
@@ -270,7 +270,7 @@ function hook_entity_insert($entity, $type) {
  * @param $entity
  *   The entity object.
  * @param $type
- *   The type of entity being updated (i.e. node, user, comment).
+ *   The type of entity being updated (e.g. node, user, comment).
  */
 function hook_entity_update($entity, $type) {
   // Update the entity's entry in a fictional table of all entities.
@@ -286,10 +286,42 @@ function hook_entity_update($entity, $type) {
 }
 
 /**
- * Act on entities when deleted.
+ * Act before entity deletion.
+ *
+ * This hook runs after the entity-specific predelete hook.
  *
  * @param $entity
- *   The entity object.
+ *   The entity object for the entity that is about to be deleted.
+ * @param $type
+ *   The type of entity being deleted (e.g. node, user, comment).
+ */
+function hook_entity_predelete($entity, $type) {
+  // Count references to this entity in a custom table before they are removed
+  // upon entity deletion.
+  list($id) = entity_extract_ids($type, $entity);
+  $count = db_select('example_entity_data')
+    ->condition('type', $type)
+    ->condition('id', $id)
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+
+  // Log the count in a table that records this statistic for deleted entities.
+  $ref_count_record = (object) array(
+    'count' => $count,
+    'type' => $type,
+    'id' => $id,
+  );
+  drupal_write_record('example_deleted_entity_statistics', $ref_count_record);
+}
+
+/**
+ * Respond to entity deletion.
+ *
+ * This hook runs after the entity-specific delete hook.
+ *
+ * @param $entity
+ *   The entity object for the entity that has been deleted.
  * @param $type
  *   The type of entity being deleted (i.e. node, user, comment).
  */
diff --git a/core/modules/entity/tests/entity_crud_hook_test.module b/core/modules/entity/tests/entity_crud_hook_test.module
index 873a162..435f7bf 100644
--- a/core/modules/entity/tests/entity_crud_hook_test.module
+++ b/core/modules/entity/tests/entity_crud_hook_test.module
@@ -213,6 +213,59 @@ function entity_crud_hook_test_user_update() {
 }
 
 //
+// Predelete hooks
+//
+
+/**
+ * Implements hook_entity_predelete().
+ */
+function entity_crud_hook_test_entity_predelete($entity, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_predelete().
+ */
+function entity_crud_hook_test_comment_predelete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_predelete().
+ */
+function entity_crud_hook_test_file_predelete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_predelete().
+ */
+function entity_crud_hook_test_node_predelete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_predelete().
+ */
+function entity_crud_hook_test_taxonomy_term_predelete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_predelete().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_predelete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_predelete().
+ */
+function entity_crud_hook_test_user_predelete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+//
 // Delete hooks
 //
 
diff --git a/core/modules/entity/tests/entity_crud_hook_test.test b/core/modules/entity/tests/entity_crud_hook_test.test
index 3f18fc8..906daf8 100644
--- a/core/modules/entity/tests/entity_crud_hook_test.test
+++ b/core/modules/entity/tests/entity_crud_hook_test.test
@@ -6,6 +6,7 @@
  * - hook_entity_insert()
  * - hook_entity_load()
  * - hook_entity_update()
+ * - hook_entity_predelete()
  * - hook_entity_delete()
  * As well as all type-specific hooks, like hook_node_insert(),
  * hook_comment_update(), etc.
@@ -103,6 +104,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     $_SESSION['entity_crud_hook_test'] = array();
     comment_delete($comment->cid);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_predelete called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_predelete called');
     $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment');
     $this->assertHookMessage('entity_crud_hook_test_comment_delete called');
   }
@@ -149,6 +152,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     $_SESSION['entity_crud_hook_test'] = array();
     file_delete($file);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_predelete called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_predelete called');
     $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file');
     $this->assertHookMessage('entity_crud_hook_test_file_delete called');
   }
@@ -195,6 +200,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     $_SESSION['entity_crud_hook_test'] = array();
     node_delete($node->nid);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_predelete called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_predelete called');
     $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node');
     $this->assertHookMessage('entity_crud_hook_test_node_delete called');
   }
@@ -243,6 +250,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     $_SESSION['entity_crud_hook_test'] = array();
     taxonomy_term_delete($term->tid);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_predelete called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_predelete called');
     $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term');
     $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called');
   }
@@ -283,6 +292,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     $_SESSION['entity_crud_hook_test'] = array();
     taxonomy_vocabulary_delete($vocabulary->vid);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_predelete called');
     $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary');
     $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called');
   }
@@ -325,6 +336,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     $_SESSION['entity_crud_hook_test'] = array();
     user_delete($account->uid);
 
+    $this->assertHookMessage('entity_crud_hook_test_entity_predelete called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_predelete called');
     $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user');
     $this->assertHookMessage('entity_crud_hook_test_user_delete called');
   }
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 204a48f..afa7483 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -341,9 +341,9 @@ function file_progress_implementation() {
 }
 
 /**
- * Implements hook_file_delete().
+ * Implements hook_file_predelete().
  */
-function file_file_delete($file) {
+function file_file_predelete($file) {
   // TODO: Remove references to a file that is in-use.
 }
 
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 65c5489..045cc3c 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -404,9 +404,9 @@ function forum_node_insert($node) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function forum_node_delete($node) {
+function forum_node_predelete($node) {
   if (_forum_node_check_node_type($node)) {
     db_delete('forum')
       ->condition('nid', $node->nid)
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 54d98eb..ddcc55e 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -323,9 +323,9 @@ function image_file_move($file, $source) {
 }
 
 /**
- * Implements hook_file_delete().
+ * Implements hook_file_predelete().
  */
-function image_file_delete($file) {
+function image_file_predelete($file) {
   // Delete any image derivatives of this image.
   image_path_flush($file->uri);
 }
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index cd42256..df41599 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -532,9 +532,9 @@ function menu_node_save($node) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function menu_node_delete($node) {
+function menu_node_predelete($node) {
   // Delete all menu module links that point to this node.
   $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path AND module = 'menu'", array(':path' => 'node/' . $node->nid), array('fetch' => PDO::FETCH_ASSOC));
   foreach ($result as $m) {
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index 66eb26a..09319ac 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -88,10 +88,12 @@
  * - Deleting a node (calling node_delete() or node_delete_multiple()):
  *   - Node is loaded (see Loading section above)
  *   - hook_delete() (node-type-specific)
- *   - hook_node_delete() (all)
- *   - hook_entity_delete() (all)
+ *   - hook_node_predelete() (all)
+ *   - hook_entity_predelete() (all)
  *   - field_attach_delete()
  *   - Node and revision information are deleted from database
+ *   - hook_node_delete() (all)
+ *   - hook_entity_delete() (all)
  * - Deleting a node revision (calling node_revision_delete()):
  *   - Node is loaded (see Loading section above)
  *   - Revision information is deleted from database
@@ -453,16 +455,18 @@ function hook_node_operations() {
 }
 
 /**
- * Respond to node deletion.
+ * Act before node deletion.
  *
  * This hook is invoked from node_delete_multiple() after the type-specific
- * hook_delete() has been invoked, but before hook_entity_delete and
+ * hook_delete() has been invoked, but before hook_entity_predelete() and
  * field_attach_delete() are called, and before the node is removed from the
  * node table in the database.
  *
  * @param $node
- *   The node that is being deleted.
+ *   The node that is about to be deleted.
  *
+ * @see hook_node_delete()
+ * @see node_delete_multiple()
  * @ingroup node_api_hooks
  */
 function hook_node_delete($node) {
@@ -472,6 +476,23 @@ function hook_node_delete($node) {
 }
 
 /**
+ * Respond to node deletion.
+ *
+ * This hook is invoked from node_delete_multiple() after field_attach_delete()
+ * has been called and after the node has been removed from the database.
+ *
+ * @param $node
+ *   The node that has been deleted.
+ *
+ * @see hook_node_predelete()
+ * @see node_delete_multiple()
+ * @ingroup node_api_hooks
+ */
+function hook_node_delete($node) {
+  drupal_set_message(t('Node: @title has been deleted', array('@title' => $node->title)));
+}
+
+/**
  * Respond to deletion of a node revision.
  *
  * This hook is invoked from node_revision_delete() after the revision has been
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index c429fe5..fb7de26 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1189,6 +1189,9 @@ function node_delete($nid) {
  *
  * @param $nids
  *   An array of node IDs.
+ *
+ * @see hook_node_predelete()
+ * @see hook_node_delete()
  */
 function node_delete_multiple($nids) {
   $transaction = db_transaction();
@@ -1199,8 +1202,11 @@ function node_delete_multiple($nids) {
       foreach ($nodes as $nid => $node) {
         // Call the node-specific callback (if any):
         node_invoke($node, 'delete');
-        module_invoke_all('node_delete', $node);
-        module_invoke_all('entity_delete', $node, 'node');
+
+        // Allow modules to act prior to node deletion.
+        module_invoke_all('node_predelete', $node);
+        module_invoke_all('entity_predelete', $node, 'node');
+
         field_attach_delete('node', $node);
 
         // Remove this node from the search index if needed.
@@ -1225,6 +1231,12 @@ function node_delete_multiple($nids) {
       db_delete('node_access')
        ->condition('nid', $nids, 'IN')
        ->execute();
+
+      foreach ($nodes as $nid => $node) {
+        // Allow modules to respond to node deletion.
+        module_invoke_all('node_delete', $node);
+        module_invoke_all('entity_delete', $node, 'node');
+      }
     }
     catch (Exception $e) {
       $transaction->rollback();
@@ -1767,9 +1779,9 @@ function node_user_cancel($edit, $account, $method) {
 }
 
 /**
- * Implements hook_user_delete().
+ * Implements hook_user_predelete().
  */
-function node_user_delete($account) {
+function node_user_predelete($account) {
   // Delete nodes (current revisions).
   // @todo Introduce node_mass_delete() or make node_mass_update() more flexible.
   $nodes = db_select('node', 'n')
diff --git a/core/modules/node/tests/node_access_test.module b/core/modules/node/tests/node_access_test.module
index 91c117a..83d7181 100644
--- a/core/modules/node/tests/node_access_test.module
+++ b/core/modules/node/tests/node_access_test.module
@@ -184,10 +184,10 @@ function node_access_test_node_load($nodes, $types) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
 
-function node_access_test_node_delete($node) {
+function node_access_test_node_predelete($node) {
   db_delete('node_access_test')->condition('nid', $node->nid)->execute();
 }
 
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index df11936..563bfc7 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -217,9 +217,9 @@ function path_node_update($node) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function path_node_delete($node) {
+function path_node_predelete($node) {
   // Delete all aliases associated with this node.
   path_delete(array('source' => 'node/' . $node->nid));
 }
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index ec5452e..7fe34e4 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -986,9 +986,9 @@ function poll_user_cancel($edit, $account, $method) {
 }
 
 /**
- * Implements hook_user_delete().
+ * Implements hook_user_predelete().
  */
-function poll_user_delete($account) {
+function poll_user_predelete($account) {
   db_delete('poll_vote')
     ->condition('uid', $account->uid)
     ->execute();
diff --git a/core/modules/simpletest/tests/file_test.module b/core/modules/simpletest/tests/file_test.module
index b3c43e0..ebe97e4 100644
--- a/core/modules/simpletest/tests/file_test.module
+++ b/core/modules/simpletest/tests/file_test.module
@@ -303,9 +303,9 @@ function file_test_file_move($file, $source) {
 }
 
 /**
- * Implements hook_file_delete().
+ * Implements hook_file_predelete().
  */
-function file_test_file_delete($file) {
+function file_test_file_predelete($file) {
   _file_test_log_call('delete', array($file));
 }
 
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 4f72553..191d82b 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -218,9 +218,9 @@ function statistics_user_cancel($edit, $account, $method) {
 }
 
 /**
- * Implements hook_user_delete().
+ * Implements hook_user_predelete().
  */
-function statistics_user_delete($account) {
+function statistics_user_predelete($account) {
   db_delete('accesslog')
     ->condition('uid', $account->uid)
     ->execute();
@@ -391,9 +391,9 @@ function _statistics_format_item($title, $path) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function statistics_node_delete($node) {
+function statistics_node_predelete($node) {
   // clean up statistics table when node is deleted
   db_delete('node_counter')
     ->condition('nid', $node->nid)
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 0684f26..88d672d 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -2316,20 +2316,41 @@ function hook_file_move($file, $source) {
 }
 
 /**
- * Respond to a file being deleted.
+ * Act prior to file deletion.
+ *
+ * This hook is invoked from file_delete() before the file is removed from the
+ * filesystem and before its records are removed from the database.
  *
  * @param $file
- *   The file that has just been deleted.
+ *   The file that is about to be deleted.
  *
+ * @see hook_file_delete()
  * @see file_delete()
  * @see upload_file_delete()
  */
-function hook_file_delete($file) {
+function hook_file_predelete($file) {
   // Delete all information associated with the file.
   db_delete('upload')->condition('fid', $file->fid)->execute();
 }
 
 /**
+ * Respond to file deletion.
+ *
+ * This hook is invoked from file_delete() after the file has been removed from
+ * the filesystem and after its records have been removed from the database.
+ *
+ * @param $file
+ *   The file that has just been deleted.
+ *
+ * @see hook_file_predelete()
+ * @see file_delete()
+ * @see upload_file_delete()
+ */
+function hook_file_delete($file) {
+  drupal_set_message(t('File: @file has been deleted', array('@file' => $file->uri)));
+}
+
+/**
  * Control access to private file downloads and specify HTTP headers.
  *
  * This hook allows modules enforce permissions on file downloads when the
diff --git a/core/modules/taxonomy/taxonomy.api.php b/core/modules/taxonomy/taxonomy.api.php
index cb778c9..426306d 100644
--- a/core/modules/taxonomy/taxonomy.api.php
+++ b/core/modules/taxonomy/taxonomy.api.php
@@ -70,13 +70,37 @@ function hook_taxonomy_vocabulary_update($vocabulary) {
 }
 
 /**
- * Respond to the deletion of taxonomy vocabularies.
+ * Act before taxonomy vocabulary deletion.
  *
- * Modules implementing this hook can respond to the deletion of taxonomy
- * vocabularies from the database.
+ * This hook is invoked from taxonomy_vocabulary_delete() before
+ * field_attach_delete_bundle() is called and before the vocabulary is actually
+ * removed from the database.
  *
  * @param $vocabulary
- *   A taxonomy vocabulary object.
+ *   The taxonomy vocabulary object for the vocabulary that is about to be
+ *   deleted.
+ *
+ * @see hook_taxonomy_vocabulary_delete()
+ * @see taxonomy_vocabulary_delete()
+ */
+function hook_taxonomy_vocabulary_predelete($vocabulary) {
+  if (variable_get('taxonomy_' . $vocabulary->vid . '_synonyms', FALSE)) {
+    variable_del('taxonomy_' . $vocabulary->vid . '_synonyms');
+  }
+}
+
+/**
+ * Respond to taxonomy vocabulary deletion.
+ *
+ * This hook is invoked from taxonomy_vocabulary_delete() after
+ * field_attach_delete_bundle() has been called and after the vocabulary has
+ * been removed from the database.
+ *
+ * @param $vocabulary
+ *   The taxonomy vocabulary object for the vocabulary that has been deleted.
+ *
+ * @see hook_taxonomy_vocabulary_predelete()
+ * @see taxonomy_vocabulary_delete()
  */
 function hook_taxonomy_vocabulary_delete($vocabulary) {
   if (variable_get('taxonomy_' . $vocabulary->vid . '_synonyms', FALSE)) {
@@ -169,13 +193,31 @@ function hook_taxonomy_term_update($term) {
 }
 
 /**
- * Respond to the deletion of taxonomy terms.
+ * Act before taxonomy term deletion.
  *
- * Modules implementing this hook can respond to the deletion of taxonomy
- * terms from the database.
+ * This hook is invoked from taxonomy_term_delete() before
+ * field_attach_delete() is called and before the term is actually removed from
+ * the database.
  *
  * @param $term
- *   A taxonomy term object.
+ *   The taxonomy term object for the term that is about to be deleted.
+ *
+ * @see taxonomy_term_delete()
+ */
+function hook_taxonomy_term_predelete($term) {
+  db_delete('term_synoynm')->condition('tid', $term->tid)->execute();
+}
+
+/**
+ * Respond to taxonomy term deletion.
+ *
+ * This hook is invoked from taxonomy_term_delete() after field_attach_delete()
+ * has been called and after the term has been removed from the database.
+ *
+ * @param $term
+ *   The taxonomy term object for the term that has been deleted.
+ *
+ * @see taxonomy_term_delete()
  */
 function hook_taxonomy_term_delete($term) {
   db_delete('term_synoynm')->condition('tid', $term->tid)->execute();
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index eb20c26..db01d74 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -449,12 +449,19 @@ function taxonomy_vocabulary_save($vocabulary) {
  *   A vocabulary ID.
  * @return
  *   Constant indicating items were deleted.
+ *
+ * @see hook_taxonomy_vocabulary_predelete()
+ * @see hook_taxonomy_vocabulary_delete()
  */
 function taxonomy_vocabulary_delete($vid) {
   $vocabulary = taxonomy_vocabulary_load($vid);
 
   $transaction = db_transaction();
   try {
+    // Allow modules to act before vocabulary deletion.
+    module_invoke_all('taxonomy_vocabulary_predelete', $vocabulary);
+    module_invoke_all('entity_predelete', $vocabulary, 'taxonomy_vocabulary');
+
     // Only load terms without a parent, child terms will get deleted too.
     $result = db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid = :vid AND th.parent = 0', array(':vid' => $vid))->fetchCol();
     foreach ($result as $tid) {
@@ -465,6 +472,8 @@ function taxonomy_vocabulary_delete($vid) {
       ->execute();
 
     field_attach_delete_bundle('taxonomy_term', $vocabulary->machine_name);
+
+    // Allow modules to respond to vocabulary deletion.
     module_invoke_all('taxonomy_vocabulary_delete', $vocabulary);
     module_invoke_all('entity_delete', $vocabulary, 'taxonomy_vocabulary');
 
@@ -659,6 +668,9 @@ function taxonomy_term_save($term) {
  *   The term ID.
  * @return
  *   Status constant indicating deletion.
+ *
+ * @see hook_taxonomy_term_predelete()
+ * @see hook_taxonomy_term_delete()
  */
 function taxonomy_term_delete($tid) {
   $transaction = db_transaction();
@@ -667,6 +679,12 @@ function taxonomy_term_delete($tid) {
     while ($tids) {
       $children_tids = $orphans = array();
       foreach ($tids as $tid) {
+        // Allow modules to act before term deletion.
+        if ($term = taxonomy_term_load($tid)) {
+          module_invoke_all('taxonomy_term_predelete', $term);
+          module_invoke_all('entity_predelete', $term, 'taxonomy_term');
+        }
+
         // See if any of the term's children are about to be become orphans:
         if ($children = taxonomy_get_children($tid)) {
           foreach ($children as $child) {
@@ -678,7 +696,7 @@ function taxonomy_term_delete($tid) {
           }
         }
 
-        if ($term = taxonomy_term_load($tid)) {
+        if ($term) {
           db_delete('taxonomy_term_data')
             ->condition('tid', $tid)
             ->execute();
@@ -687,6 +705,8 @@ function taxonomy_term_delete($tid) {
             ->execute();
 
           field_attach_delete('taxonomy_term', $term);
+
+          // Allow modules to respond to term deletion.
           module_invoke_all('taxonomy_term_delete', $term);
           module_invoke_all('entity_delete', $term, 'taxonomy_term');
           taxonomy_terms_static_reset();
@@ -1769,9 +1789,9 @@ function taxonomy_field_update($entity_type, $entity, $field, $instance, $langco
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function taxonomy_node_delete($node) {
+function taxonomy_node_predelete($node) {
   if (variable_get('taxonomy_maintain_index_table', TRUE)) {
     // Clean up the {taxonomy_index} table when nodes are deleted.
     db_delete('taxonomy_index')->condition('nid', $node->nid)->execute();
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index 227cf72..89907b5 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -177,9 +177,9 @@ function tracker_node_update($node, $arg = 0) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function tracker_node_delete($node, $arg = 0) {
+function tracker_node_predelete($node, $arg = 0) {
   db_delete('tracker_node')
     ->condition('nid', $node->nid)
     ->execute();
diff --git a/core/modules/translation/translation.module b/core/modules/translation/translation.module
index 7d47a6d..527a220 100644
--- a/core/modules/translation/translation.module
+++ b/core/modules/translation/translation.module
@@ -387,9 +387,9 @@ function translation_node_validate($node, $form) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function translation_node_delete($node) {
+function translation_node_predelete($node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->type)) {
     translation_remove_from_set($node);
diff --git a/core/modules/trigger/trigger.module b/core/modules/trigger/trigger.module
index c0e516f..75f0133 100644
--- a/core/modules/trigger/trigger.module
+++ b/core/modules/trigger/trigger.module
@@ -309,9 +309,9 @@ function trigger_node_insert($node) {
 }
 
 /**
- * Implements hook_node_delete().
+ * Implements hook_node_predelete().
  */
-function trigger_node_delete($node) {
+function trigger_node_predelete($node) {
   _trigger_node($node, 'node_delete');
 }
 
@@ -501,9 +501,9 @@ function trigger_user_cancel($edit, $account, $method) {
 }
 
 /**
- * Implements hook_user_delete().
+ * Implements hook_user_predelete().
  */
-function trigger_user_delete($account) {
+function trigger_user_predelete($account) {
   $edit = array();
   _trigger_user('user_delete', $edit, $account);
 }
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index 8cf788c..12256fe 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -31,26 +31,47 @@ function hook_user_load($users) {
 }
 
 /**
- * Respond to user deletion.
+ * Act before user deletion.
  *
- * This hook is invoked from user_delete_multiple() before field_attach_delete()
- * is called and before users are actually removed from the database.
+ * This hook is invoked from user_delete_multiple() before
+ * field_attach_delete() is called and before the user is actually removed from
+ * the database.
  *
  * Modules should additionally implement hook_user_cancel() to process stored
  * user data for other account cancellation methods.
  *
  * @param $account
- *   The account that is being deleted.
+ *   The account that is about to be deleted.
  *
+ * @see hook_user_delete()
  * @see user_delete_multiple()
  */
-function hook_user_delete($account) {
+function hook_user_predelete($account) {
   db_delete('mytable')
     ->condition('uid', $account->uid)
     ->execute();
 }
 
 /**
+ * Respond to user deletion.
+ *
+ * This hook is invoked from user_delete_multiple() after field_attach_delete()
+ * has been called and after the user has been removed from the database.
+ *
+ * Modules should additionally implement hook_user_cancel() to process stored
+ * user data for other account cancellation methods.
+ *
+ * @param $account
+ *   The account that has been deleted.
+ *
+ * @see hook_user_predelete()
+ * @see user_delete_multiple()
+ */
+function hook_user_delete($account) {
+  drupal_set_message(t('User: @name has been deleted.', array('@name' => $account->name)));
+}
+
+/**
  * Act on user account cancellations.
  *
  * This hook is invoked from user_cancel() before a user account is canceled.
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 260124c..1a08669 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -843,9 +843,9 @@ function user_file_move($file, $source) {
 }
 
 /**
- * Implements hook_file_delete().
+ * Implements hook_file_predelete().
  */
-function user_file_delete($file) {
+function user_file_predelete($file) {
   // Remove any references to the file.
   db_update('users')
     ->fields(array('picture' => 0))
@@ -2436,6 +2436,9 @@ function user_delete($uid) {
  *
  * @param $uids
  *   An array of user IDs.
+ *
+ * @see hook_user_predelete()
+ * @see hook_user_delete()
  */
 function user_delete_multiple(array $uids) {
   if (!empty($uids)) {
@@ -2444,8 +2447,10 @@ function user_delete_multiple(array $uids) {
     $transaction = db_transaction();
     try {
       foreach ($accounts as $uid => $account) {
-        module_invoke_all('user_delete', $account);
-        module_invoke_all('entity_delete', $account, 'user');
+        // Allow modules to act prior to user deletion.
+        module_invoke_all('user_predelete', $account);
+        module_invoke_all('entity_predelete', $account, 'user');
+
         field_attach_delete('user', $account);
         drupal_session_destroy_uid($account->uid);
       }
@@ -2459,6 +2464,10 @@ function user_delete_multiple(array $uids) {
       db_delete('authmap')
         ->condition('uid', $uids, 'IN')
         ->execute();
+
+        // Allow modules to respond to user deletion.
+        module_invoke_all('user_delete', $account);
+        module_invoke_all('entity_delete', $account, 'user');
     }
     catch (Exception $e) {
       $transaction->rollback();
