diff --git modules/comment/comment.admin.inc modules/comment/comment.admin.inc
index f3d98a6..cb8ab33 100644
--- modules/comment/comment.admin.inc
+++ modules/comment/comment.admin.inc
@@ -64,7 +64,7 @@ function comment_admin_overview($type = 'new', $arg) {
while ($comment = db_fetch_object($result)) {
$comments[$comment->cid] = '';
$comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid)));
+ $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'comment/perm/'. $comment->cid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid)));
$form['username'][$comment->cid] = array('#value' => theme('username', $comment));
$form['node_title'][$comment->cid] = array('#value' => l($comment->node_title, 'node/'. $comment->nid));
$form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small'));
diff --git modules/comment/comment.module modules/comment/comment.module
index 1b1a413..a6d549b 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -224,6 +224,14 @@ function comment_menu() {
'file' => 'comment.admin.inc',
);
+ $items['comment/perm'] = array(
+ 'title' => 'Comment permalink',
+ 'page callback' => 'comment_permalink',
+ 'access arguments' => array('access comments'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'comment.module',
+ );
+
$items['comment/edit'] = array(
'title' => 'Edit comment',
'page callback' => 'comment_edit',
@@ -244,6 +252,48 @@ function comment_menu() {
return $items;
}
+/*
+ * Redirects comment links to the correct page depending on comment settings.
+ *
+ * Since comments are paged there is no way to guarantee which page a comment
+ * appears on. Comment paging and threading settings may be changed at any time.
+ * With threaded comments, an individual comment may move between pages as
+ * comments can be added either before or after it in the overall discussion.
+ * Therefore we use a central routing function for comment links, which
+ * calculates the page number based on current comment settings and returns
+ * the full comment view with the pager set dynamically.
+ *
+ * @param $comment
+ * A comment object.
+ * @return
+ * The comment listing set to the page on which the comment appears.
+ */
+
+function comment_permalink($cid) {
+ settype($cid, "integer");
+ $comment = db_fetch_object(db_query('SELECT c.cid, c.nid FROM {comments} c WHERE c.cid = %d', $cid));
+ $comment = drupal_unpack($comment);
+
+ $node = node_load($comment->nid);
+ if ($node && $comment) {
+
+ // Find the current display page for this comment.
+ $page = comment_get_display_page($comment->cid, $node);
+ // Set $_GET['q'] and $_GET['page'] ourselves so that the node callback
+ // behaves as it would when visiting the page directly.
+ $_GET['q'] = 'node/' . $node->nid;
+ $_GET['page'] = $page;
+
+ // Set the node path as the canonical URL to prevent duplicate content.
+ drupal_add_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)));
+
+ // Return the node view, this will show the correct comment in context.
+ return menu_execute_active_handler('node/' . $node->nid);
+ }
+ drupal_not_found();
+}
+
+
/**
* Implementation of hook_node_type().
*/
@@ -393,7 +443,7 @@ function comment_new_page_count($num_comments, $new_replies, $node) {
function theme_comment_block() {
$items = array();
foreach (comment_get_recent() as $comment) {
- $items[] = l($comment->subject, 'node/'. $comment->nid, array('fragment' => 'comment-'. $comment->cid)) .'
'. t('@time ago', array('@time' => format_interval(time() - $comment->timestamp)));
+ $items[] = l($comment->subject, 'comment/perm/'. $comment->cid, array('fragment' => 'comment-'. $comment->cid)) .'
'. t('@time ago', array('@time' => format_interval(time() - $comment->timestamp)));
}
if ($items) {
return theme('item_list', $items);
@@ -979,7 +1029,7 @@ function comment_render($node, $cid = 0) {
}
else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
- $query .= ' ORDER BY c.cid';
+ $query .= ' ORDER BY c.cid ASC';
}
else {
// See comment above. Analysis reveals that this doesn't cost too
@@ -1062,6 +1112,84 @@ function comment_render($node, $cid = 0) {
return $output;
}
+
+ /**
+ * Get the display ordinal for a comment, starting from 0.
+ *
+ * Count the number of comments which appear before the comment we want to
+ * display, taking into account display settings and threading.
+ *
+ * @param $cid
+ * The comment ID.
+ * @param $node_type
+ * The node type of the comment's parent.
+ * @return
+ * The display ordinal for the comment.
+ * @see comment_get_display_page()
+ */
+function comment_get_display_ordinal($cid, $node) {
+ // Count how many comments (c1) are before $cid (c2) in display order. This is
+ // the 0-based display ordinal.
+
+ // basic query skeleton...
+ $query = 'SELECT COUNT(*) as count FROM {comments} c1 INNER JOIN {comments} c2 ON c2.nid = c1.nid where c2.cid = %d ';
+
+ // exclude unpublished comments for users w/o the required user rights.
+
+ if (!user_access('administer comments')) {
+ $query .= 'AND c1.status = %d ';
+ }
+
+ $mode = _comment_get_display_setting('mode', $node);
+ $mode_sort = _comment_get_display_setting('sort', $node);
+
+ if ($mode === COMMENT_MODE_FLAT_EXPANDED || $mode === COMMENT_MODE_FLAT_COLLAPSED) {
+ // For flat comments, cid is used for ordering comments due to
+ // unpredicatable behavior with timestamp, so we make the same assumption
+ // here.
+ if($mode_sort == COMMENT_ORDER_OLDEST_FIRST) {
+ $query .= 'AND c1.cid < c2.cid ';
+ }
+ else {
+ $query .= 'AND c1.cid > c2.cid ';
+ }
+ }
+ else {
+ // For threaded comments, the c.thread column is used for ordering. We can
+ // use the vancode for comparison, but must remove the trailing slash.
+ // @see comment_render().
+ if($mode_sort == COMMENT_ORDER_OLDEST_FIRST) {
+ $query .= 'AND SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))';
+ }
+ else {
+ $query .= 'AND SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) > SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))';
+ }
+ }
+ $count = db_fetch_object(db_query($query, $cid, COMMENT_PUBLISHED));
+ $count = drupal_unpack($count);
+ return $count->count;
+}
+
+/**
+ * Return the page number for a comment.
+ *
+ * Finds the correct page number for a comment taking into account display
+ * and paging settings.
+ *
+ * @param $cid
+ * The comment ID.
+ * @param $node_type
+ * The node type the comment is attached to.
+ * @return
+ * The page number.
+ */
+function comment_get_display_page($cid, $node) {
+ $ordinal = comment_get_display_ordinal($cid, $node);
+ $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+ return floor($ordinal / $comments_per_page);
+}
+
+
/**
* Comment operations. We offer different update operations depending on
* which comment administration page we're on.
@@ -1677,7 +1805,7 @@ function template_preprocess_comment(&$variables) {
$variables['picture'] = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', $comment) : '';
$variables['signature'] = $comment->signature;
$variables['submitted'] = theme('comment_submitted', $comment);
- $variables['title'] = l($comment->subject, $_GET['q'], array('fragment' => "comment-$comment->cid"));
+ $variables['title'] = l($comment->subject, 'comment/perm/'. $comment->cid, array('fragment' => 'comment-'. $comment->cid));
$variables['template_files'][] = 'comment-'. $node->type;
// set status to a string representation of comment->status.
if (isset($comment->preview)) {
@@ -1699,7 +1827,7 @@ function template_preprocess_comment_folded(&$variables) {
$variables['author'] = theme('username', $comment);
$variables['date'] = format_date($comment->timestamp);
$variables['new'] = $comment->new ? t('new') : '';
- $variables['title'] = l($comment->subject, comment_node_url() .'/'. $comment->cid, array('fragment' => "comment-$comment->cid"));
+ $variables['title'] = l($comment->subject, 'comment/perm/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
}
/**