[ Index ]
 

Code source de Drupal 5.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/modules/comment/ -> comment.module (source)

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


Généré le : Fri Nov 30 16:20:15 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics