Index: database/database.mysql
===================================================================
RCS file: /cvs/drupal/drupal/database/database.mysql,v
retrieving revision 1.201
diff -u -F^function -r1.201 database.mysql
--- database/database.mysql	18 Oct 2005 14:41:26 -0000	1.201
+++ database/database.mysql	27 Oct 2005 20:38:44 -0000
@@ -239,6 +239,20 @@
 ) TYPE=MyISAM;
 
 --
+-- Table structure for table 'deleted'
+--
+
+CREATE TABLE deleted (
+  tid int(10) NOT NULL default 0,
+  did int(10) NOT NULL default 0,
+  root_row varchar(255) NOT NULL default '',
+  data longtext NOT NULL,
+  preview longtext NOT NULL,
+  timestamp int(10) unsigned NOT NULL default 0,
+  PRIMARY KEY  (tid)
+) TYPE=MyISAM;
+
+--
 -- Table structure for table 'files'
 --
 
Index: database/database.pgsql
===================================================================
RCS file: /cvs/drupal/drupal/database/database.pgsql,v
retrieving revision 1.140
diff -u -F^function -r1.140 database.pgsql
--- database/database.pgsql	18 Oct 2005 14:41:26 -0000	1.140
+++ database/database.pgsql	27 Oct 2005 20:38:44 -0000
@@ -235,6 +235,20 @@
 );
 
 --
+-- Table structure for table 'deleted'
+--
+
+CREATE TABLE deleted (
+  tid integer NOT NULL default '0',
+  did integer NOT NULL default '0',
+  root_row text NOT NULL default '',
+  data text NOT NULL default '',
+  preview text NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  PRIMARY KEY (tid)
+);
+
+--
 -- Table structure for table 'files'
 --
 
Index: database/updates.inc
===================================================================
RCS file: /cvs/drupal/drupal/database/updates.inc,v
retrieving revision 1.140
diff -u -F^function -r1.140 updates.inc
--- database/updates.inc	22 Oct 2005 15:14:46 -0000	1.140
+++ database/updates.inc	27 Oct 2005 20:38:45 -0000
@@ -67,7 +67,8 @@
   "2005-09-07" => "update_147",
   "2005-09-18" => "update_148",
   "2005-09-27" => "update_149",
-  "2005-10-15" => "update_150"
+  "2005-10-15" => "update_150",
+  "2005-10-27" => "update_151"
 );
 
 function update_110() {
@@ -919,6 +920,38 @@ function update_150() {
   return $ret;
 }
 
+function update_151() {
+  $ret = array();
+  
+  switch ($GLOBALS['db_type']) {
+    case 'mysqli':
+    case 'mysql':
+      $ret[] = update_sql("CREATE TABLE deleted (
+                           tid int(10) NOT NULL default 0,
+                           did int(10) NOT NULL default 0,
+                           root_row varchar(255) NOT NULL default '',
+                           data longtext NOT NULL,
+                           preview longtext NOT NULL,
+                           timestamp int(10) unsigned NOT NULL default 0,
+                           PRIMARY KEY  (tid)
+                           )");
+      break;
+    case 'pgsql':
+      $ret[] = update_sql("CREATE TABLE deleted (
+                           tid integer NOT NULL default 0 PRIMARY KEY,
+                           did integer NOT NULL default 0,
+                           root_row text NOT NULL default '',
+                           data text NOT NULL,
+                           preview text NOT NULL,
+                           timestamp integer NOT NULL default 0
+                           )");
+      break;
+    default:
+      break;
+  }
+  return $ret;
+}
+
 function update_sql($sql) {
   $edit = $_POST["edit"];
   $result = db_query($sql);
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.56
diff -u -F^function -r1.56 locale.inc
--- includes/locale.inc	21 Oct 2005 11:14:28 -0000	1.56
+++ includes/locale.inc	27 Oct 2005 20:38:47 -0000
@@ -935,10 +935,14 @@ function _locale_export_remove_plural($e
 }
 
 function _locale_string_delete($lid) {
-  db_query('DELETE FROM {locales_source} WHERE lid = %d', $lid);
-  db_query('DELETE FROM {locales_target} WHERE lid = %d', $lid);
+  $did = db_next_id('{deleted}_did');
+  $result = db_fetch_object(db_query('SELECT source, location FROM {locales_source} WHERE lid = %d', $lid));
+  $preview = array('source' => $result->source, 'location' => $result->location);
+  system_trash($did, 'locale', $preview, 'DELETE FROM {locales_source} WHERE lid = %d', $lid);
+  system_trash($did, NULL, NULL, 'DELETE FROM {locales_target} WHERE lid = %d', $lid);
   locale_refresh_cache();
-  drupal_set_message(t('The string has been removed.'));
+  drupal_set_message(t('The string has been moved to trash.'));
+  drupal_goto('admin/locale/string/search');
 }
 
 /**
Index: modules/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block.module,v
retrieving revision 1.184
diff -u -F^function -r1.184 block.module
--- modules/block.module	22 Oct 2005 15:14:46 -0000	1.184
+++ modules/block.module	27 Oct 2005 20:38:48 -0000
@@ -408,10 +408,12 @@ function block_box_add() {
  */
 function block_box_delete($bid = 0) {
   $box = block_box_get($bid);
-  $form['info'] = array('#type' => 'hidden', '#value' => $box['info'] ? $box['info'] : $box['title']);
-  $form['bid'] = array('#type' => 'hidden', '#value' => $bid);
-
-  return confirm_form('block_box_delete_confirm', $form, t('Are you sure you want to delete the block %name?', array('%name' => theme('placeholder', $info))), 'admin/block', '', t('Delete'), t('Cancel'));
+  $did = db_next_id('{deleted}_did');
+  $preview = array('title' => $box['title'], 'info' => $box['info'], 'body' => $box['body']);
+  system_trash($did, 'block', $preview, 'DELETE FROM {boxes} WHERE bid = %d', $bid);
+  drupal_set_message(t('The block %name has been moved to trash.', array('%name' => theme('placeholder', $box['info']))));
+  cache_clear_all();
+  drupal_goto('admin/block');
 }
 
 /**
@@ -419,10 +421,7 @@ function block_box_delete($bid = 0) {
  */
 function block_box_delete_confirm_execute($form_id, $edit) {
   $form = $GLOBALS['form_values'];
-  db_query('DELETE FROM {boxes} WHERE bid = %d', $form['bid']);
-  drupal_set_message(t('The block %name has been removed.', array('%name' => theme('placeholder', $form['info']))));
-  cache_clear_all();
-  drupal_goto('admin/block');
+  
 };
 
 
Index: modules/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book.module,v
retrieving revision 1.325
diff -u -F^function -r1.325 book.module
--- modules/book.module	24 Oct 2005 19:03:09 -0000	1.325
+++ modules/book.module	27 Oct 2005 20:38:49 -0000
@@ -217,7 +217,7 @@ function book_update($node) {
  * Implementation of hook_delete().
  */
 function book_delete(&$node) {
-  db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
+  system_trash($node->did, NULL, NULL, 'DELETE FROM {book} WHERE nid = %d', $node->nid);
 }
 
 /**
@@ -295,7 +295,9 @@ function book_outline() {
         break;
 
       case t('Remove from book outline'):
-        db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
+        $did = db_next_id('{deleted}_did');
+        $preview = array('title' => $node-title, 'body' => $node->body);
+        system_trash($did, 'book', $preview, 'DELETE FROM {book} WHERE nid = %d', $node->nid);
         drupal_set_message(t('The post has been removed from the book.'));
         drupal_goto("node/$node->nid");
         break;
Index: modules/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment.module,v
retrieving revision 1.384
diff -u -F^function -r1.384 comment.module
--- modules/comment.module	20 Oct 2005 09:27:36 -0000	1.384
+++ modules/comment.module	27 Oct 2005 20:38:51 -0000
@@ -232,8 +232,8 @@ function comment_nodeapi(&$node, $op, $a
       break;
 
     case 'delete':
-      db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
-      db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
+      system_trash($node->did, NULL, NULL, 'DELETE FROM {comments} WHERE nid = %d', $node->nid);
+      system_trash($node->did, NULL, NULL, 'DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
       break;
 
     case 'update index':
@@ -909,12 +909,10 @@ function comment_delete($cid) {
   $comment = db_fetch_object(db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.cid = %d', $cid));
   $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
 
-  $output = '';
-
   // We'll only delete if the user has confirmed the
   // deletion using the form in our else clause below.
-  if ($comment->cid && $_POST['edit']['confirm']) {
-    drupal_set_message(t('The comment and all its replies have been deleted.'));
+  if ($comment->cid) {
+    drupal_set_message(t('The comment and all its replies have been moved to trash.'));
 
     // Delete comment and its replies.
     _comment_delete_thread($comment);
@@ -926,20 +924,10 @@ function comment_delete($cid) {
 
     drupal_goto("node/$comment->nid");
   }
-  else if ($comment->cid) {
-    $output = confirm_form('comment_confirm_delete',
-                    array(),
-                    t('Are you sure you want to delete the comment %title?', array('%title' => theme('placeholder', $comment->subject))),
-                    'node/'. $comment->nid,
-                    t('Any replies to this comment will be lost. This action cannot be undone.'),
-                    t('Delete'),
-                    t('Cancel'));
-  }
   else {
     drupal_set_message(t('The comment no longer exists.'));
   }
 
-  return $output;
 }
 
 /**
@@ -1289,9 +1277,18 @@ function theme_comment_post_forbidden() 
 }
 
 function _comment_delete_thread($comment) {
+  static $did;
+  if ($did) {
+    $root_row = NULL;
+    $preview = NULL;
+  } else {
+    $did = db_next_id('{deleted}_did');
+    $root_row = 'comment';
+    $preview = array('subject' => $comment->subject, 'comment' => $comment->comment);
+  }
   // Delete the comment:
-  db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
-  watchdog('content', t('Comment: deleted %subject.', array('%subject' => theme('placeholder', $comment->subject))));
+  system_trash($did, $root_row, $preview, 'DELETE FROM {comments} WHERE cid = %d', $comment->cid);
+  watchdog('content', t('Comment: moved %subject to trash.', array('%subject' => theme('placeholder', $comment->subject))));
 
   comment_invoke_comment($comment, 'delete');
 
Index: modules/contact.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact.module,v
retrieving revision 1.26
diff -u -F^function -r1.26 contact.module
--- modules/contact.module	11 Oct 2005 19:44:34 -0000	1.26
+++ modules/contact.module	27 Oct 2005 20:38:51 -0000
@@ -197,18 +197,11 @@ function contact_admin_edit($category = 
 }
 
 function contact_admin_delete($category) {
-  if ($_POST['op'] != t('Delete')) {
-    return confirm_form('contact_admin_delete', array(),
-                    t('Are you sure you want to delete %category?', array('%category' => theme('placeholder', $category))),
-                    'admin/contact',
-                    t('This action cannot be undone.'),
-                    t('Delete'),
-                    t('Cancel'));
-  }
-  else {
-    db_query("DELETE FROM {contact} WHERE category = '%s'", $category);
+    $did = db_next_id('{deleted}_did');
+    $preview = array('title' => $category);
+    system_trash($did, 'contact', $preview, "DELETE FROM {contact} WHERE category = '%s'", $category);
+    drupal_set_message(t('Contact category %category moved to trash', array('%category' => t($category))));
     drupal_goto('admin/contact');
-  }
 }
 
 
Index: modules/filter.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter.module,v
retrieving revision 1.77
diff -u -F^function -r1.77 filter.module
--- modules/filter.module	22 Oct 2005 15:14:46 -0000	1.77
+++ modules/filter.module	27 Oct 2005 20:38:52 -0000
@@ -339,33 +339,24 @@ function filter_admin_add() {
  * Menu callback; confirm deletion of a format.
  */
 function filter_admin_delete() {
-  $edit = $_POST['edit'];
-  if ($edit['confirm']) {
-    if ($edit['format'] != variable_get('filter_default_format', 1)) {
-      db_query("DELETE FROM {filter_formats} WHERE format = %d", $edit['format']);
-      db_query("DELETE FROM {filters} WHERE format = %d", $edit['format']);
-
-      $default = variable_get('filter_default_format', 1);
-      db_query("UPDATE {node_revisions} SET format = %d WHERE format = %d", $default, $edit['format']);
-      db_query("UPDATE {comments} SET format = %d WHERE format = %d", $default, $edit['format']);
-      db_query("UPDATE {boxes} SET format = %d WHERE format = %d", $default, $edit['format']);
-
-      cache_clear_all('filter:'. $edit['format'], true);
-
-      drupal_set_message(t('Deleted input format %format.', array('%format' => theme('placeholder', $edit['name']))));
-    }
-    drupal_goto('admin/filters');
-  }
-
   $format = arg(3);
-  $format = db_fetch_object(db_query('SELECT * FROM {filter_formats} WHERE format = %d', $format));
-
-  $form['format'] = array('#type' => 'hidden', '#value' => $format->format);
-  $form['name'] = array('#type' => 'hidden', '#value' => $format->name);
-
-  return confirm_form('filter_admin_delete', $form, t('Are you sure you want to delete the input format %format?', array('%format' => theme('placeholder', $format->name))), 'admin/filters', t('If you have any content left in this input format, it will be switched to the default input format. This action cannot be undone.'), t('Delete'), t('Cancel'));
+  if ($format != variable_get('filter_default_format', 1)) {
+    $result = db_fetch_object(db_query('SELECT * FROM {filter_formats} WHERE format = %d', $format));
+    $did = db_next_id('{deleted}_did');
+    $preview = array('name' => $result->name);
+    system_trash($did, 'filter', $preview, "DELETE FROM {filters} WHERE format = %d", $format);
+    system_trash($did, NULL, NULL, "DELETE FROM {filter_formats} WHERE format = %d", $format);
+
+    $default = variable_get('filter_default_format', 1);
+    db_query("UPDATE {node_revisions} SET format = %d WHERE format = %d", $default, $format);
+    db_query("UPDATE {comments} SET format = %d WHERE format = %d", $default, $format);
+    db_query("UPDATE {boxes} SET format = %d WHERE format = %d", $default, $format);
 
+    cache_clear_all('filter:'. $format, true);
 
+    drupal_set_message(t('Input format %format sent to trash.', array('%format' => theme('placeholder', $result->name))));
+  }
+  drupal_goto('admin/filters');
 }
 
 /**
Index: modules/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum.module,v
retrieving revision 1.277
diff -u -F^function -r1.277 forum.module
--- modules/forum.module	21 Oct 2005 11:12:46 -0000	1.277
+++ modules/forum.module	27 Oct 2005 20:38:53 -0000
@@ -86,14 +86,8 @@ function forum_admin() {
       }
       break;
     case t('Delete'):
-      if (!$edit['confirm']) {
-        $output = _forum_confirm_delete($edit['tid']);
-        break;
-      }
-      else {
         $name = $edit['name'];
         $edit['name'] = 0;
-      }
     case t('Submit'):
       $status = taxonomy_save_term($edit);
       if (arg(3) == 'container') {
@@ -139,10 +133,10 @@ function forum_admin() {
 function forum_taxonomy($op, $type, $object = NULL) {
   if ($op == 'delete' && $type == 'term' && $object->vid == _forum_get_vid())  {
     $results = db_query('SELECT f.nid FROM {forum} f WHERE f.tid = %d', $object->tid);
-    while ($node = db_fetch_object($results)) {
-      $edit['nid'] = $node->nid;
-      $edit['confirm'] = TRUE;
-      node_delete($edit);
+    while ($nid = db_fetch_object($results)) {
+      $node = node_load($nid->nid);
+      $node->did = $object->did;
+      node_delete($node);
     }
   }
   elseif ($op == 'delete' && $type == 'vocabulary' && $object->vid == _forum_get_vid())  {
@@ -151,20 +145,6 @@ function forum_taxonomy($op, $type, $obj
 }
 
 /**
- * Returns a confirmation page for deleting a forum taxonomy term
- *
- * @param $tid ID of the term to be deleted
- */
-function _forum_confirm_delete($tid) {
-  $term = taxonomy_get_term($tid);
-
-  $form['tid'] = array('#type' => 'hidden', '#value' => $tid);
-
-  return confirm_form('forum_confirm_delete', $form, t('Are you sure you want to delete the forum %name?', array('%name' => theme('placeholder', $term->name))),
-                  'admin/forums', t('Deleting a forum or container will delete all sub-forums as well. This action cannot be undone.'), t('Delete'), t('Cancel'));
-}
-
-/**
  * Returns a form for adding a container to the forum vocabulary
  *
  * @param $edit Associative array containing a container term to be added or edited.
@@ -593,7 +573,7 @@ function forum_insert($node) {
  * Implementation of hook_delete().
  */
 function forum_delete(&$node) {
-  db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
+  system_trash($node->did, NULL, NULL, 'DELETE FROM {forum} WHERE nid = %d', $node->nid);
 }
 
 /**
Index: modules/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale.module,v
retrieving revision 1.128
diff -u -F^function -r1.128 locale.module
--- modules/locale.module	13 Oct 2005 10:02:31 -0000	1.128
+++ modules/locale.module	27 Oct 2005 20:38:54 -0000
@@ -294,39 +294,28 @@ function locale_admin_manage() {
 function locale_admin_manage_delete_screen() {
   include_once './includes/locale.inc';
   $langcode = arg(4);
-  $edit = $_POST['edit'];
-
-  // Check confirmation and if so, delete language
-  if ($edit['confirm']) {
-    $languages = locale_supported_languages(FALSE, TRUE);
-    if (isset($languages['name'][$edit['langcode']])) {
-      db_query("DELETE FROM {locales_meta} WHERE locale = '%s'", $edit['langcode']);
-      db_query("DELETE FROM {locales_target} WHERE locale = '%s'", $edit['langcode']);
-      $message = t('The language %locale has been removed.', array('%locale' => theme('placeholder', t($languages['name'][$edit['langcode']]))));
-      drupal_set_message($message);
-      watchdog('locale', $message);
-    }
-
-    // Changing the locale settings impacts the interface:
-    cache_clear_all();
-    drupal_goto('admin/locale/language/overview');
-  }
-
+  
   // Do not allow deletion of English locale
   if ($langcode == 'en') {
     drupal_goto('admin/locale/language/overview');
     return;
   }
 
-  // For other locales, warn user that data loss is ahead
+  // Delete language
   $languages = locale_supported_languages(FALSE, TRUE);
+  if (isset($languages['name'][$langcode])) {
+    $did = db_next_id('{deleted}_did');
+    $preview = array('language code' => $langcode, 'name' => $languages['name'][$langcode]);
+    system_trash($did, 'locale', $preview, "DELETE FROM {locales_meta} WHERE locale = '%s'", $langcode);
+    system_trash($did, NULL, NULL, "DELETE FROM {locales_target} WHERE locale = '%s'", $langcode);
+    $message = t('The language %locale has been sent to trash.', array('%locale' => theme('placeholder', t($languages['name'][$langcode]))));
+    drupal_set_message($message);
+    watchdog('locale', $message);
+  }
 
-  $form['langcode'] = array('#type' => 'hidden', '#value' => $langcode);
-  return confirm_form('locale_admin_manage_delete_screen', $form,
-                  t('Are you sure you want to delete the language %name?', array('%name' => theme('placeholder', t($languages['name'][$langcode])))),
-                  'admin/locale/language/overview',
-                  t('Deleting a language will remove all data associated with it. This action cannot be undone.'),
-                  t('Delete'), t('Cancel'));
+  // Changing the locale settings impacts the interface:
+  cache_clear_all();
+  drupal_goto('admin/locale/language/overview');
 }
 
 /**
Index: modules/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node.module,v
retrieving revision 1.541
diff -u -F^function -r1.541 node.module
--- modules/node.module	26 Oct 2005 14:49:59 -0000	1.541
+++ modules/node.module	27 Oct 2005 20:38:56 -0000
@@ -866,6 +866,9 @@ function node_menu($may_cache) {
       'access' => user_access('administer nodes'));
     $items[] = array('path' => 'admin/node/action', 'title' => t('content'),
       'type' => MENU_CALLBACK);
+    $items[] = array('path' => 'admin/node/batch_delete', 'title' => t('content'),
+      'type' => MENU_CALLBACK, 'access' => user_access('administer nodes'),
+      'callback' => 'node_multiple_delete_confirm');  
     $items[] = array('path' => 'admin/node/overview', 'title' => t('list'),
       'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
     $items[] = array('path' => 'admin/settings/node', 'title' => t('posts'),
@@ -907,7 +910,7 @@ function node_menu($may_cache) {
           'weight' => 1,
           'type' => MENU_LOCAL_TASK);
         $items[] = array('path' => 'node/'. arg(1) .'/delete', 'title' => t('delete'),
-          'callback' => 'node_delete_page',
+          'callback' => 'node_delete_confirm',
           'access' => node_access('delete', $node),
           'weight' => 1,
           'type' => MENU_CALLBACK);
@@ -1100,18 +1103,20 @@ function node_filter_form_execute() {
  * Generate the content administration overview.
  */
 function node_admin_nodes_execute($form_id, $edit) {
-  $operations = node_operations();
-  if ($operations[$edit['operation']][1]) {
-    // Flag changes
-    $operation = $operations[$edit['operation']][1];
-    foreach ($edit['nodes'] as $nid => $value) {
-      if ($value) {
-        db_query($operation, $nid);
+  if ($edit['operation'] != 'delete') {
+    $operations = node_operations();
+    if ($operations[$edit['operation']][1]) {
+      // Flag changes
+      $operation = $operations[$edit['operation']][1];
+      foreach ($edit['nodes'] as $nid => $value) {
+        if ($value) {
+          db_query($operation, $nid);
+        }
       }
+      drupal_set_message(t('The update has been performed.'));
     }
-    drupal_set_message(t('The update has been performed.'));
+    drupal_goto('admin/node');
   }
-  drupal_goto('admin/node');
 }
 
 function node_admin_nodes_validate($form_id, $edit) {
@@ -1160,7 +1165,7 @@ function node_admin_nodes() {
 
   // If you are attempting to delete nodes, display the multiple deletion form.
   if ($form_values['operation'] == 'delete') {
-    $output = node_multiple_delete_form();
+    $output = node_multiple_delete_confirm();
   }
 
   return $output;
@@ -1197,30 +1202,79 @@ function theme_node_admin_nodes($form) {
   return $output;
 }
 
-function node_multiple_delete_form() {
+function node_multiple_delete_confirm() {
   global $form_values;
-  $form['nodes'] = array('#prefix' => '<ul>', '#suffix' => '</ul>');
-  foreach ($form_values['nodes'] as $nid => $value) {
-    if ($value) {
-      $title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $nid));
-      $form['nodes'][$nid] = array('#type' => 'hidden', '#value' => $nid, '#tree' => TRUE, '#prefix' => '<li>', '#suffix' => check_plain($title) .'</li>');
+  $edit = $_POST['edit'];
+  // Grab nids from url for execute
+  if ($edit['confirm']) {
+    $nids = array_keys($edit['checkboxes']);
+  } else {
+    // Keep only checked nodes' nids
+    $nids = array_keys(array_intersect($form_values['nodes'], array('1')));
+  }
+  foreach ($nids as $nid) {
+    $node = node_load($nid);
+    $extras = node_invoke_nodeapi($node, 'delete pre');
+    // No extras, just delete
+    if (!count($extras)) {
+      node_delete($node);
+    } else {
+      // Add node and extras to the list only if no module has cancelled deletion for this node.
+      if (!in_array(FALSE, $extras)) {
+        $form['nodes'][$node->nid] = array('#type' => 'value', '#value' => 1);
+        $form['title'][$node->nid] = array('#type' => 'markup', '#value' => check_plain($node->title));
+        $form['extras'][$node->nid] = $extras;
+        $options[$node->nid] = '';
+      }
     }
   }
-  $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
-
-  return confirm_form('node_multiple_delete_form', $form,
-                      t('Are you sure you want to delete these items?'),
-                      'admin/node', t('This action cannot be undone.'),
-                      t('Delete all'), t('Cancel'));
+  if ($edit['confirm']) {
+    confirm_form('node_multiple_delete_confirm', $form,
+                    t('The following items need to be reviewed'),
+                    'admin/node', t('Selected items will be sent to trash.'),
+                    t('Delete selected'), t('Cancel'));
+    }
+  // Back to the admin page if no nodes can be deleted.
+  if (!count($options)) {
+    drupal_goto('admin/node');
+  } else {
+    $form['nodes']['#tree'] = TRUE;
+    $form['title']['#tree'] = TRUE;
+    $form['extras']['#tree'] = TRUE;
+    $form['checkboxes'] = array('#type' => 'checkboxes', '#options' => $options);
+    $form['confirm'] = array('#type' => 'value', '#value' => 1);
+    $form['operation'] = array('#type' => 'value', '#value' => 'delete');
+    $form['#method'] = 'post';
+    $form['#action'] =  url('admin/node/batch_delete');
+    return confirm_form('node_multiple_delete_confirm', $form,
+                      t('The following items need to be reviewed'),
+                      'admin/node', t('Selected items will be sent to trash.'),
+                      t('Delete selected'), t('Cancel'));
+  }
 }
 
+function theme_node_multiple_delete_confirm($form) {
+  $rows = array();
+  $header = array('');
+  $output = '';
+  foreach(element_children($form['nodes']) as $nid) {
+    $rows[] = array('<div class="container-inline">'. '<b>'. form_render($form['title'][$nid]) .'</b>'.  form_render($form['checkboxes'][$nid]) .'</div>'. form_render($form['extras'][$nid]) . form_render($form['nodes'][$nid]));
+    $rows[] = array('');
+  }
+  $output .= theme('table', $header, $rows);
+  $output .= form_render($form);
+  return $output;
+}
 
-function node_multiple_delete_form_execute($form_id, $edit) {
-  if ($edit['confirm']) {
-    foreach ($edit['nodes'] as $nid => $value) {
-      node_delete(array('nid' => $nid, 'confirm' => 1));
+function node_multiple_delete_confirm_execute($form_id, $form_values) {
+  $edit = $_POST['edit'];
+  if ($form_values['confirm']) {
+    foreach ($edit['checkboxes'] as $nid => $value) {
+      // Merge extra confirm data with node
+      $node = object2array(node_load($nid));
+      $node = array2object(array_merge($node, $edit['extras'][$nid]));
+      node_delete($node);
     }
-    drupal_set_message(t('The items have been deleted.'));
   }
   drupal_goto('admin/node');
 }
@@ -1334,8 +1388,11 @@ function node_revision_delete($nid, $rev
     $count_revisions = db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', $nid));
     // Don't delete the last revision of the node or the current revision
     if ($count_revisions > 1) {
-      db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $nid, $revision);
-      drupal_set_message(t('Deleted revision with the ID %revision.', array('%revision' => theme('placeholder', $revision))));
+      $node = node_load($nid);
+      $did = db_next_id('{deleted}_did');
+      $preview = array('title' => $node->title, 'body' => $node->body);
+      system_trash($did, 'node revision', $preview, "DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $nid, $revision);
+      drupal_set_message(t('Revision ID %revision moved to trash.', array('%revision' => theme('placeholder', $revision))));
     }
     else {
       drupal_set_message(t('Deletion failed. You tried to delete the current revision.'));
@@ -1847,29 +1904,72 @@ function node_form_execute($form_id, $ed
 /**
  * Ask for confirmation, and delete the node.
  */
-function node_delete($edit) {
+function node_delete_confirm() {
+  $edit = $_POST['edit'];
+  $edit['nid'] = $edit['nid'] ? $edit['nid'] : arg(1);
   $node = node_load($edit['nid']);
-
   if (node_access('delete', $node)) {
-    $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
-    $output = confirm_form('node_delete_confirm', $form,
-                   t('Are you sure you want to delete %title?', array('%title' => theme('placeholder', $node->title))),
-                   $_GET['destination'] ? $_GET['destination'] : 'node/'. $node->nid, t('This action cannot be undone.'),
-                   t('Delete'), t('Cancel')  );
+    $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
+    $form['confirm'] = array('#type' => 'value', '#value' => 1);
+    // Check for extra info from modules
+    $extras = node_invoke_nodeapi($node, 'delete pre');
+    $form['extras'] = $extras;
+    if ($edit['confirm']) {
+      confirm_form('node_delete_confirm', $form,
+                 t('Are you sure you want to delete %title?', array('%title' => theme('placeholder', $node->title))),
+                 $_GET['destination'] ? $_GET['destination'] : 'node/'. $node->nid, t('This action will send the 
+                 item to trash'), t('Delete'), t('Cancel'));
+    }
+    // No extras, just delete the node
+    if (!count($extras)) {
+      node_delete($node);
+      drupal_goto('node');
+    } else {
+      // Cancel deletion if necessary, otherwise display confirm form
+      if (in_array(FALSE, $extras)) {
+        drupal_goto('node/'. $node->nid .'/edit');
+      } else {
+        return confirm_form('node_delete_confirm', $form,
+               t('Are you sure you want to delete %title?', array('%title' => theme('placeholder', $node->title))),
+               $_GET['destination'] ? $_GET['destination'] : 'node/'. $node->nid, t('This action will send the 
+                 item to trash'), t('Delete'), t('Cancel'));
+      }
+    }
   }
-
-  return $output;
 }
 
-function node_delete_confirm_execute() {
-  global $form_values;
-  $node = node_load($form_values['nid']);
-
-  if (node_access('delete', $node)) {
+function node_delete_confirm_execute($form_id, $form_values) {
+  if ($form_values['confirm']) {
+    // Merge extra confirm data with node
+    $node = object2array(node_load($form_values['nid']));
+    $node = array2object(array_merge($node, $form_values));
+    node_delete($node);
+    drupal_goto('node');
+  }
+}
 
-    if ($form_values['confirm']) {
-      db_query('DELETE FROM {node} WHERE nid = %d', $node->nid);
-      db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid);
+function node_delete($node) {
+      if (is_numeric($node)) {
+        $node = node_load($node);
+      } elseif (is_array($node)) {
+        $node = node_load($node['nid']);
+      }
+      if (!$node->did) {
+        $node->did = db_next_id('{deleted}_did');
+        $root_row = 'node';
+      } else {
+        $root_row = NULL;
+      }
+      $preview = array('title' => $node->title, 'body' => $node->body);
+      // Poll trash preview
+      if ($node->choice) {
+        foreach ($node->choice as $key => $array) {
+          $c++;
+          $preview["choice $c"] = $key['chtext'];
+        }
+      }
+      system_trash($node->did, $root_row, $preview, 'DELETE FROM {node} WHERE nid = %d', $node->nid);
+      system_trash($node->did, NULL, NULL, 'DELETE FROM {node_revisions} WHERE nid = %d', $node->nid);
 
       // Call the node-specific callback (if any):
       node_invoke($node, 'delete');
@@ -1882,11 +1982,8 @@ function node_delete_confirm_execute() {
       if (function_exists('search_wipe')) {
         search_wipe($node->nid, 'node');
       }
-
+      drupal_set_message(t('%title moved to trash.', array('%title' => theme('placeholder', $node->title))));
       watchdog('content', t('%type: deleted %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))));
-    }
-  }
-  drupal_goto('node');
 }
 
 /**
@@ -2008,20 +2105,6 @@ function node_page() {
 }
 
 /**
- * Menu callback; the page for deleting a single node.
- */
-function node_delete_page() {
-  $edit = $_POST['edit'];
-  $edit['nid'] = $edit['nid'] ? $edit['nid'] : arg(1);
-  $node = node_load($edit['nid']);
-  if (!($output = node_delete($edit))) {
-    drupal_set_message(t('%title has been deleted.', array('%title' => theme('placeholder', $node->title))));
-    drupal_goto('');
-  }
-  return $output;
-}
-
-/**
  * Implementation of hook_update_index().
  */
 function node_update_index() {
Index: modules/path.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/path.module,v
retrieving revision 1.67
diff -u -F^function -r1.67 path.module
--- modules/path.module	11 Oct 2005 19:44:35 -0000	1.67
+++ modules/path.module	27 Oct 2005 20:38:57 -0000
@@ -105,17 +105,24 @@ function path_admin_edit($pid = 0) {
  * Menu callback; handles deletion of an URL alias.
  */
 function path_admin_delete($pid = 0) {
-  db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid);
-  drupal_set_message(t('The alias has been deleted.'));
+  $did = db_next_id('{deleted}_did');
+  $result = db_fetch_object(db_query('SELECT src, dst FROM {url_alias} WHERE pid = %d', $pid));
+  $preview = array('source' => $result->src, 'destination' => $result->dst);
+  system_trash($did, 'url alias', $preview, 'DELETE FROM {url_alias} WHERE pid = %d', $pid);
+  drupal_set_message(t('The alias has been moved to trash.'));
   drupal_goto('admin/path');
 }
 
 /**
  * Set an aliased path for a given Drupal path, preventing duplicates.
  */
-function path_set_alias($path = NULL, $alias = NULL, $pid = NULL) {
+function path_set_alias($path = NULL, $alias = NULL, $pid = NULL, $node = NULL) {
   if ($path && !$alias) {
-    db_query("DELETE FROM {url_alias} WHERE src = '%s'", $path);
+    if ($node->did) {
+      system_trash($node->did, NULL, NULL, "DELETE FROM {url_alias} WHERE src = '%s'", $path);
+    } else {
+      db_query("DELETE FROM {url_alias} WHERE src = '%s'", $path);
+    }
     drupal_clear_path_cache();
   }
   else if (!$path && $alias) {
@@ -221,7 +228,7 @@ function path_nodeapi(&$node, $op, $arg)
       case 'delete':
         $path = "node/$node->nid";
         if (drupal_get_path_alias($path) != $path) {
-          path_set_alias($path);
+          path_set_alias($path, NULL, NULL, $node);
         }
         break;
     }
Index: modules/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll.module,v
retrieving revision 1.173
diff -u -F^function -r1.173 poll.module
--- modules/poll.module	11 Oct 2005 19:44:35 -0000	1.173
+++ modules/poll.module	27 Oct 2005 20:38:57 -0000
@@ -83,9 +83,9 @@ function poll_cron() {
 /**
  * Implementation of hook_delete().
  */
-function poll_delete($node) {
-  db_query("DELETE FROM {poll} WHERE nid = %d", $node->nid);
-  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
+function poll_delete(&$node) {
+  system_trash($node->did, NULL, NULL, "DELETE FROM {poll} WHERE nid = %d", $node->nid);
+  system_trash($node->did, NULL, NULL, "DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
 }
 
 /**
Index: modules/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile.module,v
retrieving revision 1.115
diff -u -F^function -r1.115 profile.module
--- modules/profile.module	21 Oct 2005 11:14:55 -0000	1.115
+++ modules/profile.module	27 Oct 2005 20:38:58 -0000
@@ -533,18 +533,13 @@ function profile_admin_edit($fid) {
  * Menu callback; deletes a field from all user profiles.
  */
 function profile_admin_delete($fid) {
-  $field = db_fetch_object(db_query("SELECT title FROM {profile_fields} WHERE fid = %d", $fid));
-  if ($_POST['edit']['confirm']) {
-    db_query('DELETE FROM {profile_fields} WHERE fid = %d', $fid);
-    cache_clear_all();
-    drupal_set_message(t('The field %field has been deleted.', array('%field' => theme('placeholder', $field->title))));
-    drupal_goto('admin/settings/profile');
-  }
-  else {
-    return confirm_form('profile_confirm_delete', $form, t('Do you want to remove the field %field?',
-                    array('%field' => $field->title)),
-                  'admin/settings/profile', '', t('Delete'), t('Cancel'));
-  }
+  $did = db_next_id('{deleted}_did');
+  $result = db_fetch_object(db_query("SELECT title, explanation, category FROM {profile_fields} WHERE fid = %d", $fid));
+  $preview = array('title' => $result->title, 'explanation' => $result->explanation, 'category' => $result->category);
+  system_trash($did, 'profile', $preview, 'DELETE FROM {profile_fields} WHERE fid = %d', $fid);
+  cache_clear_all();
+  drupal_set_message(t('The field %field has been moved to trash.', array('%field' => theme('placeholder', $result->title))));
+  drupal_goto('admin/settings/profile');
 }
 
 function _profile_field_form($type, $edit = array()) {
Index: modules/statistics.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics.module,v
retrieving revision 1.210
diff -u -F^function -r1.210 statistics.module
--- modules/statistics.module	12 Oct 2005 01:00:24 -0000	1.210
+++ modules/statistics.module	27 Oct 2005 20:38:58 -0000
@@ -474,7 +474,7 @@ function statistics_nodeapi(&$node, $op,
   switch ($op) {
     case 'delete':
       // clean up statistics table when node is deleted
-      db_query('DELETE FROM {node_counter} WHERE nid = %d', $node->nid);
+      system_trash($node->did, NULL, NULL, 'DELETE FROM {node_counter} WHERE nid = %d', $node->nid);
   }
 }
 
Index: modules/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system.module,v
retrieving revision 1.247
diff -u -F^function -r1.247 system.module
--- modules/system.module	26 Oct 2005 01:24:09 -0000	1.247
+++ modules/system.module	27 Oct 2005 20:39:00 -0000
@@ -138,6 +138,16 @@ function system_menu($may_cache) {
     }
     $items[] = array('path' => 'admin/modules', 'title' => t('modules'),
       'callback' => 'system_modules', 'access' => $access);
+      
+    // Trash
+    $items[] = array('path' => 'admin/trash', 'title' => t('trash'),
+      'callback' => 'system_admin_trash', 'access' => user_access('administer nodes'));
+    $items[] = array('path' => 'trash/preview', 'title' => t('preview'),
+      'callback' => 'system_trash_preview', 'access' => TRUE, 'type' => MENU_CALLBACK);
+    $items[] = array('path' => 'trash/recover', 'title' => t('recover'),
+      'callback' => 'system_trash_recover', 'access' => TRUE, 'type' => MENU_CALLBACK);
+    $items[] = array('path' => 'trash/delete', 'title' => t('delete'),
+      'callback' => 'system_trash_delete', 'access' => TRUE, 'type' => MENU_CALLBACK);
   }
 
   return $items;
@@ -1231,3 +1241,150 @@ function confirm_form($form_id, $form, $
   $form['actions']['cancel'] = array('#value' => l($no ? $no : t('Cancel'), $path));
   return drupal_get_form($form_id, $form, 'confirm_form');
 }
+
+function system_admin_trash() {
+  $result = pager_query("SELECT * FROM deleted WHERE root_row != '' ORDER BY timestamp DESC", 25  , 0);
+  if (db_num_rows($result) == 0) {
+    $output = t('There are no items in your trash');
+  } else {
+    $header = array(t('Deleted'), t('Type'), t('Title'), t('Operations'));
+    $rows = array();
+    while ($recover = db_fetch_object($result)) {
+      $preview = unserialize($recover->preview);
+      $title = is_array($preview) ? array_shift($preview) : '[none]';
+      $rows[] = array(format_date($recover->timestamp, 'small'), t($recover->root_row), t($title), l(t('Preview'), "trash/preview/$recover->tid") .' '. l(t('Recover'), "trash/recover/$recover->did/admin") .' '. l(t('Delete'), "trash/delete/$recover->did"));
+    }
+    $output = '<h3>'. l(t('Delete all trash'), 'trash/delete/all') .'</h3>';
+    $output .= theme('table', $header, $rows) . theme('pager', NULL, 25, 0);
+  }
+  return $output;
+}
+
+function system_trash($did, $root_row, $preview, $query) {
+  static $same_did;
+  // Grab the args for the query
+  $all_args = func_get_args();
+  if (count($all_args) > 4) {
+    $args = array_slice($all_args, 4);
+  } else {
+    $args = array();
+  }
+  // Turn delete into select and get result
+  $delete = preg_replace('/DELETE.*?FROM/i', 'SELECT * FROM', $query);
+  $result = db_query($delete, $args);
+  // Grab table name and store
+  $table = preg_match('/^DELETE FROM \{([^}]+)/', $query, $m);
+  $recover['table']  = $m[1];
+  $root_row = $root_row ? $root_row : '';
+  $preview = is_array($preview) ? serialize($preview) : '';
+  // Store file info in case of later permanent deletion
+  if (module_exist('upload') && $root_row == 'node') {
+    $node = node_load($args[0]);
+    $file_info = upload_load($node);
+    $files = array();
+    foreach ($file_info as $file) {
+      $filepaths[] = $file->filepath;
+    }
+    $recover['files'] = $filepaths;
+  }
+  // Loop through result inserting rows into deleted table
+  while ($recover['data'] = db_fetch_array($result)) {
+    $data = serialize($recover);
+    $tid = db_next_id('{deleted}_tid');
+    db_query("INSERT INTO {deleted} SET tid = %d, did = %d, root_row = '%s', data = '%s', preview = '%s', timestamp = %d", $tid, $did, $root_row, $data, $preview, time());
+    $root_row = '';
+    $preview = '';
+  }
+  // Delete from original table
+  db_query($query, $args);
+  // Only one message for each did
+  if ($same_did != $did) {
+    // Test if user can be directed to trash
+    if (user_access('administer nodes')) { 
+      drupal_set_message(t('Click %here to recover from %trash.', array('%here' => l(t('here'), "trash/recover/$did/admin"), '%trash' => l(t('trash'), "admin/trash"))));
+    } else {
+      drupal_set_message(t('Click %here to recover.', array('%here' => l(t('here'), "trash/recover/$did"))));
+    }
+    $same_did = $did;
+  }  
+}
+
+function system_trash_preview($tid) {
+  if (user_access('administer nodes')) {
+    $did = db_result(db_query('SELECT did FROM {deleted} WHERE tid = %d', $tid));
+    $output = '';
+    $output .= '<h3>'. l(t('Recover'), "trash/recover/$did/admin") .' '. l(t('Delete'), "trash/delete/$did") .' '. l(t('Cancel'), "admin/trash") .'</h3>';
+    $result = db_query("SELECT root_row, preview, timestamp FROM {deleted} WHERE tid = %d", $tid);
+    // Loop through grabbing preview data for all rows
+    while ($insert = db_fetch_object($result)) {
+      $values = array();
+      $data = unserialize($insert->preview);
+      if ($data) {
+        foreach ($data as $key => $value) {
+          $values[] = '<b>'. t($key) . ':</b> '. t($value);
+        }
+      }
+      $output .= '<b>'. t('Trashed') .': </b>'. format_date($insert->timestamp, 'small') .'<br>';
+      $output .= '<b>'. t('Type') .': </b>'. t($insert->root_row) .'<br>';
+      $output .= '<h3>'. t('Data') .': </h3>';
+      $output .= implode('<br>', $values);
+    }
+    return $output;
+  } else {
+    drupal_access_denied();
+  }
+}
+
+function system_trash_recover($did, $is_admin = FALSE) {
+  $result = db_query('SELECT * FROM deleted WHERE did = %d', $did);
+  // Loop through reinserting all rows
+  while ($insert = db_fetch_object($result)) {
+    $values = array();
+    $recover = unserialize($insert->data);
+    foreach ($recover['data'] as $key => $value) {
+      $values[] = is_numeric($value) ? '%d' : '\'%s\'';
+      // Best guess at recovered node if present
+      if ($key == 'nid') {
+        $nid = $value;
+      }
+    }
+    db_query("INSERT INTO {$recover['table']} VALUES(". implode(', ', $values). ')', $recover['data']);  
+  }
+  db_query('DELETE FROM {deleted} WHERE did = %d', $did);
+  $link = $nid ? t('The %item has been recovered', array('%item' => l(t('item'), "node/$nid"))) : t('The item has been recovered');
+  drupal_set_message($link);
+  if ($is_admin == 'admin') {
+    drupal_goto('admin/trash');
+  }  else {
+    drupal_goto('node');
+  }
+}
+
+function system_trash_delete($did) {
+  if (user_access('administer nodes')) {
+    // Grab all node deletions and delete asscociated files
+    if (module_exist('upload')) {
+      if ($did == 'all') {
+        $result = db_query("SELECT * FROM {deleted} WHERE root_row = 'node'");
+      } else {
+        $result = db_query("SELECT * FROM {deleted} WHERE did = %d AND root_row = 'node'", $did);
+      }
+      while ($node = db_fetch_object($result)) {
+        $data = unserialize($node->data);
+        foreach ($data['files'] as $filepath) {
+          file_delete($filepath);
+        }
+      }
+    }
+    // Permanently delete
+    if ($did == 'all') {
+      $result = db_query('DELETE FROM {deleted}');
+    } else {
+      $result = db_query('DELETE FROM {deleted} WHERE did = %d', $did);
+    }
+    drupal_set_message(t('Permanent delete successful'));
+    drupal_goto("admin/trash");
+  } else {
+    drupal_access_denied();
+  }
+}
Index: modules/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy.module,v
retrieving revision 1.232
diff -u -F^function -r1.232 taxonomy.module
--- modules/taxonomy.module	21 Oct 2005 11:12:46 -0000	1.232
+++ modules/taxonomy.module	27 Oct 2005 20:39:02 -0000
@@ -179,14 +179,19 @@ function taxonomy_save_vocabulary(&$edit
 
 function taxonomy_del_vocabulary($vid) {
   $vocabulary = taxonomy_get_vocabulary($vid);
-
-  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
-  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
+  $did = db_next_id('{deleted}_did');
+  $preview = array('name' => $vocabulary->name, 'description' => $vocabulary->description, 'children' => '');
+  system_trash($did, 'vocabulary', $preview, 'DELETE FROM {vocabulary} WHERE vid = %d', $vid);
+  system_trash($did, NULL, NULL, 'DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
   $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
   while ($term = db_fetch_object($result)) {
-    taxonomy_del_term($term->tid);
-  }
-
+    // Build terms preview as well
+    $previews = taxonomy_del_term($term->tid, $did);
+    $preview = array_merge($preview, $previews);
+  }
+  $preview = serialize($preview);
+  db_query("UPDATE {deleted} SET preview = '%s' WHERE did = %d AND root_row != ''", $preview, $did);
+  $vocabulary->did = $did;
   module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
 
   cache_clear_all();
@@ -194,20 +199,6 @@ function taxonomy_del_vocabulary($vid) {
   return SAVED_DELETED;
 }
 
-function _taxonomy_confirm_del_vocabulary($vid) {
-  $vocabulary = taxonomy_get_vocabulary($vid);
-
-  $form['type'] = array('#type' => 'hidden', '#value' => 'vocabulary');
-  $form['vid'] = array('#type' => 'hidden', '#value' => $vid);
-  $form['name'] = array('#type' => 'hidden', '#value' => $vocabulary->name);
-  return confirm_form('vocabulary_confirm_delete', $form,
-                  t('Are you sure you want to delete the vocabulary %title?',
-                  array('%title' => theme('placeholder', $vocabulary->name))),
-                  'admin/taxonomy', t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
-                  t('Delete'),
-                  t('Cancel'));
-}
-
 function taxonomy_form_term($edit = array()) {
   $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
   $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
@@ -324,7 +315,12 @@ function taxonomy_save_term(&$edit) {
   return $status;
 }
 
-function taxonomy_del_term($tid) {
+function taxonomy_del_term($tid, $vocab_did = NULL) {
+  if ($vocab_did) {
+    $did = $vocab_did;
+  } else {
+    $did = db_next_id('{deleted}_did');
+  }
   $tids = array($tid);
   while ($tids) {
     $children_tids = $orphans = array();
@@ -341,35 +337,33 @@ function taxonomy_del_term($tid) {
       }
 
       $term = taxonomy_get_term($tid);
-
-      db_query('DELETE FROM {term_data} WHERE tid = %d', $tid);
-      db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
-      db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
-      db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid);
-      db_query('DELETE FROM {term_node} WHERE tid = %d', $tid);
-
+      if (!$recurse && !$vocab_did) {
+        $root_row = 'taxonomy term';
+        $preview = array('root term' => $term->name, 'description' => $term->description, 'children' => '');
+        $recurse = 1;
+      } else {
+        $root_row = NULL;
+        $preview[] = $term->name;
+      }
+      system_trash($did, $root_row, NULL, 'DELETE FROM {term_data} WHERE tid = %d', $tid);
+      system_trash($did, NULL, NULL, 'DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
+      system_trash($did, NULL, NULL, 'DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
+      system_trash($did, NULL, NULL, 'DELETE FROM {term_synonym} WHERE tid = %d', $tid);
+      system_trash($did, NULL, NULL, 'DELETE FROM {term_node} WHERE tid = %d', $tid);
+      $term->did = $did;
       module_invoke_all('taxonomy', 'delete', 'term', $term);
-      drupal_set_message(t('Deleted term %name.', array('%name' => theme('placeholder', $term->name))));
+      drupal_set_message(t('Moved term %name to trash.', array('%name' => theme('placeholder', $term->name))));
     }
 
     $tids = $orphans;
   }
-
   cache_clear_all();
-}
-
-function _taxonomy_confirm_del_term($tid) {
-  $term = taxonomy_get_term($tid);
-
-  $form['type'] = array('#type' => 'hidden', '#value' => 'term');
-  $form['tid'] = array('#type' => 'hidden', '#value' => $tid);
-  return confirm_form('term_confirm_delete', $form,
-                  t('Are you sure you want to delete the term %title?',
-                  array('%title' => theme('placeholder', $term->name))),
-                  'admin/taxonomy',
-                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
-                  t('Delete'),
-                  t('Cancel'));
+  if (!$vocab_did) {
+    $preview = serialize($preview);
+    db_query("UPDATE {deleted} SET preview = '%s' WHERE did = %d AND root_row != ''", $preview, $did);
+  } else {
+    return $preview;
+  }
 }
 
 /**
@@ -593,7 +587,7 @@ function taxonomy_node_validate(&$node) 
  * Save term associations for a given node.
  */
 function taxonomy_node_save($nid, $terms) {
-  taxonomy_node_delete($nid);
+  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
 
   // Free tagging vocabularies do not send their tids in the form,
   // so we'll detect them here and process them independently.
@@ -654,13 +648,6 @@ function taxonomy_node_save($nid, $terms
 }
 
 /**
- * Remove associations of a node to its terms.
- */
-function taxonomy_node_delete($nid) {
-  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
-}
-
-/**
  * Find all term objects related to a given term ID.
  */
 function taxonomy_get_related($tid, $key = 'tid') {
@@ -1041,7 +1028,7 @@ function taxonomy_nodeapi($node, $op, $a
       taxonomy_node_save($node->nid, $node->taxonomy);
       break;
     case 'delete':
-      taxonomy_node_delete($node->nid);
+      system_trash($node->did, NULL, NULL, 'DELETE FROM {term_node} WHERE nid = %d', $node->nid);
       break;
     case 'validate':
       taxonomy_node_validate($node);
@@ -1170,20 +1157,9 @@ function taxonomy_admin() {
       }
       break;
     case t('Delete'):
-      if (!$edit['confirm']) {
-        if (arg(3) == 'vocabulary') {
-          $output = _taxonomy_confirm_del_vocabulary($edit['vid']);
-        }
-        else {
-          $output = _taxonomy_confirm_del_term($edit['tid']);
-        }
-        break;
-      }
-      else {
-        $deleted_name = $edit['name'];
-        $edit['name'] = 0;
-        // fall through:
-      }
+      $deleted_name = $edit['name'];
+      $edit['name'] = 0;
+      // fall through:
     case t('Submit'):
       if (arg(3) == 'vocabulary') {
         switch (taxonomy_save_vocabulary($edit)) {
@@ -1194,7 +1170,7 @@ function taxonomy_admin() {
             drupal_set_message(t('Updated vocabulary %name.', array('%name' => theme('placeholder', $edit['name']))));
             break;
           case SAVED_DELETED:
-            drupal_set_message(t('Deleted vocabulary %name.', array('%name' => theme('placeholder', $deleted_name))));
+            drupal_set_message(t('Moved vocabulary %name to trash.', array('%name' => theme('placeholder', $deleted_name))));
             break;
         }
       }
Index: modules/upload.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload.module,v
retrieving revision 1.55
diff -u -F^function -r1.55 upload.module
--- modules/upload.module	11 Oct 2005 19:44:35 -0000	1.55
+++ modules/upload.module	27 Oct 2005 20:39:02 -0000
@@ -289,7 +289,7 @@ function upload_nodeapi(&$node, $op, $ar
       break;
 
     case 'delete':
-      upload_delete($node);
+      system_trash($node->did, NULL, NULL, "DELETE FROM {files} WHERE nid = %d", $node->nid);
       break;
     case 'search result':
       return $node->files ? format_plural(count($node->files), '1 attachment', '%count attachments') : null;
@@ -380,14 +380,6 @@ function upload_save($node) {
   return;
 }
 
-function upload_delete($node) {
-  $node->files = upload_load($node);
-  foreach ($node->files as $file) {
-    file_delete($file->filepath);
-  }
-  db_query("DELETE FROM {files} WHERE nid = %d", $node->nid);
-}
-
 function upload_form($node) {
   drupal_add_js('misc/progress.js');
   drupal_add_js('misc/upload.js');
Index: modules/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user.module,v
retrieving revision 1.523
diff -u -F^function -r1.523 user.module
--- modules/user.module	22 Oct 2005 15:14:46 -0000	1.523
+++ modules/user.module	27 Oct 2005 20:39:05 -0000
@@ -1257,18 +1257,16 @@ function user_edit($category = 'account'
     }
   }
   else if (arg(2) == 'delete') {
-    if ($edit['confirm']) {
-      db_query('DELETE FROM {users} WHERE uid = %d', $account->uid);
-      db_query('DELETE FROM {sessions} WHERE uid = %d', $account->uid);
-      db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
-      db_query('DELETE FROM {authmap} WHERE uid = %d', $account->uid);
-      drupal_set_message(t('The account has been deleted.'));
-      module_invoke_all('user', 'delete', $edit, $account);
-      drupal_goto('admin/user');
-    }
-    else {
-      return confirm_form('user_confirm_delete', $form, t('Are you sure you want to delete the account %name?', array('%name' => theme('placeholder', $account->name))), 'user/'. $account->uid, t('Deleting a user will remove all their submissions as well. This action cannot be undone.'), t('Delete'));
-    }
+    $did = db_next_id('{deleted}_did');
+    $preview = array('name' => $account->name, 'email' => $account->mail);
+    system_trash($did, 'user', $preview, 'DELETE FROM {users} WHERE uid = %d', $account->uid);
+    db_query('DELETE FROM {sessions} WHERE uid = %d', $account->uid);
+    system_trash($did, NULL, NULL, 'DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
+    system_trash($did, NULL, NULL, 'DELETE FROM {authmap} WHERE uid = %d', $account->uid);
+    drupal_set_message(t('The account has been moved to trash.'));
+    $account->did = $did;
+    module_invoke_all('user', 'delete', $edit, $account);
+    drupal_goto('admin/user');
   }
   else if ($_POST['op'] == t('Delete')) {
     // Note: we redirect from user/uid/edit to user/uid/delete to make the tabs disappear.
@@ -1458,20 +1456,9 @@ function user_admin_access_add($mask = N
 function user_admin_access_delete($aid = 0) {
   $access_types = array('user' => t('username'), 'mail' => t('e-mail'));
   $edit = db_fetch_object(db_query('SELECT aid, type, status, mask FROM {access} WHERE aid = %d', $aid));
-
-  $form = array();
-  $form['aid'] = array('#type' => 'hidden', '#value' => $aid);
-  $output = confirm_form('user_admin_access_delete_confirm', $form,
-                  t('Are you sure you want to delete the %type rule for %rule?', array('%type' => $access_types[$edit->type], '%rule' => theme('placeholder', $edit->mask))),
-                  'admin/access/rules',
-                  t('This action cannot be undone.'),
-                  t('Delete'),
-                  t('Cancel'));
-  return $output;
-}
-
-function user_admin_access_delete_confirm_execute($form_id, $edit) {
-  db_query('DELETE FROM {access} WHERE aid = %d', $edit['aid']);
+  $did = db_next_id('{deleted}_did');
+  $preview = array('type' => $access_types[$edit->type], 'rule' => $edit->mask);
+  system_trash($did, 'access rule', $preview, 'DELETE FROM {access} WHERE aid = %d', $aid);
   drupal_set_message(t('The access rule has been deleted.'));
   drupal_goto('admin/access/rules');
 }
@@ -1665,8 +1652,10 @@ function user_admin_role() {
     }
   }
   else if ($op == t('Delete role')) {
-    db_query('DELETE FROM {role} WHERE rid = %d', $id);
-    db_query('DELETE FROM {permission} WHERE rid = %d', $id);
+    $did = db_next_id('{deleted}_did');
+    $preview = db_fetch_array(db_query('SELECT name FROM {role} WHERE rid = %d', $id));
+    system_trash($did, 'role', $preview, 'DELETE FROM {role} WHERE rid = %d', $id);
+    system_trash($did, NULL, NULL, 'DELETE FROM {permission} WHERE rid = %d', $id);
 
     // Update the users who have this role set:
     $result = db_query('SELECT DISTINCT(ur1.uid) FROM {users_roles} ur1 LEFT JOIN {users_roles} ur2 ON ur2.uid = ur1.uid WHERE ur1.rid = %d AND ur2.rid != ur1.rid', $id);
@@ -1677,13 +1666,13 @@ function user_admin_role() {
     }
 
     if ($uid) {
-      db_query('DELETE FROM {users_roles} WHERE rid = %d AND uid IN (%s)', $id, implode(', ', $uid));
+      system_trash($did, NULL, NULL, 'DELETE FROM {users_roles} WHERE rid = %d AND uid IN (%s)', $id, implode(', ', $uid));
     }
 
     // Users with only the deleted role are put back in the authenticated users pool.
     db_query('UPDATE {users_roles} SET rid = %d WHERE rid = %d', _user_authenticated_id(), $id);
 
-    drupal_set_message(t('The role has been deleted.'));
+    drupal_set_message(t('The role has been moved to trash.  All users with only this role have been moved to the authenticated users pool.'));
     drupal_goto('admin/access/roles');
   }
   else if ($op == t('Add role')) {
