Index: modules/comment/comment.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.admin.inc,v retrieving revision 1.23 diff -u -p -r1.23 comment.admin.inc --- modules/comment/comment.admin.inc 6 Jun 2009 10:27:42 -0000 1.23 +++ modules/comment/comment.admin.inc 16 Jun 2009 14:11:55 -0000 @@ -86,7 +86,7 @@ function comment_admin_overview($type = foreach ($result as $comment) { $options[$comment->cid] = array( - 'subject' => l($comment->subject, 'node/' . $comment->nid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-' . $comment->cid)), + 'subject' => l($comment->subject, 'comment/' . $comment->cid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-' . $comment->cid)), 'author' => theme('username', $comment), 'posted_in' => l($comment->node_title, 'node/' . $comment->nid), 'time' => format_date($comment->timestamp, 'small'), Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.723 diff -u -p -r1.723 comment.module --- modules/comment/comment.module 15 Jun 2009 19:28:55 -0000 1.723 +++ modules/comment/comment.module 16 Jun 2009 14:11:56 -0000 @@ -204,10 +204,18 @@ function comment_menu() { 'access arguments' => array('administer comments'), 'type' => MENU_CALLBACK, ); + $items['comment/%comment'] = array( + 'title' => 'Comment router', + 'page callback' => 'comment_router', + 'page arguments' => array(1), + 'access arguments' => array('access comments'), + 'type' => MENU_CALLBACK, + ); return $items; } + /** * Implement hook_node_type(). */ @@ -301,6 +309,20 @@ function comment_block_view($delta = '') } /** + * Redirects comment links to the correct page depending on comment settings. + */ +function comment_router($comment) { + $node = node_load($comment->nid); + if ($node && $comment) { + $page = comment_get_display_page($comment->cid, $node->type); + drupal_goto('node/' . $node->nid, array('page' => $page), 'comment-' . $comment->cid, 301); + } + else { + drupal_not_found(); + } +} + +/** * Find the most recent comments that are available to the current user. * * This is done in two steps: @@ -404,7 +426,7 @@ function theme_comment_block() { $items = array(); $number = variable_get('comment_block_count', 10); foreach (comment_get_recent($number) as $comment) { - $items[] = l($comment->subject, 'node/' . $comment->nid, array('fragment' => 'comment-' . $comment->cid)) . '
' . t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->timestamp))); + $items[] = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)) . '
' . t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->timestamp))); } if ($items) { @@ -1348,6 +1370,60 @@ function comment_num_new($nid, $timestam } /** + * Get the display ordinal for a comment, starting from 0. + * + * @param $cid + * The comment ID. + * @param $node_type + * The node type of the comment's parent. + * @return + * The display ordinal for the comment. + */ +function comment_get_display_ordinal($cid, $node_type) { + // Count how many comments (c) are before $cid (d) in display order. This is + // the 0-based display ordinal. + $query = db_select('comment', 'c'); + $query->innerJoin('comment', 'd', 'd.nid = c.nid'); + $query->addExpression('COUNT(*)', 'count'); + $query->condition('d.cid', $cid); + if (!user_access('administer comments')) { + $query->condition('c.status', COMMENT_PUBLISHED); + } + $mode = variable_get('comment_default_mode_' . $node_type, COMMENT_MODE_THREADED_EXPANDED); + + 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. + $query->condition('c.cid', 'd.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(). + $query->where('SUBSTRING(c.thread, 1, (LENGTH(c.thread) -1)) < SUBSTRING(d.thread, 1, (LENGTH(d.thread) -1))'); + } + + return $query->execute()->fetchField(); +} + +/** + * Return the page number for a comment. + * + * @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_type) { + $ordinal = comment_get_display_ordinal($cid, $node_type); + $comments_per_page = variable_get('comment_default_per_page_' . $node_type, 50); + return floor($ordinal / $comments_per_page); +} + +/** * Validate comment data. * * @param $edit @@ -1801,11 +1877,7 @@ function _comment_form_submit(&$comment_ function comment_form_submit($form, &$form_state) { _comment_form_submit($form_state['values']); if ($cid = comment_save($form_state['values'])) { - $node = node_load($form_state['values']['nid']); - // Add 1 to existing $node->comment count to include new comment. - $comment_count = $node->comment_count + 1; - $page = comment_new_page_count($comment_count, 1, $node); - $form_state['redirect'] = array('node/' . $node->nid, $page, "comment-$cid"); + $form_state['redirect'] = array('comment/' . $cid, array(), "comment-$cid"); return; } } @@ -1868,7 +1940,7 @@ function template_preprocess_comment(&$v $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/' . $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)) { @@ -1909,7 +1981,7 @@ function template_preprocess_comment_fol $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/' . $comment->cid, array('fragment' => "comment-$comment->cid")); // Gather comment classes. if ($comment->uid === 0) { $variables['classes_array'][] = 'comment-by-anonymous'; Index: modules/comment/comment.test =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.test,v retrieving revision 1.31 diff -u -p -r1.31 comment.test --- modules/comment/comment.test 3 Jun 2009 06:52:29 -0000 1.31 +++ modules/comment/comment.test 16 Jun 2009 14:11:57 -0000 @@ -123,7 +123,9 @@ class CommentHelperCase extends DrupalWe * Form value. */ function setCommentForm($enabled) { - $this->setCommentSettings('comment_form_location', ($enabled ? '1' : '3'), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.'); + $this->setCommentSettings('comment_form_location', + ($enabled ? COMMENT_FORM_BELOW : COMMENT_FORM_SEPARATE_PAGE), + 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.'); } /** @@ -143,7 +145,7 @@ class CommentHelperCase extends DrupalWe * Comments per page value. */ function setCommentsPerPage($number) { - $this->setCommentSettings('comment_default_per_page_article', $number, 'Number of comments per page set to ' . $number . '.'); + $this->setCommentSettings('comment_default_per_page', $number, 'Number of comments per page set to ' . $number . '.'); } /** @@ -244,7 +246,9 @@ class CommentInterfaceTest extends Comme // Set comments to not have subject. $this->drupalLogin($this->admin_user); $this->setCommentPreview(TRUE); + $this->setCommentForm(TRUE); $this->setCommentSubject(FALSE); + $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED_EXPANDED, t('Comment paging changed.')); $this->drupalLogout(); // Post comment without subject. @@ -300,11 +304,10 @@ class CommentInterfaceTest extends Comme $this->drupalGet('node'); $this->assertRaw('3 comments', t('Link to the 3 comments exist.')); - // Pager + // Confirm a new comment is posted to the correct page. $this->setCommentsPerPage(2); $comment_new_page = $this->postComment($this->node, $this->randomName(), $this->randomName()); - $this->drupalGet('node/' . $this->node->nid); - $this->assertTrue($this->commentExists($comment) && $this->commentExists($comment_new_page), t('Page one exists. %s')); + $this->assertTrue($this->commentExists($comment_new_page), t('Page one exists. %s')); $this->drupalGet('node/' . $this->node->nid, array('query' => 'page=1')); $this->assertTrue($this->commentExists($reply, TRUE), t('Page two exists. %s')); $this->setCommentsPerPage(50); @@ -454,6 +457,78 @@ class CommentAnonymous extends CommentHe } } +/** + * Verify pagination of comments + */ +class CommentPagerTest extends CommentHelperCase { + + public static function getInfo() { + return array( + 'name' => t('Comment paging settings'), + 'description' => t('Test paging of comments and their settings.'), + 'group' => t('Comment'), + ); + } + + function testCommentPaging() { + $this->drupalLogin($this->admin_user); + $this->setCommentForm(TRUE); + $this->setCommentSubject(TRUE); + $this->setCommentPreview(FALSE); + $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); + $comments = array(); + $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), FALSE, TRUE); + $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), FALSE, TRUE); + $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), FALSE, TRUE); + + $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT_EXPANDED, t('Comment paging changed.')); + $this->setCommentsPerPage(1); + $this->drupalGet('node/' . $node->nid); + $this->assertRaw(t('next'), t('Paging links found.')); + $this->assertTrue($this->commentExists($comments[0]), t('Comment 1 appears on page 1.')); + $this->assertFalse($this->commentExists($comments[1]), t('Comment 2 does not appear on page 1.')); + $this->assertFalse($this->commentExists($comments[2]), t('Comment 3 does not appear on page 1.')); + + $this->drupalGet('node/' . $node->nid, array('query' => 'page=1')); + $this->assertTrue($this->commentExists($comments[1]), t('Comment 2 appears on page 2.')); + $this->assertFalse($this->commentExists($comments[0]), t('Comment 1 does not appear on page 2.')); + $this->assertFalse($this->commentExists($comments[2]), t('Comment 3 does not appear on page 2.')); + + $this->drupalGet('node/' . $node->nid, array('query' => 'page=2')); + $this->assertTrue($this->commentExists($comments[2]), t('Comment 3 appears on page 3.')); + $this->assertFalse($this->commentExists($comments[0]), t('Comment 1 does not appear on page 3.')); + $this->assertFalse($this->commentExists($comments[1]), t('Comment 2 does not appear on page 3.')); + + // Post a reply to the oldest comment and test again. + $replies = array(); + $oldest_comment = reset($comments); + $this->drupalGet('comment/reply/' . $node->nid . '/' . $oldest_comment->id); + $reply = $this->postComment(null, $this->randomName(), $this->randomName(), FALSE, TRUE); + + $this->setCommentsPerPage(2); + // We are still in flat view - the replies should not be on the first page, + // even though they are replies to the oldest comment. + $this->drupalGet('node/' . $node->nid, array('query' => 'page=0')); + $this->assertFalse($this->commentExists($reply, TRUE), t('In flat mode, reply does not appear on page 1.')); + + // If we switch to threaded mode, the replies on the oldest comment + // should be bumped to the first page and comment 6 should be bumped + // to the second page. + $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED_EXPANDED, t('Switched to threaded mode.')); + $this->drupalGet('node/' . $node->nid, array('query' => 'page=0')); + $this->assertTrue($this->commentExists($reply, TRUE), t('In threaded mode, reply appears on page 1.')); + $this->assertFalse($this->commentExists($comments[1]), t('In threaded mode, comment 2 has been bumped off of page 1.')); + + // If (# replies > # comments per page) in threaded expanded view, + // the overage should be bumped. + $reply2 = $this->postComment(NULL, $this->randomName(), $this->randomName(), FALSE, TRUE); + $this->drupalGet('node/' . $node->nid, array('query' => 'page=0')); + $this->assertFalse($this->commentExists($reply2, TRUE), t('In threaded mode where # replies > # comments per page, the newest reply does not appear on page 1.')); + + $this->drupalLogout(); + } +} + class CommentApprovalTest extends CommentHelperCase { public static function getInfo() { return array( @@ -595,10 +670,25 @@ class CommentBlockFunctionalTest extends $this->drupalPost('admin/build/block/configure/comment/recent', $block, t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block saved.')); - // Test that all three comments are shown. + // Post an additional comment. + $comment4 = $this->postComment($this->node, $this->randomName(), $this->randomName()); + + // Test that all four comments are shown. $this->assertText($comment1->subject, t('Comment found in block.')); $this->assertText($comment2->subject, t('Comment found in block.')); $this->assertText($comment3->comment, t('Comment found in block.')); + $this->assertText($comment4->subject, t('Comment found in block.')); + + // Test that links to comments work when comments are across pages. + $this->setCommentsPerPage(1); + $this->drupalGet(''); + $this->clickLink($comment1->subject); + $this->assertText($comment1->subject, t('Comment link goes to correct page.')); + $this->drupalGet(''); + $this->clickLink($comment2->subject); + $this->assertText($comment2->subject, t('Comment link goes to correct page.')); + $this->clickLink($comment4->subject); + $this->assertText($comment4->subject, t('Comment link goes to correct page.')); } }