Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.841 diff -u -p -r1.841 comment.module --- modules/comment/comment.module 9 Feb 2010 12:29:39 -0000 1.841 +++ modules/comment/comment.module 11 Feb 2010 13:47:43 -0000 @@ -1241,7 +1241,7 @@ function comment_node_insert($node) { */ function comment_node_delete($node) { $cids = db_query('SELECT cid FROM {comment} WHERE nid = :nid', array(':nid' => $node->nid))->fetchCol(); - comment_delete_multiple($cids); + _comment_mass_delete_helper($cids); db_delete('node_comment_statistics') ->condition('nid', $node->nid) ->execute(); @@ -1509,6 +1509,32 @@ function comment_delete($cid) { * The comment to delete. */ function comment_delete_multiple($cids) { + // We use batch processing to prevent timeout when deleting a large number + // of nodes or comments. + if (count($cids) > 10) { + $batch = array( + 'operations' => array( + array('_comment_mass_delete_batch_process', array($cids)) + ), + 'finished' => '_comment_mass_delete_batch_finished', + 'title' => t('Processing'), + // We use a single multi-pass operation, so the default + // 'Remaining x of y operations' message will be confusing here. + 'progress_message' => '', + 'error_message' => t('The delete has encountered an error.'), + ); + batch_set($batch); + } + else { + _comment_mass_delete_helper($cids); + } +} + +/** + * Comment Mass Delete - helper function. + */ + +function _comment_mass_delete_helper($cids) { $comments = comment_load_multiple($cids); if ($comments) { @@ -1522,13 +1548,56 @@ function comment_delete_multiple($cids) // Delete the comment's replies. $child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $comment->cid))->fetchCol(); - comment_delete_multiple($child_cids); + _comment_mass_delete_helper($child_cids); _comment_update_node_statistics($comment->nid); } } } /** + * Node Mass Delete Batch operation + */ +function _comment_mass_delete_batch_process($cids, &$context) { + if (!isset($context['sandbox']['progress'])) { + $context['sandbox']['progress'] = 0; + $context['sandbox']['max'] = count($cids); + $context['sandbox']['cids'] = $cids; + } + + // Process comments by groups of 5. + $count = min(5, count($context['sandbox']['cids'])); + for ($i = 1; $i <= $count; $i++) { + // For each cid, load the comment, reset the values, and save it. + $cid = array_shift($context['sandbox']['cids']); + $comment = comment_load($cid); + _comment_mass_delete_helper(array($cid)); + // Store result for post-processing in the finished callback. + $context['results'][] = $comment->subject; + + // Update our progress information. + $context['sandbox']['progress']++; + } + + // Inform the batch engine that we are not finished, + // and provide an estimation of the completion level we reached. + if ($context['sandbox']['progress'] != $context['sandbox']['max']) { + $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; + } +} + +/** + * Comment Mass Update Batch 'finished' callback. + */ +function _comment_mass_delete_batch_finished($success, $results, $operations) { + if (!$success) { + drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); + $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:'); + $message .= theme('item_list', array('items' => $results)); + drupal_set_message($message); + } +} + +/** * Comment operations. Offer different update operations depending on * which comment administration page is being viewed. * Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1227 diff -u -p -r1.1227 node.module --- modules/node/node.module 7 Feb 2010 17:29:09 -0000 1.1227 +++ modules/node/node.module 11 Feb 2010 13:48:39 -0000 @@ -1101,40 +1101,107 @@ function node_delete($nid) { * An array of node IDs. */ function node_delete_multiple($nids) { - if (!empty($nids)) { - $nodes = node_load_multiple($nids, array()); + // We use batch processing to prevent timeout when deleting a large number + // of nodes. + if (count($nids) > 10) { + $batch = array( + 'operations' => array( + array('_node_mass_delete_batch_process', array($nids)) + ), + 'finished' => '_node_mass_delete_batch_finished', + 'title' => t('Processing'), + // We use a single multi-pass operation, so the default + // 'Remaining x of y operations' message will be confusing here. + 'progress_message' => '', + 'error_message' => t('The delete has encountered an error.'), + ); + batch_set($batch); + } + else { + foreach ($nids as $nid) { + _node_mass_delete_helper($nid); + } + drupal_set_message(t('The delete has been performed.')); + } +} - db_delete('node') - ->condition('nid', $nids, 'IN') - ->execute(); - db_delete('node_revision') - ->condition('nid', $nids, 'IN') - ->execute(); - db_delete('history') - ->condition('nid', $nids, 'IN') - ->execute(); +/** + * Node Mass Delete - helper function. + */ +function _node_mass_delete_helper($nid) { + $node = node_load($nid, NULL, TRUE); + // Call the node-specific callback (if any): + node_invoke($node, 'delete'); + module_invoke_all('node_delete', $node); + field_attach_delete('node', $node); - foreach ($nodes as $nid => $node) { - // Call the node-specific callback (if any): - node_invoke($node, 'delete'); - module_invoke_all('node_delete', $node); - field_attach_delete('node', $node); - - // Remove this node from the search index if needed. - // This code is implemented in node module rather than in search module, - // because node module is implementing search module's API, not the other - // way around. - if (module_exists('search')) { - search_reindex($nid, 'node'); - } - } + // Remove this node from the search index if needed. + // This code is implemented in node module rather than in search module, + // because node module is implementing search module's API, not the other + // way around. + if (module_exists('search')) { + search_reindex($nid, 'node'); + } + db_delete('node') + ->condition('nid', $nid, '=') + ->execute(); + db_delete('node_revision') + ->condition('nid', $nid, '=') + ->execute(); + db_delete('history') + ->condition('nid', $nid, '=') + ->execute(); + + // Clear the page and block and node_load_multiple caches. + cache_clear_all(); + entity_get_controller('node')->resetCache(); + return $node; +} - // Clear the page and block and node_load_multiple caches. - cache_clear_all(); - entity_get_controller('node')->resetCache(); +/** + * Node Mass Delete Batch operation + */ +function _node_mass_delete_batch_process($nids, &$context) { + if (!isset($context['sandbox']['progress'])) { + $context['sandbox']['progress'] = 0; + $context['sandbox']['max'] = count($nids); + $context['sandbox']['nids'] = $nids; + } + + // Process nodes by groups of 5. + $count = min(5, count($context['sandbox']['nids'])); + for ($i = 1; $i <= $count; $i++) { + // For each nid, load the node, reset the values, and save it. + $nid = array_shift($context['sandbox']['nids']); + $node = _node_mass_delete_helper($nid); + + // Store result for post-processing in the finished callback. + $context['results'][] = l($node->title, 'node/' . $node->nid); + + // Update our progress information. + $context['sandbox']['progress']++; + } + + // Inform the batch engine that we are not finished, + // and provide an estimation of the completion level we reached. + if ($context['sandbox']['progress'] != $context['sandbox']['max']) { + $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; + } +} + +/** + * Node Mass Update Batch 'finished' callback. + */ +function _node_mass_delete_batch_finished($success, $results, $operations) { + if (!$success) { + drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); + $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:'); + $message .= theme('item_list', array('items' => $results)); + drupal_set_message($message); } } + /** * Delete a node revision. *