/[drupal]/contributions/modules/comment_og/comment.module.patched
ViewVC logotype

Contents of /contributions/modules/comment_og/comment.module.patched

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download)
Thu Apr 3 07:26:00 2008 UTC (19 months, 3 weeks ago) by rapsli
Branch: MAIN
CVS Tags: DRUPAL-5--0-3beta2, DRUPAL-5--0-3beta1, DRUPAL-5--0-2beta, DRUPAL-5--0-2beta1, DRUPAL-5--0-1beta, DRUPAL-5--0-1beta2
some minor bugs.
1 <?php
2 // $Id: comment.module,v 1.1 2008/03/01 14:39:25 rapsli 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 n.nid FROM {node_comment_statistics} n WHERE n.comment_count > 0 ORDER BY n.last_comment_timestamp DESC"), 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->created, $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 $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'], $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 ($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 else
807 {
808 comment_invoke_comment($edit, 'publish');
809 }
810 return $edit['cid'];
811 }
812 else {
813 return FALSE;
814 }
815 }
816 else {
817 $txt = t('Comment: unauthorized comment submitted or comment submitted to a closed node %subject.', array('%subject' => $edit['subject']));
818 watchdog('content', $txt, WATCHDOG_WARNING);
819 drupal_set_message($txt, 'error');
820 return FALSE;
821 }
822 }
823
824 function comment_links($comment, $return = 1) {
825 global $user;
826
827 $links = array();
828
829 // If we are viewing just this comment, we link back to the node.
830 if ($return) {
831 $links['comment_parent'] = array(
832 'title' => t('parent'),
833 'href' => comment_node_url(),
834 'fragment' => "comment-$comment->cid"
835 );
836 }
837
838 if (node_comment_mode($comment->nid) == COMMENT_NODE_READ_WRITE) {
839 if (user_access('administer comments') && user_access('post comments')) {
840 $links['comment_delete'] = array(
841 'title' => t('delete'),
842 'href' => "comment/delete/$comment->cid"
843 );
844 $links['comment_edit'] = array(
845 'title' => t('edit'),
846 'href' => "comment/edit/$comment->cid"
847 );
848 $links['comment_reply'] = array(
849 'title' => t('reply'),
850 'href' => "comment/reply/$comment->nid/$comment->cid"
851 );
852 }
853 else if (user_access('post comments')) {
854 if (comment_access('edit', $comment)) {
855 $links['comment_edit'] = array(
856 'title' => t('edit'),
857 'href' => "comment/edit/$comment->cid"
858 );
859 }
860 $links['comment_reply'] = array(
861 'title' => t('reply'),
862 'href' => "comment/reply/$comment->nid/$comment->cid"
863 );
864 }
865 else {
866 $links['comment_forbidden']['title'] = theme('comment_post_forbidden', $comment->nid);
867 }
868 }
869
870 return $links;
871 }
872
873 /**
874 * Renders comment(s).
875 *
876 * @param $node
877 * The node which comment(s) needs rendering.
878 * @param $cid
879 * Optional, if given, only one comment is rendered.
880 *
881 * To display threaded comments in the correct order we keep a 'thread' field
882 * and order by that value. This field keeps this data in
883 * a way which is easy to update and convenient to use.
884 *
885 * A "thread" value starts at "1". If we add a child (A) to this comment,
886 * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
887 * brother of (A) will get "1.2". Next brother of the parent of (A) will get
888 * "2" and so on.
889 *
890 * First of all note that the thread field stores the depth of the comment:
891 * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
892 *
893 * Now to get the ordering right, consider this example:
894 *
895 * 1
896 * 1.1
897 * 1.1.1
898 * 1.2
899 * 2
900 *
901 * If we "ORDER BY thread ASC" we get the above result, and this is the
902 * natural order sorted by time. However, if we "ORDER BY thread DESC"
903 * we get:
904 *
905 * 2
906 * 1.2
907 * 1.1.1
908 * 1.1
909 * 1
910 *
911 * Clearly, this is not a natural way to see a thread, and users will get
912 * confused. The natural order to show a thread by time desc would be:
913 *
914 * 2
915 * 1
916 * 1.2
917 * 1.1
918 * 1.1.1
919 *
920 * which is what we already did before the standard pager patch. To achieve
921 * this we simply add a "/" at the end of each "thread" value. This way out
922 * thread fields will look like depicted below:
923 *
924 * 1/
925 * 1.1/
926 * 1.1.1/
927 * 1.2/
928 * 2/
929 *
930 * we add "/" since this char is, in ASCII, higher than every number, so if
931 * now we "ORDER BY thread DESC" we get the correct order. However this would
932 * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
933 * to consider the trailing "/" so we use a substring only.
934 */
935 function comment_render($node, $cid = 0) {
936 global $user;
937
938 $output = '';
939
940 if (user_access('access comments')) {
941 // Pre-process variables.
942 $nid = $node->nid;
943 if (empty($nid)) {
944 $nid = 0;
945 }
946
947 $mode = _comment_get_display_setting('mode');
948 $order = _comment_get_display_setting('sort');
949 $comments_per_page = _comment_get_display_setting('comments_per_page');
950
951 if ($cid) {
952 // Single comment view.
953 $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';
954 $query_args = array($cid);
955 if (!user_access('administer comments')) {
956 $query .= ' AND c.status = %d';
957 $query_args[] = COMMENT_PUBLISHED;
958 }
959
960 $result = db_query($query, $query_args);
961
962 if ($comment = db_fetch_object($result)) {
963 $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
964 $links = module_invoke_all('link', 'comment', $comment, 1);
965
966 foreach (module_implements('link_alter') as $module) {
967 $function = $module .'_link_alter';
968 $function($node, $links);
969 }
970
971 $output .= theme('comment_view', $comment, $links);
972 }
973 }
974 else {
975 // Multiple comment view
976 $query_count = 'SELECT COUNT(*) FROM {comments} WHERE nid = %d';
977 $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';
978
979 $query_args = array($nid);
980 if (!user_access('administer comments')) {
981 $query .= ' AND c.status = %d';
982 $query_count .= ' AND status = %d';
983 $query_args[] = COMMENT_PUBLISHED;
984 }
985
986 if ($order == COMMENT_ORDER_NEWEST_FIRST) {
987 if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
988 $query .= ' ORDER BY c.timestamp DESC';
989 }
990 else {
991 $query .= ' ORDER BY c.thread DESC';
992 }
993 }
994 else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
995 if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
996 $query .= ' ORDER BY c.timestamp';
997 }
998 else {
999
1000 /*
1001 ** See comment above. Analysis learns that this doesn't cost
1002 ** too much. It scales much much better than having the whole
1003 ** comment structure.
1004 */
1005
1006 $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
1007 }
1008 }
1009
1010 // Start a form, for use with comment control.
1011 $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
1012 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)) {
1013 $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
1014 }
1015
1016 $divs = 0;
1017 $last_depth = 0;
1018 drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
1019 while ($comment = db_fetch_object($result)) {
1020 $comment = drupal_unpack($comment);
1021 $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
1022 $comment->depth = count(explode('.', $comment->thread)) - 1;
1023
1024 if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
1025 if ($comment->depth > $last_depth) {
1026 $divs++;
1027 $output .= '<div class="indented">';
1028 $last_depth++;
1029 }
1030 else {
1031 while ($comment->depth < $last_depth) {
1032 $divs--;
1033 $output .= '</div>';
1034 $last_depth--;
1035 }
1036 }
1037 }
1038
1039 if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
1040 $output .= theme('comment_flat_collapsed', $comment);
1041 }
1042 else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
1043 $output .= theme('comment_flat_expanded', $comment);
1044 }
1045 else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
1046 $output .= theme('comment_thread_collapsed', $comment);
1047 }
1048 else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
1049 $output .= theme('comment_thread_expanded', $comment);
1050 }
1051 }
1052 for ($i = 0; $i < $divs; $i++) {
1053 $output .= '</div>';
1054 }
1055 $output .= theme('pager', NULL, $comments_per_page, 0);
1056
1057 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)) {
1058 $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
1059 }
1060 }
1061
1062 // If enabled, show new comment form.
1063 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)) {
1064 $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
1065 }
1066
1067 $output = theme('comment_wrapper', $output);
1068 }
1069
1070 return $output;
1071 }
1072
1073 /**
1074 * Menu callback; delete a comment.
1075 */
1076 function comment_delete($cid = NULL) {
1077 $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));
1078 $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
1079
1080 $output = '';
1081
1082 // We'll only delete if the user has confirmed the
1083 // deletion using the form in our else clause below.
1084 if (is_object($comment) && is_numeric($comment->cid) && $_POST['confirm']) {
1085 drupal_set_message(t('The comment and all its replies have been deleted.'));
1086
1087 // Delete comment and its replies.
1088 _comment_delete_thread($comment);
1089
1090 _comment_update_node_statistics($comment->nid);
1091
1092 // Clear the cache so an anonymous user sees that his comment was deleted.
1093 cache_clear_all();
1094
1095 drupal_goto("node/$comment->nid");
1096 }
1097 else if (is_object($comment) && is_numeric($comment->cid)) {
1098 $output = drupal_get_form('comment_confirm_delete', $comment->subject, $comment->nid);
1099 }
1100 else {
1101 drupal_set_message(t('The comment no longer exists.'));
1102 }
1103
1104 return $output;
1105 }
1106
1107 function comment_confirm_delete($subject, $nid) {
1108 return confirm_form(
1109 array(),
1110 t('Are you sure you want to delete the comment %title?', array('%title' => $subject)),
1111 'node/'. $nid,
1112 t('Any replies to this comment will be lost. This action cannot be undone.'),
1113 t('Delete'),
1114 t('Cancel'));
1115 }
1116
1117 /**
1118 * Comment operations. We offer different update operations depending on
1119 * which comment administration page we're on.
1120 */
1121 function comment_operations($action = NULL) {
1122 if ($action == 'publish') {
1123 $operations = array(
1124 'publish' => array(t('Publish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d'),
1125 'delete' => array(t('Delete the selected comments'), '')
1126 );
1127 }
1128 else if ($action == 'unpublish') {
1129 $operations = array(
1130 'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED .' WHERE cid = %d'),
1131 'delete' => array(t('Delete the selected comments'), '')
1132 );
1133 }
1134 else {
1135 $operations = array(
1136 'publish' => array(t('Publish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d'),
1137 'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED .' WHERE cid = %d'),
1138 'delete' => array(t('Delete the selected comments'), '')
1139 );
1140 }
1141 return $operations;
1142 }
1143
1144 /**
1145 * Menu callback; present an administrative comment listing.
1146 */
1147 function comment_admin($type = 'new') {
1148 $edit = $_POST;
1149
1150 if ($edit['operation'] == 'delete' && $edit['comments']) {
1151 return drupal_get_form('comment_multiple_delete_confirm');
1152 }
1153 else {
1154 return drupal_get_form('comment_admin_overview', $type, arg(4));
1155 }
1156 }
1157
1158 function comment_admin_overview($type = 'new', $arg) {
1159 // build an 'Update options' form
1160 $form['options'] = array(
1161 '#type' => 'fieldset', '#title' => t('Update options'),
1162 '#prefix' => '<div class="container-inline">', '#suffix' => '</div>'
1163 );
1164 $options = array();
1165 foreach (comment_operations($arg == 'approval' ? 'publish' : 'unpublish') as $key => $value) {
1166 $options[$key] = $value[0];
1167 }
1168 $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'publish');
1169 $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update'));
1170
1171 // load the comments that we want to display
1172 $status = ($type == 'approval') ? COMMENT_NOT_PUBLISHED : COMMENT_PUBLISHED;
1173 $form['header'] = array('#type' => 'value', '#value' => array(
1174 theme('table_select_header_cell'),
1175 array('data' => t('Subject'), 'field' => 'subject'),
1176 array('data' => t('Author'), 'field' => 'name'),
1177 array('data' => t('Time'), 'field' => 'timestamp', 'sort' => 'desc'),
1178 array('data' => t('Operations'))
1179 ));
1180 $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);
1181
1182 // build a table listing the appropriate comments
1183 $destination = drupal_get_destination();
1184 while ($comment = db_fetch_object($result)) {
1185 $comments[$comment->cid] = '';
1186 $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
1187 $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('title' => truncate_utf8($comment->comment, 128)), NULL, 'comment-'. $comment->cid));
1188 $form['username'][$comment->cid] = array('#value' => theme('username', $comment));
1189 $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small'));
1190 $form['operations'][$comment->cid] = array('#value' => l(t('edit'), 'comment/edit/'. $comment->cid, array(), $destination));
1191 }
1192 $form['comments'] = array('#type' => 'checkboxes', '#options' => $comments);
1193 $form['pager'] = array('#value' => theme('pager', NULL, 50, 0));
1194 return $form;
1195 }
1196
1197 /**
1198 * We can't execute any 'Update options' if no comments were selected.
1199 */
1200 function comment_admin_overview_validate($form_id, $form_values) {
1201 $form_values['comments'] = array_diff($form_values['comments'], array(0));
1202 if (count($form_values['comments']) == 0) {
1203 form_set_error('', t('Please select one or more comments to perform the update on.'));
1204 drupal_goto('admin/content/comment');
1205 }
1206 }
1207
1208 /**
1209 * Execute the chosen 'Update option' on the selected comments, such as
1210 * publishing, unpublishing or deleting.
1211 */
1212 function comment_admin_overview_submit($form_id, $form_values) {
1213 $operations = comment_operations();
1214 if ($operations[$form_values['operation']][1]) {
1215 // extract the appropriate database query operation
1216 $query = $operations[$form_values['operation']][1];
1217 foreach ($form_values['comments'] as $cid => $value) {
1218 if ($value) {
1219 // perform the update action, then refresh node statistics
1220 db_query($query, $cid);
1221 $comment = _comment_load($cid);
1222 _comment_update_node_statistics($comment->nid);
1223 // Allow modules to respond to the updating of a comment.
1224 comment_invoke_comment($comment, $form_values['operation']);
1225 // Add an entry to the watchdog log.
1226 watchdog('content', t('Comment: updated %subject.', array('%subject' => $comment->subject)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $comment->nid, NULL, NULL, 'comment-'. $comment->cid));
1227 }
1228 }
1229 cache_clear_all();
1230 drupal_set_message(t('The update has been performed.'));
1231 drupal_goto('admin/content/comment');
1232 }
1233 }
1234
1235 function theme_comment_admin_overview($form) {
1236 $output = drupal_render($form['options']);
1237 if (isset($form['subject']) && is_array($form['subject'])) {
1238 foreach (element_children($form['subject']) as $key) {
1239 $row = array();
1240 $row[] = drupal_render($form['comments'][$key]);
1241 $row[] = drupal_render($form['subject'][$key]);
1242 $row[] = drupal_render($form['username'][$key]);
1243 $row[] = drupal_render($form['timestamp'][$key]);
1244 $row[] = drupal_render($form['operations'][$key]);
1245 $rows[] = $row;
1246 }
1247 }
1248 else {
1249 $rows[] = array(array('data' => t('No comments available.'), 'colspan' => '6'));
1250 }
1251
1252 $output .= theme('table', $form['header']['#value'], $rows);
1253 if ($form['pager']['#value']) {
1254 $output .= drupal_render($form['pager']);
1255 }
1256
1257 $output .= drupal_render($form);
1258
1259 return $output;
1260 }
1261
1262 /**
1263 * List the selected comments and verify that the admin really wants to delete
1264 * them.
1265 */
1266 function comment_multiple_delete_confirm() {
1267 $edit = $_POST;
1268
1269 $form['comments'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
1270 // array_filter() returns only elements with actual values
1271 $comment_counter = 0;
1272 foreach (array_filter($edit['comments']) as $cid => $value) {
1273 $comment = _comment_load($cid);
1274 if (is_object($comment) && is_numeric($comment->cid)) {
1275 $subject = db_result(db_query('SELECT subject FROM {comments} WHERE cid = %d', $cid));
1276 $form['comments'][$cid] = array('#type' => 'hidden', '#value' => $cid, '#prefix' => '<li>', '#suffix' => check_plain($subject) .'</li>');
1277 $comment_counter++;
1278 }
1279 }
1280 $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
1281
1282 if (!$comment_counter) {
1283 drupal_set_message(t('There do not appear to be any comments to delete or your selected comment was deleted by another administrator.'));
1284 drupal_goto('admin/content/comment');
1285 }
1286 else {
1287 return confirm_form($form,
1288 t('Are you sure you want to delete these comments and all their children?'),
1289 'admin/content/comment', t('This action cannot be undone.'),
1290 t('Delete comments'), t('Cancel'));
1291 }
1292 }
1293
1294 /**
1295 * Perform the actual comment deletion.
1296 */
1297 function comment_multiple_delete_confirm_submit($form_id, $form_values) {
1298 if ($form_values['confirm']) {
1299 foreach ($form_values['comments'] as $cid => $value) {
1300 $comment = _comment_load($cid);
1301 _comment_delete_thread($comment);
1302 _comment_update_node_statistics($comment->nid);
1303 }
1304 cache_clear_all();
1305 drupal_set_message(t('The comments have been deleted.'));
1306 }
1307 drupal_goto('admin/content/comment');
1308 }
1309
1310 /**
1311 *** misc functions: helpers, privates, history
1312 **/
1313
1314 /**
1315 * Load the entire comment by cid.
1316 */
1317 function _comment_load($cid) {
1318 return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $cid));
1319 }
1320
1321 function comment_num_all($nid) {
1322 static $cache;
1323
1324 if (!isset($cache[$nid])) {
1325 $cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
1326 }
1327 return $cache[$nid];
1328 }
1329
1330 function comment_num_replies($pid) {
1331 static $cache;
1332
1333 if (!isset($cache[$pid])) {
1334 $cache[$pid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND status = %d', $pid, COMMENT_PUBLISHED));
1335 }
1336
1337 return $cache[$pid];
1338 }
1339
1340 /**
1341 * get number of new comments for current user and specified node
1342 *
1343 * @param $nid node-id to count comments for
1344 * @param $timestamp time to count from (defaults to time of last user access
1345 * to node)
1346 */
1347 function comment_num_new($nid, $timestamp = 0) {
1348 global $user;
1349
1350 if ($user->uid) {
1351 // Retrieve the timestamp at which the current user last viewed the
1352 // specified node.
1353 if (!$timestamp) {
1354 $timestamp = node_last_viewed($nid);
1355 }
1356 $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
1357
1358 // Use the timestamp to retrieve the number of new comments.
1359 $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));
1360
1361 return $result;
1362 }
1363 else {
1364 return 0;
1365 }
1366
1367 }
1368
1369 function comment_validate($edit) {
1370 global $user;
1371
1372 // Invoke other validation handlers
1373 comment_invoke_comment($edit, 'validate');
1374
1375 if (isset($edit['date'])) {
1376 // As of PHP 5.1.0, strtotime returns FALSE upon failure instead of -1.
1377 if (strtotime($edit['date']) <= 0) {
1378 form_set_error('date', t('You have to specify a valid date.'));
1379 }
1380 }
1381 if (isset($edit['author']) && !$account = user_load(array('name' => $edit['author']))) {
1382 form_set_error('author', t('You have to specify a valid author.'));
1383 }
1384
1385 // Check validity of name, mail and homepage (if given)
1386 if (!$user->uid || isset($edit['is_anonymous'])) {
1387 if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) > COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
1388 if ($edit['name']) {
1389 $taken = db_result(db_query("SELECT COUNT(uid) FROM {users} WHERE LOWER(name) = '%s'", $edit['name']), 0);
1390
1391 if ($taken != 0) {
1392 form_set_error('name', t('The name you used belongs to a registered user.'));
1393 }
1394
1395 }
1396 else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
1397 form_set_error('name', t('You have to leave your name.'));
1398 }
1399
1400 if ($edit['mail']) {
1401 if (!valid_email_address($edit['mail'])) {
1402 form_set_error('mail', t('The e-mail address you specified is not valid.'));
1403 }
1404 }
1405 else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
1406 form_set_error('mail', t('You have to leave an e-mail address.'));
1407 }
1408
1409 if ($edit['homepage']) {
1410 if (!valid_url($edit['homepage'], TRUE)) {
1411 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>.'));
1412 }
1413 }
1414 }
1415 }
1416
1417 return $edit;
1418 }
1419
1420 /*
1421 ** Generate the basic commenting form, for appending to a node or display on a separate page.
1422 ** This is rendered by theme_comment_form.
1423 */
1424
1425 function comment_form($edit, $title = NULL) {
1426 global $user;
1427
1428 $op = isset($_POST['op']) ? $_POST['op'] : '';
1429
1430 if ($user->uid) {
1431 if ($edit['cid'] && user_access('administer comments')) {
1432 if ($edit['author']) {
1433 $author = $edit['author'];
1434 }
1435 elseif ($edit['name']) {
1436 $author = $edit['name'];
1437 }
1438 else {
1439 $author = $edit['registered_name'];
1440 }
1441
1442 if ($edit['status']) {
1443 $status = $edit['status'];
1444 }
1445 else {
1446 $status = 0;
1447 }
1448
1449 if ($edit['date']) {
1450 $date = $edit['date'];
1451 }
1452 else {
1453 $date = format_date($edit['timestamp'], 'custom', 'Y-m-d H:i O');
1454 }
1455
1456 $form['admin'] = array(
1457 '#type' => 'fieldset',
1458 '#title' => t('Administration'),
1459 '#collapsible' => TRUE,
1460 '#collapsed' => TRUE,
1461 '#weight' => -2,
1462 );
1463
1464 if ($edit['registered_name'] != '') {
1465 // The comment is by a registered user
1466 $form['admin']['author'] = array(
1467 '#type' => 'textfield',
1468 '#title' => t('Authored by'),
1469 '#size' => 30,
1470 '#maxlength' => 60,
1471 '#autocomplete_path' => 'user/autocomplete',
1472 '#default_value' => $author,
1473 '#weight' => -1,
1474 );
1475 }
1476 else {
1477 // The comment is by an anonymous user
1478 $form['is_anonymous'] = array(
1479 '#type' => 'value',
1480 '#value' => TRUE,
1481 );
1482 $form['admin']['name'] = array(
1483 '#type' => 'textfield',
1484 '#title' => t('Authored by'),
1485 '#size' => 30,
1486 '#maxlength' => 60,
1487 '#default_value' => $author,
1488 '#weight' => -1,
1489 );
1490 $form['admin']['mail'] = array(
1491 '#type' => 'textfield',
1492 '#title' => t('E-mail'),
1493 '#maxlength' => 64,
1494 '#size' => 30,
1495 '#default_value' => $edit['mail'],
1496 '#description' => t('The content of this field is kept private and will not be shown publicly.'),
1497 );
1498
1499 $form['admin']['homepage'] = array(
1500 '#type' => 'textfield',
1501 '#title' => t('Homepage'),
1502 '#maxlength' => 255,
1503 '#size' => 30,
1504 '#default_value' => $edit['homepage'],
1505 );
1506 }
1507
1508 $form['admin']['date'] = array('#type' => 'textfield', '#parents' => array('date'), '#title' => t('Authored on'), '#size' => 20, '#maxlength' => 25, '#default_value' => $date, '#weight' => -1);
1509
1510 $form['admin']['status'] = array('#type' => 'radios', '#parents' => array('status'), '#title' => t('Status'), '#default_value' => $status, '#options' => array(t('Published'), t('Not published')), '#weight' => -1);
1511
1512 }
1513 else {
1514 $form['_author'] = array('#type' => 'item', '#title' => t('Your name'), '#value' => theme('username', $user)
1515 );
1516 $form['author'] = array('#type' => 'value', '#value' => $user->name);
1517 }
1518 }
1519 else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MAY_CONTACT) {
1520 $form['name'] = array('#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 60, '#size' => 30, '#default_value' => $edit['name'] ? $edit['name'] : variable_get('anonymous', t('Anonymous'))
1521 );
1522
1523 $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.')
1524 );
1525
1526 $form['homepage'] = array('#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $edit['homepage']);
1527 }
1528 else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
1529 $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);
1530
1531 $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);
1532
1533 $form['homepage'] = array('#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $edit['homepage']);
1534 }
1535
1536 if (variable_get('comment_subject_field', 1) == 1) {
1537 $form['subject'] = array('#type' => 'textfield', '#title' => t('Subject'), '#maxlength' => 64, '#default_value' => $edit['subject']);
1538 }
1539
1540 $form['comment_filter']['comment'] = array('#type' => 'textarea', '#title' => t('Comment'), '#rows' => 15, '#default_value' => $edit['comment'] ? $edit['comment'] : $user->signature, '#required' => TRUE);
1541 if (!isset($edit['format'])) {
1542 $edit['format'] = FILTER_FORMAT_DEFAULT;
1543 }
1544 $form['comment_filter']['format'] = filter_form($edit['format']);
1545
1546 $form['cid'] = array('#type' => 'value', '#value' => $edit['cid']);
1547 $form['pid'] = array('#type' => 'value', '#value' => $edit['pid']);
1548 $form['nid'] = array('#type' => 'value', '#value' => $edit['nid']);
1549 $form['uid'] = array('#type' => 'value', '#value' => $edit['uid']);
1550
1551 $form['preview'] = array('#type' => 'button', '#value' => t('Preview comment'), '#weight' => 19);
1552 $form['#token'] = 'comment'. $edit['nid'] . $edit['pid'];
1553
1554 // Only show post button if preview is optional or if we are in preview mode.
1555 // We show the post button in preview mode even if there are form errors so that
1556 // optional form elements (e.g., captcha) can be updated in preview mode.
1557 if (!form_get_errors() && ((variable_get('comment_preview', COMMENT_PREVIEW_REQUIRED) == COMMENT_PREVIEW_OPTIONAL) || ($op == t('Preview comment')) || ($op == t('Post comment')))) {
1558 $form['submit'] = array('#type' => 'submit', '#value' => t('Post comment'), '#weight' => 20);
1559 }
1560
1561 if ($op == t('Preview comment')) {
1562 $form['#after_build'] = array('comment_form_add_preview');
1563 }
1564
1565 if ($_REQUEST['destination']) {
1566 $form['#attributes']['destination'] = $_REQUEST['destination'];
1567 }
1568
1569 if (empty($edit['cid']) && empty($edit['pid'])) {
1570 $form['#action'] = url('comment/reply/'. $edit['nid']);
1571 }
1572
1573 // Graft in extra form additions
1574 $form = array_merge($form, comment_invoke_comment($form, 'form'));
1575 return $form;
1576 }
1577
1578 function comment_form_box($edit, $title = NULL) {
1579 return theme('box', $title, drupal_get_form('comment_form', $edit, $title));
1580 }
1581
1582 function comment_form_add_preview($form, $edit) {
1583 global $user;
1584
1585 drupal_set_title(t('Preview comment'));
1586
1587 $output = '';
1588
1589 // Invoke full validation for the form, to protect against cross site
1590 // request forgeries (CSRF) and setting arbitrary values for fields such as
1591 // the input format. Preview the comment only when form validation does not
1592 // set any errors.
1593 drupal_validate_form($form['form_id']['#value'], $form);
1594 if (!form_get_errors()) {
1595 $comment = (object)_comment_form_submit($edit);
1596
1597 // Attach the user and time information.
1598 if ($edit['author']) {
1599 $account = user_load(array('name' => $edit['author']));
1600 }
1601 elseif ($user->uid && !isset($edit['is_anonymous'])) {
1602 $account = $user;
1603 }
1604 if ($account) {
1605 $comment->uid = $account->uid;
1606 $comment->name = check_plain($account->name);
1607 }
1608 $comment->timestamp = $edit['timestamp'] ? $edit['timestamp'] : time();
1609 $output .= theme('comment_view', $comment);
1610 }
1611 $form['comment_preview'] = array(
1612 '#value' => $output,
1613 '#weight' => -100,
1614 '#prefix' => '<div class="preview">',
1615 '#suffix' => '</div>',
1616 );
1617
1618 $output = '';
1619
1620 if ($edit['pid']) {
1621 $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));
1622 $comment = drupal_unpack($comment);
1623 $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
1624 $output .= theme('comment_view', $comment);
1625 }
1626 else {
1627 $form['#suffix'] = node_view(node_load($edit['nid']));
1628 $edit['pid'] = 0;
1629 }
1630
1631 $form['comment_preview_below'] = array('#value' => $output, '#weight' => 100);
1632
1633 return $form;
1634 }
1635
1636 function comment_form_validate($form_id, $form_values) {
1637 comment_validate($form_values);
1638 }
1639
1640 function _comment_form_submit($form_values) {
1641 if (!isset($form_values['date'])) {
1642 $form_values['date'] = 'now';
1643 }
1644 $form_values['timestamp'] = strtotime($form_values['date']);
1645 if (isset($form_values['author'])) {
1646 $account = user_load(array('name' => $form_values['author']));
1647 $form_values['uid'] = $account->uid;
1648 $form_values['name'] = $form_values['author'];
1649 }
1650 // Validate the comment's subject. If not specified, extract
1651 // one from the comment's body.
1652 if (trim($form_values['subject']) == '') {
1653 // The body may be in any format, so we:
1654 // 1) Filter it into HTML
1655 // 2) Strip out all HTML tags
1656 // 3) Convert entities back to plain-text.
1657 // Note: format is checked by check_markup().
1658 $form_values['subject'] = trim(truncate_utf8(decode_entities(strip_tags(check_markup($form_values['comment'], $form_values['format']))), 29, TRUE));
1659 // Edge cases where the comment body is populated only by HTML tags will
1660 // require a default subject.
1661 if ($form_values['subject'] == '') {
1662 $form_values['subject'] = t('(No subject)');
1663 }
1664 }
1665
1666 return $form_values;
1667 }
1668
1669 function comment_form_submit($form_id, $form_values) {
1670 $form_values = _comment_form_submit($form_values);
1671 if ($cid = comment_save($form_values)) {
1672 return array('node/'. $form_values['nid'], NULL, "comment-$cid");
1673 }
1674 }
1675
1676 /*
1677 ** Renderer or visualization functions this can be optionally
1678 ** overridden by themes.
1679 */
1680
1681 function theme_comment_preview($comment, $links = array(), $visible = 1) {
1682 $output = '<div class="preview">';
1683 $output .= theme('comment_view', $comment, $links, $visible);
1684 $output .= '</div>';
1685 return $output;
1686 };
1687
1688 function theme_comment_view($comment, $links = array(), $visible = 1) {
1689 static $first_new = TRUE;
1690
1691 $output = '';
1692 $comment->new = node_mark($comment->nid, $comment->timestamp);
1693 if ($first_new && $comment->new != MARK_READ) {
1694 // Assign the anchor only for the first new comment. This avoids duplicate
1695 // id attributes on a page.
1696 $first_new = FALSE;
1697 $output .= "<a id=\"new\"></a>\n";
1698 }
1699
1700 $output .= "<a id=\"comment-$comment->cid\"></a>\n";
1701
1702 // Switch to folded/unfolded view of the comment
1703 if ($visible) {
1704 $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
1705
1706 // Comment API hook
1707 comment_invoke_comment($comment, 'view');
1708
1709 $output .= theme('comment', $comment, $links);
1710 }
1711 else {
1712 $output .= theme('comment_folded', $comment);
1713 }
1714
1715 return $output;
1716 }
1717
1718 function comment_controls($mode = COMMENT_MODE_THREADED_EXPANDED, $order = COMMENT_ORDER_NEWEST_FIRST, $comments_per_page = 50) {
1719 $form['mode'] = array('#type' => 'select',
1720 '#default_value' => $mode,
1721 '#options' => _comment_get_modes(),
1722 '#weight' => 1,
1723 );
1724 $form['order'] = array(
1725 '#type' => 'select',
1726 '#default_value' => $order,
1727 '#options' => _comment_get_orders(),
1728 '#weight' => 2,
1729 );
1730 foreach (_comment_per_page() as $i) {
1731 $options[$i] = t('!a comments per page', array('!a' => $i));
1732 }
1733 $form['comments_per_page'] = array('#type' => 'select',
1734 '#default_value' => $comments_per_page,
1735 '#options' => $options,
1736 '#weight' => 3,
1737 );
1738
1739 $form['submit'] = array('#type' => 'submit',
1740 '#value' => t('Save settings'),
1741 '#weight' => 20,
1742 );
1743
1744 return $form;
1745 }
1746
1747 function theme_comment_controls($form) {
1748 $output .= '<div class="container-inline">';
1749 $output .= drupal_render($form);
1750 $output .= '</div>';
1751 $output .= '<div class="description">'. t('Select your preferred way to display the comments and click "Save settings" to activate your changes.') .'</div>';
1752 return theme('box', t('Comment viewing options'), $output);
1753 }
1754
1755 function comment_controls_submit($form_id, $form_values) {
1756 global $user;
1757
1758 $mode = $form_values['mode'];
1759 $order = $form_values['order'];
1760 $comments_per_page = $form_values['comments_per_page'];
1761
1762 if ($user->uid) {
1763 $user = user_save($user, array('mode' => $mode, 'sort' => $order, 'comments_per_page' => $comments_per_page));
1764 }
1765 else {
1766 $_SESSION['comment_mode'] = $mode;
1767 $_SESSION['comment_sort'] = $order;
1768 $_SESSION['comment_comments_per_page'] = $comments_per_page;
1769 }
1770 }
1771
1772 function theme_comment($comment, $links = array()) {
1773 $output = '<div class="comment'. ($comment->status == COMMENT_NOT_PUBLISHED ? ' comment-unpublished' : '') .'">';
1774 $output .= '<div class="subject">'. l($comment->subject, $_GET['q'], NULL, NULL, "comment-$comment->cid") .' '. theme('mark', $comment->new) ."</div>\n";
1775 $output .= '<div class="credit">'. t('by %a on %b', array('%a' => theme('username', $comment), '%b' => format_date($comment->timestamp))) ."</div>\n";
1776 $output .= '<div class="body">'. $comment->comment .'</div>';
1777 $output .= '<div class="links">'. theme('links', $links) .'</div>';
1778 $output .= '</div>';
1779 return $output;
1780 }
1781
1782 function theme_comment_folded($comment) {
1783 $output = "<div class=\"comment-folded\">\n";
1784 $output .= ' <span class="subject">'. l($comment->subject, comment_node_url() .'/'. $comment->cid, NULL, NULL, "comment-$comment->cid") .' '. theme('mark', $comment->new) .'</span> ';
1785 $output .= '<span class="credit">'. t('by') .' '. theme('username', $comment) ."</span>\n";
1786 $output .= "</div>\n";
1787 return $output;
1788 }
1789
1790 function theme_comment_flat_collapsed($comment) {
1791 return theme('comment_view', $comment, '', 0);
1792 }
1793
1794 function theme_comment_flat_expanded($comment) {
1795 return theme('comment_view', $comment, module_invoke_all('link', 'comment', $comment, 0));
1796 }
1797
1798 function theme_comment_thread_collapsed($comment) {
1799 $output .= theme('comment_view', $comment, '', 0);
1800 return $output;
1801 }
1802
1803 function theme_comment_thread_expanded($comment) {
1804 $output = '';
1805 $output .= theme('comment_view', $comment, module_invoke_all('link', 'comment', $comment, 0));
1806 return $output;
1807 }
1808
1809 function theme_comment_post_forbidden($nid) {
1810 global $user;
1811 if ($user->uid) {
1812 return t("you can't post comments");
1813 }
1814 else {
1815 // we cannot use drupal_get_destination() because these links sometimes appear on /node and taxo listing pages
1816 if (variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
1817 $destination = "destination=". drupal_urlencode("comment/reply/$nid#comment_form");
1818 }
1819 else {
1820 $destination = "destination=". drupal_urlencode("node/$nid#comment_form");
1821 }
1822
1823 if (variable_get('user_register', 1)) {
1824 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)));
1825 }
1826 else {
1827 return t('<a href="@login">Login</a> to post comments', array('@login' => url('user/login', $destination)));
1828 }
1829 }
1830 }
1831
1832 /**
1833 * Allow themable wrapping of all comments.
1834 */
1835 function theme_comment_wrapper($content) {
1836 return '<div id="comments">'. $content .'</div>';
1837 }
1838
1839 function _comment_delete_thread($comment) {
1840 if (!is_object($comment) || !is_numeric($comment->cid)) {
1841 watchdog('content', t('Can not delete non-existent comment.'), WATCHDOG_WARNING);
1842 return;
1843 }
1844
1845 // Delete the comment:
1846 db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
1847 watchdog('content', t('Comment: deleted %subject.', array('%subject' => $comment->subject)));
1848
1849 comment_invoke_comment($comment, 'delete');
1850
1851 // Delete the comment's replies
1852 $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);
1853 while ($comment = db_fetch_object($result)) {
1854 $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
1855 _comment_delete_thread($comment);
1856 }
1857 }
1858
1859 /**
1860 * Return an array of viewing modes for comment listings.
1861 *
1862 * We can't use a global variable array because the locale system
1863 * is not initialized yet when the comment module is loaded.
1864 */
1865 function _comment_get_modes() {
1866 return array(
1867 COMMENT_MODE_FLAT_COLLAPSED => t('Flat list - collapsed'),
1868 COMMENT_MODE_FLAT_EXPANDED => t('Flat list - expanded'),
1869 COMMENT_MODE_THREADED_COLLAPSED => t('Threaded list - collapsed'),
1870 COMMENT_MODE_THREADED_EXPANDED => t('Threaded list - expanded')
1871 );
1872 }
1873
1874 /**
1875 * Return an array of viewing orders for comment listings.
1876 *
1877 * We can't use a global variable array because the locale system
1878 * is not initialized yet when the comment module is loaded.
1879 */
1880 function _comment_get_orders() {
1881 return array(
1882 COMMENT_ORDER_NEWEST_FIRST => t('Date - newest first'),
1883 COMMENT_ORDER_OLDEST_FIRST => t('Date - oldest first')
1884 );
1885 }
1886
1887 /**
1888 * Return an array of "comments per page" settings from which the user
1889 * can choose.
1890 */
1891 function _comment_per_page() {
1892 return drupal_map_assoc(array(10, 30, 50, 70, 90, 150, 200, 250, 300));
1893 }
1894
1895 /**
1896 * Return a current comment display setting
1897 *
1898 * $setting can be one of these: 'mode', 'sort', 'comments_per_page'
1899 */
1900 function _comment_get_display_setting($setting) {
1901 global $user;
1902
1903 if (isset($_GET[$setting])) {
1904 $value = $_GET[$setting];
1905 }
1906 else {
1907 // get the setting's site default
1908 switch ($setting) {
1909 case 'mode':
1910 $default = variable_get('comment_default_mode', COMMENT_MODE_THREADED_EXPANDED);
1911 break;
1912 case 'sort':
1913 $default = variable_get('comment_default_order', COMMENT_ORDER_NEWEST_FIRST);
1914 break;
1915 case 'comments_per_page':
1916 $default = variable_get('comment_default_per_page', '50');
1917 }
1918 if (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_HIDDEN) {
1919 // if comment controls are disabled use site default
1920 $value = $default;
1921 }
1922 else {
1923 // otherwise use the user's setting if set
1924 if ($user->$setting) {
1925 $value = $user->$setting;
1926 }
1927 else if ($_SESSION['comment_'. $setting]) {
1928 $value = $_SESSION['comment_'. $setting];
1929 }
1930 else {
1931 $value = $default;
1932 }
1933 }
1934 }
1935 return $value;
1936 }
1937
1938 /**
1939 * Updates the comment statistics for a given node. This should be called any
1940 * time a comment is added, deleted, or updated.
1941 *
1942 * The following fields are contained in the node_comment_statistics table.
1943 * - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
1944 * - last_comment_name: the name of the anonymous poster for the last comment
1945 * - 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.
1946 * - comment_count: the total number of approved/published comments on this node.
1947 */
1948 function _comment_update_node_statistics($nid) {
1949 $count = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = %d', $nid, COMMENT_PUBLISHED));
1950
1951 // comments exist
1952 if ($count > 0) {
1953 $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));
1954 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);
1955 }
1956
1957 // no comments
1958 else {
1959 $node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE nid = %d", $nid));
1960 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);
1961 }
1962 }
1963
1964 /**
1965 * Invoke a hook_comment() operation in all modules.
1966 *
1967 * @param &$comment
1968 * A comment object.
1969 * @param $op
1970 * A string containing the name of the comment operation.
1971 * @return
1972 * The returned value of the invoked hooks.
1973 */
1974 function comment_invoke_comment(&$comment, $op) {
1975 $return = array();
1976 foreach (module_implements('comment') as $name) {
1977 $function = $name .'_comment';
1978 $result = $function($comment, $op);
1979 if (isset($result) && is_array($result)) {
1980 $return = array_merge($return, $result);
1981 }
1982 else if (isset($result)) {
1983 $return[] = $result;
1984 }
1985 }
1986 return $return;
1987 }
1988
1989 /**
1990 * Generate vancode.
1991 *
1992 * Consists of a leading character indicating length, followed by N digits
1993 * with a numerical value in base 36. Vancodes can be sorted as strings
1994 * without messing up numerical order.
1995 *
1996 * It goes:
1997 * 00, 01, 02, ..., 0y, 0z,
1998 * 110, 111, ... , 1zy, 1zz,
1999 * 2100, 2101, ..., 2zzy, 2zzz,
2000 * 31000, 31001, ...
2001 */
2002 function int2vancode($i = 0) {
2003 $num = base_convert((int)$i, 10, 36);
2004 $length = strlen($num);
2005 return chr($length + ord('0') - 1) . $num;
2006 }
2007
2008 /**
2009 * Decode vancode back to an integer.
2010 */
2011 function vancode2int($c = '00') {
2012 return base_convert(substr($c, 1), 36, 10);
2013 }
2014

  ViewVC Help
Powered by ViewVC 1.1.2