Same name and namespace in other branches
  1. 4.6.x modules/comment.module \comment_render()
  2. 4.7.x modules/comment.module \comment_render()
  3. 5.x modules/comment/comment.module \comment_render()

Renders comment(s).

Parameters

$node: The node which comment(s) needs rendering.

$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 out thread fields will look like depicted below:

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.

1 call to comment_render()
node_show in modules/node/node.module
Generate a page displaying a single node, along with its comments.

File

modules/comment/comment.module, line 920
Enables users to comment on published content.

Code

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);
    $order = _comment_get_display_setting('sort', $node);
    $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
    if ($cid && is_numeric($cid)) {

      // Single comment view.
      $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.signature_format, u.picture, u.data, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
      $query_args = array(
        $cid,
      );
      if (!user_access('administer comments')) {
        $query .= ' AND c.status = %d';
        $query_args[] = COMMENT_PUBLISHED;
      }
      $query = db_rewrite_sql($query, 'c', 'cid');
      $result = db_query($query, $query_args);
      if ($comment = db_fetch_object($result)) {
        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
        $links = module_invoke_all('link', 'comment', $comment, 1);
        drupal_alter('link', $links, $node, $comment);
        $output .= theme('comment_view', $comment, $node, $links);
      }
    }
    else {

      // Multiple comment view
      $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid = %d';
      $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.signature_format, u.picture, u.data, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d';
      $query_args = array(
        $nid,
      );
      if (!user_access('administer comments')) {
        $query .= ' AND c.status = %d';
        $query_count .= ' AND c.status = %d';
        $query_args[] = COMMENT_PUBLISHED;
      }
      if ($order == COMMENT_ORDER_NEWEST_FIRST) {
        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
          $query .= ' ORDER BY c.cid DESC';
        }
        else {
          $query .= ' ORDER BY c.thread DESC';
        }
      }
      else {
        if ($order == COMMENT_ORDER_OLDEST_FIRST) {
          if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
            $query .= ' ORDER BY c.cid';
          }
          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 .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
          }
        }
      }
      $query = db_rewrite_sql($query, 'c', 'cid');
      $query_count = db_rewrite_sql($query_count, 'c', 'cid');

      // Start a form, for use with comment control.
      $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
      $divs = 0;
      $num_rows = FALSE;
      $comments = '';
      drupal_add_css(drupal_get_path('module', 'comment') . '/comment.css');
      while ($comment = db_fetch_object($result)) {
        $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_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
          if ($comment->depth > $divs) {
            $divs++;
            $comments .= '<div class="indented">';
          }
          else {
            while ($comment->depth < $divs) {
              $divs--;
              $comments .= '</div>';
            }
          }
        }
        if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
          $comments .= theme('comment_flat_collapsed', $comment, $node);
        }
        else {
          if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
            $comments .= theme('comment_flat_expanded', $comment, $node);
          }
          else {
            if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
              $comments .= theme('comment_thread_collapsed', $comment, $node);
            }
            else {
              if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
                $comments .= theme('comment_thread_expanded', $comment, $node);
              }
            }
          }
        }
        $num_rows = TRUE;
      }
      while ($divs-- > 0) {
        $comments .= '</div>';
      }
      $comment_controls = variable_get('comment_controls_' . $node->type, COMMENT_CONTROLS_HIDDEN);
      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
      }
      $output .= $comments;
      $output .= theme('pager', NULL, $comments_per_page, 0);
      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
      }
    }

    // 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_mode($nid) == COMMENT_NODE_READ_WRITE && variable_get('comment_form_location_' . $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW && !$reply) {
      $output .= comment_form_box(array(
        'nid' => $nid,
      ), t('Post new comment'));
    }
    if ($output) {
      $output = theme('comment_wrapper', $output, $node);
    }
  }
  return $output;
}