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