diff --git modules/comment/comment.module modules/comment/comment.module
index cd9fc46..d3fa901 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -104,34 +104,18 @@ function comment_theme() {
'arguments' => array(),
),
'comment_preview' => array(
- 'arguments' => array('comment' => NULL, 'node' => NULL, 'links' => array(), 'visible' => 1),
- ),
- 'comment_view' => array(
- 'arguments' => array('comment' => NULL, 'node' => NULL, 'links' => array(), 'visible' => 1),
+ 'arguments' => array('comment' => NULL),
),
'comment' => array(
'template' => 'comment',
- 'arguments' => array('comment' => NULL, 'node' => NULL, 'links' => array()),
- ),
- 'comment_form_box' => array(
- 'arguments' => array('edit' => NULL, 'title' => NULL),
- ),
- 'comment_folded' => array(
- 'template' => 'comment-folded',
- 'arguments' => array('comment' => NULL, 'node' => NULL),
- ),
- 'comment_flat_expanded' => array(
- 'arguments' => array('comment' => NULL, 'node' => NULL),
- ),
- 'comment_thread_expanded' => array(
- 'arguments' => array('comment' => NULL, 'node' => NULL),
+ 'arguments' => array('elements' => NULL),
),
'comment_post_forbidden' => array(
'arguments' => array('nid' => NULL),
),
'comment_wrapper' => array(
'template' => 'comment-wrapper',
- 'arguments' => array('content' => NULL, 'node' => NULL),
+ 'arguments' => array('elements' => NULL),
),
'comment_submitted' => array(
'arguments' => array('comment' => NULL),
@@ -533,16 +517,278 @@ function comment_node_view($node, $build_mode) {
);
// Append the list of comments to $node->content for node detail pages.
- if ($node->comment && (bool)menu_get_object() && empty($node->in_preview)) {
- $node->content['comments'] = array(
- '#markup' => comment_render($node),
- '#sorted' => TRUE,
- );
+ if ($node->comment && (bool)menu_get_object() && empty($node->in_preview) && user_access('access comments')) {
+ $comments = array();
+ // Only attempt to render comments if the node has visible comments.
+ // Unpublished comments are not included in $node->comment_count, so show
+ // comments unconditionally if the user is an administrator.
+ if ($node->comment_count || user_access('administer comments')) {
+ if ($cids = comment_get_thread($node)) {
+ $comments = comment_load_multiple($cids);
+ comment_prepare_thread($comments);
+ $node->content += comment_build_multiple($comments);
+ $node->content['comments']['#attached_css'][] = drupal_get_path('module', 'comment') . '/comment.css';
+ $node->content['comments']['pager']['#theme'] = 'pager';
+
+ // If enabled, show new comment form if it's not already being displayed.
+ $reply = arg(0) == 'comment' && arg(1) == 'reply';
+ if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW) && !$reply) {
+ $build = array(
+ // @todo make this a render array.
+ '#markup' => drupal_render(drupal_get_form('comment_form', array('nid' => $node->nid))),
+ );
+ $build['#prefix'] = '
' . t('Post new comment') . '
';
+ $build['#suffix'] = '
';
+ $node->content['comments']['comment_form'] = $build;
+ }
+ }
+ }
}
}
}
/**
+ * Retrieve comment(s) for a thread.
+ *
+ * @param $node
+ * The node whose comment(s) needs rendering.
+ *
+ * To display threaded comments in the correct order we keep a 'thread' field
+ * and order by that value. This field keeps this data in
+ * a way which is easy to update and convenient to use.
+ *
+ * A "thread" value starts at "1". If we add a child (A) to this comment,
+ * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
+ * brother of (A) will get "1.2". Next brother of the parent of (A) will get
+ * "2" and so on.
+ *
+ * First of all note that the thread field stores the depth of the comment:
+ * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
+ *
+ * Now to get the ordering right, consider this example:
+ *
+ * 1
+ * 1.1
+ * 1.1.1
+ * 1.2
+ * 2
+ *
+ * If we "ORDER BY thread ASC" we get the above result, and this is the
+ * natural order sorted by time. However, if we "ORDER BY thread DESC"
+ * we get:
+ *
+ * 2
+ * 1.2
+ * 1.1.1
+ * 1.1
+ * 1
+ *
+ * Clearly, this is not a natural way to see a thread, and users will get
+ * confused. The natural order to show a thread by time desc would be:
+ *
+ * 2
+ * 1
+ * 1.2
+ * 1.1
+ * 1.1.1
+ *
+ * which is what we already did before the standard pager patch. To achieve
+ * this we simply add a "/" at the end of each "thread" value. This way, the
+ * thread fields will look like this:
+ *
+ * 1/
+ * 1.1/
+ * 1.1.1/
+ * 1.2/
+ * 2/
+ *
+ * we add "/" since this char is, in ASCII, higher than every number, so if
+ * now we "ORDER BY thread DESC" we get the correct order. However this would
+ * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
+ * to consider the trailing "/" so we use a substring only.
+ */
+ function comment_get_thread($node) {
+ $mode = _comment_get_display_setting('mode', $node);
+ $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+
+ $query = db_select('comment', 'c')->extend('PagerDefault');
+ $query->addField('c', 'cid');
+ $query
+ ->condition('c.nid', $node->nid)
+ ->addTag('node_access')
+ ->limit($comments_per_page);
+
+ $count_query = db_select('comment', 'c');
+ $count_query->addExpression('COUNT(*)');
+ $count_query
+ ->condition('c.nid', $node->nid)
+ ->addTag('node_access');
+
+ if (!user_access('administer comments')) {
+ $query->condition('c.status', COMMENT_PUBLISHED);
+ $count_query->condition('c.status', COMMENT_PUBLISHED);
+ }
+ if ($mode === COMMENT_MODE_FLAT) {
+ $query->orderBy('c.cid', 'ASC');
+ }
+ else {
+ // See comment above. Analysis reveals that this doesn't cost too
+ // much. It scales much much better than having the whole comment
+ // structure.
+ $query->orderBy('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'ASC');
+ }
+
+ $query->setCountQuery($count_query);
+ $cids = $query->execute()->fetchCol();
+
+ return $cids;
+}
+
+/**
+ * Loop over comment thread, noting indentation level.
+ *
+ * @param array $comments
+ * An array of comment objects, keyed by cid.
+ * @return void
+ * An array of comment objects, enriched with indentation information.
+ */
+function comment_prepare_thread(&$comments) {
+ // A flag stating if we are still searching for first new comment on the thread.
+ $first_new = TRUE;
+
+ // A counter that helps track how indented we are.
+ $divs = 0;
+
+ foreach ($comments as $key => $comment) {
+ $comment = drupal_unpack($comment);
+ $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+ $comment->new = node_mark($comment->nid, $comment->timestamp);
+
+ if ($first_new && $comment->new != MARK_READ) {
+ // Assign the anchor only for the first new comment. This avoids duplicate
+ // id attributes on a page.
+ $first_new = FALSE;
+ $comment->first_new = TRUE; // "\n";
+ }
+
+ // the 'divs element instructs #prefix whether to add an indent div or
+ // close existing divs (a negative value).
+ $comment->depth = count(explode('.', $comment->thread)) - 1;
+ if ($comment->depth > $divs) {
+ $comment->divs = 1;
+ $divs++;
+ }
+ else {
+ $comment->divs = $comment->depth - $divs;
+ $divs = 0;
+ }
+ $comments[$key] = $comment;
+ }
+}
+
+/**
+ * Generate an array for rendering the given comment.
+ *
+ * @param $comment
+ * A comment object.
+ * @param $build_mode
+ * Build mode, e.g. 'full', 'teaser'...
+ *
+ * @return
+ * An array as expected by drupal_render().
+ */
+function comment_build($comment, $build_mode = 'full') {
+ // $comment = (object)$comment;
+ $node = node_load($comment->nid);
+ $comment = comment_build_content($comment, $build_mode);
+
+ $build = $comment->content;
+
+ $build += array(
+ '#theme_wrapper' => 'comment',
+ '#comment' => $comment,
+ '#build_mode' => $build_mode,
+ );
+ if (_comment_get_display_setting('mode', $node) == COMMENT_MODE_THREADED) {
+ $prefix = "\n";
+ // Add indentation or close existing divs as needed.
+ $prefix .= $comment->divs <= 0 ? str_repeat('', abs($comment->divs)) : "\n" . '';
+ $build['#prefix'] = $prefix;
+ }
+ return $build;
+}
+
+/**
+ * Builds a structured array representing the comment's content.
+ *
+ * The content built for the comment (field values, comments, file attachments or
+ * other comment components) will vary depending on the $build_mode parameter.
+ *
+ * @param $comment
+ * A comment object.
+ * @param $build_mode
+ * Build mode, e.g. 'full', 'teaser'...
+ *
+ * @return
+ * A structured array containing the individual elements
+ * of the comment's content.
+ */
+function comment_build_content($comment, $build_mode = 'full') {
+ if (empty($comment->content)) {
+ $comment->content = array();
+ }
+
+ // Build comment body.
+ $comment->content['comment_body'] = array(
+ '#markup' => check_markup($comment->comment, $comment->format, '', FALSE),
+ );
+
+ $comment->content['links']['comment'] = array(
+ '#theme' => 'links',
+ '#links' => comment_links($comment),
+ '#attributes' => array('class' => 'links inline'),
+ );
+
+ // Build fields content.
+ $comment->content += field_attach_view('comment', $comment, $build_mode);
+
+ // Allow modules to make their own additions to the comment.
+ module_invoke_all('comment_view', $comment, $build_mode);
+
+ // Allow modules to modify the structured comment.
+ drupal_alter('comment_build', $comment, $build_mode);
+
+ return $comment;
+}
+
+/**
+ * Construct a drupal_render() style array from an array of loaded comments.
+ *
+ * @param $comments
+ * An array of comments as returned by comment_load_multiple().
+ * @param $build_mode
+ * Build mode, e.g. 'full', 'teaser'...
+ * @param $weight
+ * An integer representing the weight of the first comment in the list.
+ * @return
+ * An array in the format expected by drupal_render().
+ */
+function comment_build_multiple($comments, $build_mode = 'full', $weight = 0) {
+ $build['comments']['prelude'] = array('#markup' => '', '#weight' => -20);
+ foreach ($comments as $comment) {
+ $build['comments'][$comment->cid] = comment_build($comment, $build_mode);
+ $build['comments'][$comment->cid]['#weight'] = $weight;
+ $weight++;
+ }
+ $build['comments'] += array(
+ '#sorted' => TRUE,
+ '#attributes' => array('id' => 'comments'),
+ '#theme_wrapper' => 'comment_wrapper',
+ );
+ return $build;
+}
+
+/**
* Implement hook_form_FORM_ID_alter().
*/
function comment_form_node_type_form_alter(&$form, $form_state) {
@@ -878,7 +1124,7 @@ function comment_save($comment) {
}
else {
// Add the comment to database. This next section builds the thread field.
- // Also see the documentation for comment_render().
+ // Also see the documentation for comment_build().
if ($comment->pid == 0) {
// This is a comment with no parent comment (depth 0): we start
// by retrieving the maximum thread level.
@@ -977,24 +1223,13 @@ function comment_link($type, $object, $build_mode) {
*
* @param $comment
* The comment to which the links will be related.
- * @param $return
- * Not used.
* @return
* An associative array containing the links.
*/
-function comment_links($comment, $return = 1) {
+function comment_links(&$comment) {
global $user;
$links = array();
- // If viewing just this comment, link back to the in-context view.
- if ($return) {
- $links['comment_parent'] = array(
- 'title' => t('parent'),
- 'href' => 'comment/' . $comment->cid,
- 'fragment' => "comment-$comment->cid"
- );
- }
-
$node = node_load($comment->nid);
if ($node->comment == COMMENT_NODE_OPEN) {
if (user_access('administer comments') && user_access('post comments')) {
@@ -1041,185 +1276,13 @@ function comment_links($comment, $return = 1) {
$links['comment_forbidden']['html'] = TRUE;
}
}
+
+
return $links;
}
/**
- * Renders comment(s).
- *
- * @param $node
- * The node which comment(s) needs rendering.
- * @param $cid
- * Optional, if given, only one comment is rendered.
- *
- * To display threaded comments in the correct order we keep a 'thread' field
- * and order by that value. This field keeps this data in
- * a way which is easy to update and convenient to use.
- *
- * A "thread" value starts at "1". If we add a child (A) to this comment,
- * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
- * brother of (A) will get "1.2". Next brother of the parent of (A) will get
- * "2" and so on.
- *
- * First of all note that the thread field stores the depth of the comment:
- * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
- *
- * Now to get the ordering right, consider this example:
- *
- * 1
- * 1.1
- * 1.1.1
- * 1.2
- * 2
- *
- * If we "ORDER BY thread ASC" we get the above result, and this is the
- * natural order sorted by time. However, if we "ORDER BY thread DESC"
- * we get:
- *
- * 2
- * 1.2
- * 1.1.1
- * 1.1
- * 1
- *
- * Clearly, this is not a natural way to see a thread, and users will get
- * confused. The natural order to show a thread by time desc would be:
- *
- * 2
- * 1
- * 1.2
- * 1.1
- * 1.1.1
- *
- * which is what we already did before the standard pager patch. To achieve
- * this we simply add a "/" at the end of each "thread" value. This way, the
- * thread fields will look like this:
- *
- * 1/
- * 1.1/
- * 1.1.1/
- * 1.2/
- * 2/
- *
- * we add "/" since this char is, in ASCII, higher than every number, so if
- * now we "ORDER BY thread DESC" we get the correct order. However this would
- * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
- * to consider the trailing "/" so we use a substring only.
- */
-function comment_render($node, $cid = 0) {
- global $user;
- $output = '';
-
- if (user_access('access comments')) {
- // Pre-process variables.
- $nid = $node->nid;
- if (empty($nid)) {
- $nid = 0;
- }
-
- $mode = _comment_get_display_setting('mode', $node);
- $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
-
- if ($cid && is_numeric($cid)) {
- $comment = current(comment_load_multiple(array('cid' => $cid, 'status' => COMMENT_PUBLISHED)));
- // Single comment view.
- if ($comment) {
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $links = module_invoke_all('link', 'comment', $comment, 1);
- drupal_alter('link', $links, $node);
-
- $output .= theme('comment_view', $comment, $node, $links);
- }
- }
- // Only attempt to render comments if the node has visible comments.
- // Unpublished comments are not included in $node->comment_count, so show
- // comments unconditionally if the user is an administrator.
- elseif ($node->comment_count || user_access('administer comments')) {
-
- // Multiple comment view.
- $query = db_select('comment', 'c')->extend('PagerDefault');
- $query->addField('c', 'cid');
- $query
- ->condition('c.nid', $nid)
- ->addTag('node_access')
- ->limit($comments_per_page);
-
- $count_query = db_select('comment', 'c');
- $count_query->addExpression('COUNT(*)');
- $count_query
- ->condition('c.nid', $nid)
- ->addTag('node_access');
-
- if (!user_access('administer comments')) {
- $query->condition('c.status', COMMENT_PUBLISHED);
- $count_query->condition('c.status', COMMENT_PUBLISHED);
- }
- if ($mode === COMMENT_MODE_FLAT) {
- $query->orderBy('c.cid', 'ASC');
- }
- else {
- // See comment above. Analysis reveals that this doesn't cost too
- // much. It scales much much better than having the whole comment
- // structure.
- $query->orderBy('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'ASC');
- }
-
- $query->setCountQuery($count_query);
- $cids = $query->execute()->fetchCol();
-
- $divs = 0;
- $num_rows = FALSE;
- $render = '';
- $comments = comment_load_multiple($cids);
- drupal_add_css(drupal_get_path('module', 'comment') . '/comment.css');
- foreach ($comments as $comment) {
- $comment = drupal_unpack($comment);
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $comment->depth = count(explode('.', $comment->thread)) - 1;
-
- if ($mode == COMMENT_MODE_THREADED) {
- if ($comment->depth > $divs) {
- $divs++;
- $render .= '
';
- }
- else {
- while ($comment->depth < $divs) {
- $divs--;
- $render .= '
';
- }
- }
- }
-
- if ($mode == COMMENT_MODE_FLAT) {
- $render .= theme('comment_flat_expanded', $comment, $node);
- }
- elseif ($mode == COMMENT_MODE_THREADED) {
- $render .= theme('comment_thread_expanded', $comment, $node);
- }
- $num_rows = TRUE;
- }
- while ($divs-- > 0) {
- $render .= '
';
- }
- $output .= $render;
- $output .= theme('pager', NULL);
- }
-
- // If enabled, show new comment form if it's not already being displayed.
- $reply = arg(0) == 'comment' && arg(1) == 'reply';
- if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW) && !$reply) {
- $output .= theme('comment_form_box', array('nid' => $nid), t('Post new comment'));
- }
- if ($output) {
- $output = theme('comment_wrapper', $output, $node);
- }
- }
-
- return $output;
-}
-
-/**
* Comment operations. Offer different update operations depending on
* which comment administration page is being viewed.
*
@@ -1395,7 +1458,7 @@ function comment_get_display_ordinal($cid, $node_type) {
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().
+ // @see comment_build_multiple().
$query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))');
}
@@ -1667,22 +1730,6 @@ function comment_form(&$form_state, $edit, $title = NULL) {
}
/**
- * Theme the comment form box.
- *
- * @param $edit
- * The form structure.
- * @param $title
- * The form title.
- * @return
- * A string containing the box output.
- */
-function theme_comment_form_box($edit, $title = NULL) {
- $content = drupal_render(drupal_get_form('comment_form', $edit, $title));
- $output = '' . $title . '
' . $content . '
';
- return $output;
-}
-
-/**
* Form builder; Generate and validate a comment preview form.
*
* @ingroup forms
@@ -1885,65 +1932,24 @@ function comment_form_submit($form, &$form_state) {
}
/**
- * Theme a single comment block.
- *
- * @param $comment
- * The comment object.
- * @param $node
- * The comment node.
- * @param $links
- * An associative array containing control links.
- * @param $visible
- * Switches between folded/unfolded view.
- * @ingroup themeable
- */
-function theme_comment_view($comment, $node, $links = array(), $visible = TRUE) {
- $first_new = &drupal_static(__FUNCTION__, TRUE);
- $comment->new = node_mark($comment->nid, $comment->timestamp);
- $output = '';
-
- if ($first_new && $comment->new != MARK_READ) {
- // Assign the anchor only for the first new comment. This avoids duplicate
- // id attributes on a page.
- $first_new = FALSE;
- $output .= "\n";
- }
-
- $output .= "\n";
-
- // Switch to folded/unfolded view of the comment.
- if ($visible) {
- $comment->comment = check_markup($comment->comment, $comment->format, '', FALSE);
- // Comment API hook.
- module_invoke_all('comment_view', $comment);
- $output .= theme('comment', $comment, $node, $links);
- }
- else {
- $output .= theme('comment_folded', $comment, $node);
- }
-
- return $output;
-}
-
-/**
* Process variables for comment.tpl.php.
*
* @see comment.tpl.php
- * @see theme_comment()
*/
function template_preprocess_comment(&$variables) {
- $comment = $variables['comment'];
- $node = $variables['node'];
+ $comment = $variables['elements']['#comment'];
+ $variables['comment'] = $comment;
+ $variables['node'] = node_load($comment->nid);
$variables['author'] = theme('username', $comment);
- $variables['content'] = $comment->comment;
+ $variables['content'] = $variables['elements']['#children'];
$variables['date'] = format_date($comment->timestamp);
$variables['links'] = isset($variables['links']) ? theme('links', $variables['links']) : '';
- $variables['new'] = $comment->new ? t('new') : '';
+ $variables['new'] = !empty($comment->new) ? t('new') : '';
$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, 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
- $variables['template_files'][] = 'comment-' . $node->type;
+ $variables['template_files'][] = 'comment-' . $variables['node']->type;
// Set status to a string representation of comment->status.
if (isset($comment->preview)) {
$variables['status'] = 'comment-preview';
@@ -1980,6 +1986,7 @@ function template_preprocess_comment(&$variables) {
*/
function template_preprocess_comment_folded(&$variables) {
$comment = $variables['comment'];
+ $variables['node'] = node_load($comment->nid);
$variables['author'] = theme('username', $comment);
$variables['date'] = format_date($comment->timestamp);
$variables['new'] = $comment->new ? t('new') : '';
@@ -2006,32 +2013,6 @@ function template_preprocess_comment_folded(&$variables) {
}
/**
- * Theme comment flat expanded view.
- *
- * @param $comment
- * The comment to be themed.
- * @param $node
- * The comment node.
- * @ingroup themeable
- */
-function theme_comment_flat_expanded($comment, $node) {
- return theme('comment_view', $comment, $node, module_invoke_all('link', 'comment', $comment, 0));
-}
-
-/**
- * Theme comment thread expanded view.
- *
- * @param $comment
- * The comment to be themed.
- * @param $node
- * The comment node.
- * @ingroup themeable
- */
-function theme_comment_thread_expanded($comment, $node) {
- return theme('comment_view', $comment, $node, module_invoke_all('link', 'comment', $comment, 0));
-}
-
-/**
* Theme a "you can't post comments" notice.
*
* @param $node
@@ -2075,6 +2056,9 @@ function theme_comment_post_forbidden($node) {
* @see theme_comment_wrapper()
*/
function template_preprocess_comment_wrapper(&$variables) {
+ $variables['node'] = menu_get_object();
+ $variables['content'] = $variables['elements']['#children'];
+
// Provide contextual information.
$variables['display_mode'] = _comment_get_display_setting('mode', $variables['node']);
$variables['template_files'][] = 'comment-wrapper-' . $variables['node']->type;
diff --git themes/garland/template.php themes/garland/template.php
index 1952353..97fab30 100644
--- themes/garland/template.php
+++ themes/garland/template.php
@@ -15,18 +15,6 @@ function garland_breadcrumb($breadcrumb) {
}
/**
- * Allow themable wrapping of all comments.
- */
-function garland_comment_wrapper($content, $node) {
- if (!$content || $node->type == 'forum') {
- return '';
- }
- else {
- return '';
- }
-}
-
-/**
* Override or insert variables into the page template.
*/
function garland_preprocess_page(&$vars) {