/[drupal]/contributions/modules/hidden/hidden.module
ViewVC logotype

Contents of /contributions/modules/hidden/hidden.module

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


Revision 1.8 - (show annotations) (download) (as text)
Thu Dec 18 15:29:07 2008 UTC (11 months, 1 week ago) by ekes
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.7: +340 -164 lines
File MIME type: text/x-php
#276732 Continuing D6 migration
  admin pages forms upgraded to D6
  tests written for admin pages
  filters section reworked and using drupal_write_record
1 <?php
2 // $Id: hidden.module,v 1.7 2008/12/13 18:42:43 ekes Exp $
3
4 /**
5 * @file
6 * Module to create, track and display hidden content.
7 */
8
9 // @todo descriptions of these
10 define('HIDDEN_LOG_DELETE', 1);
11 define('HIDDEN_LOG_HIDE', 2);
12 define('HIDDEN_LOG_UNHIDE', 3);
13 define('HIDDEN_LOG_REPORTED', 4);
14 define('HIDDEN_LOG_SEEN', 5);
15 define('HIDDEN_LOG_REASONS', 6);
16 define('HIDDEN_LOG_SPAM', 7);
17 define('HIDDEN_LOG_ERROR', 51);
18 define('HIDDEN_LOG_DEBUG_FILTER', 52);
19 define('HIDDEN_LOG_DEBUG', 53);
20
21 define('HIDDEN_MAIL_NEVER', 0);
22 define('HIDDEN_MAIL_NOW', 1);
23 define('HIDDEN_MAIL_LATER', 2);
24
25 define('HIDDEN_FILTER_PLAIN', 1);
26 define('HIDDEN_FILTER_PREG', 2);
27
28 define('HIDDEN_PAGER_LIMIT', 20);
29
30 /********************************************************************
31 * Standard hooks
32 */
33
34 /**
35 * Implementation of hook_boot().
36 */
37 function hidden_boot() {
38 if (module_exists('views')) {
39 // @todo not yet D6
40 //include_once drupal_get_path('module', 'hidden') .'/hidden.views.inc';
41 }
42 }
43
44 /**
45 * Implementation of hook_init().
46 */
47 function hidden_init() {
48 drupal_add_css(drupal_get_path('module', 'hidden') .'/hidden.css', 'module', 'all', FALSE);
49 }
50
51 /**
52 * Implementation of hook_help().
53 */
54 function hidden_help($path, $arg) {
55 $output = '';
56
57 switch ($path) {
58 case 'admin/content/comment/list/hidden':
59 $output = '<p>'. t('The following comments have been hidden.') .'</p>';
60 break;
61 case 'admin/content/comment/list/hiddenreported':
62 $output = '<p>'. t('The following comments have been reported for hiding.') .'</p>';
63 case 'admin/content/node/list/hidden':
64 $output = '<p>'. t('The following nodes have been hidden.') .'</p>';
65 break;
66 case 'admin/content/node/list/hiddenreported':
67 $output = '<p>'. t('The following nodes have been reported for hiding.') .'</p>';
68 break;
69 case 'admin/content/hidden':
70 case 'admin/content/hidden/reasons':
71 $output = '<p>'. t('The default reasons offered for hiding an item. Disabled reasons do not show in the list.') .'</p>';
72 break;
73 case 'admin/content/hidden/reasons/edit':
74 $output = '<p>'. t('Changing the text of a reason will change it for all previously hidden items as well.') .'</p>';
75 break;
76 case 'admin/content/hidden/filters':
77 $output = '<p>'. t('Custom filters to automate hiding of posts. If you set a time delay they will be hidden after that time when cron is run.') .'</p>';
78 break;
79 case 'admin/settings/hidden':
80 $output = '<p>'. t('How much detail to log and send in any e-mails. Hide reports can be sent immediately or saved and sent together in one e-mail periodically.') .'</p>';
81 break;
82 case 'admin/help#hidden':
83 $output = '<p>'. t('Hidden enables you to un-/hide nodes and comments.') ."</p>\n".
84 '<p>'. t("Every node or comment has a link called 'hide' (or 'unhide' if hidden).") ."</p>\n".
85 '<p>'. t("To 'hide':") ."<br />\n".
86 '- '. t('Click the link.') ."<br />\n".
87 '- '. t('On the following page you have to supply a reason why the item will be hidden before you submit the form.') ."</p>\n".
88 '<p>'. t('Hidden items should not appear in any listings for non-admin users except under hidden/.') ."</p>\n".
89 '<p>'. t("To 'unhide':") ."<br />\n".
90 '- '. t("Click the link after the contents' title/subject or click on the contents' title/subject.") ."<br />\n".
91 '- '. t("Then click 'unhide'.") ."</p>\n";
92 break;
93 }
94
95 return $output;
96 }
97
98 /**
99 * Implementation of hook_perm().
100 */
101 function hidden_perm() {
102 return array('access hidden', 'report for hiding', 'mark as hidden', 'administer hidden', 'bypass hidden filter');
103 }
104
105 /**
106 * Implementation of hook_theme().
107 */
108 function hidden_theme() {
109 return array(
110 'hidden_admin_settings' => array(
111 'arguments' => array(
112 'form' => NULL,
113 ),
114 ),
115 'hidden_link' => array(
116 'arguments' => array(
117 'links' => NULL,
118 ),
119 ),
120 'hidden_list_nodes' => array(
121 'arguments' => array(
122 'table' => NULL,
123 ),
124 ),
125 'hidden_list_admin_nodes' => array(
126 'arguments' => array(
127 'form' => NULL,
128 ),
129 ),
130 'hidden_list_admin_nodes_reported' => array(
131 'arguments' => array(
132 'form' => NULL,
133 ),
134 ),
135 'hidden_list_comments' => array(
136 'arguments' => array(
137 'table' => NULL,
138 ),
139 ),
140 'hidden_list_admin_comments' => array(
141 'arguments' => array(
142 'form' => NULL,
143 ),
144 ),
145 'hidden_list_admin_comments_reported' => array(
146 'arguments' => array(
147 'form' => NULL,
148 ),
149 ),
150 'hidden_view_reason' => array(
151 'arguments' => array(
152 'title' => NULL,
153 'public' => '',
154 'description' => '',
155 'private' => '',
156 'content' => '',
157 'view' => TRUE,
158 ),
159 ),
160 'hidden_filters_admin_enabled_form' => array(
161 'arguments' => array(
162 'form' => NULL,
163 ),
164 )
165 );
166 }
167
168 /**
169 * Implementation of hook_menu().
170 */
171 function hidden_menu() {
172 $items = array();
173 $view = array('access hidden');
174 $report = array('report for hiding');
175 $hide = array('mark as hidden');
176 $admin = array('administer hidden');
177
178 $items['admin/settings/hidden'] = array(
179 'title' => 'Hidden settings',
180 'description' => 'Administer settings for the hidden module.',
181 'page callback' => 'drupal_get_form',
182 'page arguments' => array('hidden_admin_settings'),
183 'access arguments' => $admin,
184 'file' => 'hidden.admin.inc',
185 );
186 $items['admin/content/hidden'] = array(
187 'title' => 'Hidden reasons and filters',
188 'description' => 'Manage reasons for hiding and content filters',
189 'page callback' => 'hidden_reasons_admin',
190 'access arguments' => $admin,
191 'type' => MENU_NORMAL_ITEM,
192 'file' => 'hidden.admin.inc',
193 );
194 $items['admin/content/hidden/reasons'] = array(
195 'title' => 'Reasons',
196 'type' => MENU_DEFAULT_LOCAL_TASK,
197 'weight' => -10,
198 );
199 $items['admin/content/hidden/reasons/add'] = array(
200 'title' => 'Add hidden reason',
201 'page callback' => 'drupal_get_form',
202 'page arguments' => array('hidden_reasons_admin_form', 'add'),
203 'access arguments' => $admin,
204 'type' => MENU_CALLBACK,
205 'file' => 'hidden.admin.inc',
206 );
207 $items['admin/content/hidden/reasons/edit'] = array(
208 'title' => 'Edit hidden reason',
209 'page callback' => 'drupal_get_form',
210 'page arguments' => array('hidden_reasons_admin_form', 'edit'),
211 'access arguments' => $admin,
212 'type' => MENU_CALLBACK,
213 'file' => 'hidden.admin.inc',
214 );
215 $items['admin/content/hidden/reasons/enable'] = array(
216 'title' => 'Enable hidden reason',
217 'page callback' => 'hidden_reasons_admin_able',
218 'page arguments' => array('enable'),
219 'access arguments' => $admin,
220 'type' => MENU_CALLBACK,
221 'file' => 'hidden.admin.inc',
222 );
223 $items['admin/content/hidden/reasons/disable'] = array(
224 'title' => 'Disable hidden reason',
225 'page callback' => 'hidden_reasons_admin_able',
226 'page arguments' => array('disable'),
227 'access arguments' => $admin,
228 'type' => MENU_CALLBACK,
229 'file' => 'hidden.admin.inc',
230 );
231 $items['admin/content/hidden/filters'] = array(
232 'title' => 'Filters',
233 'description' => 'Manage filters for hidden content',
234 'page callback' => 'hidden_filters_admin',
235 'access arguments' => $admin,
236 'type' => MENU_LOCAL_TASK,
237 'file' => 'hidden.admin.inc',
238 );
239 $items['admin/content/hidden/filters/add'] = array(
240 'title' => 'Add filter',
241 'page callback' => 'drupal_get_form',
242 'page arguments' => array('hidden_filters_admin_form', 'add'),
243 'access arguments' => $admin,
244 'type' => MENU_CALLBACK,
245 'file' => 'hidden.admin.inc',
246 );
247 $items['admin/content/hidden/filters/edit'] = array(
248 'title' => 'Edit filter',
249 'page callback' => 'drupal_get_form',
250 'page arguments' => array('hidden_filters_admin_form', 'edit'),
251 'access arguments' => $admin,
252 'type' => MENU_CALLBACK,
253 'file' => 'hidden.admin.inc',
254 );
255 $items['admin/content/hidden/filters/enable'] = array(
256 'title' => 'Enable filter',
257 'page callback' => 'hidden_filters_admin_able',
258 'page arguments' => array('enable'),
259 'access arguments' => $admin,
260 'type' => MENU_CALLBACK,
261 'file' => 'hidden.admin.inc',
262 );
263 $items['admin/content/hidden/filters/disable'] = array(
264 'title' => 'Disable filter',
265 'page callback' => 'hidden_filters_admin_able',
266 'page arguments' => array('disable'),
267 'access arguments' => $admin,
268 'type' => MENU_CALLBACK,
269 'file' => 'hidden.admin.inc',
270 );
271 $items['admin/content/comment/list/hidden'] = array(
272 'title' => 'Hidden',
273 'description' => 'Hidden comments',
274 'page callback' => 'drupal_get_form',
275 'page arguments' => array('hidden_list_admin_comments'),
276 'access arguments' => $hide,
277 'type' => MENU_LOCAL_TASK,
278 'file' => 'hidden.display-admin.inc'
279 );
280 $items['admin/content/comment/list/hiddenreported'] = array(
281 'title' => 'Reported',
282 'description' => 'Reported comments',
283 'page callback' => 'drupal_get_form',
284 'page arguments' => array('hidden_list_admin_comments_reported'),
285 'access arguments' => $hide,
286 'type' => MENU_LOCAL_TASK,
287 'file' => 'hidden.display-admin.inc',
288 );
289 $items['admin/content/node/list/hidden'] = array(
290 'title' => 'Hidden',
291 'description' => 'Hidden nodes',
292 'page callback' => 'drupal_get_form',
293 'page arguments' => array('hidden_list_admin_nodes'),
294 'access arguments' => $hide,
295 'type' => MENU_LOCAL_TASK,
296 'file' => 'hidden.display-admin.inc',
297 );
298 $items['admin/content/node/list/hiddenreported'] = array(
299 'title' => 'Reported',
300 'description' => 'Reported nodes',
301 'page callback' => 'drupal_get_form',
302 'page arguments' => array('hidden_list_admin_nodes_reported'),
303 'access arguments' => $hide,
304 'type' => MENU_LOCAL_TASK,
305 'file' => 'hidden.display-admin.inc',
306 );
307
308 $items['hidden'] = array(
309 'title' => 'Hidden',
310 'description' => 'Access hidden content',
311 'page callback' => 'hidden_list_nodes',
312 'access arguments' => $view,
313 // 'type' => MENU_SUGGESTED_ITEM,
314 'type' => MENU_NORMAL_ITEM,
315 'file' => 'hidden.display-pages.inc',
316 );
317 $items['hidden/node'] = array(
318 'title' => 'Hidden nodes',
319 'type' => MENU_DEFAULT_LOCAL_TASK,
320 );
321 $items['hidden/comment'] = array(
322 'title' => 'Hidden comments',
323 'page callback' => 'hidden_list_comments',
324 'access arguments' => $view,
325 'type' => MENU_LOCAL_TASK,
326 'file' => 'hidden.display-pages.inc',
327 );
328 $items["hidden/%/%"] = array(
329 'title' => 'view',
330 'page callback' => 'hidden_view',
331 'page arguments' => array(1, 2),
332 'access arguments' => $view,
333 'type' => MENU_CALLBACK,
334 'file' => 'hidden.display-pages.inc',
335 );
336
337 $items['hidden/%/%/hide'] = array(
338 'title' => 'Report post',
339 'page callback' => 'drupal_get_form',
340 'page arguments' => array('hidden_hide', 1, 2),
341 'access arguments' => $hide,
342 'type' => MENU_CALLBACK,
343 'file' => 'hidden.action-pages.inc',
344 );
345 $items['hidden/%/%/report'] = array(
346 'title' => 'Report post',
347 'page callback' => 'drupal_get_form',
348 'page arguments' => array('hidden_report', 1, 2),
349 'access arguments' => $report,
350 'type' => MENU_CALLBACK,
351 'file' => 'hidden.action-pages.inc',
352 );
353 $items['hidden/%/%/unhide'] = array(
354 'title' => 'Don\'t hide',
355 'page callback' => 'hidden_unhide',
356 'page arguments' => array(1, 2),
357 'access arguments' => $hide,
358 'type' => MENU_CALLBACK,
359 'file' => 'hidden.action-pages.inc',
360 );
361
362 return $items;
363 }
364
365
366 /**
367 * Implementation of hook_cron().
368 *
369 * sends periodic e-mails reports for actions set to HIDDEN_MAIL_LATER
370 * unpublishes content marked as delayed hidden
371 */
372 function hidden_cron() {
373 $now = time();
374 $email = _hidden_settings_mail();
375 if ($email['domail'] == TRUE) {
376 if (variable_get('hidden_mail_sent', 0) + $email['frequency'] < $now) {
377 // log emailing on, hidden mail not sent since frequency reached
378 $message = '';
379 foreach ($email as $type => $when) {
380 if ($when['when'] == HIDDEN_MAIL_LATER) {
381 // $email set action to be mailed later
382 $query = 'SELECT * FROM {hidden_log} WHERE action=%d';
383 if (! $result = db_query($query, $type)) {
384 _hidden_log(HIDDEN_LOG_ERROR, t('Error retrieving logged messages for e-mailing'));
385 $error = TRUE;
386 }
387 else {
388 while ($logged = db_fetch_object($result)) {
389 // retrive additional information about each action that has happened
390 $message .= _hidden_mail_cron_message($logged->action, $logged->description, unserialize($logged->ids), $logged->uid);
391 }
392 }
393 }
394 }
395 if ($message != '') {
396 // send out e-mail
397 if (_hidden_mail(t('Collected log messages'), $message) && !$error) {
398 $query = 'DELETE FROM {hidden_log} WHERE hidetime < %d';
399 if (! db_query($query, $now)) {
400 _hidden_log(HIDDEN_LOG_ERROR, t('Error removing sent logged messages from database'));
401 }
402 else {
403 variable_set(hidden_mail_sent, $now);
404 }
405 }
406 else {
407 _hidden_log(HIDDEN_LOG_DEBUG, 'Logged messages left in database because of error retrieving or sending');
408 }
409 }
410 }
411 }
412 // unpublish delayed hides
413 // TODO if filter settings delayed filters
414 foreach (array('node', 'comment') as $type) {
415 $query = "SELECT * FROM {hidden_$type} WHERE delay != 0 AND delay < %d";
416 $result = db_query($query, time());
417 while ($hidden = db_fetch_object($result)) {
418 _hidden_hidden_hide_unpublish($type, ($type == 'node') ? $hidden->nid : $hidden->cid);
419 $query = "UPDATE {hidden_$hidden} SET delay=0 WHERE hid=%d";
420 db_query($query, $hidden->hid);
421 _hidden_log(HIDDEN_LOG_HIDE, t('Hidden %type', array('%type' => $type)), $type,
422 ($type == 'node') ? $hidden->nid : $hidden->cid,
423 $hidden->rid, $hidden->publicnote, $hidden->privatenote, $hidden->uid);
424 }
425 }
426 }
427
428 /**
429 * Implementation of hook_comment().
430 *
431 * adds reasons and div tags to viewed comments.
432 * passes comment through any filters.
433 * removes deleted or published comments from the hidden records.
434 */
435 function hidden_comment(&$comment, $action) {
436 switch ($action) {
437 case 'view':
438 if (isset($comment->status) && $comment->status == COMMENT_NOT_PUBLISHED) {
439 // all hidden comments are unpublished so no point making unnecessary queries
440 if ($hidden=_hidden_hidden_get('comment', $comment->cid)) {
441 $title = check_plain($hidden->title);
442 $public = check_markup($hidden->publicnote, FILTER_HTML_STRIP, FALSE);
443 $description = check_markup($hidden->description, FILTER_HTML_STRIP, FALSE);
444 $private = check_markup($hidden->privatenote, FILTER_HTML_STRIP, FALSE);
445 $comment->comment = theme('hidden_view_reason', $title, $public, $description, $private, $comment->comment);
446 $comment->subject = t('HIDDEN: @title', array('@title' => $comment->subject));
447 }
448 }
449 break;
450 case 'update':
451 // TODO reinput if hidden
452 case 'insert':
453 if (user_access('bypass hidden filter')) {
454 return;
455 }
456 // $comment an array here!
457 hidden_filter_content($comment['subject'] .' '. $comment['comment'], 'comment', $comment['cid']);
458 break;
459 case 'publish':
460 // @todo array, always?
461 // what should happen here? filter?
462 if (! hidden_hidden_check('comment', $comment['cid'])) {
463 break;
464 }
465 break;
466 case 'delete':
467 if (_hidden_hidden_delete('comment', $comment->cid)) {
468 if (db_affected_rows()>0) {
469 if ($action == 'delete') {
470 _hidden_log(HIDDEN_LOG_DELETE, t('Hidden comment deleted'));
471 }
472 else {
473 _hidden_log(HIDDEN_LOG_UNHIDE, t('Hidden comment published'), 'comment', $comment->cid);
474 drupal_set_message(t('Hidden comment published'));
475 }
476 }
477 }
478 else {
479 _hidden_log(HIDDEN_LOG_ERROR, t('Error deleting hidden comment'), 'comment', $comment->cid);
480 }
481 break;
482 case 'form':
483 if ( hidden_hidden_check('comment', $comment->cid) && user_access('administer hidden')) {
484 //TODO edit form
485 return;
486 }
487 }
488 }
489
490 /**
491 * Implementation of hook_nodeapi().
492 *
493 * adds reasons and div tags to viewed nodes.
494 * passes node through any filters
495 * removes deleted node from the records.
496 */
497 function hidden_nodeapi(&$node, $action, $arg=0) {
498 switch ($action) {
499 case 'alter':
500 if (! $node->status) {
501 // all hidden nodes are unpublished so no point making unnecessary queries
502 if ($hidden=_hidden_hidden_get('node', $node->nid)) {
503 $title = check_plain($hidden->title);
504 $public = check_markup($hidden->publicnote, FILTER_HTML_STRIP, FALSE);
505 $description = check_markup($hidden->description, FILTER_HTML_STRIP, FALSE);
506 $private = check_markup($hidden->privatenote, FILTER_HTML_STRIP, FALSE);
507 // arg TRUE on teaser view
508 $node->body = theme('hidden_view_reason', $title, $public, $description, $private, $node->body, !($arg));
509 $node->title = t('HIDDEN: @title', array('@title' => $node->title));
510 // sadly usually set by this point for nodes
511 }
512 }
513 break;
514 case 'update':
515 if ($action=='update' && $node->status) {
516 // node being updated and status is published check it wasn't hidden
517 if (hidden_hidden_check('node', $node->nid)) {
518 if (_hidden_hidden_delete('node', $node->nid)) {
519 _hidden_log(HIDDEN_LOG_UNHIDE, t('Hidden node published.'), 'node', $node->nid);
520 drupal_set_message(t('Hidden node published.'));
521 }
522 else {
523 _hidden_log(HIDDEN_LOG_ERROR, t('Error unhiding publish node.'), 'node', $node->nid);
524 drupal_set_message(t('Error unhiding published node.'), 'error');
525 }
526 }
527 }
528 case 'insert':
529 if (user_access('bypass hidden filter')) {
530 return;
531 }
532 hidden_filter_content($node->title .' '. $node->teaser .' '. $node->body, 'node', $node->nid);
533 break;
534 case 'delete':
535 // comment_nodeapi doesn't call hook_comment delete but this query will delete comments too
536 if (_hidden_hidden_delete('both', $node->nid)) {
537 if (db_affected_rows()>0) {
538 _hidden_log(HIDDEN_LOG_DELETE, t('Deleted hidden node and/or attached comments.'));
539 }
540 }
541 else {
542 _hidden_log(HIDDEN_LOG_ERROR, t('Error deleting hidden node and/or comments.'), 'node', $node->nid);
543 }
544 }
545 }
546
547 /**
548 * Implementation of hook_node_operations().
549 */
550 function hidden_node_operations() {
551 $operations = array(
552 'hide' => array(
553 'label' => t('Hide'),
554 'page callback' => 'hidden_operations_hide',
555 ),
556 'unhide' => array(
557 'label' => t('Unhide'),
558 'page callback' => 'hidden_operations_unhide',
559 ),
560 );
561
562 return $operations;
563 }
564
565 /**
566 * Implementation of hook_link().
567 */
568 function hidden_link($type, $item = NULL, $teaser = FALSE) {
569 $links = array();
570 if (!$item) {
571 return $links;
572 }
573
574 if ($type == 'comment') {
575 $target = $item->cid;
576 }
577 elseif ($type == 'node') {
578 $target = $item->nid;
579 }
580 else {
581 return $links;
582 }
583
584 $hidden=_hidden_hidden_get($type, $target);
585
586 if ($hidden == FALSE) {
587 // not hidden
588 if (user_access('mark as hidden')) {
589 $links['hidden-hide'] = array(
590 'title' => t('hide'),
591 'href' => "hidden/$type/$target/hide",
592 'query' => drupal_get_destination(),
593 );
594 }
595 // not hidden and is published
596 if (user_access('report for hiding') && (
597 ($type == 'comment' && $item->status != COMMENT_NOT_PUBLISHED) ||
598 ($type == 'node' && $item->status)
599 )) {
600 $links['hidden-report'] = array(
601 'title' => t('report'),
602 'href' => "hidden/$type/$target/report",
603 'query' => drupal_get_destination(),
604 );
605 }
606 }
607 elseif ($hidden->delay!=0) {
608 // marked to be hidden by a cron job
609 if (user_access('mark as hidden')) {
610 $links['hidden-hide'] = array(
611 'title' => t('hide now'),
612 'href' => "hidden/$type/$target/hide",
613 'query' => drupal_get_destination(),
614 );
615 $links['hidden-unhide'] = array(
616 'title' => t("don't hide"),
617 'href' => "hidden/$type/$target/unhide",
618 'query' => drupal_get_destination(),
619 );
620 }
621 if (user_access('report for hiding')) {
622 $links['hidden-report'] = array(
623 'title' => t('report'),
624 'href' => "hidden/$type/$target/report",
625 'query' => drupal_get_destination(),
626 );
627 }
628 }
629 else {
630 // hidden
631 if (user_access('mark as hidden')) {
632 $links['hidden-unhide'] = array(
633 'title' => t('unhide'),
634 'href' => "hidden/$type/$target/unhide",
635 );
636 }
637 }
638
639 return $links;
640 }
641
642 /**
643 * Creates array of rows for hidden content tables.
644 *
645 * @param $query
646 * string sql query for the pager
647 * @param $type
648 * string type of content (comment, node)
649 * @return
650 * array of hidden content for theme_table()
651 */
652 function _hidden_list_get($query, $type) {
653 $result = pager_query($query, HIDDEN_PAGER_LIMIT);
654
655 $rows = array();
656
657 while ($links = db_fetch_object($result)) {
658 if ($type == 'comment') {
659 $target = 'comment/'. $links->cid;
660 }
661 else {
662 $target = 'node/'. $links->nid;
663 }
664
665 $rows[] = array(
666 l($links->title, 'hidden/'. $target),
667 theme('username', $links),
668 check_plain($links->reason),
669 );
670
671 if (count($rows)==0) {
672 $rows[] = array(t('No hidden content'), '');
673 }
674 }
675
676 return $rows;
677 }
678
679 /**
680 * Creates pulldown box for admin list form mass unhide operation.
681 *
682 * @return
683 * form array
684 */
685 function _hidden_list_admin_options() {
686 // build an 'Update options' form
687 $form['options'] = array(
688 '#type' => 'fieldset', '#title' => t('Update options'),
689 '#prefix' => '<div class="container-inline">', '#suffix' => '</div>'
690 );
691
692 $form['options']['operation'] = array(
693 '#type' => 'select',
694 '#options' => array('UNHIDE' => t('Unhide the selected posts')),
695 '#description' => t(''),
696 );
697
698 $form['options']['submit'] = array(
699 '#type' => 'submit',
700 '#value' => t('Update'),
701 );
702
703 return $form;
704 }
705
706 /**
707 * Creates a pulldown for admin reported list form mass hide/seen operations.
708 *
709 * @return form array
710 */
711 function _hidden_list_admin_reported_options() {
712 // build an 'Update options' form
713 $form['options'] = array(
714 '#type' => 'fieldset', '#title' => t('Update options'),
715 '#prefix' => '<div class="container-inline">', '#suffix' => '</div>'
716 );
717
718 $options = array('SEEN' => t('Mark the selected posts as seen'),
719 'HIDE' => t('Hide the selected posts'),
720 );
721 if (module_exists('spam')) {
722 $options['SPAM'] = t('Mark the selected posts as spam');
723 }
724
725 $form['options']['operation'] = array(
726 '#type' => 'select',
727 '#options' => $options,
728 '#description' => t(''),
729 );
730
731 $form['options']['submit'] = array(
732 '#type' => 'submit',
733 '#value' => t('Update'),
734 );
735
736 return $form;
737 }
738
739 /**
740 * Formats reason for hiding in node and comment display.
741 *
742 * This is shown in all views of hidden nodes and comments, it's called from the
743 * nodeapi('view') and comment('view') hooks.
744 *
745 * @param $title
746 * string plain text standard reason for hiding.
747 * @param $public
748 * string html public note about hiding.
749 * @param $description
750 * string html description of standard reason for hiding (optional).
751 * @param $private
752 * string html private note about hiding (optional).
753 * @param $content
754 * string html formated node/comment (optional).
755 * @param $view
756 * bool FALSE if short display without additional info and content (for teaser view) (optional).
757 * @return
758 * string formatted output
759 * @ingroup themeable
760 */
761 function theme_hidden_view_reason($title, $public, $description='', $private='', $content='', $view=TRUE) {
762 $output = '<div class="hidden"><div class="reason">';
763 $output .= t('<p>Hidden content:</p>');
764 if ($title) {
765 $output .= '<p>'. $title .'</p>';
766 if ($view) {
767 $output .= $description;
768 }
769 }
770 $output .= $public;
771 if (user_access('mark as hidden') && $view) {
772 $output .= $private;
773 }
774 $output .= '</div>';
775 if ($view) {
776 $output .= $content;
777 }
778 $output .= '</div>';
779 return $output;
780 }
781
782 /**
783 * Admin node_operations() callback.
784 */
785 function hidden_operations_hide($nids) {
786 global $user;
787
788 $hide = array();
789 $error = array();
790 foreach ($nids as $nid) {
791 if (! hidden_hidden_check('node', $nid)) {
792 $nid = (int)$nid;
793 if (hidden_hidden_hide('node', $nid, $user->uid, 1)) {
794 $hide[] = $nid;
795 }
796 else {
797 $error[] = $nid;
798 }
799 }
800 }
801 if ($n = count($error)) {
802 $msg = format_plural($n, 'Error hiding node.', 'Error hiding nodes.');
803 _hidden_log(HIDDEN_LOG_ERROR, $msg, 'node', $error);
804 drupal_set_message($msg, 'error');
805 }
806 if ($n = count($hide)) {
807 $msg = format_plural($n, 'Hidden node.', 'Hidden nodes.');
808 _hidden_log(HIDDEN_LOG_HIDE, $msg, 'node', $hide);
809 drupal_set_message($msg);
810 }
811
812 drupal_goto('admin/content/node');
813 }
814
815 /**
816 * Admin node_operations() callback.
817 */
818 function hidden_operations_unhide($nids) {
819 $unhide = array();
820 $error = array();
821 foreach ($nids as $nid) {
822 $nid = (int)$nid;
823 if (hidden_hidden_check('node', $nid)) {
824 if (_hidden_hidden_unhide('node', $nid)) {
825 $unhide[] = $nid;
826 }
827 else {
828 $error[] = $nid;
829 }
830 }
831 }
832 if ($n = count($error)) {
833 $msg = format_plural($n, 'Error unhiding node.', 'Error unhiding nodes.');
834 _hidden_log(HIDDEN_LOG_ERROR, $msg, 'node', $error);
835 drual_set_message($msg, 'error');
836 }
837 if ($n = count($unhide)) {
838 $msg = format_plural($n, 'Unhidden node.', 'Unhidden nodes.');
839 _hidden_log(HIDDEN_LOG_UNHIDE, $msg, 'node', $unhide);
840 drupal_set_message($msg);
841 }
842 }
843
844 /**
845 * Checks if item is hidden.
846 *
847 * @param $type
848 * string content 'comment' or 'node'.
849 * @param $id
850 * int cid or nid.
851 * @return
852 * bool TRUE hidden FALSE not/fail.
853 */
854 function hidden_hidden_check($type, $id) {
855 static $hidden, $ltype, $lid, $count;
856 if ($type == $ltype && $id == $lid) {
857 return $hidden;
858 }
859 else {
860 $ltype = $type;
861 $lid = $id;
862 $hidden = NULL;
863 }
864
865 if ($type != 'node' && $type != 'comment') {
866 _hidden_log(HIDDEN_LOG_DEBUG, 'hidden_hidden_check() called with invalid $type');
867 return FALSE;
868 }
869 $table = ($type == 'node') ? 'node' : 'comment';
870 $col = ($type == 'node') ? 'nid' : 'cid';
871 $query = "SELECT COUNT(*) FROM {hidden_$table WHERE $col = %d";
872
873 $result = db_query($query, $id);
874 if (db_result($result) != 0) {
875 $hidden = TRUE;
876 return TRUE;
877 }
878 else {
879 $hidden = FALSE;
880 return FALSE;
881 }
882 }
883
884 /**
885 * Gets a hidden item.
886 *
887 * @param $type
888 * string content type 'comment' or 'node'
889 * @param $id
890 * int cid or nid
891 * @return
892 * object -> hid, rid, title, description, publicnote, privatenote, or FALSE not hidden
893 */
894 function _hidden_hidden_get($type, $id) {
895 if ($type == 'node') {
896 $query = 'SELECT h.hid, h.nid, h.rid, h.uid, h.publicnote, h.privatenote, h.delay,'.
897 ' r.title, r.description'.
898 " FROM {hidden_node} AS h".
899 ' LEFT JOIN {hidden_reasons} AS r ON h.rid=r.rid'.
900 " WHERE nid=%d";
901 }
902 elseif ($type == 'comment') {
903 $query = 'SELECT h.hid, h.cid, h.rid, h.uid, h.publicnote, h.privatenote, h.delay,'.
904 ' r.title, r.description'.
905 " FROM {hidden_comment} AS h".
906 ' LEFT JOIN {hidden_reasons} AS r ON h.rid=r.rid'.
907 " WHERE cid=%d";
908 }
909 else {
910 _hidden_log(HIDDEN_LOG_DEBUG, '_hidden_hidden_get() called with invalid $type');
911 $hidden = FALSE;
912 return $hidden;
913 }
914
915 if ($result = db_query($query, $id)) {
916 if ($hidden = db_fetch_object($result)) {
917 $hidden->type = $type;
918 $hidden->id = $id;
919 return $hidden;
920 }
921 else {
922 $hidden = FALSE;
923 return $hidden;
924 }
925 }
926 }
927
928 /**
929 * Removes hidden items from table.
930 *
931 * @param $type
932 * string content type to remove 'comment', 'node' or 'both'.
933 * @param $id
934 * int cid (comment only) or nid (node only or both).
935 * @return
936 * FALSE on fail.
937 */
938 function _hidden_hidden_delete($type, $id) {
939
940 if ($type == 'both' || $type == 'node') {
941 $query = 'DELETE FROM {hidden_node} WHERE nid=%d';
942 // error catching
943 if (! db_query($query, $id)) {
944 return FALSE;
945 }
946 }
947
948 if ($type == 'comment') {
949 $query = 'DELETE FROM {hidden_comment} WHERE cid=%d';
950 }
951 elseif ($type == 'both') {
952 $query = 'DELETE FROM {hidden_comment} WHERE nid=%d';
953 }
954 elseif ($type == 'node') {
955 return TRUE;
956 }
957
958 return db_query($query, $id);
959 }
960
961 /**
962 * Performs db queries required to hide an item.
963 *
964 * Inserts into hidden table.
965 * Also if needed marks as seen in hidden_reported, unpublishes, stops comments on nodes.
966 *
967 * @param $nid
968 * int nid.
969 * @param $cid
970 * int if hiding a comment, if node pass '0'.
971 * @param $rid
972 * int id of reason for hiding (optional).
973 * @param $public
974 * string html of public note (optional).
975 * @param $private
976 * string html of private note (optional).
977 * @param $filter
978 * int if called by filtering time to delay posting till, 0 for being done
979 * @return
980 * FALSE on fail. hid on success.
981 */
982 function hidden_hidden_hide($type, $id, $uid, $rid=0, $public='', $private='', $filter=FALSE) {
983 // 'hide now' delay !=0 // in the future also editing a hidden if desired?
984 // this check here to prevent just hidden, or calls from report as hidden etc. clashing
985
986 if ($hidden = _hidden_hidden_get($type, $id)) {
987 if ($hidden->delay==0) {
988 drupal_set_message(t('The %type is already hidden!', array('%type' => $type)), 'error');
989 return FALSE;
990 }
991 else {
992 // OK to update existing delayed hide
993 $hid = $hidden->hid;
994 if ($type == 'node') {
995 $query = "UPDATE {hidden_node} SET nid=%d, uid=%d, created=%d, rid=%d, publicnote='%s', privatenote='%s', delay=%d WHERE hid=%d";
996 $result = db_query($query, $id, $uid, $created, $rid, $public, $private, $filter, $hid);
997 }
998 else {
999 $query = "UPDATE {hidden_comment} SET cid=%d, uid=%d, created=%d, rid=%d, publicnote='%s', privatenote='%s', delay=%d WHERE hid=%d";
1000 $result = db_query($query, $id, $uid, $created, $rid, $public, $private, $filter, $hid);
1001 }
1002 }
1003 }
1004 else {
1005 // Insert into hidden table
1006 if ($type == 'node') {
1007 $query = "INSERT INTO {hidden_node} (nid, uid, created, rid, publicnote, privatenote, delay) VALUES (%d, %d, %d, %d, '%s', '%s', %d)";
1008 $result = db_query($query, $id, $uid, time(), $rid, $public, $private, $filter);
1009 }
1010 else {
1011 $query = "INSERT INTO {hidden_comment} (cid, uid, created, rid, publicnote, privatenote, delay) VALUES (%d, %d, %d, %d, '%s', '%s', %d)";
1012 $result = db_query($query, $id, $uid, time(), $rid, $public, $private, $filter);
1013 }
1014 $hid = db_last_insert_id("{hidden_$type}_hid", "hid");
1015 }
1016 if (! $result) {
1017 if ($filter === FALSE) {
1018 drupal_set_message(t('Database error while storing hidden.'), 'error');
1019 }
1020 _hidden_log(HIDDEN_LOG_DEBUG, "hidden_hidden_hide() failed to insert into {hidden_$type}", 'hidden', $hid);
1021 return FALSE;
1022 }
1023
1024 if ($filter > 0) {
1025 // unpublishing handled by cron
1026 return TRUE;
1027 }
1028
1029 $col = $type == 'node' ? 'nid' : 'cid';
1030 // If it has been reported mark as seen
1031 $reported = db_query("SELECT COUNT(*) FROM {hidden_reported_$type} WHERE $col=%d", $id);
1032 if (db_result($reported)>0) {
1033 if (! _hidden_reported_seen($type, $id, $uid)) {
1034 _hidden_log(HIDDEN_LOG_ERROR, t('Error marking as seen while hiding.'), 'hidden', $hid);
1035 }
1036 }
1037
1038 if (! _hidden_hidden_hide_unpublish($type, $id)) {
1039 // It's a bit messy if something is in the hidden db but published
1040 // on display it checks published status before hidden bothing with hidden db
1041 return FALSE;
1042 }
1043
1044 return TRUE;
1045 }
1046
1047 /**
1048 * Unpublishes a node or comment
1049 *
1050 * Updates comments to status COMMENT_NOT_PUBLISHED
1051 * and nodes to status 0, and stops further comments
1052 *
1053 * @todo consider loading objects, manipulating (with actions), and saving
1054 *
1055 * @param $type
1056 * string 'node' or 'comment'
1057 * @param $id
1058 * int nid or cid
1059 * @return
1060 * TRUE on success
1061 */
1062 function _hidden_hidden_hide_unpublish($type, $id) {
1063 if ($type == 'comment') {
1064 // Set comment to unpublished
1065 if (! db_query('UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED ." WHERE cid=%d", $id)) {
1066 drupal_set_message(t("Database error occurred when updating comment's status."), 'error');
1067 _hidden_log(HIDDEN_LOG_DEBUG, 'hidden_hide_do() error unpublishing comment.', 'comment', $id);
1068 return FALSE;
1069 }
1070 }
1071 elseif ($type == 'node') {
1072 // set node to unpublished and comments to read-only
1073 if (! db_query("UPDATE {node} SET status = 0, comment = 1 WHERE nid = %d", $id)) {
1074 drupal_set_message(t("Database error occurred when updating node's status."), 'error');
1075 _hidden_log(HIDDEN_LOG_DEBUG, 'hidden_hide_do() error unpublishing node.', 'node', $id);
1076 return FALSE;
1077 }
1078 }
1079 else {
1080 // @todo error
1081 return FALSE;
1082 }
1083
1084 return TRUE;
1085 }
1086
1087 /**
1088 * Performs db queries required to unhide an item.
1089 *
1090 * @param $type
1091 * string type 'node' or 'comment'.
1092 * @param $id
1093 * int nid or cid.
1094 * @return
1095 * bool TRUE on success.
1096 */
1097 function _hidden_hidden_unhide($type, $id) {
1098 if ($type == 'comment') {
1099 $query = 'DELETE FROM {hidden_comment} WHERE cid = %d';
1100 }
1101 elseif ($type == 'node') {
1102 $query = 'DELETE FROM {hidden_node} WHERE nid = %d';
1103 }
1104 else {
1105 _hidden_log(HIDDEN_LOG_DEBUG, '_hidden_hidden_unhide() called with invalid $type');
1106 return FALSE;
1107 }
1108
1109 $result = db_query($query, $id);
1110 if (!$result) {
1111 drupal_set_message(t('Database error occurred when deleting item from hidden table.'), 'error');
1112 _hidden_log(HIDDEN_LOG_DEBUG, "_hidden_hidden_unhide() failed to delete from {hidden_$type}", $type, $id);
1113 return FALSE;
1114 }
1115
1116 $result = _hidden_hidden_publish($type, $id);
1117
1118 if ($result) {
1119 return TRUE;
1120 }
1121 else {
1122 _hidden_log(HIDDEN_LOG_DEBUG, '_hidden_hidden_unhide() failed to publish.', $type, $id);
1123 return FALSE;
1124 }
1125 }
1126
1127 /**
1128 * Opposite actions to _hidden_hidden_unpublish().
1129 *
1130 * @todo set comment status to correct for type, or back to previous settings (need storing).
1131 */
1132 function _hidden_hidden_publish($type, $id) {
1133 if ($type == 'comment') {
1134 $result = db_query('UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d', $id);
1135 }
1136 else {
1137 // set comments to read/write
1138 $result = db_query('UPDATE {node} SET status = 1, comment = 2 WHERE nid = %d', $id);
1139 }
1140
1141 return $result;
1142 }
1143
1144 /**
1145 * Check parameters.
1146 *
1147 * @param $type
1148 * string 'node' or 'comment'.
1149 * @param $id
1150 * int nid or cid.
1151 * @return
1152 * bool TRUE if pass, FALSE on fail
1153 */
1154 function _hidden_check_param($type, $id) {
1155 if ($type != 'comment' && $type != 'node') {
1156 return FALSE;
1157 }
1158 // @todo $nid is a string from node_save(); should be is_int
1159 if (! is_numeric($id) || $id < 1) {
1160 return FALSE;
1161 }
1162 return TRUE;
1163 }
1164
1165 /**
1166 * Marks reported posts as seen.
1167 *
1168 * @param $type
1169 * string 'node' or 'comment'
1170 * @param $id
1171 * int nid or cid
1172 * @param $uid
1173 * int uid
1174 * @return
1175 * FALSE on error
1176 */
1177 function _hidden_reported_seen($type, $id, $uid) {
1178 if ($type == 'node') {
1179 $query = 'UPDATE {hidden_reported_node} SET seen=1, suid=%d WHERE nid=%d';
1180 }
1181 elseif ($type == 'comment') {
1182 $query = 'UPDATE {hidden_reported_comment} SET seen=1, suid=%d WHERE cid=%d';
1183 }
1184 else {
1185 return FALSE;
1186 }
1187
1188 return db_query($query, $uid, $id);