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")); } /**