Permit the administrator to delete a single comment, without also deleting all of its descendents. http://drupal.org/node/11877 I still can't believe Drupal thinks it's a good idea to force an editor, who wants to remove one little piece of content, to also remove other content, sight unseen. This is Drupal's biggest flaw -- it's should be a content-MANAGEMENT system, not a content-ARBITRARILY-DISCARDING system. --- /Users/kevin/doc/dl/drupal-4.6.0/modules/comment.module Fri Apr 8 10:00:02 2005 +++ comment.module Thu Apr 14 11:57:37 2005 @@ -557,76 +557,7 @@ function comment_post($edit) { // Here we are building the thread field. See the comment // in comment_render(). - if ($edit['pid'] == 0) { - // This is a comment with no parent comment (depth 0): we start - // by retrieving the maximum thread level. - $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $edit['nid'])); - - // Strip the "/" from the end of the thread. - $max = rtrim($max, '/'); - - // Next, we increase this value by one. Note that we can't - // use 1, 2, 3, ... 9, 10, 11 because we order by string and - // 10 would be right after 1. We use 1, 2, 3, ..., 9, 91, - // 92, 93, ... instead. Ugly but fast. - $decimals = (string) substr($max, 0, strlen($max) - 1); - $units = substr($max, -1, 1); - if ($units) { - $units++; - } - else { - $units = 1; - } - - if ($units == 10) { - $units = '90'; - } - - // Finally, build the thread field for this new comment. - $thread = $decimals . $units .'/'; - } - else { - // This is comment with a parent comment: we increase - // the part of the thread value at the proper depth. - - // Get the parent comment: - $parent = db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $edit['pid'])); - - // Strip the "/" from the end of the parent thread. - $parent->thread = (string) rtrim((string) $parent->thread, '/'); - - // Get the max value in _this_ thread. - $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid'])); - - if ($max == '') { - // First child of this parent. - $thread = $parent->thread .'.1/'; - } - else { - // Strip the "/" at the end of the thread. - $max = rtrim($max, '/'); - - // We need to get the value at the correct depth. - $parts = explode('.', $max); - $parent_depth = count(explode('.', $parent->thread)); - $last = $parts[$parent_depth]; - - // Next, we increase this value by one. Note that we can't - // use 1, 2, 3, ... 9, 10, 11 because we order by string and - // 10 would be right after 1. We use 1, 2, 3, ..., 9, 91, - // 92, 93, ... instead. Ugly but fast. - $decimals = (string)substr($last, 0, strlen($last) - 1); - $units = substr($last, -1, 1); - $units++; - if ($units == 10) { - $units = '90'; - } - - // Finally, build the thread field for this new comment. - $thread = $parent->thread .'.'. $decimals . $units .'/'; - } - } - + $thread = comment_thread($edit['nid'], $edit['pid']); $edit['cid'] = db_next_id('{comments}_cid'); $edit['timestamp'] = time(); @@ -669,6 +600,96 @@ function comment_post($edit) { } } +function comment_thread($nid, $pid) { + if ($pid == 0) { + /* + ** This is a comment with no parent comment (depth 0): we start + ** by retrieving the maximum thread level. + */ + + $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $nid)); + + // Strip the "/" from the end of the thread. + $max = rtrim($max, '/'); + if (($dot = strpos($max, '.')) !== FALSE) { + $max = substr($max, 0, $dot); + } + + /* + ** Next, we increase this value by one. Note that we can't + ** use 1, 2, 3, ... 9, 10, 11 because we order by string and + ** 10 would be right after 1. We use 1, 2, 3, ..., 9, 91, + ** 92, 93, ... instead. Ugly but fast. + */ + + $decimals = (string)substr($max, 0, strlen($max) - 1); + $units = substr($max, -1, 1); + if ($units) { + $units++; + } + else { + $units = 1; + } + + if ($units == 10) { + $units = '90'; + } + + // Finally, build the thread field for this new comment. + $thread = "$decimals$units/"; + } + else { + /* + ** This is comment with a parent comment: we increase + ** the part of the thread value at the proper depth. + */ + + // Get the parent comment: + $parent = db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $pid)); + + // Strip the "/" from the end of the parent thread. + $parent->thread = (string)rtrim((string)$parent->thread, '/'); + + // Get the max value in _this_ thread. + $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $nid)); + + if ($max == '') { + // First child of this parent. + $thread = "$parent->thread.1/"; + } + else { + // Strip the "/" at the end of the thread. + $max = rtrim($max, '/'); + // We need to get the value at the correct depth. + $parts = explode('.', $max); + $parent_depth = count(explode('.', $parent->thread)); + $last = $parts[$parent_depth]; + // We need to get the value at the correct depth. + $parts = explode('.', $max); + $parent_depth = count(explode('.', $parent->thread)); + $last = $parts[$parent_depth]; + + /* + ** Next, we increase this value by one. Note that we can't + ** use 1, 2, 3, ... 9, 10, 11 because we order by string and + ** 10 would be right after 1. We use 1, 2, 3, ..., 9, 91, + ** 92, 93, ... instead. Ugly but fast. + */ + + $decimals = (string)substr($last, 0, strlen($last) - 1); + $units = substr($last, -1, 1); + $units++; + if ($units == 10) { + $units = '90'; + } + + // Finally, build the thread field for this new comment. + $thread = "$parent->thread.". $decimals.$units .'/'; + } + } + return $thread; +} + function comment_links($comment, $return = 1) { global $user; @@ -961,10 +982,18 @@ function comment_delete($cid) { // 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 ($_POST['edit']['thread']) { + drupal_set_message(t('The comment and all its replies have been deleted.')); - // Delete comment and its replies. - _comment_delete_thread($comment); + // Delete comment and its replies. + _comment_delete_thread($comment); + } + else { + drupal_set_message(t('The comment has been deleted.')); + + // Delete comment. + _comment_delete_single($comment); + } _comment_update_node_statistics($comment->nid); @@ -975,11 +1004,21 @@ function comment_delete($cid) { } else if ($comment->cid) { + $children = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE pid = %d', $comment->cid)); + if ($children) { + $extra = form_checkbox(t('Delete thread'), 'thread', 1, FALSE, + t('If selected, any replies to this comment will be lost.')); + } + else { + $extra = form_hidden('thread', 0); + } $output = theme('confirm', - t('Are you sure you want to delete the comment %title?', array('%title' => theme('placeholder', $comment->subject))), + t('Are you sure you want to delete the comment %title?', array('%title' => ''. $comment->subject .'')), 'node/'. $comment->nid, - t('Any replies to this comment will be lost. This action cannot be undone.'), - t('Delete')); + NULL, + t('Delete'), + NULL, + $extra); // Show comment that is being deleted $comment->comment = check_output($comment->comment, $comment->format); $output .= theme('comment', $comment); @@ -1596,6 +1635,42 @@ function theme_comment_post_forbidden() else { return t('login to post comments', array('%login' => url('user/login'))); } + } +} + +function _comment_delete_single($comment) { + $cid = $comment->cid; + $pid = $comment->pid; + // Delete the comment: + db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid); + watchdog('special', t('Comment: deleted %subject.', array('%subject' => "$comment->subject"))); + + module_invoke_all('comment', 'delete', $comment); + + // Rethread this comment's descendents + $result = db_query('SELECT cid, nid, thread FROM {comments} WHERE pid = %d ORDER BY timestamp', $cid); + while ($child = db_fetch_object($result)) { + $oldthread = $child->thread; + $newthread = comment_thread($child->nid, $pid); + db_query("UPDATE {comments} SET thread = '%s', pid = %d WHERE cid = %d", $newthread, $pid, $child->cid); + _comment_rethread($child->cid, strlen($oldthread)-1, rtrim($newthread,'/')); + } +} + +/** + * revise the thread field of descendents of a deleted comment + * + * @param $cid comment whose descendents are rethreaded + * @param $oldlength length of the former thread prefix + * @param $newprefix revised thread prefix + */ +function _comment_rethread($cid, $oldlength, $newprefix) { + $result = db_query('SELECT cid, thread FROM {comments} WHERE pid = %d', $cid); + while ($child = db_fetch_object($result)) { + $suffix = substr($child->thread, $oldlength); + $thread = $newprefix . $suffix; + db_query("UPDATE {comments} SET thread = '%s' WHERE cid = %d", $thread, $child->cid); + _comment_rethread($child->cid, $oldlength, $newprefix); } }