web/drupal/modules/comment/comment.module
branchdrupal
changeset 74 0ff3ba646492
equal deleted inserted replaced
73:fcf75e232c5b 74:0ff3ba646492
       
     1 <?php
       
     2 // $Id: comment.module,v 1.617.2.8 2009/07/01 20:51:55 goba 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 published.
       
    15  */
       
    16 define('COMMENT_PUBLISHED', 0);
       
    17 
       
    18 /**
       
    19  * Comment is awaiting approval.
       
    20  */
       
    21 define('COMMENT_NOT_PUBLISHED', 1);
       
    22 
       
    23 /**
       
    24  * Comments are displayed in a flat list - collapsed.
       
    25  */
       
    26 define('COMMENT_MODE_FLAT_COLLAPSED', 1);
       
    27 
       
    28 /**
       
    29  * Comments are displayed in a flat list - expanded.
       
    30  */
       
    31 define('COMMENT_MODE_FLAT_EXPANDED', 2);
       
    32 
       
    33 /**
       
    34  * Comments are displayed as a threaded list - collapsed.
       
    35  */
       
    36 define('COMMENT_MODE_THREADED_COLLAPSED', 3);
       
    37 
       
    38 /**
       
    39  * Comments are displayed as a threaded list - expanded.
       
    40  */
       
    41 define('COMMENT_MODE_THREADED_EXPANDED', 4);
       
    42 
       
    43 /**
       
    44  * Comments are ordered by date - newest first.
       
    45  */
       
    46 define('COMMENT_ORDER_NEWEST_FIRST', 1);
       
    47 
       
    48 /**
       
    49  * Comments are ordered by date - oldest first.
       
    50  */
       
    51 define('COMMENT_ORDER_OLDEST_FIRST', 2);
       
    52 
       
    53 /**
       
    54  * Comment controls should be shown above the comment list.
       
    55  */
       
    56 define('COMMENT_CONTROLS_ABOVE', 0);
       
    57 
       
    58 /**
       
    59  * Comment controls should be shown below the comment list.
       
    60  */
       
    61 define('COMMENT_CONTROLS_BELOW', 1);
       
    62 
       
    63 /**
       
    64  * Comment controls should be shown both above and below the comment list.
       
    65  */
       
    66 define('COMMENT_CONTROLS_ABOVE_BELOW', 2);
       
    67 
       
    68 /**
       
    69  * Comment controls are hidden.
       
    70  */
       
    71 define('COMMENT_CONTROLS_HIDDEN', 3);
       
    72 
       
    73 /**
       
    74  * Anonymous posters may not enter their contact information.
       
    75  */
       
    76 define('COMMENT_ANONYMOUS_MAYNOT_CONTACT', 0);
       
    77 
       
    78 /**
       
    79  * Anonymous posters may leave their contact information.
       
    80  */
       
    81 define('COMMENT_ANONYMOUS_MAY_CONTACT', 1);
       
    82 
       
    83 /**
       
    84  * Anonymous posters must leave their contact information.
       
    85  */
       
    86 define('COMMENT_ANONYMOUS_MUST_CONTACT', 2);
       
    87 
       
    88 /**
       
    89  * Comment form should be displayed on a separate page.
       
    90  */
       
    91 define('COMMENT_FORM_SEPARATE_PAGE', 0);
       
    92 
       
    93 /**
       
    94  * Comment form should be shown below post or list of comments.
       
    95  */
       
    96 define('COMMENT_FORM_BELOW', 1);
       
    97 
       
    98 /**
       
    99  * Comments for this node are disabled.
       
   100  */
       
   101 define('COMMENT_NODE_DISABLED', 0);
       
   102 
       
   103 /**
       
   104  * Comments for this node are locked.
       
   105  */
       
   106 define('COMMENT_NODE_READ_ONLY', 1);
       
   107 
       
   108 /**
       
   109  * Comments are enabled on this node.
       
   110  */
       
   111 define('COMMENT_NODE_READ_WRITE', 2);
       
   112 
       
   113 /**
       
   114  * Comment preview is optional.
       
   115  */
       
   116 define('COMMENT_PREVIEW_OPTIONAL', 0);
       
   117 
       
   118 /**
       
   119  * Comment preview is required.
       
   120  */
       
   121 define('COMMENT_PREVIEW_REQUIRED', 1);
       
   122 
       
   123 /**
       
   124  * Implementation of hook_help().
       
   125  */
       
   126 function comment_help($path, $arg) {
       
   127   switch ($path) {
       
   128     case 'admin/help#comment':
       
   129       $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 (some display settings are customizable by individual users).', array('@content-type' => url('admin/content/types'))) .'</p>';
       
   130       $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>';
       
   131       $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>';
       
   132       return $output;
       
   133     case 'admin/content/comment':
       
   134       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>';
       
   135     case 'admin/content/comment/approval':
       
   136       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>';
       
   137   }
       
   138 }
       
   139 
       
   140 /**
       
   141  * Implementation of hook_theme().
       
   142  */
       
   143 function comment_theme() {
       
   144   return array(
       
   145     'comment_block' => array(
       
   146       'arguments' => array(),
       
   147     ),
       
   148     'comment_admin_overview' => array(
       
   149       'arguments' => array('form' => NULL),
       
   150     ),
       
   151     'comment_preview' => array(
       
   152       'arguments' => array('comment' => NULL, 'node' => NULL, 'links' => array(), 'visible' => 1),
       
   153     ),
       
   154     'comment_view' => array(
       
   155       'arguments' => array('comment' => NULL, 'node' => NULL, 'links' => array(), 'visible' => 1),
       
   156     ),
       
   157     'comment_controls' => array(
       
   158       'arguments' => array('form' => NULL),
       
   159     ),
       
   160     'comment' => array(
       
   161       'template' => 'comment',
       
   162       'arguments' => array('comment' => NULL, 'node' => NULL, 'links' => array()),
       
   163     ),
       
   164     'comment_folded' => array(
       
   165       'template' => 'comment-folded',
       
   166       'arguments' => array('comment' => NULL),
       
   167     ),
       
   168     'comment_flat_collapsed' => array(
       
   169       'arguments' => array('comment' => NULL, 'node' => NULL),
       
   170     ),
       
   171     'comment_flat_expanded' => array(
       
   172       'arguments' => array('comment' => NULL, 'node' => NULL),
       
   173     ),
       
   174     'comment_thread_collapsed' => array(
       
   175       'arguments' => array('comment' => NULL, 'node' => NULL),
       
   176     ),
       
   177     'comment_thread_expanded' => array(
       
   178       'arguments' => array('comment' => NULL, 'node' => NULL),
       
   179     ),
       
   180     'comment_post_forbidden' => array(
       
   181       'arguments' => array('nid' => NULL),
       
   182     ),
       
   183     'comment_wrapper' => array(
       
   184       'template' => 'comment-wrapper',
       
   185       'arguments' => array('content' => NULL, 'node' => NULL),
       
   186     ),
       
   187     'comment_submitted' => array(
       
   188       'arguments' => array('comment' => NULL),
       
   189     ),
       
   190   );
       
   191 }
       
   192 
       
   193 /**
       
   194  * Implementation of hook_menu().
       
   195  */
       
   196 function comment_menu() {
       
   197   $items['admin/content/comment'] = array(
       
   198     'title' => 'Comments',
       
   199     'description' => 'List and edit site comments and the comment moderation queue.',
       
   200     'page callback' => 'comment_admin',
       
   201     'access arguments' => array('administer comments'),
       
   202     'file' => 'comment.admin.inc',
       
   203   );
       
   204 
       
   205   // Tabs:
       
   206   $items['admin/content/comment/new'] = array(
       
   207     'title' => 'Published comments',
       
   208     'type' => MENU_DEFAULT_LOCAL_TASK,
       
   209     'weight' => -10,
       
   210   );
       
   211   $items['admin/content/comment/approval'] = array(
       
   212     'title' => 'Approval queue',
       
   213     'page arguments' => array('approval'),
       
   214     'access arguments' => array('administer comments'),
       
   215     'type' => MENU_LOCAL_TASK,
       
   216     'file' => 'comment.admin.inc',
       
   217   );
       
   218 
       
   219   $items['comment/delete'] = array(
       
   220     'title' => 'Delete comment',
       
   221     'page callback' => 'comment_delete',
       
   222     'access arguments' => array('administer comments'),
       
   223     'type' => MENU_CALLBACK,
       
   224     'file' => 'comment.admin.inc',
       
   225   );
       
   226 
       
   227   $items['comment/edit'] = array(
       
   228     'title' => 'Edit comment',
       
   229     'page callback' => 'comment_edit',
       
   230     'access arguments' => array('post comments'),
       
   231     'type' => MENU_CALLBACK,
       
   232     'file' => 'comment.pages.inc',
       
   233   );
       
   234   $items['comment/reply/%node'] = array(
       
   235     'title' => 'Reply to comment',
       
   236     'page callback' => 'comment_reply',
       
   237     'page arguments' => array(2),
       
   238     'access callback' => 'node_access',
       
   239     'access arguments' => array('view', 2),
       
   240     'type' => MENU_CALLBACK,
       
   241     'file' => 'comment.pages.inc',
       
   242   );
       
   243 
       
   244   return $items;
       
   245 }
       
   246 
       
   247 /**
       
   248  * Implementation of hook_node_type().
       
   249  */
       
   250 function comment_node_type($op, $info) {
       
   251   $settings = array(
       
   252     'comment',
       
   253     'comment_default_mode',
       
   254     'comment_default_order',
       
   255     'comment_default_per_page',
       
   256     'comment_controls',
       
   257     'comment_anonymous',
       
   258     'comment_subject_field',
       
   259     'comment_preview',
       
   260     'comment_form_location',
       
   261   );
       
   262   switch ($op) {
       
   263     case 'delete':
       
   264       foreach ($settings as $setting) {
       
   265         variable_del($setting .'_'. $info->type);
       
   266       }
       
   267       break;
       
   268   }
       
   269 }
       
   270 
       
   271 /**
       
   272  * Implementation of hook_perm().
       
   273  */
       
   274 function comment_perm() {
       
   275   return array('access comments', 'post comments', 'administer comments', 'post comments without approval');
       
   276 }
       
   277 
       
   278 /**
       
   279  * Implementation of hook_block().
       
   280  *
       
   281  * Generates a block with the most recent comments.
       
   282  */
       
   283 function comment_block($op = 'list', $delta = 0) {
       
   284   if ($op == 'list') {
       
   285     $blocks[0]['info'] = t('Recent comments');
       
   286     return $blocks;
       
   287   }
       
   288   else if ($op == 'view' && user_access('access comments')) {
       
   289     $block['subject'] = t('Recent comments');
       
   290     $block['content'] = theme('comment_block');
       
   291     return $block;
       
   292   }
       
   293 }
       
   294 
       
   295 /**
       
   296  * Find a number of recent comments. This is done in two steps.
       
   297  *   1. Find the n (specified by $number) nodes that have the most recent
       
   298  *      comments.  This is done by querying node_comment_statistics which has
       
   299  *      an index on last_comment_timestamp, and is thus a fast query.
       
   300  *   2. Loading the information from the comments table based on the nids found
       
   301  *      in step 1.
       
   302  *
       
   303  * @param $number
       
   304  *   (optional) The maximum number of comments to find.
       
   305  * @return
       
   306  *   An array of comment objects each containing a nid,
       
   307  *   subject, cid, and timestamp, or an empty array if there are no recent
       
   308  *   comments visible to the current user.
       
   309  */
       
   310 function comment_get_recent($number = 10) {
       
   311   // Select the $number nodes (visible to the current user) with the most
       
   312   // recent comments. This is efficient due to the index on
       
   313   // last_comment_timestamp.
       
   314   $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);
       
   315 
       
   316   $nids = array();
       
   317   while ($row = db_fetch_object($result)) {
       
   318     $nids[] = $row->nid;
       
   319   }
       
   320 
       
   321   $comments = array();
       
   322   if (!empty($nids)) {
       
   323     // From among the comments on the nodes selected in the first query,
       
   324     // find the $number most recent comments.
       
   325     $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);
       
   326     while ($comment = db_fetch_object($result)) {
       
   327       $comments[] = $comment;
       
   328     }
       
   329   }
       
   330 
       
   331   return $comments;
       
   332 }
       
   333 
       
   334 /**
       
   335  * Calculate page number for first new comment.
       
   336  *
       
   337  * @param $num_comments
       
   338  *   Number of comments.
       
   339  * @param $new_replies
       
   340  *   Number of new replies.
       
   341  * @param $node
       
   342  *   The first new comment node.
       
   343  * @return
       
   344  *   "page=X" if the page number is greater than zero; empty string otherwise.
       
   345  */
       
   346 function comment_new_page_count($num_comments, $new_replies, $node) {
       
   347   $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
       
   348   $mode = _comment_get_display_setting('mode', $node);
       
   349   $order = _comment_get_display_setting('sort', $node);
       
   350   $pagenum = NULL;
       
   351   $flat = in_array($mode, array(COMMENT_MODE_FLAT_COLLAPSED, COMMENT_MODE_FLAT_EXPANDED));
       
   352   if ($num_comments <= $comments_per_page || ($flat && $order == COMMENT_ORDER_NEWEST_FIRST)) {
       
   353     // Only one page of comments or flat forum and newest first.
       
   354     // First new comment will always be on first page.
       
   355     $pageno = 0;
       
   356   }
       
   357   else {
       
   358     if ($flat) {
       
   359       // Flat comments and oldest first.
       
   360       $count = $num_comments - $new_replies;
       
   361     }
       
   362     else {
       
   363       // Threaded comments. See the documentation for comment_render().
       
   364       if ($order == COMMENT_ORDER_NEWEST_FIRST) {
       
   365         // Newest first: find the last thread with new comment
       
   366         $result = db_query('(SELECT thread FROM {comments} WHERE nid = %d  AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY thread DESC LIMIT 1', $node->nid, $new_replies);
       
   367         $thread = db_result($result);
       
   368         $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid = %d AND status = 0 AND thread > '". $thread ."'", $node->nid);
       
   369       }
       
   370       else {
       
   371         // Oldest first: find the first thread with new comment
       
   372         $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);
       
   373         $thread = substr(db_result($result), 0, -1);
       
   374         $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid = %d AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '". $thread ."'", $node->nid);
       
   375       }
       
   376       $count = db_result($result_count);
       
   377     }
       
   378     $pageno =  $count / $comments_per_page;
       
   379   }
       
   380   if ($pageno >= 1) {
       
   381     $pagenum = "page=". intval($pageno);
       
   382   }
       
   383   return $pagenum;
       
   384 }
       
   385 
       
   386 /**
       
   387  * Returns a formatted list of recent comments to be displayed in the comment block.
       
   388  *
       
   389  * @return
       
   390  *   The comment list HTML.
       
   391  * @ingroup themeable
       
   392  */
       
   393 function theme_comment_block() {
       
   394   $items = array();
       
   395   foreach (comment_get_recent() as $comment) {
       
   396     $items[] = l($comment->subject, 'node/'. $comment->nid, array('fragment' => 'comment-'. $comment->cid)) .'<br />'. t('@time ago', array('@time' => format_interval(time() - $comment->timestamp)));
       
   397   }
       
   398   if ($items) {
       
   399     return theme('item_list', $items);
       
   400   }
       
   401 }
       
   402 
       
   403 /**
       
   404  * Implementation of hook_link().
       
   405  */
       
   406 function comment_link($type, $node = NULL, $teaser = FALSE) {
       
   407   $links = array();
       
   408 
       
   409   if ($type == 'node' && $node->comment) {
       
   410 
       
   411     if ($teaser) {
       
   412       // Main page: display the number of comments that have been posted.
       
   413 
       
   414       if (user_access('access comments')) {
       
   415         $all = comment_num_all($node->nid);
       
   416 
       
   417         if ($all) {
       
   418           $links['comment_comments'] = array(
       
   419             'title' => format_plural($all, '1 comment', '@count comments'),
       
   420             'href' => "node/$node->nid",
       
   421             'attributes' => array('title' => t('Jump to the first comment of this posting.')),
       
   422             'fragment' => 'comments'
       
   423           );
       
   424 
       
   425           $new = comment_num_new($node->nid);
       
   426 
       
   427           if ($new) {
       
   428             $links['comment_new_comments'] = array(
       
   429               'title' => format_plural($new, '1 new comment', '@count new comments'),
       
   430               'href' => "node/$node->nid",
       
   431               'query' => comment_new_page_count($all, $new, $node),
       
   432               'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
       
   433               'fragment' => 'new'
       
   434             );
       
   435           }
       
   436         }
       
   437         else {
       
   438           if ($node->comment == COMMENT_NODE_READ_WRITE) {
       
   439             if (user_access('post comments')) {
       
   440               $links['comment_add'] = array(
       
   441                 'title' => t('Add new comment'),
       
   442                 'href' => "comment/reply/$node->nid",
       
   443                 'attributes' => array('title' => t('Add a new comment to this page.')),
       
   444                 'fragment' => 'comment-form'
       
   445               );
       
   446             }
       
   447             else {
       
   448               $links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node);
       
   449             }
       
   450           }
       
   451         }
       
   452       }
       
   453     }
       
   454     else {
       
   455       // Node page: add a "post comment" link if the user is allowed to
       
   456       // post comments, if this node is not read-only, and if the comment form isn't already shown
       
   457 
       
   458       if ($node->comment == COMMENT_NODE_READ_WRITE) {
       
   459         if (user_access('post comments')) {
       
   460           if (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
       
   461             $links['comment_add'] = array(
       
   462               'title' => t('Add new comment'),
       
   463               'href' => "comment/reply/$node->nid",
       
   464               'attributes' => array('title' => t('Share your thoughts and opinions related to this posting.')),
       
   465               'fragment' => 'comment-form'
       
   466             );
       
   467           }
       
   468         }
       
   469         else {
       
   470           $links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node);
       
   471         }
       
   472       }
       
   473     }
       
   474   }
       
   475 
       
   476   if ($type == 'comment') {
       
   477     $links = comment_links($node, $teaser);
       
   478   }
       
   479   if (isset($links['comment_forbidden'])) {
       
   480     $links['comment_forbidden']['html'] = TRUE;
       
   481   }
       
   482 
       
   483   return $links;
       
   484 }
       
   485 
       
   486 /**
       
   487  * Implementation of hook_form_alter().
       
   488  */
       
   489 function comment_form_alter(&$form, $form_state, $form_id) {
       
   490   if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
       
   491     $form['comment'] = array(
       
   492       '#type' => 'fieldset',
       
   493       '#title' => t('Comment settings'),
       
   494       '#collapsible' => TRUE,
       
   495       '#collapsed' => TRUE,
       
   496     );
       
   497     $form['comment']['comment'] = array(
       
   498       '#type' => 'radios',
       
   499       '#title' => t('Default comment setting'),
       
   500       '#default_value' => variable_get('comment_'. $form['#node_type']->type, COMMENT_NODE_READ_WRITE),
       
   501       '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')),
       
   502       '#description' => t('Users with the <em>administer comments</em> permission will be able to override this setting.'),
       
   503     );
       
   504     $form['comment']['comment_default_mode'] = array(
       
   505       '#type' => 'radios',
       
   506       '#title' => t('Default display mode'),
       
   507       '#default_value' => variable_get('comment_default_mode_'. $form['#node_type']->type, COMMENT_MODE_THREADED_EXPANDED),
       
   508       '#options' => _comment_get_modes(),
       
   509       '#description' => t('The default view for comments. Expanded views display the body of the comment. Threaded views keep replies together.'),
       
   510     );
       
   511     $form['comment']['comment_default_order'] = array(
       
   512       '#type' => 'radios',
       
   513       '#title' => t('Default display order'),
       
   514       '#default_value' => variable_get('comment_default_order_'. $form['#node_type']->type, COMMENT_ORDER_NEWEST_FIRST),
       
   515       '#options' => _comment_get_orders(),
       
   516       '#description' => t('The default sorting for new users and anonymous users while viewing comments. These users may change their view using the comment control panel. For registered users, this change is remembered as a persistent user preference.'),
       
   517     );
       
   518     $form['comment']['comment_default_per_page'] = array(
       
   519       '#type' => 'select',
       
   520       '#title' => t('Default comments per page'),
       
   521       '#default_value' => variable_get('comment_default_per_page_'. $form['#node_type']->type, 50),
       
   522       '#options' => _comment_per_page(),
       
   523       '#description' => t('Default number of comments for each page: more comments are distributed in several pages.'),
       
   524     );
       
   525     $form['comment']['comment_controls'] = array(
       
   526       '#type' => 'radios',
       
   527       '#title' => t('Comment controls'),
       
   528       '#default_value' => variable_get('comment_controls_'. $form['#node_type']->type, COMMENT_CONTROLS_HIDDEN),
       
   529       '#options' => array(
       
   530         t('Display above the comments'),
       
   531         t('Display below the comments'),
       
   532         t('Display above and below the comments'),
       
   533         t('Do not display')),
       
   534       '#description' => t('Position of the comment controls box. The comment controls let the user change the default display mode and display order of comments.'),
       
   535     );
       
   536     $form['comment']['comment_anonymous'] = array(
       
   537       '#type' => 'radios',
       
   538       '#title' => t('Anonymous commenting'),
       
   539       '#default_value' => variable_get('comment_anonymous_'. $form['#node_type']->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT),
       
   540       '#options' => array(
       
   541         COMMENT_ANONYMOUS_MAYNOT_CONTACT => t('Anonymous posters may not enter their contact information'),
       
   542         COMMENT_ANONYMOUS_MAY_CONTACT => t('Anonymous posters may leave their contact information'),
       
   543         COMMENT_ANONYMOUS_MUST_CONTACT => t('Anonymous posters must leave their contact information')),
       
   544       '#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')))),
       
   545     );
       
   546     if (!user_access('post comments', drupal_anonymous_user())) {
       
   547       $form['comment']['comment_anonymous']['#disabled'] = TRUE;
       
   548     }
       
   549     $form['comment']['comment_subject_field'] = array(
       
   550       '#type' => 'radios',
       
   551       '#title' => t('Comment subject field'),
       
   552       '#default_value' => variable_get('comment_subject_field_'. $form['#node_type']->type, 1),
       
   553       '#options' => array(t('Disabled'), t('Enabled')),
       
   554       '#description' => t('Can users provide a unique subject for their comments?'),
       
   555     );
       
   556     $form['comment']['comment_preview'] = array(
       
   557       '#type' => 'radios',
       
   558       '#title' => t('Preview comment'),
       
   559       '#default_value' => variable_get('comment_preview_'. $form['#node_type']->type, COMMENT_PREVIEW_REQUIRED),
       
   560       '#options' => array(t('Optional'), t('Required')),
       
   561       '#description' => t("Forces a user to look at their comment by clicking on a 'Preview' button before they can actually add the comment"),
       
   562     );
       
   563     $form['comment']['comment_form_location'] = array(
       
   564       '#type' => 'radios',
       
   565       '#title' => t('Location of comment submission form'),
       
   566       '#default_value' => variable_get('comment_form_location_'. $form['#node_type']->type, COMMENT_FORM_SEPARATE_PAGE),
       
   567       '#options' => array(t('Display on separate page'), t('Display below post or comments')),
       
   568     );
       
   569   }
       
   570   elseif (isset($form['type']) && isset($form['#node'])) {
       
   571     if ($form['type']['#value'] .'_node_form' == $form_id) {
       
   572       $node = $form['#node'];
       
   573       $form['comment_settings'] = array(
       
   574         '#type' => 'fieldset',
       
   575         '#access' => user_access('administer comments'),
       
   576         '#title' => t('Comment settings'),
       
   577         '#collapsible' => TRUE,
       
   578         '#collapsed' => TRUE,
       
   579         '#weight' => 30,
       
   580       );
       
   581       $form['comment_settings']['comment'] = array(
       
   582         '#type' => 'radios',
       
   583         '#parents' => array('comment'),
       
   584         '#default_value' => $node->comment,
       
   585         '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')),
       
   586       );
       
   587     }
       
   588   }
       
   589 }
       
   590 
       
   591 /**
       
   592  * Implementation of hook_nodeapi().
       
   593  */
       
   594 function comment_nodeapi(&$node, $op, $arg = 0) {
       
   595   switch ($op) {
       
   596     case 'load':
       
   597       return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, comment_count FROM {node_comment_statistics} WHERE nid = %d", $node->nid));
       
   598       break;
       
   599 
       
   600     case 'prepare':
       
   601       if (!isset($node->comment)) {
       
   602         $node->comment = variable_get("comment_$node->type", COMMENT_NODE_READ_WRITE);
       
   603       }
       
   604       break;
       
   605 
       
   606     case 'insert':
       
   607       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);
       
   608       break;
       
   609 
       
   610     case 'delete':
       
   611       db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
       
   612       db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
       
   613       break;
       
   614 
       
   615     case 'update index':
       
   616       $text = '';
       
   617       $comments = db_query('SELECT subject, comment, format FROM {comments} WHERE nid = %d AND status = %d', $node->nid, COMMENT_PUBLISHED);
       
   618       while ($comment = db_fetch_object($comments)) {
       
   619         $text .= '<h2>'. check_plain($comment->subject) .'</h2>'. check_markup($comment->comment, $comment->format, FALSE);
       
   620       }
       
   621       return $text;
       
   622 
       
   623     case 'search result':
       
   624       $comments = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $node->nid));
       
   625       return format_plural($comments, '1 comment', '@count comments');
       
   626 
       
   627     case 'rss item':
       
   628       if ($node->comment != COMMENT_NODE_DISABLED) {
       
   629         return array(array('key' => 'comments', 'value' => url('node/'. $node->nid, array('fragment' => 'comments', 'absolute' => TRUE))));
       
   630       }
       
   631       else {
       
   632         return array();
       
   633       }
       
   634   }
       
   635 }
       
   636 
       
   637 /**
       
   638  * Implementation of hook_user().
       
   639  */
       
   640 function comment_user($type, $edit, &$user, $category = NULL) {
       
   641   if ($type == 'delete') {
       
   642     db_query('UPDATE {comments} SET uid = 0 WHERE uid = %d', $user->uid);
       
   643     db_query('UPDATE {node_comment_statistics} SET last_comment_uid = 0 WHERE last_comment_uid = %d', $user->uid);
       
   644   }
       
   645 }
       
   646 
       
   647 /**
       
   648  * This is *not* a hook_access() implementation. This function is called
       
   649  * to determine whether the current user has access to a particular comment.
       
   650  *
       
   651  * Authenticated users can edit their comments as long they have not been
       
   652  * replied to. This prevents people from changing or revising their
       
   653  * statements based on the replies to their posts.
       
   654  *
       
   655  * @param $op
       
   656  *   The operation that is to be performed on the comment. Only 'edit' is recognized now.
       
   657  * @param $comment
       
   658  *   The comment object.
       
   659  * @return
       
   660  *   TRUE if the current user has acces to the comment, FALSE otherwise.
       
   661  */
       
   662 function comment_access($op, $comment) {
       
   663   global $user;
       
   664 
       
   665   if ($op == 'edit') {
       
   666     return ($user->uid && $user->uid == $comment->uid && comment_num_replies($comment->cid) == 0) || user_access('administer comments');
       
   667   }
       
   668 }
       
   669 
       
   670 /**
       
   671  * A simple helper function.
       
   672  *
       
   673  * @return
       
   674  *   The 0th and the 1st path components joined by a slash.
       
   675  */
       
   676 function comment_node_url() {
       
   677   return arg(0) .'/'. arg(1);
       
   678 }
       
   679 
       
   680 /**
       
   681  * Accepts a submission of new or changed comment content.
       
   682  *
       
   683  * @param $edit
       
   684  *   A comment array.
       
   685  *
       
   686  * @return
       
   687  *   If the comment is successfully saved the comment ID is returned. If the comment
       
   688  *   is not saved, FALSE is returned.
       
   689  */
       
   690 function comment_save($edit) {
       
   691   global $user;
       
   692   if (user_access('post comments') && (user_access('administer comments') || node_comment_mode($edit['nid']) == COMMENT_NODE_READ_WRITE)) {
       
   693     if (!form_get_errors()) {
       
   694       $edit += array(
       
   695         'mail' => '',
       
   696         'homepage' => '',
       
   697         'name' => '',
       
   698         'status' => user_access('post comments without approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED,
       
   699       );
       
   700       if ($edit['cid']) {
       
   701         // Update the comment in the database.
       
   702         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']);
       
   703 
       
   704         // Allow modules to respond to the updating of a comment.
       
   705         comment_invoke_comment($edit, 'update');
       
   706 
       
   707         // Add an entry to the watchdog log.
       
   708         watchdog('content', 'Comment: updated %subject.', array('%subject' => $edit['subject']), WATCHDOG_NOTICE, l(t('view'), 'node/'. $edit['nid'], array('fragment' => 'comment-'. $edit['cid'])));
       
   709       }
       
   710       else {
       
   711         // Add the comment to database.
       
   712         // Here we are building the thread field. See the documentation for
       
   713         // comment_render().
       
   714         if ($edit['pid'] == 0) {
       
   715           // This is a comment with no parent comment (depth 0): we start
       
   716           // by retrieving the maximum thread level.
       
   717           $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $edit['nid']));
       
   718 
       
   719           // Strip the "/" from the end of the thread.
       
   720           $max = rtrim($max, '/');
       
   721 
       
   722           // Finally, build the thread field for this new comment.
       
   723           $thread = int2vancode(vancode2int($max) + 1) .'/';
       
   724         }
       
   725         else {
       
   726           // This is comment with a parent comment: we increase
       
   727           // the part of the thread value at the proper depth.
       
   728 
       
   729           // Get the parent comment:
       
   730           $parent = _comment_load($edit['pid']);
       
   731 
       
   732           // Strip the "/" from the end of the parent thread.
       
   733           $parent->thread = (string) rtrim((string) $parent->thread, '/');
       
   734 
       
   735           // Get the max value in _this_ thread.
       
   736           $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
       
   737 
       
   738           if ($max == '') {
       
   739             // First child of this parent.
       
   740             $thread = $parent->thread .'.'. int2vancode(0) .'/';
       
   741           }
       
   742           else {
       
   743             // Strip the "/" at the end of the thread.
       
   744             $max = rtrim($max, '/');
       
   745 
       
   746             // We need to get the value at the correct depth.
       
   747             $parts = explode('.', $max);
       
   748             $parent_depth = count(explode('.', $parent->thread));
       
   749             $last = $parts[$parent_depth];
       
   750 
       
   751             // Finally, build the thread field for this new comment.
       
   752             $thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
       
   753           }
       
   754         }
       
   755 
       
   756         if (empty($edit['timestamp'])) {
       
   757           $edit['timestamp'] = time();
       
   758         }
       
   759 
       
   760         if ($edit['uid'] === $user->uid) { // '===' because we want to modify anonymous users too
       
   761           $edit['name'] = $user->name;
       
   762         }
       
   763 
       
   764         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']);
       
   765         $edit['cid'] = db_last_insert_id('comments', 'cid');
       
   766 
       
   767         // Tell the other modules a new comment has been submitted.
       
   768         comment_invoke_comment($edit, 'insert');
       
   769 
       
   770         // Add an entry to the watchdog log.
       
   771         watchdog('content', 'Comment: added %subject.', array('%subject' => $edit['subject']), WATCHDOG_NOTICE, l(t('view'), 'node/'. $edit['nid'], array('fragment' => 'comment-'. $edit['cid'])));
       
   772       }
       
   773       _comment_update_node_statistics($edit['nid']);
       
   774 
       
   775       // Clear the cache so an anonymous user can see his comment being added.
       
   776       cache_clear_all();
       
   777 
       
   778       // Explain the approval queue if necessary, and then
       
   779       // redirect the user to the node he's commenting on.
       
   780       if ($edit['status'] == COMMENT_NOT_PUBLISHED) {
       
   781         drupal_set_message(t('Your comment has been queued for moderation by site administrators and will be published after approval.'));
       
   782       }
       
   783       else {
       
   784         comment_invoke_comment($edit, 'publish');
       
   785       }
       
   786       return $edit['cid'];
       
   787     }
       
   788     else {
       
   789       return FALSE;
       
   790     }
       
   791   }
       
   792   else {
       
   793     watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $edit['subject']), WATCHDOG_WARNING);
       
   794     drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $edit['subject'])), 'error');
       
   795     return FALSE;
       
   796   }
       
   797 }
       
   798 
       
   799 /**
       
   800  * Build command links for a comment (e.g.\ edit, reply, delete) with respect to the current user's access permissions.
       
   801  *
       
   802  * @param $comment
       
   803  *   The comment to which the links will be related.
       
   804  * @param $return
       
   805  *   Not used.
       
   806  * @return
       
   807  *   An associative array containing the links.
       
   808  */
       
   809 function comment_links($comment, $return = 1) {
       
   810   global $user;
       
   811 
       
   812   $links = array();
       
   813 
       
   814   // If we are viewing just this comment, we link back to the node.
       
   815   if ($return) {
       
   816     $links['comment_parent'] = array(
       
   817       'title' => t('parent'),
       
   818       'href' => comment_node_url(),
       
   819       'fragment' => "comment-$comment->cid"
       
   820     );
       
   821   }
       
   822 
       
   823   if (node_comment_mode($comment->nid) == COMMENT_NODE_READ_WRITE) {
       
   824     if (user_access('administer comments') && user_access('post comments')) {
       
   825       $links['comment_delete'] = array(
       
   826         'title' => t('delete'),
       
   827         'href' => "comment/delete/$comment->cid"
       
   828       );
       
   829       $links['comment_edit'] = array(
       
   830         'title' => t('edit'),
       
   831         'href' => "comment/edit/$comment->cid"
       
   832       );
       
   833       $links['comment_reply'] = array(
       
   834         'title' => t('reply'),
       
   835         'href' => "comment/reply/$comment->nid/$comment->cid"
       
   836       );
       
   837     }
       
   838     else if (user_access('post comments')) {
       
   839       if (comment_access('edit', $comment)) {
       
   840         $links['comment_edit'] = array(
       
   841           'title' => t('edit'),
       
   842           'href' => "comment/edit/$comment->cid"
       
   843         );
       
   844       }
       
   845       $links['comment_reply'] = array(
       
   846         'title' => t('reply'),
       
   847         'href' => "comment/reply/$comment->nid/$comment->cid"
       
   848       );
       
   849     }
       
   850     else {
       
   851       $node = node_load($comment->nid);
       
   852       $links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node);
       
   853     }
       
   854   }
       
   855 
       
   856   return $links;
       
   857 }
       
   858 
       
   859 /**
       
   860  * Renders comment(s).
       
   861  *
       
   862  * @param $node
       
   863  *   The node which comment(s) needs rendering.
       
   864  * @param $cid
       
   865  *   Optional, if given, only one comment is rendered.
       
   866  *
       
   867  * To display threaded comments in the correct order we keep a 'thread' field
       
   868  * and order by that value. This field keeps this data in
       
   869  * a way which is easy to update and convenient to use.
       
   870  *
       
   871  * A "thread" value starts at "1". If we add a child (A) to this comment,
       
   872  * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
       
   873  * brother of (A) will get "1.2". Next brother of the parent of (A) will get
       
   874  * "2" and so on.
       
   875  *
       
   876  * First of all note that the thread field stores the depth of the comment:
       
   877  * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
       
   878  *
       
   879  * Now to get the ordering right, consider this example:
       
   880  *
       
   881  * 1
       
   882  * 1.1
       
   883  * 1.1.1
       
   884  * 1.2
       
   885  * 2
       
   886  *
       
   887  * If we "ORDER BY thread ASC" we get the above result, and this is the
       
   888  * natural order sorted by time. However, if we "ORDER BY thread DESC"
       
   889  * we get:
       
   890  *
       
   891  * 2
       
   892  * 1.2
       
   893  * 1.1.1
       
   894  * 1.1
       
   895  * 1
       
   896  *
       
   897  * Clearly, this is not a natural way to see a thread, and users will get
       
   898  * confused. The natural order to show a thread by time desc would be:
       
   899  *
       
   900  * 2
       
   901  * 1
       
   902  * 1.2
       
   903  * 1.1
       
   904  * 1.1.1
       
   905  *
       
   906  * which is what we already did before the standard pager patch. To achieve
       
   907  * this we simply add a "/" at the end of each "thread" value. This way out
       
   908  * thread fields will look like depicted below:
       
   909  *
       
   910  * 1/
       
   911  * 1.1/
       
   912  * 1.1.1/
       
   913  * 1.2/
       
   914  * 2/
       
   915  *
       
   916  * we add "/" since this char is, in ASCII, higher than every number, so if
       
   917  * now we "ORDER BY thread DESC" we get the correct order. However this would
       
   918  * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
       
   919  * to consider the trailing "/" so we use a substring only.
       
   920  */
       
   921 function comment_render($node, $cid = 0) {
       
   922   global $user;
       
   923 
       
   924   $output = '';
       
   925 
       
   926   if (user_access('access comments')) {
       
   927     // Pre-process variables.
       
   928     $nid = $node->nid;
       
   929     if (empty($nid)) {
       
   930       $nid = 0;
       
   931     }
       
   932 
       
   933     $mode = _comment_get_display_setting('mode', $node);
       
   934     $order = _comment_get_display_setting('sort', $node);
       
   935     $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
       
   936 
       
   937     if ($cid && is_numeric($cid)) {
       
   938       // Single comment view.
       
   939       $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';
       
   940       $query_args = array($cid);
       
   941       if (!user_access('administer comments')) {
       
   942         $query .= ' AND c.status = %d';
       
   943         $query_args[] = COMMENT_PUBLISHED;
       
   944       }
       
   945 
       
   946       $query = db_rewrite_sql($query, 'c', 'cid');
       
   947       $result = db_query($query, $query_args);
       
   948 
       
   949       if ($comment = db_fetch_object($result)) {
       
   950         $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
       
   951         $links = module_invoke_all('link', 'comment', $comment, 1);
       
   952         drupal_alter('link', $links, $node);
       
   953 
       
   954         $output .= theme('comment_view', $comment, $node, $links);
       
   955       }
       
   956     }
       
   957     else {
       
   958       // Multiple comment view
       
   959       $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid = %d';
       
   960       $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';
       
   961 
       
   962       $query_args = array($nid);
       
   963       if (!user_access('administer comments')) {
       
   964         $query .= ' AND c.status = %d';
       
   965         $query_count .= ' AND c.status = %d';
       
   966         $query_args[] = COMMENT_PUBLISHED;
       
   967       }
       
   968 
       
   969       if ($order == COMMENT_ORDER_NEWEST_FIRST) {
       
   970         if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
       
   971           $query .= ' ORDER BY c.cid DESC';
       
   972         }
       
   973         else {
       
   974           $query .= ' ORDER BY c.thread DESC';
       
   975         }
       
   976       }
       
   977       else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
       
   978         if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
       
   979           $query .= ' ORDER BY c.cid';
       
   980         }
       
   981         else {
       
   982           // See comment above. Analysis reveals that this doesn't cost too
       
   983           // much. It scales much much better than having the whole comment
       
   984           // structure.
       
   985           $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
       
   986         }
       
   987       }
       
   988       $query = db_rewrite_sql($query, 'c', 'cid');
       
   989       $query_count = db_rewrite_sql($query_count, 'c', 'cid');
       
   990 
       
   991       // Start a form, for use with comment control.
       
   992       $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
       
   993 
       
   994       $divs = 0;
       
   995       $num_rows = FALSE;
       
   996       $comments = '';
       
   997       drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
       
   998       while ($comment = db_fetch_object($result)) {
       
   999         $comment = drupal_unpack($comment);
       
  1000         $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
       
  1001         $comment->depth = count(explode('.', $comment->thread)) - 1;
       
  1002 
       
  1003         if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
       
  1004           if ($comment->depth > $divs) {
       
  1005             $divs++;
       
  1006             $comments .= '<div class="indented">';
       
  1007           }
       
  1008           else {
       
  1009             while ($comment->depth < $divs) {
       
  1010               $divs--;
       
  1011               $comments .= '</div>';
       
  1012             }
       
  1013           }
       
  1014         }
       
  1015 
       
  1016         if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
       
  1017           $comments .= theme('comment_flat_collapsed', $comment, $node);
       
  1018         }
       
  1019         else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
       
  1020           $comments .= theme('comment_flat_expanded', $comment, $node);
       
  1021         }
       
  1022         else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
       
  1023           $comments .= theme('comment_thread_collapsed', $comment, $node);
       
  1024         }
       
  1025         else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
       
  1026           $comments .= theme('comment_thread_expanded', $comment, $node);
       
  1027         }
       
  1028 
       
  1029         $num_rows = TRUE;
       
  1030       }
       
  1031       while ($divs-- > 0) {
       
  1032         $comments .= '</div>';
       
  1033       }
       
  1034 
       
  1035       $comment_controls = variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN);
       
  1036       if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
       
  1037         $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
       
  1038       }
       
  1039 
       
  1040       $output .= $comments;
       
  1041       $output .= theme('pager', NULL, $comments_per_page, 0);
       
  1042 
       
  1043       if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
       
  1044         $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
       
  1045       }
       
  1046     }
       
  1047 
       
  1048     // If enabled, show new comment form if it's not already being displayed.
       
  1049     $reply = arg(0) == 'comment' && arg(1) == 'reply';
       
  1050     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) {
       
  1051       $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
       
  1052     }
       
  1053 
       
  1054     if ($output) {
       
  1055       $output = theme('comment_wrapper', $output, $node);
       
  1056     }
       
  1057   }
       
  1058 
       
  1059   return $output;
       
  1060 }
       
  1061 
       
  1062 /**
       
  1063  * Comment operations. We offer different update operations depending on
       
  1064  * which comment administration page we're on.
       
  1065  *
       
  1066  * @param $action
       
  1067  *   The comment administration page.
       
  1068  * @return
       
  1069  *   An associative array containing the offered operations.
       
  1070  */
       
  1071 function comment_operations($action = NULL) {
       
  1072   if ($action == 'publish') {
       
  1073     $operations = array(
       
  1074       'publish' => array(t('Publish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d'),
       
  1075       'delete' => array(t('Delete the selected comments'), '')
       
  1076     );
       
  1077   }
       
  1078   else if ($action == 'unpublish') {
       
  1079     $operations = array(
       
  1080       'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED .' WHERE cid = %d'),
       
  1081       'delete' => array(t('Delete the selected comments'), '')
       
  1082     );
       
  1083   }
       
  1084   else {
       
  1085     $operations = array(
       
  1086       'publish' => array(t('Publish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d'),
       
  1087       'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED .' WHERE cid = %d'),
       
  1088       'delete' => array(t('Delete the selected comments'), '')
       
  1089     );
       
  1090   }
       
  1091   return $operations;
       
  1092 }
       
  1093 
       
  1094 /**
       
  1095  * Misc functions: helpers, privates, history
       
  1096  */
       
  1097 
       
  1098 /**
       
  1099  * Load the entire comment by cid.
       
  1100  *
       
  1101  * @param $cid
       
  1102  *   The identifying comment id.
       
  1103  * @return
       
  1104  *   The comment object.
       
  1105  */
       
  1106 function _comment_load($cid) {
       
  1107   return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $cid));
       
  1108 }
       
  1109 
       
  1110 /**
       
  1111  * Get comment count for a node.
       
  1112  *
       
  1113  * @param $nid
       
  1114  *   The node id.
       
  1115  * @return
       
  1116  *   The comment count.
       
  1117  */
       
  1118 function comment_num_all($nid) {
       
  1119   static $cache;
       
  1120 
       
  1121   if (!isset($cache[$nid])) {
       
  1122     $cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
       
  1123   }
       
  1124   return $cache[$nid];
       
  1125 }
       
  1126 
       
  1127 /**
       
  1128  * Get replies count for a comment.
       
  1129  *
       
  1130  * @param $pid
       
  1131  *   The comment id.
       
  1132  * @return
       
  1133  *   The replies count.
       
  1134  */
       
  1135 function comment_num_replies($pid) {
       
  1136   static $cache;
       
  1137 
       
  1138   if (!isset($cache[$pid])) {
       
  1139     $cache[$pid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND status = %d', $pid, COMMENT_PUBLISHED));
       
  1140   }
       
  1141 
       
  1142   return $cache[$pid];
       
  1143 }
       
  1144 
       
  1145 /**
       
  1146  * Get number of new comments for current user and specified node.
       
  1147  *
       
  1148  * @param $nid
       
  1149  *   node-id to count comments for
       
  1150  * @param $timestamp
       
  1151  *   time to count from (defaults to time of last user access
       
  1152  *   to node)
       
  1153  */
       
  1154 function comment_num_new($nid, $timestamp = 0) {
       
  1155   global $user;
       
  1156 
       
  1157   if ($user->uid) {
       
  1158     // Retrieve the timestamp at which the current user last viewed the
       
  1159     // specified node.
       
  1160     if (!$timestamp) {
       
  1161       $timestamp = node_last_viewed($nid);
       
  1162     }
       
  1163     $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
       
  1164 
       
  1165     // Use the timestamp to retrieve the number of new comments.
       
  1166     $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));
       
  1167 
       
  1168     return $result;
       
  1169   }
       
  1170   else {
       
  1171     return 0;
       
  1172   }
       
  1173 
       
  1174 }
       
  1175 
       
  1176 /**
       
  1177  * Validate comment data.
       
  1178  *
       
  1179  * @param $edit
       
  1180  *   An associative array containig the comment data.
       
  1181  * @return
       
  1182  *   The original $edit.
       
  1183  */
       
  1184 function comment_validate($edit) {
       
  1185   global $user;
       
  1186 
       
  1187   // Invoke other validation handlers
       
  1188   comment_invoke_comment($edit, 'validate');
       
  1189 
       
  1190   if (isset($edit['date'])) {
       
  1191     // As of PHP 5.1.0, strtotime returns FALSE upon failure instead of -1.
       
  1192     if (strtotime($edit['date']) <= 0) {
       
  1193       form_set_error('date', t('You have to specify a valid date.'));
       
  1194     }
       
  1195   }
       
  1196   if (isset($edit['author']) && !$account = user_load(array('name' => $edit['author']))) {
       
  1197     form_set_error('author', t('You have to specify a valid author.'));
       
  1198   }
       
  1199 
       
  1200   // Check validity of name, mail and homepage (if given)
       
  1201   if (!$user->uid || isset($edit['is_anonymous'])) {
       
  1202     $node = node_load($edit['nid']);
       
  1203     if (variable_get('comment_anonymous_'. $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) > COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
       
  1204       if ($edit['name']) {
       
  1205         $taken = db_result(db_query("SELECT COUNT(uid) FROM {users} WHERE LOWER(name) = '%s'", $edit['name']));
       
  1206 
       
  1207         if ($taken != 0) {
       
  1208           form_set_error('name', t('The name you used belongs to a registered user.'));
       
  1209         }
       
  1210 
       
  1211       }
       
  1212       else if (variable_get('comment_anonymous_'. $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
       
  1213         form_set_error('name', t('You have to leave your name.'));
       
  1214       }
       
  1215 
       
  1216       if ($edit['mail']) {
       
  1217         if (!valid_email_address($edit['mail'])) {
       
  1218           form_set_error('mail', t('The e-mail address you specified is not valid.'));
       
  1219         }
       
  1220       }
       
  1221       else if (variable_get('comment_anonymous_'. $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
       
  1222         form_set_error('mail', t('You have to leave an e-mail address.'));
       
  1223       }
       
  1224 
       
  1225       if ($edit['homepage']) {
       
  1226         if (!valid_url($edit['homepage'], TRUE)) {
       
  1227           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>.'));
       
  1228         }
       
  1229       }
       
  1230     }
       
  1231   }
       
  1232 
       
  1233   return $edit;
       
  1234 }
       
  1235 
       
  1236 /**
       
  1237  * Generate the basic commenting form, for appending to a node or display on a separate page.
       
  1238  *
       
  1239  * @param $title
       
  1240  *   Not used.
       
  1241  * @ingroup forms
       
  1242  * @see comment_form_validate()
       
  1243  * @see comment_form_submit()
       
  1244  */
       
  1245 function comment_form(&$form_state, $edit, $title = NULL) {
       
  1246   global $user;
       
  1247 
       
  1248   $op = isset($_POST['op']) ? $_POST['op'] : '';
       
  1249   $node = node_load($edit['nid']);
       
  1250 
       
  1251   if (!$user->uid && variable_get('comment_anonymous_'. $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
       
  1252     drupal_add_js(drupal_get_path('module', 'comment') .'/comment.js');
       
  1253   }
       
  1254   $edit += array('name' => '', 'mail' => '', 'homepage' => '');
       
  1255   if ($user->uid) {
       
  1256     if (!empty($edit['cid']) && user_access('administer comments')) {
       
  1257       if (!empty($edit['author'])) {
       
  1258         $author = $edit['author'];
       
  1259       }
       
  1260       elseif (!empty($edit['name'])) {
       
  1261         $author = $edit['name'];
       
  1262       }
       
  1263       else {
       
  1264         $author = $edit['registered_name'];
       
  1265       }
       
  1266 
       
  1267       if (!empty($edit['status'])) {
       
  1268         $status = $edit['status'];
       
  1269       }
       
  1270       else {
       
  1271         $status = 0;
       
  1272       }
       
  1273 
       
  1274       if (!empty($edit['date'])) {
       
  1275         $date = $edit['date'];
       
  1276       }
       
  1277       else {
       
  1278         $date = format_date($edit['timestamp'], 'custom', 'Y-m-d H:i O');
       
  1279       }
       
  1280 
       
  1281       $form['admin'] = array(
       
  1282         '#type' => 'fieldset',
       
  1283         '#title' => t('Administration'),
       
  1284         '#collapsible' => TRUE,
       
  1285         '#collapsed' => TRUE,
       
  1286         '#weight' => -2,
       
  1287       );
       
  1288 
       
  1289       if ($edit['registered_name'] != '') {
       
  1290         // The comment is by a registered user
       
  1291         $form['admin']['author'] = array(
       
  1292           '#type' => 'textfield',
       
  1293           '#title' => t('Authored by'),
       
  1294           '#size' => 30,
       
  1295           '#maxlength' => 60,
       
  1296           '#autocomplete_path' => 'user/autocomplete',
       
  1297           '#default_value' => $author,
       
  1298           '#weight' => -1,
       
  1299         );
       
  1300       }
       
  1301       else {
       
  1302         // The comment is by an anonymous user
       
  1303         $form['is_anonymous'] = array(
       
  1304           '#type' => 'value',
       
  1305           '#value' => TRUE,
       
  1306         );
       
  1307         $form['admin']['name'] = array(
       
  1308           '#type' => 'textfield',
       
  1309           '#title' => t('Authored by'),
       
  1310           '#size' => 30,
       
  1311           '#maxlength' => 60,
       
  1312           '#default_value' => $author,
       
  1313           '#weight' => -1,
       
  1314         );
       
  1315         $form['admin']['mail'] = array(
       
  1316           '#type' => 'textfield',
       
  1317           '#title' => t('E-mail'),
       
  1318           '#maxlength' => 64,
       
  1319           '#size' => 30,
       
  1320           '#default_value' => $edit['mail'],
       
  1321           '#description' => t('The content of this field is kept private and will not be shown publicly.'),
       
  1322         );
       
  1323 
       
  1324         $form['admin']['homepage'] = array(
       
  1325           '#type' => 'textfield',
       
  1326           '#title' => t('Homepage'),
       
  1327           '#maxlength' => 255,
       
  1328           '#size' => 30,
       
  1329           '#default_value' => $edit['homepage'],
       
  1330         );
       
  1331       }
       
  1332 
       
  1333       $form['admin']['date'] = array('#type' => 'textfield', '#parents' => array('date'), '#title' => t('Authored on'), '#size' => 20, '#maxlength' => 25, '#default_value' => $date, '#weight' => -1);
       
  1334 
       
  1335       $form['admin']['status'] = array('#type' => 'radios', '#parents' => array('status'), '#title' => t('Status'), '#default_value' =>  $status, '#options' => array(t('Published'), t('Not published')), '#weight' => -1);
       
  1336 
       
  1337     }
       
  1338     else {
       
  1339       $form['_author'] = array('#type' => 'item', '#title' => t('Your name'), '#value' => theme('username', $user)
       
  1340       );
       
  1341       $form['author'] = array('#type' => 'value', '#value' => $user->name);
       
  1342     }
       
  1343   }
       
  1344   else if (variable_get('comment_anonymous_'. $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MAY_CONTACT) {
       
  1345     $form['name'] = array('#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 60, '#size' => 30, '#default_value' => $edit['name'] ? $edit['name'] : variable_get('anonymous', t('Anonymous'))
       
  1346     );
       
  1347 
       
  1348     $form['mail'] = array('#type' => 'textfield', '#title' => t('E-mail'), '#maxlength' => 64, '#size' => 30, '#default_value' => $edit['mail'], '#description' => t('The content of this field is kept private and will not be shown publicly.')
       
  1349     );
       
  1350 
       
  1351     $form['homepage'] = array('#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $edit['homepage']);
       
  1352   }
       
  1353   else if (variable_get('comment_anonymous_'. $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
       
  1354     $form['name'] = array('#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 60, '#size' => 30, '#default_value' => $edit['name'] ? $edit['name'] : variable_get('anonymous', t('Anonymous')), '#required' => TRUE);
       
  1355 
       
  1356     $form['mail'] = array('#type' => 'textfield', '#title' => t('E-mail'), '#maxlength' => 64, '#size' => 30, '#default_value' => $edit['mail'], '#description' => t('The content of this field is kept private and will not be shown publicly.'), '#required' => TRUE);
       
  1357 
       
  1358     $form['homepage'] = array('#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $edit['homepage']);
       
  1359   }
       
  1360 
       
  1361   if (variable_get('comment_subject_field_'. $node->type, 1) == 1) {
       
  1362     $form['subject'] = array('#type' => 'textfield', '#title' => t('Subject'), '#maxlength' => 64, '#default_value' => !empty($edit['subject']) ? $edit['subject'] : '');
       
  1363   }
       
  1364 
       
  1365   if (!empty($edit['comment'])) {
       
  1366     $default = $edit['comment'];
       
  1367   }
       
  1368   else {
       
  1369     $default = '';
       
  1370   }
       
  1371 
       
  1372   $form['comment_filter']['comment'] = array(
       
  1373     '#type' => 'textarea',
       
  1374     '#title' => t('Comment'),
       
  1375     '#rows' => 15,
       
  1376     '#default_value' => $default,
       
  1377     '#required' => TRUE,
       
  1378   );
       
  1379   if (!isset($edit['format'])) {
       
  1380     $edit['format'] = FILTER_FORMAT_DEFAULT;
       
  1381   }
       
  1382   $form['comment_filter']['format'] = filter_form($edit['format']);
       
  1383 
       
  1384   $form['cid'] = array('#type' => 'value', '#value' => !empty($edit['cid']) ? $edit['cid'] : NULL);
       
  1385   $form['pid'] = array('#type' => 'value', '#value' => !empty($edit['pid']) ? $edit['pid'] : NULL);
       
  1386   $form['nid'] = array('#type' => 'value', '#value' => $edit['nid']);
       
  1387   $form['uid'] = array('#type' => 'value', '#value' => !empty($edit['uid']) ? $edit['uid'] : NULL);
       
  1388 
       
  1389   // Only show save button if preview is optional or if we are in preview mode.
       
  1390   // We show the save button in preview mode even if there are form errors so that
       
  1391   // optional form elements (e.g., captcha) can be updated in preview mode.
       
  1392   if (!form_get_errors() && ((variable_get('comment_preview_'. $node->type, COMMENT_PREVIEW_REQUIRED) == COMMENT_PREVIEW_OPTIONAL) || ($op == t('Preview')) || ($op == t('Save')))) {
       
  1393     $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 19);
       
  1394   }
       
  1395 
       
  1396   $form['preview'] = array('#type' => 'button', '#value' => t('Preview'), '#weight' => 20);
       
  1397   $form['#token'] = 'comment'. $edit['nid'] . (isset($edit['pid']) ? $edit['pid'] : '');
       
  1398 
       
  1399   if ($op == t('Preview')) {
       
  1400     $form['#after_build'] = array('comment_form_add_preview');
       
  1401   }
       
  1402 
       
  1403   if (empty($edit['cid']) && empty($edit['pid'])) {
       
  1404     $form['#action'] = url('comment/reply/'. $edit['nid']);
       
  1405   }
       
  1406 
       
  1407   return $form;
       
  1408 }
       
  1409 
       
  1410 /**
       
  1411  * Theme the comment form box.
       
  1412  *
       
  1413  * @param $edit
       
  1414  *   The form structure.
       
  1415  * @param $title
       
  1416  *   The form title.
       
  1417  */
       
  1418 function comment_form_box($edit, $title = NULL) {
       
  1419   return theme('box', $title, drupal_get_form('comment_form', $edit, $title));
       
  1420 }
       
  1421 
       
  1422 /**
       
  1423  * Form builder; Generate and validate a comment preview form.
       
  1424  *
       
  1425  * @ingroup forms
       
  1426  */
       
  1427 function comment_form_add_preview($form, &$form_state) {
       
  1428   global $user;
       
  1429   $edit = $form_state['values'];
       
  1430   drupal_set_title(t('Preview comment'));
       
  1431 
       
  1432   $output = '';
       
  1433   $node = node_load($edit['nid']);
       
  1434 
       
  1435   // Invoke full validation for the form, to protect against cross site
       
  1436   // request forgeries (CSRF) and setting arbitrary values for fields such as
       
  1437   // the input format. Preview the comment only when form validation does not
       
  1438   // set any errors.
       
  1439   drupal_validate_form($form['form_id']['#value'], $form, $form_state);
       
  1440   if (!form_get_errors()) {
       
  1441     _comment_form_submit($edit);
       
  1442     $comment = (object)$edit;
       
  1443 
       
  1444     // Attach the user and time information.
       
  1445     if (!empty($edit['author'])) {
       
  1446       $account = user_load(array('name' => $edit['author']));
       
  1447     }
       
  1448     elseif ($user->uid && !isset($edit['is_anonymous'])) {
       
  1449       $account = $user;
       
  1450     }
       
  1451     if (!empty($account)) {
       
  1452       $comment->uid = $account->uid;
       
  1453       $comment->name = check_plain($account->name);
       
  1454     }
       
  1455     elseif (empty($comment->name)) {
       
  1456       $comment->name = variable_get('anonymous', t('Anonymous'));
       
  1457     }
       
  1458     $comment->timestamp = !empty($edit['timestamp']) ? $edit['timestamp'] : time();
       
  1459     $output .= theme('comment_view', $comment, $node);
       
  1460   }
       
  1461   $form['comment_preview'] = array(
       
  1462     '#value' => $output,
       
  1463     '#weight' => -100,
       
  1464     '#prefix' => '<div class="preview">',
       
  1465     '#suffix' => '</div>',
       
  1466   );
       
  1467 
       
  1468   $output = '';
       
  1469 
       
  1470   if ($edit['pid']) {
       
  1471     $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.signature, u.signature_format, 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));
       
  1472     $comment = drupal_unpack($comment);
       
  1473     $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
       
  1474     $output .= theme('comment_view', $comment, $node);
       
  1475   }
       
  1476   else {
       
  1477     $suffix = empty($form['#suffix']) ? '' : $form['#suffix'];
       
  1478     $form['#suffix'] = $suffix . node_view($node);
       
  1479     $edit['pid'] = 0;
       
  1480   }
       
  1481 
       
  1482   $form['comment_preview_below'] = array('#value' => $output, '#weight' => 100);
       
  1483 
       
  1484   return $form;
       
  1485 }
       
  1486 
       
  1487 /**
       
  1488  * Validate comment form submissions.
       
  1489  */
       
  1490 function comment_form_validate($form, &$form_state) {
       
  1491   global $user;
       
  1492   if ($user->uid === 0) {
       
  1493     foreach (array('name', 'homepage', 'mail') as $field) {
       
  1494       // Set cookie for 365 days.
       
  1495       if (isset($form_state['values'][$field])) {
       
  1496         setcookie('comment_info_'. $field, $form_state['values'][$field], time() + 31536000, '/');
       
  1497       }
       
  1498     }
       
  1499   }
       
  1500   comment_validate($form_state['values']);
       
  1501 }
       
  1502 
       
  1503 /**
       
  1504  * Prepare a comment for submission.
       
  1505  *
       
  1506  * @param $comment_values
       
  1507  *   An associative array containing the comment data.
       
  1508  */
       
  1509 function _comment_form_submit(&$comment_values) {
       
  1510   $comment_values += array('subject' => '');
       
  1511   if (!isset($comment_values['date'])) {
       
  1512     $comment_values['date'] = 'now';
       
  1513   }
       
  1514   $comment_values['timestamp'] = strtotime($comment_values['date']);
       
  1515   if (isset($comment_values['author'])) {
       
  1516     $account = user_load(array('name' => $comment_values['author']));
       
  1517     $comment_values['uid'] = $account->uid;
       
  1518     $comment_values['name'] = $comment_values['author'];
       
  1519   }
       
  1520   // Validate the comment's subject. If not specified, extract
       
  1521   // one from the comment's body.
       
  1522   if (trim($comment_values['subject']) == '') {
       
  1523     // The body may be in any format, so we:
       
  1524     // 1) Filter it into HTML
       
  1525     // 2) Strip out all HTML tags
       
  1526     // 3) Convert entities back to plain-text.
       
  1527     // Note: format is checked by check_markup().
       
  1528     $comment_values['subject'] = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment_values['comment'], $comment_values['format'])))), 29, TRUE);
       
  1529     // Edge cases where the comment body is populated only by HTML tags will
       
  1530     // require a default subject.
       
  1531     if ($comment_values['subject'] == '') {
       
  1532       $comment_values['subject'] = t('(No subject)');
       
  1533     }
       
  1534   }
       
  1535 }
       
  1536 
       
  1537 /**
       
  1538  * Process comment form submissions; prepare the comment, store it, and set a redirection target.
       
  1539  */
       
  1540 function comment_form_submit($form, &$form_state) {
       
  1541   _comment_form_submit($form_state['values']);
       
  1542   if ($cid = comment_save($form_state['values'])) {
       
  1543     $node = node_load($form_state['values']['nid']);
       
  1544     // Add 1 to existing $node->comment count to include new comment being added.
       
  1545     $comment_count = $node->comment_count + 1;
       
  1546     $page = comment_new_page_count($comment_count, 1, $node);
       
  1547     $form_state['redirect'] = array('node/'. $node->nid, $page, "comment-$cid");
       
  1548     return;
       
  1549   }
       
  1550 }
       
  1551 
       
  1552 /**
       
  1553  * Theme a single comment block.
       
  1554  *
       
  1555  * @param $comment
       
  1556  *   The comment object.
       
  1557  * @param $node
       
  1558  *   The comment node.
       
  1559  * @param $links
       
  1560  *   An associative array containing control links.
       
  1561  * @param $visible
       
  1562  *   Switches between folded/unfolded view.
       
  1563  * @ingroup themeable
       
  1564  */
       
  1565 function theme_comment_view($comment, $node, $links = array(), $visible = TRUE) {
       
  1566   static $first_new = TRUE;
       
  1567 
       
  1568   $output = '';
       
  1569   $comment->new = node_mark($comment->nid, $comment->timestamp);
       
  1570   if ($first_new && $comment->new != MARK_READ) {
       
  1571     // Assign the anchor only for the first new comment. This avoids duplicate
       
  1572     // id attributes on a page.
       
  1573     $first_new = FALSE;
       
  1574     $output .= "<a id=\"new\"></a>\n";
       
  1575   }
       
  1576 
       
  1577   $output .= "<a id=\"comment-$comment->cid\"></a>\n";
       
  1578 
       
  1579   // Switch to folded/unfolded view of the comment
       
  1580   if ($visible) {
       
  1581     $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
       
  1582 
       
  1583     // Comment API hook
       
  1584     comment_invoke_comment($comment, 'view');
       
  1585 
       
  1586     $output .= theme('comment', $comment, $node, $links);
       
  1587   }
       
  1588   else {
       
  1589     $output .= theme('comment_folded', $comment);
       
  1590   }
       
  1591 
       
  1592   return $output;
       
  1593 }
       
  1594 
       
  1595 
       
  1596 /**
       
  1597  * Build a comment control form.
       
  1598  *
       
  1599  * @param $mode
       
  1600  *   Comment display mode.
       
  1601  * @param $order
       
  1602  *   Comment order mode.
       
  1603  * @param $comments_per_page
       
  1604  *   Comments per page.
       
  1605  * @ingroup forms
       
  1606  */
       
  1607 function comment_controls(&$form_state, $mode = COMMENT_MODE_THREADED_EXPANDED, $order = COMMENT_ORDER_NEWEST_FIRST, $comments_per_page = 50) {
       
  1608   $form['mode'] = array('#type' => 'select',
       
  1609     '#default_value' => $mode,
       
  1610     '#options' => _comment_get_modes(),
       
  1611     '#weight' => 1,
       
  1612   );
       
  1613   $form['order'] = array(
       
  1614     '#type' => 'select',
       
  1615     '#default_value' => $order,
       
  1616     '#options' => _comment_get_orders(),
       
  1617     '#weight' => 2,
       
  1618   );
       
  1619   foreach (_comment_per_page() as $i) {
       
  1620     $options[$i] = t('!a comments per page', array('!a' => $i));
       
  1621   }
       
  1622   $form['comments_per_page'] = array('#type' => 'select',
       
  1623     '#default_value' => $comments_per_page,
       
  1624     '#options' => $options,
       
  1625     '#weight' => 3,
       
  1626   );
       
  1627 
       
  1628   $form['submit'] = array('#type' => 'submit',
       
  1629     '#value' => t('Save settings'),
       
  1630     '#weight' => 20,
       
  1631   );
       
  1632 
       
  1633   return $form;
       
  1634 }
       
  1635 
       
  1636 /**
       
  1637  * Theme comment controls box where the user can change the default display mode and display order of comments.
       
  1638  *
       
  1639  * @param $form
       
  1640  *   The form structure.
       
  1641  * @ingroup themeable
       
  1642  */
       
  1643 function theme_comment_controls($form) {
       
  1644   $output = '<div class="container-inline">';
       
  1645   $output .=  drupal_render($form);
       
  1646   $output .= '</div>';
       
  1647   $output .= '<div class="description">'. t('Select your preferred way to display the comments and click "Save settings" to activate your changes.') .'</div>';
       
  1648   return theme('box', t('Comment viewing options'), $output);
       
  1649 }
       
  1650 
       
  1651 /**
       
  1652  * Process comment_controls form submissions.
       
  1653  */
       
  1654 function comment_controls_submit($form, &$form_state) {
       
  1655   global $user;
       
  1656 
       
  1657   $mode = $form_state['values']['mode'];
       
  1658   $order = $form_state['values']['order'];
       
  1659   $comments_per_page = $form_state['values']['comments_per_page'];
       
  1660 
       
  1661   if ($user->uid) {
       
  1662     $account = user_save($user, array('mode' => $mode, 'sort' => $order, 'comments_per_page' => $comments_per_page));
       
  1663     // Terminate if an error occured during user_save().
       
  1664     if (!$account) {
       
  1665       drupal_set_message(t("Error saving user account."), 'error');
       
  1666       return;
       
  1667     }
       
  1668     $user = $account;
       
  1669   }
       
  1670   else {
       
  1671     $_SESSION['comment_mode'] = $mode;
       
  1672     $_SESSION['comment_sort'] = $order;
       
  1673     $_SESSION['comment_comments_per_page'] = $comments_per_page;
       
  1674   }
       
  1675 }
       
  1676 
       
  1677 /**
       
  1678  * Process variables for comment.tpl.php.
       
  1679  *
       
  1680  * @see comment.tpl.php
       
  1681  * @see theme_comment()
       
  1682  */
       
  1683 function template_preprocess_comment(&$variables) {
       
  1684   $comment = $variables['comment'];
       
  1685   $node = $variables['node'];
       
  1686   $variables['author']    = theme('username', $comment);
       
  1687   $variables['content']   = $comment->comment;
       
  1688   $variables['date']      = format_date($comment->timestamp);
       
  1689   $variables['links']     = isset($variables['links']) ? theme('links', $variables['links']) : '';
       
  1690   $variables['new']       = $comment->new ? t('new') : '';
       
  1691   $variables['picture']   = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', $comment) : '';
       
  1692   $variables['signature'] = $comment->signature;
       
  1693   $variables['submitted'] = theme('comment_submitted', $comment);
       
  1694   $variables['title']     = l($comment->subject, $_GET['q'], array('fragment' => "comment-$comment->cid"));
       
  1695   $variables['template_files'][] = 'comment-'. $node->type;
       
  1696   // set status to a string representation of comment->status.
       
  1697   if (isset($comment->preview)) {
       
  1698     $variables['status']  = 'comment-preview';
       
  1699   }
       
  1700   else {
       
  1701     $variables['status']  = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'comment-unpublished' : 'comment-published';
       
  1702   }
       
  1703 }
       
  1704 
       
  1705 /**
       
  1706  * Process variables for comment-folded.tpl.php.
       
  1707  *
       
  1708  * @see comment-folded.tpl.php
       
  1709  * @see theme_comment_folded()
       
  1710  */
       
  1711 function template_preprocess_comment_folded(&$variables) {
       
  1712   $comment = $variables['comment'];
       
  1713   $variables['author'] = theme('username', $comment);
       
  1714   $variables['date']   = format_date($comment->timestamp);
       
  1715   $variables['new']    = $comment->new ? t('new') : '';
       
  1716   $variables['title']  = l($comment->subject, comment_node_url() .'/'. $comment->cid, array('fragment' => "comment-$comment->cid"));
       
  1717 }
       
  1718 
       
  1719 /**
       
  1720  * Theme comment flat collapsed view.
       
  1721  *
       
  1722  * @param $comment
       
  1723  *   The comment to be themed.
       
  1724  * @param $node
       
  1725  *   The comment node.
       
  1726  * @ingroup themeable
       
  1727  */
       
  1728 function theme_comment_flat_collapsed($comment, $node) {
       
  1729   return theme('comment_view', $comment, $node, '', 0);
       
  1730 }
       
  1731 
       
  1732 /**
       
  1733  * Theme comment flat expanded view.
       
  1734  *
       
  1735  * @param $comment
       
  1736  *   The comment to be themed.
       
  1737  * @param $node
       
  1738  *   The comment node.
       
  1739  * @ingroup themeable
       
  1740  */
       
  1741 function theme_comment_flat_expanded($comment, $node) {
       
  1742   return theme('comment_view', $comment, $node, module_invoke_all('link', 'comment', $comment, 0));
       
  1743 }
       
  1744 
       
  1745 /**
       
  1746  * Theme comment thread collapsed view.
       
  1747  *
       
  1748  * @param $comment
       
  1749  *   The comment to be themed.
       
  1750  * @param $node
       
  1751  *   The comment node.
       
  1752  * @ingroup themeable
       
  1753  */
       
  1754 function theme_comment_thread_collapsed($comment, $node) {
       
  1755   return theme('comment_view', $comment, $node, '', 0);
       
  1756 }
       
  1757 
       
  1758 /**
       
  1759  * Theme comment thread expanded view.
       
  1760  *
       
  1761  * @param $comment
       
  1762  *   The comment to be themed.
       
  1763  * @param $node
       
  1764  *   The comment node.
       
  1765  * @ingroup themeable
       
  1766  */
       
  1767 function theme_comment_thread_expanded($comment, $node) {
       
  1768   return theme('comment_view', $comment, $node, module_invoke_all('link', 'comment', $comment, 0));
       
  1769 }
       
  1770 
       
  1771 /**
       
  1772  * Theme a "you can't post comments" notice.
       
  1773  *
       
  1774  * @param $node
       
  1775  *   The comment node.
       
  1776  * @ingroup themeable
       
  1777  */
       
  1778 function theme_comment_post_forbidden($node) {
       
  1779   global $user;
       
  1780   static $authenticated_post_comments;
       
  1781 
       
  1782   if (!$user->uid) {
       
  1783     if (!isset($authenticated_post_comments)) {
       
  1784       // We only output any link if we are certain, that users get permission
       
  1785       // to post comments by logging in. We also locally cache this information.
       
  1786       $authenticated_post_comments = array_key_exists(DRUPAL_AUTHENTICATED_RID, user_roles(TRUE, 'post comments') + user_roles(TRUE, 'post comments without approval'));
       
  1787     }
       
  1788 
       
  1789     if ($authenticated_post_comments) {
       
  1790       // We cannot use drupal_get_destination() because these links
       
  1791       // sometimes appear on /node and taxonomy listing pages.
       
  1792       if (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
       
  1793         $destination = 'destination='. drupal_urlencode("comment/reply/$node->nid#comment-form");
       
  1794       }
       
  1795       else {
       
  1796         $destination = 'destination='. drupal_urlencode("node/$node->nid#comment-form");
       
  1797       }
       
  1798 
       
  1799       if (variable_get('user_register', 1)) {
       
  1800         // Users can register themselves.
       
  1801         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))));
       
  1802       }
       
  1803       else {
       
  1804         // Only admins can add new users, no public registration.
       
  1805         return t('<a href="@login">Login</a> to post comments', array('@login' => url('user/login', array('query' => $destination))));
       
  1806       }
       
  1807     }
       
  1808   }
       
  1809 }
       
  1810 
       
  1811 /**
       
  1812  * Process variables for comment-wrapper.tpl.php.
       
  1813  *
       
  1814  * @see comment-wrapper.tpl.php
       
  1815  * @see theme_comment_wrapper()
       
  1816  */
       
  1817 function template_preprocess_comment_wrapper(&$variables) {
       
  1818   // Provide contextual information.
       
  1819   $variables['display_mode']  = _comment_get_display_setting('mode', $variables['node']);
       
  1820   $variables['display_order'] = _comment_get_display_setting('sort', $variables['node']);
       
  1821   $variables['comment_controls_state'] = variable_get('comment_controls_'. $variables['node']->type, COMMENT_CONTROLS_HIDDEN);
       
  1822   $variables['template_files'][] = 'comment-wrapper-'. $variables['node']->type;
       
  1823 }
       
  1824 
       
  1825 /**
       
  1826  * Theme a "Submitted by ..." notice.
       
  1827  *
       
  1828  * @param $comment
       
  1829  *   The comment.
       
  1830  * @ingroup themeable
       
  1831  */
       
  1832 function theme_comment_submitted($comment) {
       
  1833   return t('Submitted by !username on @datetime.',
       
  1834     array(
       
  1835       '!username' => theme('username', $comment),
       
  1836       '@datetime' => format_date($comment->timestamp)
       
  1837     ));
       
  1838 }
       
  1839 
       
  1840 /**
       
  1841  * Return an array of viewing modes for comment listings.
       
  1842  *
       
  1843  * We can't use a global variable array because the locale system
       
  1844  * is not initialized yet when the comment module is loaded.
       
  1845  */
       
  1846 function _comment_get_modes() {
       
  1847   return array(
       
  1848     COMMENT_MODE_FLAT_COLLAPSED => t('Flat list - collapsed'),
       
  1849     COMMENT_MODE_FLAT_EXPANDED => t('Flat list - expanded'),
       
  1850     COMMENT_MODE_THREADED_COLLAPSED => t('Threaded list - collapsed'),
       
  1851     COMMENT_MODE_THREADED_EXPANDED => t('Threaded list - expanded')
       
  1852   );
       
  1853 }
       
  1854 
       
  1855 /**
       
  1856  * Return an array of viewing orders for comment listings.
       
  1857  *
       
  1858  * We can't use a global variable array because the locale system
       
  1859  * is not initialized yet when the comment module is loaded.
       
  1860  */
       
  1861 function _comment_get_orders() {
       
  1862   return array(
       
  1863     COMMENT_ORDER_NEWEST_FIRST => t('Date - newest first'),
       
  1864     COMMENT_ORDER_OLDEST_FIRST => t('Date - oldest first')
       
  1865   );
       
  1866 }
       
  1867 
       
  1868 /**
       
  1869  * Return an array of "comments per page" settings from which the user
       
  1870  * can choose.
       
  1871  */
       
  1872 function _comment_per_page() {
       
  1873   return drupal_map_assoc(array(10, 30, 50, 70, 90, 150, 200, 250, 300));
       
  1874 }
       
  1875 
       
  1876 /**
       
  1877  * Return a current comment display setting
       
  1878  *
       
  1879  * @param $setting
       
  1880  *   can be one of these: 'mode', 'sort', 'comments_per_page'
       
  1881  * @param $node
       
  1882  *   The comment node in question.
       
  1883  */
       
  1884 function _comment_get_display_setting($setting, $node) {
       
  1885   global $user;
       
  1886 
       
  1887   if (isset($_GET[$setting])) {
       
  1888     $value = $_GET[$setting];
       
  1889   }
       
  1890   else {
       
  1891     // get the setting's site default
       
  1892     switch ($setting) {
       
  1893       case 'mode':
       
  1894         $default = variable_get('comment_default_mode_'. $node->type, COMMENT_MODE_THREADED_EXPANDED);
       
  1895         break;
       
  1896       case 'sort':
       
  1897         $default = variable_get('comment_default_order_'. $node->type, COMMENT_ORDER_NEWEST_FIRST);
       
  1898         break;
       
  1899       case 'comments_per_page':
       
  1900         $default = variable_get('comment_default_per_page_'. $node->type, 50);
       
  1901     }
       
  1902     if (variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_HIDDEN) {
       
  1903       // if comment controls are disabled use site default
       
  1904       $value = $default;
       
  1905     }
       
  1906     else {
       
  1907       // otherwise use the user's setting if set
       
  1908       if (isset($user->$setting) && $user->$setting) {
       
  1909         $value = $user->$setting;
       
  1910       }
       
  1911       else if (isset($_SESSION['comment_'. $setting]) && $_SESSION['comment_'. $setting]) {
       
  1912         $value = $_SESSION['comment_'. $setting];
       
  1913       }
       
  1914       else {
       
  1915         $value = $default;
       
  1916       }
       
  1917     }
       
  1918   }
       
  1919   return $value;
       
  1920 }
       
  1921 
       
  1922 /**
       
  1923  * Updates the comment statistics for a given node. This should be called any
       
  1924  * time a comment is added, deleted, or updated.
       
  1925  *
       
  1926  * The following fields are contained in the node_comment_statistics table.
       
  1927  * - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
       
  1928  * - last_comment_name: the name of the anonymous poster for the last comment
       
  1929  * - 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.
       
  1930  * - comment_count: the total number of approved/published comments on this node.
       
  1931  */
       
  1932 function _comment_update_node_statistics($nid) {
       
  1933   $count = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = %d', $nid, COMMENT_PUBLISHED));
       
  1934 
       
  1935   // comments exist
       
  1936   if ($count > 0) {
       
  1937     $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));
       
  1938     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);
       
  1939   }
       
  1940 
       
  1941   // no comments
       
  1942   else {
       
  1943     $node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE nid = %d", $nid));
       
  1944     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);
       
  1945   }
       
  1946 }
       
  1947 
       
  1948 /**
       
  1949  * Invoke a hook_comment() operation in all modules.
       
  1950  *
       
  1951  * @param &$comment
       
  1952  *   A comment object.
       
  1953  * @param $op
       
  1954  *   A string containing the name of the comment operation.
       
  1955  * @return
       
  1956  *   The returned value of the invoked hooks.
       
  1957  */
       
  1958 function comment_invoke_comment(&$comment, $op) {
       
  1959   $return = array();
       
  1960   foreach (module_implements('comment') as $name) {
       
  1961     $function = $name .'_comment';
       
  1962     $result = $function($comment, $op);
       
  1963     if (isset($result) && is_array($result)) {
       
  1964       $return = array_merge($return, $result);
       
  1965     }
       
  1966     else if (isset($result)) {
       
  1967       $return[] = $result;
       
  1968     }
       
  1969   }
       
  1970   return $return;
       
  1971 }
       
  1972 
       
  1973 /**
       
  1974  * Generate vancode.
       
  1975  *
       
  1976  * Consists of a leading character indicating length, followed by N digits
       
  1977  * with a numerical value in base 36. Vancodes can be sorted as strings
       
  1978  * without messing up numerical order.
       
  1979  *
       
  1980  * It goes:
       
  1981  * 00, 01, 02, ..., 0y, 0z,
       
  1982  * 110, 111, ... , 1zy, 1zz,
       
  1983  * 2100, 2101, ..., 2zzy, 2zzz,
       
  1984  * 31000, 31001, ...
       
  1985  */
       
  1986 function int2vancode($i = 0) {
       
  1987   $num = base_convert((int)$i, 10, 36);
       
  1988   $length = strlen($num);
       
  1989   return chr($length + ord('0') - 1) . $num;
       
  1990 }
       
  1991 
       
  1992 /**
       
  1993  * Decode vancode back to an integer.
       
  1994  */
       
  1995 function vancode2int($c = '00') {
       
  1996   return base_convert(substr($c, 1), 36, 10);
       
  1997 }
       
  1998 
       
  1999 /**
       
  2000  * Implementation of hook_hook_info().
       
  2001  */
       
  2002 function comment_hook_info() {
       
  2003   return array(
       
  2004     'comment' => array(
       
  2005       'comment' => array(
       
  2006         'insert' => array(
       
  2007           'runs when' => t('After saving a new comment'),
       
  2008         ),
       
  2009         'update' => array(
       
  2010           'runs when' => t('After saving an updated comment'),
       
  2011         ),
       
  2012         'delete' => array(
       
  2013           'runs when' => t('After deleting a comment')
       
  2014         ),
       
  2015         'view' => array(
       
  2016           'runs when' => t('When a comment is being viewed by an authenticated user')
       
  2017         ),
       
  2018       ),
       
  2019     ),
       
  2020   );
       
  2021 }
       
  2022 
       
  2023 /**
       
  2024  * Implementation of hook_action_info().
       
  2025  */
       
  2026 function comment_action_info() {
       
  2027   return array(
       
  2028     'comment_unpublish_action' => array(
       
  2029       'description' => t('Unpublish comment'),
       
  2030       'type' => 'comment',
       
  2031       'configurable' => FALSE,
       
  2032       'hooks' => array(
       
  2033         'comment' => array('insert', 'update'),
       
  2034       )
       
  2035     ),
       
  2036     'comment_unpublish_by_keyword_action' => array(
       
  2037       'description' => t('Unpublish comment containing keyword(s)'),
       
  2038       'type' => 'comment',
       
  2039       'configurable' => TRUE,
       
  2040       'hooks' => array(
       
  2041         'comment' => array('insert', 'update'),
       
  2042       )
       
  2043     )
       
  2044   );
       
  2045 }
       
  2046 
       
  2047 /**
       
  2048  * Drupal action to unpublish a comment.
       
  2049  *
       
  2050  * @param $context
       
  2051  *   Keyed array. Must contain the id of the comment if $comment is not passed.
       
  2052  * @param $comment
       
  2053  *   An optional comment object.
       
  2054  */
       
  2055 function comment_unpublish_action($comment, $context = array()) {
       
  2056   if (isset($comment->cid)) {
       
  2057     $cid = $comment->cid;
       
  2058     $subject = $comment->subject;
       
  2059   }
       
  2060   else {
       
  2061     $cid = $context['cid'];
       
  2062     $subject = db_result(db_query("SELECT subject FROM {comments} WHERE cid = %d", $cid));
       
  2063   }
       
  2064   db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $cid);
       
  2065   watchdog('action', 'Unpublished comment %subject.', array('%subject' => $subject));
       
  2066 }
       
  2067 
       
  2068 /**
       
  2069  * Form builder; Prepare a form for blacklisted keywords.
       
  2070  *
       
  2071  * @ingroup forms
       
  2072  */
       
  2073 function comment_unpublish_by_keyword_action_form($context) {
       
  2074   $form['keywords'] = array(
       
  2075     '#title' => t('Keywords'),
       
  2076     '#type' => 'textarea',
       
  2077     '#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.'),
       
  2078     '#default_value' => isset($context['keywords']) ? drupal_implode_tags($context['keywords']) : '',
       
  2079   );
       
  2080   return $form;
       
  2081 }
       
  2082 
       
  2083 /**
       
  2084  * Process comment_unpublish_by_keyword_action_form form submissions.
       
  2085  */
       
  2086 function comment_unpublish_by_keyword_action_submit($form, $form_state) {
       
  2087   return array('keywords' => drupal_explode_tags($form_state['values']['keywords']));
       
  2088 }
       
  2089 
       
  2090 /**
       
  2091  * Implementation of a configurable Drupal action.
       
  2092  * Unpublish a comment if it contains a certain string.
       
  2093  *
       
  2094  * @param $context
       
  2095  *   An array providing more information about the context of the call to this action.
       
  2096  *   Unused here since this action currently only supports the insert and update ops of
       
  2097  *   the comment hook, both of which provide a complete $comment object.
       
  2098  * @param $comment
       
  2099  *   A comment object.
       
  2100  */
       
  2101 function comment_unpublish_by_keyword_action($comment, $context) {
       
  2102   foreach ($context['keywords'] as $keyword) {
       
  2103     if (strstr($comment->comment, $keyword) || strstr($comment->subject, $keyword)) {
       
  2104       db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $comment->cid);
       
  2105       watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
       
  2106       break;
       
  2107     }
       
  2108   }
       
  2109 }