Code coverage for /20080809/modules/comment/comment.module

Line #Times calledCode
1
<?php
2
// $Id: comment.module,v 1.645 2008/08/03 18:52:27 dries Exp $
3
4
/**
5
 * @file
6
 * Enables users to comment on published content.
7
 *
8
 * When enabled, the Drupal comment module creates a discussion
9
 * board for each Drupal node. Users can post comments to discuss
10
 * a forum topic, weblog post, story, collaborative book page, etc.
11
 */
12
13
/**
14
 * Comment is awaiting approval.
15
 */
162027
define('COMMENT_NOT_PUBLISHED', 0);
17
18
/**
19
 * Comment is published.
20
 */
212027
define('COMMENT_PUBLISHED', 1);
22
23
/**
24
 * Comments are displayed in a flat list - collapsed.
25
 */
262027
define('COMMENT_MODE_FLAT_COLLAPSED', 1);
27
28
/**
29
 * Comments are displayed in a flat list - expanded.
30
 */
312027
define('COMMENT_MODE_FLAT_EXPANDED', 2);
32
33
/**
34
 * Comments are displayed as a threaded list - collapsed.
35
 */
362027
define('COMMENT_MODE_THREADED_COLLAPSED', 3);
37
38
/**
39
 * Comments are displayed as a threaded list - expanded.
40
 */
412027
define('COMMENT_MODE_THREADED_EXPANDED', 4);
42
43
/**
44
 * Anonymous posters cannot enter their contact information.
45
 */
462027
define('COMMENT_ANONYMOUS_MAYNOT_CONTACT', 0);
47
48
/**
49
 * Anonymous posters may leave their contact information.
50
 */
512027
define('COMMENT_ANONYMOUS_MAY_CONTACT', 1);
52
53
/**
54
 * Anonymous posters are required to leave their contact information.
55
 */
562027
define('COMMENT_ANONYMOUS_MUST_CONTACT', 2);
57
58
/**
59
 * Comment form should be displayed on a separate page.
60
 */
612027
define('COMMENT_FORM_SEPARATE_PAGE', 0);
62
63
/**
64
 * Comment form should be shown below post or list of comments.
65
 */
662027
define('COMMENT_FORM_BELOW', 1);
67
68
/**
69
 * Comments for this node are disabled.
70
 */
712027
define('COMMENT_NODE_DISABLED', 0);
72
73
/**
74
 * Comments for this node are locked.
75
 */
762027
define('COMMENT_NODE_READ_ONLY', 1);
77
78
/**
79
 * Comments are enabled on this node.
80
 */
812027
define('COMMENT_NODE_READ_WRITE', 2);
82
83
/**
84
 * Comment preview is optional.
85
 */
862027
define('COMMENT_PREVIEW_OPTIONAL', 0);
87
88
/**
89
 * Comment preview is required.
90
 */
912027
define('COMMENT_PREVIEW_REQUIRED', 1);
92
93
/**
94
 * Implementation of hook_help().
95
 */
962027
function comment_help($path, $arg) {
97
  switch ($path) {
981491
    case 'admin/help#comment':
9926
      $output  = '<p>' . t('The comment module allows visitors to comment
on your posts, creating ad hoc discussion boards. Any <a
href="@content-type">content type</a> may have its <em>Default comment
setting</em> set to <em>Read/Write</em> to allow comments, or
<em>Disabled</em>, to prevent comments. Comment display settings and other
controls may also be customized for each content type.',
array('@content-type' => url('admin/build/types'))) . '</p>';
10026
      $output .= '<p>' . t('Comment permissions are assigned to user roles,
and are used to determine whether anonymous users (or other roles) are
allowed to comment on posts. If anonymous users are allowed to comment,
their individual contact information may be retained in cookies stored on
their local computer for use in later comment submissions. When a comment
has no replies, it may be (optionally) edited by its author. The comment
module uses the same input formats and HTML tags available when creating
other forms of content.') . '</p>';
10126
      $output .= '<p>' . t('For more information, see the online handbook
entry for <a href="@comment">Comment module</a>.', array('@comment' =>
'http://drupal.org/handbook/modules/comment/')) . '</p>';
102
10326
      return $output;
104
1051489
    case 'admin/content/comment':
1069
      return '<p>' . t("Below is a list of the latest comments posted to
your site. Click on a subject to see the comment, the author's name to edit
the author's user information, 'edit' to modify the text, and 'delete' to
remove their submission.") . '</p>';
107
1081480
    case 'admin/content/comment/approval':
1095
      return '<p>' . t("Below is a list of the comments posted to your site
that need approval. To approve a comment, click on 'edit' and then change
its 'moderation status' to Approved. Click on a subject to see the comment,
the author's name to edit the author's user information, 'edit' to modify
the text, and 'delete' to remove their submission.") . '</p>';
1100
  }
1111475
}
112
113
/**
114
 * Implementation of hook_theme().
115
 */
1162027
function comment_theme() {
117
  return array(
118
    'comment_block' => array(
11991
      'arguments' => array(),
12091
    ),
121
    'comment_admin_overview' => array(
12291
      'arguments' => array('form' => NULL),
12391
    ),
124
    'comment_preview' => array(
12591
      'arguments' => array('comment' => NULL, 'node' => NULL, 'links' =>
array(), 'visible' => 1),
12691
    ),
127
    'comment_view' => array(
12891
      'arguments' => array('comment' => NULL, 'node' => NULL, 'links' =>
array(), 'visible' => 1),
12991
    ),
130
    'comment' => array(
13191
      'template' => 'comment',
13291
      'arguments' => array('comment' => NULL, 'node' => NULL, 'links' =>
array()),
13391
    ),
134
    'comment_folded' => array(
13591
      'template' => 'comment-folded',
13691
      'arguments' => array('comment' => NULL),
13791
    ),
138
    'comment_flat_collapsed' => array(
13991
      'arguments' => array('comment' => NULL, 'node' => NULL),
14091
    ),
141
    'comment_flat_expanded' => array(
14291
      'arguments' => array('comment' => NULL, 'node' => NULL),
14391
    ),
144
    'comment_thread_collapsed' => array(
14591
      'arguments' => array('comment' => NULL, 'node' => NULL),
14691
    ),
147
    'comment_thread_expanded' => array(
14891
      'arguments' => array('comment' => NULL, 'node' => NULL),
14991
    ),
150
    'comment_post_forbidden' => array(
15191
      'arguments' => array('nid' => NULL),
15291
    ),
153
    'comment_wrapper' => array(
15491
      'template' => 'comment-wrapper',
15591
      'arguments' => array('content' => NULL, 'node' => NULL),
15691
    ),
157
    'comment_submitted' => array(
15891
      'arguments' => array('comment' => NULL),
15991
    ),
16091
  );
1610
}
162
163
/**
164
 * Implementation of hook_menu().
165
 */
1662027
function comment_menu() {
16785
  $items['admin/content/comment'] = array(
16885
    'title' => 'Comments',
16985
    'description' => 'List and edit site comments and the comment
moderation queue.',
17085
    'page callback' => 'comment_admin',
17185
    'access arguments' => array('administer comments'),
172
  );
173
  // Tabs begin here.
17485
  $items['admin/content/comment/new'] = array(
17585
    'title' => 'Published comments',
17685
    'type' => MENU_DEFAULT_LOCAL_TASK,
17785
    'weight' => -10,
178
  );
17985
  $items['admin/content/comment/approval'] = array(
18085
    'title' => 'Approval queue',
18185
    'page arguments' => array('approval'),
18285
    'access arguments' => array('administer comments'),
18385
    'type' => MENU_LOCAL_TASK,
184
  );
18585
  $items['comment/delete'] = array(
18685
    'title' => 'Delete comment',
18785
    'page callback' => 'comment_delete',
18885
    'access arguments' => array('administer comments'),
18985
    'type' => MENU_CALLBACK,
190
  );
19185
  $items['comment/edit'] = array(
19285
    'title' => 'Edit comment',
19385
    'page callback' => 'comment_edit',
19485
    'access arguments' => array('post comments'),
19585
    'type' => MENU_CALLBACK,
196
  );
19785
  $items['comment/reply/%node'] = array(
19885
    'title' => 'Reply to comment',
19985
    'page callback' => 'comment_reply',
20085
    'page arguments' => array(2),
20185
    'access callback' => 'node_access',
20285
    'access arguments' => array('view', 2),
20385
    'type' => MENU_CALLBACK,
204
  );
20585
  $items['comment/approve'] = array(
20685
    'title' => 'Approve to comment',
20785
    'page callback' => 'comment_approve',
20885
    'page arguments' => array(2),
20985
    'access arguments' => array('administer comments'),
21085
    'type' => MENU_CALLBACK,
211
  );
212
21385
  return $items;
2140
}
215
216
/**
217
 * Implementation of hook_node_type().
218
 */
2192027
function comment_node_type($op, $info) {
220
  $settings = array(
22163
    'comment',
22263
    'comment_default_mode',
22363
    'comment_default_per_page',
22463
    'comment_anonymous',
22563
    'comment_subject_field',
22663
    'comment_preview',
22763
    'comment_form_location',
22863
  );
229
230
  switch ($op) {
23163
    case 'delete':
2320
      foreach ($settings as $setting) {
2330
        variable_del($setting . '_' . $info->type);
2340
      }
2350
      break;
2360
  }
23763
}
238
239
/**
240
 * Implementation of hook_perm().
241
 */
2422027
function comment_perm() {
243
  return array(
24487
    'access comments' => t('View comments attached to content.'),
24587
    'post comments' => t('Add comments to content (approval required).'),
24687
    'post comments without approval' => t('Add comments to content (no
approval required).'),
24787
    'administer comments' => t('Manage and approve comments, and configure
comment administration settings.'),
24887
  );
2490
}
250
251
/**
252
 * Implementation of hook_block().
253
 *
254
 * Generates a block with the most recent comments.
255
 */
2562027
function comment_block($op = 'list', $delta = '', $edit = array()) {
257
  switch ($op) {
25828
    case 'list':
25928
      $blocks['recent']['info'] = t('Recent comments');
260
26128
      return $blocks;
262
2630
    case 'configure':
2640
      $form['comment_block_count'] = array(
2650
        '#type' => 'select',
2660
        '#title' => t('Number of recent comments'),
2670
        '#default_value' => variable_get('comment_block_count', 10),
2680
        '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)),
2690
        '#description' => t('Number of comments displayed in the <em>Recent
comments</em> block.'),
270
      );
271
2720
      return $form;
273
2740
    case 'save':
2750
      variable_set('comment_block_count',
(int)$edit['comment_block_count']);
2760
      break;
277
2780
    case 'view':
2790
      if (user_access('access comments')) {
2800
        $block['subject'] = t('Recent comments');
2810
        $block['content'] = theme('comment_block');
282
2830
        return $block;
2840
      }
2850
  }
2860
}
287
288
/**
289
 * Find the most recent comments that are available to the current user.
290
 *
291
 * This is done in two steps:
292
 *   1. Query the {node_comment_statistics} table to find n number of nodes
that
293
 *      have the most recent comments. This table is indexed on
294
 *      last_comment_timestamp, thus making it a fast query.
295
 *   2. Load the information from the comments table based on the nids
found
296
 *      in step 1.
297
 *
298
 * @param integer $number
299
 *   (optional) The maximum number of comments to find.
300
 * @return
301
 *   An array of comment objects each containing a nid,
302
 *   subject, cid, and timestamp, or an empty array if there are no recent
303
 *   comments visible to the current user.
304
 */
3052027
function comment_get_recent($number = 10) {
306
  // Step 1: Select a $number of nodes which have new comments,
307
  //         and are visible to the current user.
3080
  $result = db_query_range(db_rewrite_sql("SELECT nc.nid FROM
{node_comment_statistics} nc WHERE nc.comment_count > 0 ORDER BY
nc.last_comment_timestamp DESC", 'nc'), 0, $number);
3090
  $nids = array();
3100
  while ($row = db_fetch_object($result)) {
3110
    $nids[] = $row->nid;
3120
  }
313
3140
  $comments = array();
3150
  if (!empty($nids)) {
316
    // Step 2: From among the comments on the nodes selected in the first
query,
317
    //         find the $number of most recent comments.
3180
    $result = db_query_range('SELECT c.nid, c.subject, c.cid, c.timestamp
FROM {comments} c INNER JOIN {node} n ON n.nid = c.nid WHERE c.nid IN (' .
implode(',', $nids) . ') AND n.status = 1 AND c.status = %d ORDER BY c.cid
DESC', COMMENT_PUBLISHED, 0, $number);
3190
    while ($comment = db_fetch_object($result)) {
3200
      $comments[] = $comment;
3210
    }
3220
  }
323
3240
  return $comments;
3250
}
326
327
/**
328
 * Calculate page number for first new comment.
329
 *
330
 * @param $num_comments
331
 *   Number of comments.
332
 * @param $new_replies
333
 *   Number of new replies.
334
 * @param $node
335
 *   The first new comment node.
336
 * @return
337
 *   "page=X" if the page number is greater than zero; empty string
otherwise.
338
 */
3392027
function comment_new_page_count($num_comments, $new_replies, $node) {
34015
  $comments_per_page = _comment_get_display_setting('comments_per_page',
$node);
34115
  $mode = _comment_get_display_setting('mode', $node);
34215
  $pagenum = NULL;
34315
  $flat = in_array($mode, array(COMMENT_MODE_FLAT_COLLAPSED,
COMMENT_MODE_FLAT_EXPANDED));
34415
  if ($num_comments <= $comments_per_page) {
345
    // Only one page of comments.
34615
    $pageno = 0;
34715
  }
3480
  elseif ($flat) {
349
    // Flat comments.
3500
    $count = $num_comments - $new_replies;
3510
    $pageno =  $count / $comments_per_page;
3520
  }
353
  else {
354
    // Threaded comments.
355
    // Find the first thread with a new comment.
3560
    $result = db_query('(SELECT thread FROM {comments} WHERE nid = %d  AND
status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1,
(LENGTH(thread) - 1)) LIMIT 1', $node->nid, $new_replies);
3570
    $thread = substr(db_result($result), 0, -1);
3580
    $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid =
%d AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '" .
$thread . "'", $node->nid);
3590
    $count = db_result($result_count);
3600
    $pageno =  $count / $comments_per_page;
361
  }
362
36315
  if ($pageno >= 1) {
3640
    $pagenum = "page=" . intval($pageno);
3650
  }
366
36715
  return $pagenum;
3680
}
369
370
/**
371
 * Returns a formatted list of recent comments to be displayed in the
comment block.
372
 *
373
 * @return
374
 *   The comment list HTML.
375
 * @ingroup themeable
376
 */
3772027
function theme_comment_block() {
3780
  $items = array();
3790
  $number = variable_get('comment_block_count', 10);
3800
  foreach (comment_get_recent($number) as $comment) {
3810
    $items[] = l($comment->subject, 'node/' . $comment->nid,
array('fragment' => 'comment-' . $comment->cid)) . '<br />' . t('@time
ago', array('@time' => format_interval(time() - $comment->timestamp)));
3820
  }
383
3840
  if ($items) {
3850
    return theme('item_list', $items);
3860
  }
3870
}
388
389
/**
390
 * Implementation of hook_link().
391
 */
3922027
function comment_link($type, $node = NULL, $teaser = FALSE) {
393231
  $links = array();
394
395231
  if ($type == 'node' && $node->comment) {
396199
    if ($teaser) {
397
      // Main page: display the number of comments that have been posted.
39823
      if (user_access('access comments')) {
39911
        if ($node->comment_count) {
4005
          $links['comment_comments'] = array(
4015
            'title' => format_plural($node->comment_count, '1 comment',
'@count comments'),
4025
            'href' => "node/$node->nid",
4035
            'attributes' => array('title' => t('Jump to the first comment
of this posting.')),
404
            'fragment' => 'comments'
4055
          );
406
4075
          $new = comment_num_new($node->nid);
4085
          if ($new) {
4090
            $links['comment_new_comments'] = array(
4100
              'title' => format_plural($new, '1 new comment', '@count new
comments'),
4110
              'href' => "node/$node->nid",
4120
              'query' => comment_new_page_count($node->comment_count, $new,
$node),
4130
              'attributes' => array('title' => t('Jump to the first new
comment of this posting.')),
414
              'fragment' => 'new'
4150
            );
4160
          }
4175
        }
418
        else {
4196
          if ($node->comment == COMMENT_NODE_READ_WRITE) {
4206
            if (user_access('post comments')) {
4216
              $links['comment_add'] = array(
4226
                'title' => t('Add new comment'),
4236
                'href' => "comment/reply/$node->nid",
4246
                'attributes' => array('title' => t('Add a new comment to
this page.')),
425
                'fragment' => 'comment-form'
4266
              );
4276
            }
428
            else {
4290
              $links['comment_forbidden']['title'] =
theme('comment_post_forbidden', $node);
430
            }
4316
          }
432
        }
43311
      }
43423
    }
435
    else {
436
      // Node page: add a "post comment" link if the user is allowed to
post comments,
437
      // if this node is not read-only, and if the comment form isn't
already shown.
438176
      if ($node->comment == COMMENT_NODE_READ_WRITE) {
439176
        if (user_access('post comments')) {
440176
          if (variable_get('comment_form_location_' . $node->type,
COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
441172
            $links['comment_add'] = array(
442172
              'title' => t('Add new comment'),
443172
              'href' => "comment/reply/$node->nid",
444172
              'attributes' => array('title' => t('Share your thoughts and
opinions related to this posting.')),
445
              'fragment' => 'comment-form'
446172
            );
447172
          }
448176
        }
449
        else {
4500
          $links['comment_forbidden']['title'] =
theme('comment_post_forbidden', $node);
451
        }
452176
      }
453
    }
454199
  }
455
456231
  if ($type == 'comment') {
45719
    $links = comment_links($node, $teaser);
45819
  }
459
460231
  if (isset($links['comment_forbidden'])) {
4610
    $links['comment_forbidden']['html'] = TRUE;
4620
  }
463
464231
  return $links;
4650
}
466
467
/**
468
 * Implementation of hook_form_alter().
469
 */
4702027
function comment_form_alter(&$form, $form_state, $form_id) {
4711338
  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
4723
    $form['comment'] = array(
4733
      '#type' => 'fieldset',
4743
      '#title' => t('Comment settings'),
4753
      '#collapsible' => TRUE,
4763
      '#collapsed' => TRUE,
477
    );
4783
    $form['comment']['comment'] = array(
4793
      '#type' => 'radios',
4803
      '#title' => t('Default comment setting'),
4813
      '#default_value' => variable_get('comment_' .
$form['#node_type']->type, COMMENT_NODE_READ_WRITE),
4823
      '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')),
4833
      '#description' => t('Users with the <em>administer comments</em>
permission will be able to override this setting.'),
484
    );
4853
    $form['comment']['comment_default_mode'] = array(
4863
      '#type' => 'radios',
4873
      '#title' => t('Default display mode'),
4883
      '#default_value' => variable_get('comment_default_mode_' .
$form['#node_type']->type, COMMENT_MODE_THREADED_EXPANDED),
4893
      '#options' => _comment_get_modes(),
4903
      '#description' => t('Expanded views display the body of the comment.
Threaded views keep replies together.'),
491
    );
4923
    $form['comment']['comment_default_per_page'] = array(
4933
      '#type' => 'select',
4943
      '#title' => t('Comments per page'),
4953
      '#default_value' => variable_get('comment_default_per_page_' .
$form['#node_type']->type, 50),
4963
      '#options' => _comment_per_page(),
4973
      '#description' => t('Additional comments will be displayed on
separate pages.'),
498
    );
4993
    $form['comment']['comment_anonymous'] = array(
5003
      '#type' => 'radios',
5013
      '#title' => t('Anonymous commenting'),
5023
      '#default_value' => variable_get('comment_anonymous_' .
$form['#node_type']->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT),
503
      '#options' => array(
5043
        COMMENT_ANONYMOUS_MAYNOT_CONTACT => t('Anonymous posters may not
enter their contact information'),
5053
        COMMENT_ANONYMOUS_MAY_CONTACT => t('Anonymous posters may leave
their contact information'),
5063
        COMMENT_ANONYMOUS_MUST_CONTACT => t('Anonymous posters must leave
their contact information')),
5073
      '#description' => t('This option is enabled when anonymous users have
permission to post comments on the <a href="@url">permissions page</a>.',
array('@url' => url('admin/user/permissions', array('fragment' =>
'module-comment')))),
508
    );
509
5103
    if (!user_access('post comments', drupal_anonymous_user())) {
5113
      $form['comment']['comment_anonymous']['#disabled'] = TRUE;
5123
    }
513
5143
    $form['comment']['comment_subject_field'] = array(
5153
      '#type' => 'radios',
5163
      '#title' => t('Comment subject field'),
5173
      '#default_value' => variable_get('comment_subject_field_' .
$form['#node_type']->type, 1),
5183
      '#options' => array(t('Disabled'), t('Enabled')),
5193
      '#description' => t('Can users provide a unique subject for their
comments?'),
520
    );
5213
    $form['comment']['comment_preview'] = array(
5223
      '#type' => 'radios',
5233
      '#title' => t('Preview comment'),
5243
      '#default_value' => variable_get('comment_preview_' .
$form['#node_type']->type, COMMENT_PREVIEW_REQUIRED),
5253
      '#options' => array(t('Optional'), t('Required')),
5263
      '#description' => t("Forces a user to look at their comment by
clicking on a 'Preview' button before they can actually add the comment"),
527
    );
5283
    $form['comment']['comment_form_location'] = array(
5293
      '#type' => 'radios',
5303
      '#title' => t('Location of comment submission form'),
5313
      '#default_value' => variable_get('comment_form_location_' .
$form['#node_type']->type, COMMENT_FORM_SEPARATE_PAGE),
5323
      '#options' => array(t('Display on separate page'), t('Display below
post or comments')),
533
    );
5343
  }
5351335
  elseif (isset($form['type']) && isset($form['#node'])) {
53683
    if ($form['type']['#value'] . '_node_form' == $form_id) {
53783
      $node = $form['#node'];
53883
      $form['comment_settings'] = array(
53983
        '#type' => 'fieldset',
54083
        '#access' => user_access('administer comments'),
54183
        '#title' => t('Comment settings'),
54283
        '#collapsible' => TRUE,
54383
        '#collapsed' => TRUE,
54483
        '#weight' => 30,
545
      );
54683
      $form['comment_settings']['comment'] = array(
54783
        '#type' => 'radios',
54883
        '#parents' => array('comment'),
54983
        '#default_value' => $node->comment,
55083
        '#options' => array(t('Disabled'), t('Read only'),
t('Read/Write')),
551
      );
55283
    }
55383
  }
5541338
}
555
556
/**
557
 * Implementation of hook_nodeapi().
558
 */
5592027
function comment_nodeapi(&$node, $op, $arg = 0) {
560
  switch ($op) {
561496
    case 'load':
562403
      if ($node->comment != COMMENT_NODE_DISABLED) {
563341
        return db_fetch_array(db_query("SELECT last_comment_timestamp,
last_comment_name, comment_count FROM {node_comment_statistics} WHERE nid =
%d", $node->nid));
5640
      }
56563
      return array('last_comment_timestamp' => $node->created,
'last_comment_name' => '', 'comment_count' => 0);
566
567415
    case 'prepare':
56881
      if (!isset($node->comment)) {
56941
        $node->comment = variable_get("comment_$node->type",
COMMENT_NODE_READ_WRITE);
57041
      }
57181
      break;
572
573339
    case 'insert':
57449
      db_query('INSERT INTO {node_comment_statistics} (nid,
last_comment_timestamp, last_comment_name, last_comment_uid, comment_count)
VALUES (%d, %d, NULL, %d, 0)', $node->nid, $node->changed, $node->uid);
57549
      break;
576
577339
    case 'delete':
57813
      db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
57913
      db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d',
$node->nid);
58013
      break;
581
582327
    case 'update index':
5831
      $text = '';
5841
      $comments = db_query('SELECT subject, comment, format FROM {comments}
WHERE nid = %d AND status = %d', $node->nid, COMMENT_PUBLISHED);
5851
      while ($comment = db_fetch_object($comments)) {
5861
        $text .= '<h2>' . check_plain($comment->subject) . '</h2>' .
check_markup($comment->comment, $comment->format, FALSE);
5871
      }
5881
      return $text;
589
590327
    case 'search result':
5911
      $comments = db_result(db_query('SELECT comment_count FROM
{node_comment_statistics} WHERE nid = %d', $node->nid));
5921
      return format_plural($comments, '1 comment', '@count comments');
593
594327
    case 'rss item':
5950
      if ($node->comment != COMMENT_NODE_DISABLED) {
5960
        return array(array('key' => 'comments', 'value' => url('node/' .
$node->nid, array('fragment' => 'comments', 'absolute' => TRUE))));
5970
      }
598
      else {
5990
        return array();
600
      }
6010
  }
602415
}
603
604
/**
605
 * Implementation of hook_user().
606
 */
6072027
function comment_user($type, $edit, &$user, $category = NULL) {
6081848
  if ($type == 'delete') {
6092
    db_query('UPDATE {comments} SET uid = 0 WHERE uid = %d', $user->uid);
6102
    db_query('UPDATE {node_comment_statistics} SET last_comment_uid = 0
WHERE last_comment_uid = %d', $user->uid);
6112
  }
6121848
}
613
614
/**
615
 * This is *not* a hook_access() implementation. This function is called
616
 * to determine whether the current user has access to a particular
comment.
617
 *
618
 * Authenticated users can edit their comments as long they have not been
619
 * replied to. This prevents people from changing or revising their
620
 * statements based on the replies to their posts.
621
 *
622
 * @param $op
623
 *   The operation that is to be performed on the comment. Only 'edit' is
recognized now.
624
 * @param $comment
625
 *   The comment object.
626
 * @return
627
 *   TRUE if the current user has acces to the comment, FALSE otherwise.
628
 */
6292027
function comment_access($op, $comment) {
63019
  global $user;
631
63219
  if ($op == 'edit') {
63319
    return ($user->uid && $user->uid == $comment->uid &&
comment_num_replies($comment->cid) == 0) || user_access('administer
comments');
6340
  }
6350
}
636
637
/**
638
 * A simple helper function.
639
 *
640
 * @return
641
 *   The 0th and the 1st path components joined by a slash.
642
 */
6432027
function comment_node_url() {
6440
  return arg(0) . '/' . arg(1);
6450
}
646
647
/**
648
 * Accepts a submission of new or changed comment content.
649
 *
650
 * @param $edit
651
 *   A comment array.
652
 *
653
 * @return
654
 *   If the comment is successfully saved the comment ID is returned. If
the comment
655
 *   is not saved, FALSE is returned.
656
 */
6572027
function comment_save($edit) {
65813
  global $user;
65913
  if (user_access('post comments') && (user_access('administer comments')
|| node_comment_mode($edit['nid']) == COMMENT_NODE_READ_WRITE)) {
66013
    if (!form_get_errors()) {
661
      $edit += array(
66213
        'mail' => '',
66313
        'homepage' => '',
66413
        'name' => '',
66513
        'status' => user_access('post comments without approval') ?
COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED,
6660
      );
66713
      if ($edit['cid']) {
668
        // Update the comment in the database.
6691
        db_query("UPDATE {comments} SET status = %d, timestamp = %d,
subject = '%s', comment = '%s', format = %d, uid = %d, name = '%s', mail =
'%s', homepage = '%s' WHERE cid = %d", $edit['status'], $edit['timestamp'],
$edit['subject'], $edit['comment'], $edit['format'], $edit['uid'],
$edit['name'], $edit['mail'], $edit['homepage'], $edit['cid']);
670
        // Allow modules to respond to the updating of a comment.
6711
        comment_invoke_comment($edit, 'update');
672
        // Add an entry to the watchdog log.
6731
        watchdog('content', 'Comment: updated %subject.', array('%subject'
=> $edit['subject']), WATCHDOG_NOTICE, l(t('view'), 'node/' . $edit['nid'],
array('fragment' => 'comment-' . $edit['cid'])));
6741
      }
675
      else {
676
        // Add the comment to database. This next section builds the thread
field.
677
        // Also see the documentation for comment_render().
67812
        if ($edit['pid'] == 0) {
679
          // This is a comment with no parent comment (depth 0): we start
680
          // by retrieving the maximum thread level.
68111
          $max = db_result(db_query('SELECT MAX(thread) FROM {comments}
WHERE nid = %d', $edit['nid']));
682
          // Strip the "/" from the end of the thread.
68311
          $max = rtrim($max, '/');
684
          // Finally, build the thread field for this new comment.
68511
          $thread = int2vancode(vancode2int($max) + 1) . '/';
68611
        }
687
        else {
688
          // This is a comment with a parent comment, so increase
689
          // the part of the thread value at the proper depth.
690
691
          // Get the parent comment:
6921
          $parent = comment_load($edit['pid']);
693
          // Strip the "/" from the end of the parent thread.
6941
          $parent->thread = (string) rtrim((string) $parent->thread, '/');
695
          // Get the max value in *this* thread.
6961
          $max = db_result(db_query("SELECT MAX(thread) FROM {comments}
WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
697
6981
          if ($max == '') {
699
            // First child of this parent.
7001
            $thread = $parent->thread . '.' . int2vancode(0) . '/';
7011
          }
702
          else {
703
            // Strip the "/" at the end of the thread.
7040
            $max = rtrim($max, '/');
705
            // Get the value at the correct depth.
7060
            $parts = explode('.', $max);
7070
            $parent_depth = count(explode('.', $parent->thread));
7080
            $last = $parts[$parent_depth];
709
            // Finally, build the thread field for this new comment.
7100
            $thread = $parent->thread . '.' .
int2vancode(vancode2int($last) + 1) . '/';
711
          }
712
        }
713
71412
        if (empty($edit['timestamp'])) {
7150
          $edit['timestamp'] = time();
7160
        }
717
71812
        if ($edit['uid'] === $user->uid) { // '===' Need to modify
anonymous users as well.
7197
          $edit['name'] = $user->name;
7207
        }
721
72212
        db_query("INSERT INTO {comments} (nid, pid, uid, subject, comment,
format, hostname, timestamp, status, thread, name, mail, homepage) VALUES
(%d, %d, %d, '%s', '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s')",
$edit['nid'], $edit['pid'], $edit['uid'], $edit['subject'],
$edit['comment'], $edit['format'], ip_address(), $edit['timestamp'],
$edit['status'], $thread, $edit['name'], $edit['mail'],
$edit['homepage']);
72312
        $edit['cid'] = db_last_insert_id('comments', 'cid');
724
        // Tell the other modules a new comment has been submitted.
72512
        comment_invoke_comment($edit, 'insert');
726
        // Add an entry to the watchdog log.
72712
        watchdog('content', 'Comment: added %subject.', array('%subject' =>
$edit['subject']), WATCHDOG_NOTICE, l(t('view'), 'node/' . $edit['nid'],
array('fragment' => 'comment-' . $edit['cid'])));
728
      }
72913
      _comment_update_node_statistics($edit['nid']);
730
      // Clear the cache so an anonymous user can see his comment being
added.
73113
      cache_clear_all();
732
733
      // Explain the approval queue if necessary, and then
734
      // redirect the user to the node he's commenting on.
73513
      if ($edit['status'] == COMMENT_NOT_PUBLISHED) {
7362
        drupal_set_message(t('Your comment has been queued for moderation
by site administrators and will be published after approval.'));
7372
      }
738
      else {
73911
        drupal_set_message(t('Your comment has been posted.'));
74011
        comment_invoke_comment($edit, 'publish');
741
      }
742
74313
      return $edit['cid'];
7440
    }
745
    else {
7460
      return FALSE;
747
    }
7480
  }
749
  else {
7500
    watchdog('content', 'Comment: unauthorized comment submitted or comment
submitted to a closed post %subject.', array('%subject' =>
$edit['subject']), WATCHDOG_WARNING);
7510
    drupal_set_message(t('Comment: unauthorized comment submitted or
comment submitted to a closed post %subject.', array('%subject' =>
$edit['subject'])), 'error');
752
7530
    return FALSE;
754
  }
7550
}
756
757
/**
758
 * Build command links for a comment (e.g.\ edit, reply, delete) with
respect to the current user's access permissions.
759
 *
760
 * @param $comment
761
 *   The comment to which the links will be related.
762
 * @param $return
763
 *   Not used.
764
 * @return
765
 *   An associative array containing the links.
766
 */
7672027
function comment_links($comment, $return = 1) {
76819
  global $user;
76919
  $links = array();
770
771
  // If viewing just this comment, link back to the node.
77219
  if ($return) {
7730
    $links['comment_parent'] = array(
7740
      'title' => t('parent'),
7750
      'href' => comment_node_url(),
7760
      'fragment' => "comment-$comment->cid"
7770
    );
7780
  }
779
78019
  if (node_comment_mode($comment->nid) == COMMENT_NODE_READ_WRITE) {
78119
    if (user_access('administer comments') && user_access('post comments'))
{
7823
      $links['comment_delete'] = array(
7833
        'title' => t('delete'),
7843
        'href' => "comment/delete/$comment->cid"
7853
      );
7863
      $links['comment_edit'] = array(
7873
        'title' => t('edit'),
7883
        'href' => "comment/edit/$comment->cid"
7893
      );
7903
      $links['comment_reply'] = array(
7913
        'title' => t('reply'),
7923
        'href' => "comment/reply/$comment->nid/$comment->cid"
7933
      );
7943
      if ($comment->status == COMMENT_NOT_PUBLISHED) {
7951
        $links['comment_approve'] = array(
7961
          'title' => t('approve'),
7971
          'href' => "comment/approve/$comment->cid"
7981
        );
7991
      }
8003
    }
80116
    elseif (user_access('post comments')) {
80216
      if (comment_access('edit', $comment)) {
80310
        $links['comment_edit'] = array(
80410
          'title' => t('edit'),
80510
          'href' => "comment/edit/$comment->cid"
80610
        );
80710
      }
80816
      $links['comment_reply'] = array(
80916
        'title' => t('reply'),
81016
        'href' => "comment/reply/$comment->nid/$comment->cid"
81116
      );
81216
    }
813
    else {
8140
      $node = node_load($comment->nid);
8150
      $links['comment_forbidden']['title'] =
theme('comment_post_forbidden', $node);
816
    }
81719
  }
818
81919
  return $links;
8200
}
821
822
/**
823
 * Renders comment(s).
824
 *
825
 * @param $node
826
 *   The node which comment(s) needs rendering.
827
 * @param $cid
828
 *   Optional, if given, only one comment is rendered.
829
 *
830
 * To display threaded comments in the correct order we keep a 'thread'
field
831
 * and order by that value. This field keeps this data in
832
 * a way which is easy to update and convenient to use.
833
 *
834
 * A "thread" value starts at "1". If we add a child (A) to this comment,
835
 * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
836
 * brother of (A) will get "1.2". Next brother of the parent of (A) will
get
837
 * "2" and so on.
838
 *
839
 * First of all note that the thread field stores the depth of the
comment:
840
 * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
841
 *
842
 * Now to get the ordering right, consider this example:
843
 *
844
 * 1
845
 * 1.1
846
 * 1.1.1
847
 * 1.2
848
 * 2
849
 *
850
 * If we "ORDER BY thread ASC" we get the above result, and this is the
851
 * natural order sorted by time. However, if we "ORDER BY thread DESC"
852
 * we get:
853
 *
854
 * 2
855
 * 1.2
856
 * 1.1.1
857
 * 1.1
858
 * 1
859
 *
860
 * Clearly, this is not a natural way to see a thread, and users will get
861
 * confused. The natural order to show a thread by time desc would be:
862
 *
863
 * 2
864
 * 1
865
 * 1.2
866
 * 1.1
867
 * 1.1.1
868
 *
869
 * which is what we already did before the standard pager patch. To
achieve
870
 * this we simply add a "/" at the end of each "thread" value. This way,
the
871
 * thread fields will look like this:
872
 *
873
 * 1/
874
 * 1.1/
875
 * 1.1.1/
876
 * 1.2/
877
 * 2/
878
 *
879
 * we add "/" since this char is, in ASCII, higher than every number, so
if
880
 * now we "ORDER BY thread DESC" we get the correct order. However this
would
881
 * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not
need
882
 * to consider the trailing "/" so we use a substring only.
883
 */
8842027
function comment_render($node, $cid = 0) {
885140
  global $user;
886140
  $output = '';
887
888140
  if (user_access('access comments')) {
889
    // Pre-process variables.
890140
    $nid = $node->nid;
891140
    if (empty($nid)) {
8920
      $nid = 0;
8930
    }
894
895140
    $mode = _comment_get_display_setting('mode', $node);
896140
    $comments_per_page = _comment_get_display_setting('comments_per_page',
$node);
897
898140
    if ($cid && is_numeric($cid)) {
899
      // Single comment view.
9000
      $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.picture, u.data, c.status FROM {comments} c INNER JOIN
{users} u ON c.uid = u.uid WHERE c.cid = %d';
9010
      $query_args = array($cid);
9020
      if (!user_access('administer comments')) {
9030
        $query .= ' AND c.status = %d';
9040
        $query_args[] = COMMENT_PUBLISHED;
9050
      }
906
9070
      $query = db_rewrite_sql($query, 'c', 'cid');
9080
      $result = db_query($query, $query_args);
909
9100
      if ($comment = db_fetch_object($result)) {
9110
        $comment->name = $comment->uid ? $comment->registered_name :
$comment->name;
9120
        $links = module_invoke_all('link', 'comment', $comment, 1);
9130
        drupal_alter('link', $links, $node);
914
9150
        $output .= theme('comment_view', $comment, $node, $links);
9160
      }
9170
    }
918
    else {
919
      // Multiple comment view.
920140
      $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid = %d';
921140
      $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.picture, u.data, c.thread, c.status FROM
{comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d';
922
923140
      $query_args = array($nid);
924140
      if (!user_access('administer comments')) {
925135
        $query .= ' AND c.status = %d';
926135
        $query_count .= ' AND c.status = %d';
927135
        $query_args[] = COMMENT_PUBLISHED;
928135
      }
929140
      if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode ==
COMMENT_MODE_FLAT_EXPANDED) {
9300
        $query .= ' ORDER BY c.cid';
9310
      }
932
      else {
933
        // See comment above. Analysis reveals that this doesn't cost too
934
        // much. It scales much much better than having the whole comment
935
        // structure.
936140
        $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) -
1))';
937
      }
938
939140
      $query = db_rewrite_sql($query, 'c', 'cid');
940140
      $query_count = db_rewrite_sql($query_count, 'c', 'cid');
941
942140
      $result = pager_query($query, $comments_per_page, 0, $query_count,
$query_args);
943
944140
      $divs = 0;
945140
      $num_rows = FALSE;
946140
      $comments = '';
947140
      drupal_add_css(drupal_get_path('module', 'comment') .
'/comment.css');
948140
      while ($comment = db_fetch_object($result)) {
94919
        $comment = drupal_unpack($comment);
95019
        $comment->name = $comment->uid ? $comment->registered_name :
$comment->name;
95119
        $comment->depth = count(explode('.', $comment->thread)) - 1;
952
95319
        if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode ==
COMMENT_MODE_THREADED_EXPANDED) {
95419
          if ($comment->depth > $divs) {
9555
            $divs++;
9565
            $comments .= '<div class="indented">';
9575
          }
958
          else {
95919
            while ($comment->depth < $divs) {
9603
              $divs--;
9613
              $comments .= '</div>';
9623
            }
963
          }
96419
        }
965
96619
        if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
9670
          $comments .= theme('comment_flat_collapsed', $comment, $node);
9680
        }
96919
        elseif ($mode == COMMENT_MODE_FLAT_EXPANDED) {
9700
          $comments .= theme('comment_flat_expanded', $comment, $node);
9710
        }
97219
        elseif ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
9730
          $comments .= theme('comment_thread_collapsed', $comment, $node);
9740
        }
97519
        elseif ($mode == COMMENT_MODE_THREADED_EXPANDED) {
97619
          $comments .= theme('comment_thread_expanded', $comment, $node);
97719
        }
97819
        $num_rows = TRUE;
97919
      }
980140
      while ($divs-- > 0) {
9812
        $comments .= '</div>';
9822
      }
983140
      $output .= $comments;
984140
      $output .= theme('pager', NULL, $comments_per_page, 0);
985
    }
986
987
    // If enabled, show new comment form if it's not already being
displayed.
988140
    $reply = arg(0) == 'comment' && arg(1) == 'reply';
989140
    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)
{
9902
      $output .= comment_form_box(array('nid' => $nid), t('Post new
comment'));
9912
    }
992140
    $output = theme('comment_wrapper', $output, $node);
993140
  }
994
995140
  return $output;
9960
}
997
998
/**
999
 * Comment operations. Offer different update operations depending on
1000
 * which comment administration page is being viewed.
1001
 *
1002
 * @param $action
1003
 *   The comment administration page.
1004
 * @return
1005
 *   An associative array containing the offered operations.
1006
 */
10072027
function comment_operations($action = NULL) {
100817
  if ($action == 'publish') {
1009
    $operations = array(
10108
      'publish' => array(t('Publish the selected comments'), 'UPDATE
{comments} SET status = ' . COMMENT_PUBLISHED . ' WHERE cid = %d'),
10118
      'delete' => array(t('Delete the selected comments'), '')
10128
    );
10138
  }
101411
  elseif ($action == 'unpublish') {
1015
    $operations = array(
10169
      'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE
{comments} SET status = ' . COMMENT_NOT_PUBLISHED . ' WHERE cid = %d'),
10179
      'delete' => array(t('Delete the selected comments'), '')
10189
    );
10199
  }
1020
  else {
1021
    $operations = array(
10223
      'publish' => array(t('Publish the selected comments'), 'UPDATE
{comments} SET status = ' . COMMENT_PUBLISHED . ' WHERE cid = %d'),
10233
      'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE
{comments} SET status = ' . COMMENT_NOT_PUBLISHED . ' WHERE cid = %d'),
10243
      'delete' => array(t('Delete the selected comments'), '')
10253
    );
1026
  }
1027
102817
  return $operations;
10290
}
1030
1031
/**
1032
 * Begin the misc functions: helpers, privates, history.
1033
 */
1034
1035
/**
1036
 * Load the entire comment by cid.
1037
 *
1038
 * @param $cid
1039
 *   The identifying comment id.
1040
 * @return
1041
 *   The comment object.
1042
 */
10432027
function comment_load($cid) {
10447
  return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid =
%d', $cid));
10450
}
1046
1047
/**
1048
 * Get replies count for a comment.
1049
 *
1050
 * @param $pid
1051
 *   The comment id.
1052
 * @return
1053
 *   The replies count.
1054
 */
10552027
function comment_num_replies($pid) {
105613
  static $cache;
1057
105813
  if (!isset($cache[$pid])) {
105913
    $cache[$pid] = db_result(db_query('SELECT COUNT(cid) FROM {comments}
WHERE pid = %d AND status = %d', $pid, COMMENT_PUBLISHED));
106013
  }
1061
106213
  return $cache[$pid];
10630
}
1064
1065
/**
1066
 * Get number of new comments for current user and specified node.
1067
 *
1068
 * @param $nid
1069
 *   Node-id to count comments for.
1070
 * @param $timestamp
1071
 *   Time to count from (defaults to time of last user access
1072
 *   to node).
1073
 * @return The result or FALSE on error.
1074
 */
10752027
function comment_num_new($nid, $timestamp = 0) {
107611
  global $user;
1077
107811
  if ($user->uid) {
1079
    // Retrieve the timestamp at which the current user last viewed this
node.
10807
    if (!$timestamp) {
10813
      $timestamp = node_last_viewed($nid);
10823
    }
10837
    $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp :
NODE_NEW_LIMIT);
1084
1085
    // Use the timestamp to retrieve the number of new comments.
10867
    $result = db_result(db_query('SELECT COUNT(c.cid) FROM {node} n INNER
JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND timestamp > %d AND
c.status = %d', $nid, $timestamp, COMMENT_PUBLISHED));
1087
10887
    return $result;
10890
  }
1090
  else {
10914
    return FALSE;
1092
  }
1093
10940
}
1095
1096
/**
1097
 * Validate comment data.
1098
 *
1099
 * @param $edit
1100
 *   An associative array containing the comment data.
1101
 * @return
1102
 *   The original $edit.
1103
 */
11042027
function comment_validate($edit) {
110526
  global $user;
1106
1107
  // Invoke other validation handlers.
110826
  comment_invoke_comment($edit, 'validate');
1109
111026
  if (isset($edit['date'])) {
1111
    // As of PHP 5.1.0, strtotime returns FALSE upon failure instead of
-1.
11120
    if (strtotime($edit['date']) <= 0) {
11130
      form_set_error('date', t('You have to specify a valid date.'));
11140
    }
11150
  }
111626
  if (isset($edit['author']) && !$account = user_load(array('name' =>
$edit['author']))) {
11170
    form_set_error('author', t('You have to specify a valid author.'));
11180
  }
1119
1120
  // Check validity of name, mail and homepage (if given).
112126
  if (!$user->uid || isset($edit['is_anonymous'])) {
112212
    $node = node_load($edit['nid']);
112312
    if (variable_get('comment_anonymous_' . $node->type,
COMMENT_ANONYMOUS_MAYNOT_CONTACT) > COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
11246
      if ($edit['name']) {
11256
        $taken = db_result(db_query("SELECT COUNT(uid) FROM {users} WHERE
LOWER(name) = '%s'", $edit['name']));
11266
        if ($taken != 0) {
11270
          form_set_error('name', t('The name you used belongs to a
registered user.'));
11280
        }
11296
      }
11300
      elseif (variable_get('comment_anonymous_' . $node->type,
COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
11310
        form_set_error('name', t('You have to leave your name.'));
11320
      }
1133
11346
      if ($edit['mail']) {
11352
        if (!valid_email_address($edit['mail'])) {
11360
          form_set_error('mail', t('The e-mail address you specified is not
valid.'));
11370
        }
11382
      }
11394
      elseif (variable_get('comment_anonymous_' . $node->type,
COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
11402
        form_set_error('mail', t('You have to leave an e-mail address.'));
11412
      }
1142
11436
      if ($edit['homepage']) {
11440
        if (!valid_url($edit['homepage'], TRUE)) {
11450
          form_set_error('homepage', t('The URL of your homepage is not
valid. Remember that it must be fully qualified, i.e. of the form
<code>http://example.com/directory</code>.'));
11460
        }
11470
      }
11486
    }
114912
  }
1150
115126
  return $edit;
11520
}
1153
1154
/**
1155
 * Generate the basic commenting form, for appending to a node or display
on a separate page.
1156
 *
1157
 * @param $title
1158
 *   Not used.
1159
 * @ingroup forms
1160
 * @see comment_form_validate()
1161
 * @see comment_form_submit()
1162
 */
11632027
function comment_form(&$form_state, $edit, $title = NULL) {
116444
  global $user;
116544
  $op = isset($_POST['op']) ? $_POST['op'] : '';
116644
  $node = node_load($edit['nid']);
1167
116844
  if (!$user->uid && variable_get('comment_anonymous_' . $node->type,
COMMENT_ANONYMOUS_MAYNOT_CONTACT) != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
116911
    drupal_add_js(drupal_get_path('module', 'comment') . '/comment.js');
117011
  }
117144
  $edit += array('name' => '', 'mail' => '', 'homepage' => '');
1172
117344
  if ($user->uid) {
117424
    if (!empty($edit['cid']) && user_access('administer comments')) {
11750
      if (!empty($edit['author'])) {
11760
        $author = $edit['author'];
11770
      }
11780
      elseif (!empty($edit['name'])) {
11790
        $author = $edit['name'];
11800
      }
1181
      else {
11820
        $author = $edit['registered_name'];
1183
      }
1184
11850
      if (!empty($edit['status'])) {
11860
        $status = $edit['status'];
11870
      }
1188
      else {
11890
        $status = 0;
1190
      }
1191
11920
      if (!empty($edit['date'])) {
11930
        $date = $edit['date'];
11940
      }
1195
      else {
11960
        $date = format_date($edit['timestamp'], 'custom', 'Y-m-d H:i O');
1197
      }
1198
11990
      $form['admin'] = array(
12000
        '#type' => 'fieldset',
12010
        '#title' => t('Administration'),
12020
        '#collapsible' => TRUE,
12030
        '#collapsed' => TRUE,
12040
        '#weight' => -2,
1205
      );
1206
12070
      if ($edit['registered_name'] != '') {
1208
        // The comment is by a registered user.
12090
        $form['admin']['author'] = array(
12100
          '#type' => 'textfield',
12110
          '#title' => t('Authored by'),
12120
          '#size' => 30,
12130
          '#maxlength' => 60,
12140
          '#autocomplete_path' => 'user/autocomplete',
12150
          '#default_value' => $author,
12160
          '#weight' => -1,
1217
        );
12180
      }
1219
      else {
1220
        // The comment is by an anonymous user.
12210
        $form['is_anonymous'] = array(
12220
          '#type' => 'value',
12230
          '#value' => TRUE,
1224
        );
12250
        $form['admin']['name'] = array(
12260
          '#type' => 'textfield',
12270
          '#title' => t('Authored by'),
12280
          '#size' => 30,
12290
          '#maxlength' => 60,
12300
          '#default_value' => $author,
12310
          '#weight' => -1,
1232
        );
12330
        $form['admin']['mail'] = array(
12340
          '#type' => 'textfield',
12350
          '#title' => t('E-mail'),
12360
          '#maxlength' => 64,
12370
          '#size' => 30,
12380
          '#default_value' => $edit['mail'],
12390
          '#description' => t('The content of this field is kept private
and will not be shown publicly.'),
1240
        );
12410
        $form['admin']['homepage'] = array(
12420
          '#type' => 'textfield',
12430
          '#title' => t('Homepage'),
12440
          '#maxlength' => 255,
12450
          '#size' => 30,
12460
          '#default_value' => $edit['homepage'],
1247
        );
1248
      }
12490
      $form['admin']['date'] = array(
12500
        '#type' => 'textfield',
12510
        '#parents' => array('date'),
12520
        '#title' => t('Authored on'),
12530
        '#size' => 20,
12540
        '#maxlength' => 25,
12550
        '#default_value' => $date,
12560
        '#weight' => -1,
1257
      );
12580
      $form['admin']['status'] = array(
12590
        '#type' => 'radios',
12600
        '#parents' => array('status'),
12610
        '#title' => t('Status'),
12620
        '#default_value' =>  $status,
12630
        '#options' => array(t('Published'), t('Not published')),
12640
        '#weight' => -1,
1265
      );
12660
    }
1267
    else {
126824
      $form['_author'] = array(
126924
        '#type' => 'item',
127024
        '#title' => t('Your name'),
127124
        '#markup' => theme('username', $user),
1272
      );
127324
      $form['author'] = array(
127424
        '#type' => 'value',
127524
        '#value' => $user->name,
1276
      );
1277
    }
127824
  }
127920
  elseif (variable_get('comment_anonymous_' . $node->type,
COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MAY_CONTACT) {
12804
    $form['name'] = array(
12814
      '#type' => 'textfield',
12824
      '#title' => t('Your name'),
12834
      '#maxlength' => 60,
12844
      '#size' => 30,
12854
      '#default_value' => $edit['name'] ? $edit['name'] :
variable_get('anonymous', t('Anonymous')),
1286
    );
12874
    $form['mail'] = array(
12884
      '#type' => 'textfield',
12894
      '#title' => t('E-mail'),
12904
      '#maxlength' => 64,
12914
      '#size' => 30,
12924
      '#default_value' => $edit['mail'], '#description' => t('The content
of this field is kept private and will not be shown publicly.'),
1293
    );
12944
    $form['homepage'] = array(
12954
      '#type' => 'textfield',
12964
      '#title' => t('Homepage'),
12974
      '#maxlength' => 255,
12984
      '#size' => 30,
12994
      '#default_value' => $edit['homepage'],
1300
    );
13014
  }
130216
  elseif (variable_get('comment_anonymous_' . $node->type,
COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
13037
    $form['name'] = array(
13047
      '#type' => 'textfield',
13057
      '#title' => t('Your name'),
13067
      '#maxlength' => 60,
13077
      '#size' => 30,
13087
      '#default_value' => $edit['name'] ? $edit['name'] :
variable_get('anonymous', t('Anonymous')),
13097
      '#required' => TRUE,
1310
    );
13117
    $form['mail'] = array(
13127
      '#type' => 'textfield',
13137
      '#title' => t('E-mail'),
13147
      '#maxlength' => 64,
13157
      '#size' => 30,
13167
      '#default_value' => $edit['mail'], '#description' => t('The content
of this field is kept private and will not be shown publicly.'),
13177
      '#required' => TRUE,
1318
    );
13197
    $form['homepage'] = array(
13207
      '#type' => 'textfield',
13217
      '#title' => t('Homepage'),
13227
      '#maxlength' => 255,
13237
      '#size' => 30,
13247
      '#default_value' => $edit['homepage'],
1325
    );
13267
  }
1327
132844
  if (variable_get('comment_subject_field_' . $node->type, 1) == 1) {
132943
    $form['subject'] = array(
133043
      '#type' => 'textfield',
133143
      '#title' => t('Subject'),
133243
      '#maxlength' => 64,
133343
      '#default_value' => !empty($edit['subject']) ? $edit['subject'] :
'',
1334
    );
133543
  }
1336
133744
  if (!empty($edit['comment'])) {
13383
    $default = $edit['comment'];
13393
  }
1340
  else {
134141
    $default = '';
1342
  }
1343
134444
  $form['comment_filter']['comment'] = array(
134544
    '#type' => 'textarea',
134644
    '#title' => t('Comment'),
134744
    '#rows' => 15,
134844
    '#default_value' => $default,
134944
    '#required' => TRUE,
1350
  );
135144
  if (!isset($edit['format'])) {
135241
    $edit['format'] = FILTER_FORMAT_DEFAULT;
135341
  }
135444
  $form['comment_filter']['format'] = filter_form($edit['format']);
1355
135644
  $form['cid'] = array(
135744
    '#type' => 'value',
135844
    '#value' => !empty($edit['cid']) ? $edit['cid'] : NULL,
1359
  );
136044
  $form['pid'] = array(
136144
    '#type' => 'value',
136244
    '#value' => !empty($edit['pid']) ? $edit['pid'] : NULL,
1363
  );
136444
  $form['nid'] = array(
136544
    '#type' => 'value',
136644
    '#value' => $edit['nid'],
1367
  );
136844
  $form['uid'] = array(
136944
    '#type' => 'value',
137044
    '#value' => !empty($edit['uid']) ? $edit['uid'] : NULL,
1371
  );
1372
1373
  // Only show the save button if comment previews are optional or if we
are
1374
  // already previewing the submission.  However, if there are form
errors,
1375
  // we hide the save button no matter what, so that optional form
elements
1376
  // (e.g., captchas) can be updated.
137744
  if (!form_get_errors() && ((variable_get('comment_preview_' .
$node->type, COMMENT_PREVIEW_REQUIRED) == COMMENT_PREVIEW_OPTIONAL) || ($op
== t('Preview')) || ($op == t('Save')))) {
137828
    $form['submit'] = array(
137928
      '#type' => 'submit',
138028
      '#value' => t('Save'),
138128
      '#weight' => 19,
1382
    );
138328
  }
138444
  $form['preview'] = array(
138544
    '#type' => 'button',
138644
    '#value' => t('Preview'),
138744
    '#weight' => 20,
1388
  );
138944
  $form['#token'] = 'comment' . $edit['nid'] . (isset($edit['pid']) ?
$edit['pid'] : '');
1390
139144
  if ($op == t('Preview')) {
139212
    $form['#after_build'] = array('comment_form_add_preview');
139312
  }
1394
139544
  if (empty($edit['cid']) && empty($edit['pid'])) {
139638
    $form['#action'] = url('comment/reply/' . $edit['nid']);
139738
  }
1398
139944
  return $form;
14000
}
1401
1402
/**
1403
 * Theme the comment form box.
1404
 *
1405
 * @param $edit
1406
 *   The form structure.
1407
 * @param $title
1408
 *   The form title.
1409
 */
14102027
function comment_form_box($edit, $title = NULL) {
141144
  return theme('box', $title, drupal_get_form('comment_form', $edit,
$title));
14120
}
1413
1414
/**
1415
 * Form builder; Generate and validate a comment preview form.
1416
 *
1417
 * @ingroup forms
1418
 */
14192027
function comment_form_add_preview($form, &$form_state) {
142012
  global $user;
142112
  $edit = $form_state['values'];
142212
  drupal_set_title(t('Preview comment'));
142312
  $output = '';
142412
  $node = node_load($edit['nid']);
1425
1426
  // Invoke full validation for the form, to protect against cross site
1427
  // request forgeries (CSRF) and setting arbitrary values for fields such
as
1428
  // the input format. Preview the comment only when form validation does
not
1429
  // set any errors.
143012
  drupal_validate_form($form['form_id']['#value'], $form, $form_state);
143112
  if (!form_get_errors()) {
143211
    _comment_form_submit($edit);
143311
    $comment = (object)$edit;
1434
1435
    // Attach the user and time information.
143611
    if (!empty($edit['author'])) {
14376
      $account = user_load(array('name' => $edit['author']));
14386
    }
14395
    elseif ($user->uid && !isset($edit['is_anonymous'])) {
14400
      $account = $user;
14410
    }
1442
144311
    if (!empty($account)) {
14446
      $comment->uid = $account->uid;
14456
      $comment->name = check_plain($account->name);
14466
    }
14475
    elseif (empty($comment->name)) {
14483
      $comment->name = variable_get('anonymous', t('Anonymous'));
14493
    }
1450
145111
    $comment->timestamp = !empty($edit['timestamp']) ? $edit['timestamp'] :
time();
145211
    $output .= theme('comment_view', $comment, $node);
145311
  }
1454
145512
  $form['comment_preview'] = array(
145612
    '#markup' => $output,
145712
    '#weight' => -100,
145812
    '#prefix' => '<div class="preview">',
145912
    '#suffix' => '</div>',
1460
  );
1461
146212
  $output = ''; // Isn't this line a duplication of the first $output
above?
1463
146412
  if ($edit['pid']) {
14652
    $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS
registered_name, u.signature, u.picture, u.data FROM {comments} c INNER
JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = %d',
$edit['pid'], COMMENT_PUBLISHED));
14662
    $comment = drupal_unpack($comment);
14672
    $comment->name = $comment->uid ? $comment->registered_name :
$comment->name;
14682
    $output .= theme('comment_view', $comment, $node);
14692
  }
1470
  else {
147110
    $suffix = empty($form['#suffix']) ? '' : $form['#suffix'];
147210
    $form['#suffix'] = $suffix . node_view($node);
147310
    $edit['pid'] = 0;
1474
  }
1475
147612
  $form['comment_preview_below'] = array(
147712
    '#markup' => $output,
147812
    '#weight' => 100,
1479
  );
1480
148112
  return $form;
14820
}
1483
1484
/**
1485
 * Validate comment form submissions.
1486
 */
14872027
function comment_form_validate($form, &$form_state) {
148826
  global $user;
148926
  if ($user->uid === 0) {
149012
    foreach (array('name', 'homepage', 'mail') as $field) {
1491
      // Set cookie for 365 days.
149212
      if (isset($form_state['values'][$field])) {
14936
        setcookie('comment_info_' . $field, $form_state['values'][$field],
time() + 31536000, '/');
14946
      }
149512
    }
149612
  }
149726
  comment_validate($form_state['values']);
149826
}
1499
1500
/**
1501
 * Prepare a comment for submission.
1502
 *
1503
 * @param $comment_values
1504
 *   An associative array containing the comment data.
1505
 */
15062027
function _comment_form_submit(&$comment_values) {
150724
  $comment_values += array('subject' => '');
150824
  if (!isset($comment_values['date'])) {
150924
    $comment_values['date'] = 'now';
151024
  }
1511
151224
  $comment_values['timestamp'] = strtotime($comment_values['date']);
151324
  if (isset($comment_values['author'])) {
151414
    $account = user_load(array('name' => $comment_values['author']));
151514
    $comment_values['uid'] = $account->uid;
151614
    $comment_values['name'] = $comment_values['author'];
151714
  }
1518
1519
  // Validate the comment's subject. If not specified, extract from comment
body.
152024
  if (trim($comment_values['subject']) == '') {
1521
    // The body may be in any format, so:
1522
    // 1) Filter it into HTML
1523
    // 2) Strip out all HTML tags
1524
    // 3) Convert entities back to plain-text.
1525
    // Note: format is checked by check_markup().
15260
    $comment_values['subject'] =
trim(truncate_utf8(decode_entities(strip_tags(check_markup($comment_values['comment'],
$comment_values['format']))), 29, TRUE));
1527
    // Edge cases where the comment body is populated only by HTML tags
will
1528
    // require a default subject.
15290
    if ($comment_values['subject'] == '') {
15300
      $comment_values['subject'] = t('(No subject)');
15310
    }
15320
  }
153324
}
1534
1535
/**
1536
 * Process comment form submissions; prepare the comment, store it, and set
a redirection target.
1537
 */
15382027
function comment_form_submit($form, &$form_state) {
153913
  _comment_form_submit($form_state['values']);
154013
  if ($cid = comment_save($form_state['values'])) {
154113
    $node = node_load($form_state['values']['nid']);
154213
    $page = comment_new_page_count($node->comment_count, 1, $node);
154313
    $form_state['redirect'] = array('node/' . $node->nid, $page,
"comment-$cid");
1544
154513
    return;
15460
  }
15470
}
1548
1549
/**
1550
 * Theme a single comment block.
1551
 *
1552
 * @param $comment
1553
 *   The comment object.
1554
 * @param $node
1555
 *   The comment node.
1556
 * @param $links
1557
 *   An associative array containing control links.
1558
 * @param $visible
1559
 *   Switches between folded/unfolded view.
1560
 * @ingroup themeable
1561
 */
15622027
function theme_comment_view($comment, $node, $links = array(), $visible =
TRUE) {
156332
  static $first_new = TRUE;
156432
  $comment->new = node_mark($comment->nid, $comment->timestamp);
156532
  $output = '';
1566
156732
  if ($first_new && $comment->new != MARK_READ) {
1568
    // Assign the anchor only for the first new comment. This avoids
duplicate
1569
    // id attributes on a page.
157017
    $first_new = FALSE;
157117
    $output .= "<a id=\"new\"></a>\n";
157217
  }
1573
157432
  $output .= "<a id=\"comment-$comment->cid\"></a>\n";
1575
1576
  // Switch to folded/unfolded view of the comment.
157732
  if ($visible) {
157832
    $comment->comment = check_markup($comment->comment, $comment->format,
FALSE);
1579
    // Comment API hook.
158032
    comment_invoke_comment($comment, 'view');
158132
    $output .= theme('comment', $comment, $node, $links);
158232
  }
1583
  else {
15840
    $output .= theme('comment_folded', $comment);
1585
  }
1586
158732
  return $output;
15880
}
1589
1590
/**
1591
 * Process variables for comment.tpl.php.
1592
 *
1593
 * @see comment.tpl.php
1594
 * @see theme_comment()
1595
 */
15962027
function template_preprocess_comment(&$variables) {
159732
  $comment = $variables['comment'];
159832
  $node = $variables['node'];
159932
  $variables['author']    = theme('username', $comment);
160032
  $variables['content']   = $comment->comment;
160132
  $variables['date']      = format_date($comment->timestamp);
160232
  $variables['links']     = isset($variables['links']) ? theme('links',
$variables['links']) : '';
160332
  $variables['new']       = $comment->new ? t('new') : '';
160432
  $variables['picture']   =
theme_get_setting('toggle_comment_user_picture') ? theme('user_picture',
$comment) : '';
160532
  $variables['signature'] = $comment->signature;
160632
  $variables['submitted'] = theme('comment_submitted', $comment);
160732
  $variables['title']     = l($comment->subject, $_GET['q'],
array('fragment' => "comment-$comment->cid"));
160832
  $variables['template_files'][] = 'comment-' . $node->type;
1609
  // Set status to a string representation of comment->status.
161032
  if (isset($comment->preview)) {
161111
    $variables['status']  = 'comment-preview';
161211
  }
1613
  else {
161423
    $variables['status']  = ($comment->status == COMMENT_NOT_PUBLISHED) ?
'comment-unpublished' : 'comment-published';
1615
  }
161632
}
1617
1618
/**
1619
 * Process variables for comment-folded.tpl.php.
1620
 *
1621
 * @see comment-folded.tpl.php
1622
 * @see theme_comment_folded()
1623
 */
16242027
function template_preprocess_comment_folded(&$variables) {
16250
  $comment = $variables['comment'];
16260
  $variables['author'] = theme('username', $comment);
16270
  $variables['date']   = format_date($comment->timestamp);
16280
  $variables['new']    = $comment->new ? t('new') : '';
16290
  $variables['title']  = l($comment->subject, comment_node_url() . '/' .
$comment->cid, array('fragment' => "comment-$comment->cid"));
16300
}
1631
1632
/**
1633
 * Theme comment flat collapsed view.
1634
 *
1635
 * @param $comment
1636
 *   The comment to be themed.
1637
 * @param $node
1638
 *   The comment node.
1639
 * @ingroup themeable
1640
 */
16412027
function theme_comment_flat_collapsed($comment, $node) {
16420
  return theme('comment_view', $comment, $node, '', 0);
16430
}
1644
1645
/**
1646
 * Theme comment flat expanded view.
1647
 *
1648
 * @param $comment
1649
 *   The comment to be themed.
1650
 * @param $node
1651
 *   The comment node.
1652
 * @ingroup themeable
1653
 */
16542027
function theme_comment_flat_expanded($comment, $node) {
16550
  return theme('comment_view', $comment, $node, module_invoke_all('link',
'comment', $comment, 0));
16560
}
1657
1658
/**
1659
 * Theme comment thread collapsed view.
1660
 *
1661
 * @param $comment
1662
 *   The comment to be themed.
1663
 * @param $node
1664
 *   The comment node.
1665
 * @ingroup themeable
1666
 */
16672027
function theme_comment_thread_collapsed($comment, $node) {
16680
  return theme('comment_view', $comment, $node, '', 0);
16690
}
1670
1671
/**
1672
 * Theme comment thread expanded view.
1673
 *
1674
 * @param $comment
1675
 *   The comment to be themed.
1676
 * @param $node
1677
 *   The comment node.
1678
 * @ingroup themeable
1679
 */
16802027
function theme_comment_thread_expanded($comment, $node) {
168119
  return theme('comment_view', $comment, $node, module_invoke_all('link',
'comment', $comment, 0));
16820
}
1683
1684
/**
1685
 * Theme a "you can't post comments" notice.
1686
 *
1687
 * @param $node
1688
 *   The comment node.
1689
 * @ingroup themeable
1690
 */
16912027
function theme_comment_post_forbidden($node) {
16920
  global $user;
16930
  static $authenticated_post_comments;
1694
16950
  if (!$user->uid) {
16960
    if (!isset($authenticated_post_comments)) {
1697
      // We only output any link if we are certain, that users get
permission
1698
      // to post comments by logging in. We also locally cache this
information.
16990
      $authenticated_post_comments =
array_key_exists(DRUPAL_AUTHENTICATED_RID, user_roles(TRUE, 'post
comments') + user_roles(TRUE, 'post comments without approval'));
17000
    }
1701
17020
    if ($authenticated_post_comments) {
1703
      // We cannot use drupal_get_destination() because these links
1704
      // sometimes appear on /node and taxonomy listing pages.
17050
      if (variable_get('comment_form_location_' . $node->type,
COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
17060
        $destination = 'destination=' .
drupal_urlencode("comment/reply/$node->nid#comment-form");
17070
      }
1708
      else {
17090
        $destination = 'destination=' .
drupal_urlencode("node/$node->nid#comment-form");
1710
      }
1711
17120
      if (variable_get('user_register', 1)) {
1713
        // Users can register themselves.
17140
        return t('<a href="@login">Login</a> or <a
href="@register">register</a> to post comments', array('@login' =>
url('user/login', array('query' => $destination)), '@register' =>
url('user/register', array('query' => $destination))));
17150
      }
1716
      else {
1717
        // Only admins can add new users, no public registration.
17180
        return t('<a href="@login">Login</a> to post comments',
array('@login' => url('user/login', array('query' => $destination))));
1719
      }
17200
    }
17210
  }
17220
}
1723
1724
/**
1725
 * Process variables for comment-wrapper.tpl.php.
1726
 *
1727
 * @see comment-wrapper.tpl.php
1728
 * @see theme_comment_wrapper()
1729
 */
17302027
function template_preprocess_comment_wrapper(&$variables) {
1731
  // Provide contextual information.
17320
  $variables['display_mode']  = _comment_get_display_setting('mode',
$variables['node']);
17330
  $variables['template_files'][] = 'comment-wrapper-' .
$variables['node']->type;
17340
}
1735
1736
/**
1737
 * Theme a "Submitted by ..." notice.
1738
 *
1739
 * @param $comment
1740
 *   The comment.
1741
 * @ingroup themeable
1742
 */
17432027
function theme_comment_submitted($comment) {
17440
  return t('Submitted by !username on @datetime.',
1745
    array(
17460
      '!username' => theme('username', $comment),
17470
      '@datetime' => format_date($comment->timestamp)
17480
    ));
17490
}
1750
1751
/**
1752
 * Return an array of viewing modes for comment listings.
1753
 *
1754
 * We can't use a global variable array because the locale system
1755
 * is not initialized yet when the comment module is loaded.
1756
 */
17572027
function _comment_get_modes() {
1758
  return array(
17593
    COMMENT_MODE_FLAT_COLLAPSED => t('Flat list - collapsed'),
17603
    COMMENT_MODE_FLAT_EXPANDED => t('Flat list - expanded'),
17613
    COMMENT_MODE_THREADED_COLLAPSED => t('Threaded list - collapsed'),
17623
    COMMENT_MODE_THREADED_EXPANDED => t('Threaded list - expanded')
17633
  );
17640
}
1765
1766
/**
1767
 * Return an array of "comments per page" settings from which the user
1768
 * can choose.
1769
 */
17702027
function _comment_per_page() {
17713
  return drupal_map_assoc(array(10, 30, 50, 70, 90, 150, 200, 250, 300));
17720
}
1773
1774
/**
1775
 * Return a current comment display setting
1776
 *
1777
 * @param $setting
1778
 *   can be one of these: 'mode', 'sort', 'comments_per_page'
1779
 * @param $node
1780
 *   The comment node in question.
1781
 */
17822027
function _comment_get_display_setting($setting, $node) {
1783
  switch ($setting) {
1784155
    case 'mode':
1785155
      $value = variable_get('comment_default_mode_' . $node->type,
COMMENT_MODE_THREADED_EXPANDED);
1786155
      break;
1787
1788155
    case 'comments_per_page':
1789155
      $value = variable_get('comment_default_per_page_' . $node->type,
50);
1790155
  }
1791
1792155
  return $value;
17930
}
1794
1795
/**
1796
 * Updates the comment statistics for a given node. This should be called
any
1797
 * time a comment is added, deleted, or updated.
1798
 *
1799
 * The following fields are contained in the node_comment_statistics
table.
1800
 * - last_comment_timestamp: the timestamp of the last comment for this
node or the node create stamp if no comments exist for the node.
1801
 * - last_comment_name: the name of the anonymous poster for the last
comment
1802
 * - last_comment_uid: the uid of the poster for the last comment for this
node or the node authors uid if no comments exists for the node.
1803
 * - comment_count: the total number of approved/published comments on this
node.
1804
 */
18052027
function _comment_update_node_statistics($nid) {
180619
  $count = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid
= %d AND status = %d', $nid, COMMENT_PUBLISHED));
1807
180819
  if ($count > 0) {
1809
    // Comments exist.
181016
    $last_reply = db_fetch_object(db_query_range('SELECT cid, name,
timestamp, uid FROM {comments} WHERE nid = %d AND status = %d ORDER BY cid
DESC', $nid, COMMENT_PUBLISHED, 0, 1));
181116
    db_query("UPDATE {node_comment_statistics} SET comment_count = %d,
last_comment_timestamp = %d, last_comment_name = '%s', last_comment_uid =
%d WHERE nid = %d", $count, $last_reply->timestamp, $last_reply->uid ? '' :
$last_reply->name, $last_reply->uid, $nid);
181216
  }
1813
  else {
1814
    // Comments do not exist.
18153
    $node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE
nid = %d", $nid));
18163
    db_query("UPDATE {node_comment_statistics} SET comment_count = 0,
last_comment_timestamp = %d, last_comment_name = '', last_comment_uid = %d
WHERE nid = %d", $node->created, $node->uid, $nid);
1817
  }
181819
}
1819
1820
/**
1821
 * Invoke a hook_comment() operation in all modules.
1822
 *
1823
 * @param &$comment
1824
 *   A comment object.
1825
 * @param $op
1826
 *   A string containing the name of the comment operation.
1827
 * @return
1828
 *   The returned value of the invoked hooks.
1829
 */
18302027
function comment_invoke_comment(&$comment, $op) {
183152
  $return = array();
183252
  foreach (module_implements('comment') as $name) {
183352
    $function = $name . '_comment';
183452
    $result = $function($comment, $op);
183552
    if (isset($result) && is_array($result)) {
18360
      $return = array_merge($return, $result);
18370
    }
183852
    elseif (isset($result)) {
18390
      $return[] = $result;
18400
    }
184152
  }
1842
184352
  return $return;
18440
}
1845
1846
/**
1847
 * Generate vancode.
1848
 *
1849
 * Consists of a leading character indicating length, followed by N digits
1850
 * with a numerical value in base 36. Vancodes can be sorted as strings
1851
 * without messing up numerical order.
1852
 *
1853
 * It goes:
1854
 * 00, 01, 02, ..., 0y, 0z,
1855
 * 110, 111, ... , 1zy, 1zz,
1856
 * 2100, 2101, ..., 2zzy, 2zzz,
1857
 * 31000, 31001, ...
1858
 */
18592027
function int2vancode($i = 0) {
186012
  $num = base_convert((int)$i, 10, 36);
186112
  $length = strlen($num);
1862
186312
  return chr($length + ord('0') - 1) . $num;
18640
}
1865
1866
/**
1867
 * Decode vancode back to an integer.
1868
 */
18692027
function vancode2int($c = '00') {
187011
  return base_convert(substr($c, 1), 36, 10);
18710
}
1872
1873
/**
1874
 * Implementation of hook_hook_info().
1875
 */
18762027
function comment_hook_info() {
1877
  return array(
1878
    'comment' => array(
1879
      'comment' => array(
1880
        'insert' => array(
188155
          'runs when' => t('After saving a new comment'),
188255
        ),
1883
        'update' => array(
188455
          'runs when' => t('After saving an updated comment'),
188555
        ),
1886
        'delete' => array(
188755
          'runs when' => t('After deleting a comment')
188855
        ),
1889
        'view' => array(
189055
          'runs when' => t('When a comment is being viewed by an
authenticated user')
189155
        ),
189255
      ),
189355
    ),
189455
  );
18950
}
1896
1897
/**
1898
 * Implementation of hook_action_info().
1899
 */
19002027
function comment_action_info() {
1901
  return array(
1902
    'comment_unpublish_action' => array(
1903131
      'description' => t('Unpublish comment'),
1904131
      'type' => 'comment',
1905131
      'configurable' => FALSE,
1906
      'hooks' => array(
1907131
        'comment' => array('insert', 'update'),
1908
      )
1909131
    ),
1910
    'comment_unpublish_by_keyword_action' => array(
1911131
      'description' => t('Unpublish comment containing keyword(s)'),
1912131
      'type' => 'comment',
1913131
      'configurable' => TRUE,
1914
      'hooks' => array(
1915131
        'comment' => array('insert', 'update'),
1916
      )
1917131
    )
1918131
  );
19190
}
1920
1921
/**
1922
 * Drupal action to unpublish a comment.
1923
 *
1924
 * @param $context
1925
 *   Keyed array. Must contain the id of the comment if $comment is not
passed.
1926
 * @param $comment
1927
 *   An optional comment object.
1928
 */
19292027
function comment_unpublish_action($comment, $context = array()) {
19300
  if (isset($comment->cid)) {
19310
    $cid = $comment->cid;
19320
    $subject = $comment->subject;
19330
  }
1934
  else {
19350
    $cid = $context['cid'];
19360
    $subject = db_result(db_query("SELECT subject FROM {comments} WHERE cid
= %d", $cid));
1937
  }
19380
  db_query('UPDATE {comments} SET status = %d WHERE cid = %d',
COMMENT_NOT_PUBLISHED, $cid);
19390
  watchdog('action', 'Unpublished comment %subject.', array('%subject' =>
$subject));
19400
}
1941
1942
/**
1943
 * Form builder; Prepare a form for blacklisted keywords.
1944
 *
1945
 * @ingroup forms
1946
 */
19472027
function comment_unpublish_by_keyword_action_form($context) {
19480
  $form['keywords'] = array(
19490
    '#title' => t('Keywords'),
19500
    '#type' => 'textarea',
19510
    '#description' => t('The comment will be unpublished if it contains any
of the character sequences above. Use a comma-separated list of character
sequences. Example: funny, bungee jumping, "Company, Inc." . Character
sequences are case-sensitive.'),
19520
    '#default_value' => isset($context['keywords']) ?
drupal_implode_tags($context['keywords']) : '',
1953
  );
1954
19550
  return $form;
19560
}
1957
1958
/**
1959
 * Process comment_unpublish_by_keyword_action_form form submissions.
1960
 */
19612027
function comment_unpublish_by_keyword_action_submit($form, $form_state) {
19620
  return array('keywords' =>
drupal_explode_tags($form_state['values']['keywords']));
19630
}
1964
1965
/**
1966
 * Implementation of a configurable Drupal action.
1967
 *
1968
 * Unpublish a comment if it contains a certain string.
1969
 *
1970
 * @param $context
1971
 *   An array providing more information about the context of the call to
this action.
1972
 *   Unused here, since this action currently only supports the insert and
update ops of
1973
 *   the comment hook, both of which provide a complete $comment object.
1974
 * @param $comment
1975
 *   A comment object.
1976
 */
19772027
function comment_unpublish_by_keyword_action($comment, $context) {
19780
  foreach ($context['keywords'] as $keyword) {
19790
    if (strstr($comment->comment, $keyword) || strstr($comment->subject,
$keyword)) {
19800
      db_query('UPDATE {comments} SET status = %d WHERE cid = %d',
COMMENT_NOT_PUBLISHED, $comment->cid);
19810
      watchdog('action', 'Unpublished comment %subject.', array('%subject'
=> $comment->subject));
19820
      break;
19830
    }
19840
  }
19850
}
1986
1987
/**
1988
 * Implementation of hook_ranking().
1989
 */
19902027
function comment_ranking() {
1991
  return array(
1992
    'comments' => array(
19932
      'title' => t('Number of comments'),
19942
      'join' => 'LEFT JOIN {node_comment_statistics}
node_comment_statistics ON node_comment_statistics.nid = i.sid',
1995
      // Inverse law that maps the highest reply count on the site to 1 and
0 to 0.
19962
      'score' => '2.0 - 2.0 / (1.0 + node_comment_statistics.comment_count
* %f)',
19972
      'arguments' => array(variable_get('node_cron_comments_scale', 0)),
19982
    ),
19992
  );
20000
}
20012027