Commit a3ebad06 authored by catch's avatar catch

Issue #1346032 by xjm, aspilicious: Fixed Ordering of hook_entity_delete() is inconsistent.

parent 463a710b
......@@ -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;
......
......@@ -959,9 +959,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.
......
......@@ -129,12 +129,39 @@ 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()
* @see entity_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()
* @see entity_delete_multiple()
*/
function hook_comment_delete($comment) {
drupal_set_message(t('Comment: @subject has been deleted', array('@subject' => $comment->subject)));
......
......@@ -1304,9 +1304,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')
......@@ -1402,9 +1402,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);
}
......@@ -1457,6 +1457,9 @@ function comment_delete($cid) {
*
* @param $cids
* The comment to delete.
*
* @see hook_comment_predelete()
* @see hook_comment_delete()
*/
function comment_delete_multiple($cids) {
entity_delete_multiple('comment', $cids);
......
......@@ -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 type-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 type-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).
*/
......
......@@ -454,6 +454,9 @@ public function delete($ids) {
try {
$this->preDelete($entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('predelete', $entity);
}
$ids = array_keys($entities);
db_delete($this->entityInfo['base table'])
......@@ -548,8 +551,8 @@ protected function postDelete($entities) { }
/**
* Invokes a hook on behalf of the entity.
*
* @param $op
* One of 'presave', 'insert', 'update', or 'delete'.
* @param $hook
* One of 'presave', 'insert', 'update', 'predelete', or 'delete'.
* @param $entity
* The entity object.
*/
......
......@@ -212,6 +212,59 @@ function entity_crud_hook_test_user_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
//
// 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
//
......
......@@ -347,9 +347,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.
}
......
......@@ -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)
......
......@@ -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);
}
......
......@@ -550,9 +550,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) {
......
......@@ -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
......@@ -447,24 +449,43 @@ 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_predelete()
* @see node_delete_multiple()
* @ingroup node_api_hooks
*/
function hook_node_delete($node) {
function hook_node_predelete($node) {
db_delete('mytable')
->condition('nid', $node->nid)
->execute();
}
/**
* 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.
*
......
......@@ -1221,6 +1221,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();
......@@ -1231,8 +1234,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.
......@@ -1257,6 +1263,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();
......@@ -1815,9 +1827,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')
......
......@@ -192,10 +192,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();
}
......
......@@ -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));
}
......
......@@ -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();
......
......@@ -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));
}
......
......@@ -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)
......
......@@ -2394,11 +2394,33 @@ 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 is about to be deleted.
*
* @see hook_file_delete()
* @see file_delete()
* @see upload_file_delete()
*/
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()
*/
function hook_file_delete($file) {
......
......@@ -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();
......
......@@ -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();
......@@ -1806,9 +1826,9 @@ function taxonomy_node_update($node) {
}
/**
* Implements hook_node_delete().
* Implements hook_node_predelete().
*/
function taxonomy_node_delete($node) {
function taxonomy_node_predelete($node) {
// Clean up the {taxonomy_index} table when nodes are deleted.
taxonomy_delete_node_index($node);
}
......
......@@ -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();
......
......@@ -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);
......
......@@ -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().