Parent Directory
|
Revision Log
|
Revision Graph
#274536 (alastair) Calls to url() need to be updated for D6.
| 1 | <?php |
| 2 | // $Id: akismet.module,v 1.27 2008/07/13 07:44:26 drewish Exp $ |
| 3 | |
| 4 | /** |
| 5 | * Akismet Drupal and Module versions. |
| 6 | */ |
| 7 | define('AKISMET_DRUPAL_VERSION', '6'); |
| 8 | define('AKISMET_MODULE_VERSION', '2.0'); |
| 9 | define('AKISMET_MODULE_HOMEURL', 'http://www.drupal.org/project/akismet'); |
| 10 | define('AKISMET_MODULE_USERAGENT', 'Drupal/'. AKISMET_DRUPAL_VERSION .' | akismet.module/'. AKISMET_MODULE_VERSION); |
| 11 | |
| 12 | |
| 13 | /** |
| 14 | * Akismet API constants. |
| 15 | */ |
| 16 | define('AKISMET_API_HOST', 'rest.akismet.com'); |
| 17 | define('AKISMET_API_PORT', 80); |
| 18 | define('AKISMET_API_VERSION', '1.1'); |
| 19 | define('AKISMET_API_USERAGENT', 'Drupal/'. AKISMET_DRUPAL_VERSION .' | akismet.module/'. AKISMET_MODULE_VERSION); |
| 20 | define('AKISMET_API_RESULT_ERROR', -1); |
| 21 | define('AKISMET_API_RESULT_SUCCESS', 0); |
| 22 | define('AKISMET_API_RESULT_IS_SPAM', 1); |
| 23 | define('AKISMET_API_RESULT_IS_HAM', 2); |
| 24 | |
| 25 | |
| 26 | /** |
| 27 | * Implementation of hook_help(). |
| 28 | */ |
| 29 | function akismet_help($path, $arg) { |
| 30 | switch ($path) { |
| 31 | case 'admin/help#akismet': |
| 32 | $output = t('<p>In order to use the <a href="!akismet">Akismet Service</a>, you need a <a href="!wpapikey">WordPress.com API key</a>. If you don\'t have one already, you can get it by simply signing up for a free account at <a href="!wordpress-com">wordpress.com</a>. Please, consult the <a href="!akismet-faq">Akismet FAQ</a> for further information.</p> |
| 33 | <p>The <em>akismet module</em> may automatically check for spam posted in content (nodes and/or comments) by any user, except node or comment administrators respectively. It is also possible, from the <a href="!access-control">access control</a> panel, to grant <em>%no-check-perm</em> permission to <em>user roles</em> of your choice.</p> |
| 34 | <p>Content marked as <em>spam</em> is still saved into database so it can be reviewed by content administrators. There is <a href="!akismet-settings">an option</a> that allows you to specify how long this information will be kept in the database. <em>Spam</em> older than a specified age will be automatically removed. Requires crontab.</p> |
| 35 | <p>Automatic spam detection can be enabled or disabled by content type and/or comments. In addition to this, the <em>akismet module</em> makes it easy for <em>content administrators</em> to manually <em>publish</em>/<em>unpublish</em> content and <em>mark</em>/<em>unmark</em> content as spam, from links available at the bottom of content.</p> |
| 36 | <p></p>', |
| 37 | array( |
| 38 | '!akismet' => url('http://akismet.com'), |
| 39 | '!wpapikey' => url('http://wordpress.com/api-keys/'), |
| 40 | '!wordpress-com' => url('http://wordpress.com'), |
| 41 | '!akismet-faq' => url('http://akismet.com/faq/'), |
| 42 | '!akismet-settings' => url('admin/settings/akismet'), |
| 43 | '!access-control' => url('admin/access'), |
| 44 | '%no-check-perm' => t('post with no akismet checking') |
| 45 | )); |
| 46 | return $output; |
| 47 | case 'admin/help/akismet': |
| 48 | case 'admin/settings/akismet': |
| 49 | $output = t('<p>The <a href="!akismet-module-home">akismet module</a> for <a href="!drupal">Drupal</a> allows you to use the <a href="!akismet">Akismet Service</a> to protect your site from being spammed.</p>', |
| 50 | array( |
| 51 | '!akismet-module-home' => url(AKISMET_MODULE_HOMEURL), |
| 52 | '!drupal' => url('http://drupal.org'), |
| 53 | '!akismet' => url('http://akismet.com') |
| 54 | )); |
| 55 | $output .= t('<p>Akismet has caught <strong>@count spam</strong> for you since %since.</p>', array('@count' => akismet_get_spam_counter(), '%since' => akismet_get_counting_since())); |
| 56 | return $output; |
| 57 | case 'admin/content/akismet/nodes/unpublished': |
| 58 | $output = t('Below is the list of <strong>unpublished nodes</strong> awaiting for moderation.'); |
| 59 | $output .= ' '. t('Click on the titles to see the content of the nodes or the author\'s name to view the author\'s user information. You may also wish to click on the headers to order the nodes upon your needs.'); |
| 60 | break; |
| 61 | case 'admin/content/akismet/nodes/published': |
| 62 | $output = t('Below is the list of <strong>published nodes</strong>.'); |
| 63 | $output .= ' '. t('Click on the titles to see the content of the nodes or the author\'s name to view the author\'s user information. You may also wish to click on the headers to order the nodes upon your needs.'); |
| 64 | break; |
| 65 | case 'admin/content/akismet/nodes': // spam |
| 66 | $output = t('Below is the list of <strong>nodes marked as spam</strong> awaiting for moderation.'); |
| 67 | $output .= ' '. t('Click on the titles to see the content of the nodes or the author\'s name to view the author\'s user information. You may also wish to click on the headers to order the nodes upon your needs.'); |
| 68 | break; |
| 69 | case 'admin/content/akismet/comments/unpublished': |
| 70 | $output = t('Below is the list of <strong>unpublished comments</strong> awaiting for moderation.'); |
| 71 | $output .= ' '. t('Click on the subjects to see the comments or the author\'s name to view the author\'s user information. You may also wish to click on the headers to order the comments upon your needs.'); |
| 72 | break; |
| 73 | case 'admin/content/akismet/comments/published': |
| 74 | $output = t('Below is the list of <strong>published comments</strong>.'); |
| 75 | $output .= ' '. t('Click on the subjects to see the comments or the author\'s name to view the author\'s user information. You may also wish to click on the headers to order the comments upon your needs.'); |
| 76 | break; |
| 77 | case 'admin/content/akismet/comments': // spam |
| 78 | $output = t('Below is the list of <strong>comments marked as spam</strong> awaiting for moderation.'); |
| 79 | $output .= ' '. t('Click on the subjects to see the comments or the author\'s name to view the author\'s user information. You may also wish to click on the headers to order the comments upon your needs.'); |
| 80 | break; |
| 81 | } |
| 82 | if (arg(0) == 'admin' && arg(1) == 'content '&& arg(2) == 'akismet' && !isset($_POST) && !empty($output)) { |
| 83 | $output .= '<br />'. t('<strong>Note:</strong> To interact fully with the <a href="!akismet">Akismet Service</a> you really should try putting data back into the system as well as just taking it out. If it is at all possible, please use the submit <em>ham</em> operation rather than simply publishing content that was identified as spam (false positives). This is necessary in order to let Akismet learn from its mistakes. Thank you.', array('!akismet' => url('http://akismet.com'))); |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | // Check run-time requirements and status information |
| 88 | function akismet_requirements($phase) { |
| 89 | $t = get_t(); |
| 90 | |
| 91 | if ($phase == 'runtime') { |
| 92 | if (variable_get('akismet_wpapikey', '') == '') { |
| 93 | $requirements['akismet_key'] = array( |
| 94 | 'title' => $t('Akismet API key'), |
| 95 | 'value' => $t('Not present'), |
| 96 | 'description' => $t("Akismet spam protection requires a <a href='!wpapikey'>WordPress.com API key</a> to function. Obtain a key by signing up for a free account at <a href='!wordpress-com'>WordPress.com</a>, then enter the key on the <a href='!akismet-settings'>Akismet settings page</a>.", |
| 97 | array( |
| 98 | '!wpapikey' => url('http://wordpress.com/api-keys/'), |
| 99 | '!wordpress-com' => url('http://wordpress.com'), |
| 100 | '!akismet-settings' => url('admin/settings/akismet'), |
| 101 | )), |
| 102 | 'severity' => REQUIREMENT_ERROR, |
| 103 | ); |
| 104 | return $requirements; |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Implementation of hook_perm(). |
| 111 | */ |
| 112 | function akismet_perm() { |
| 113 | $perms = array('administer akismet settings'); |
| 114 | |
| 115 | foreach (node_get_types('names') as $type => $name) { |
| 116 | $perms[] = 'moderate spam in nodes of type '. $name; |
| 117 | } |
| 118 | $perms[] = 'moderate spam in comments'; |
| 119 | $perms[] = 'post with no akismet checking'; |
| 120 | |
| 121 | return $perms; |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Implementation of hook_cron(). |
| 126 | */ |
| 127 | function akismet_cron() { |
| 128 | require_once('./'. drupal_get_path('module', 'akismet') . '/akismet_cron.inc'); |
| 129 | register_shutdown_function('akismet_cron_shutdown'); |
| 130 | } |
| 131 | |
| 132 | function _akismet_moderator_types_count($types = array()) { |
| 133 | if (empty($types)) { |
| 134 | $types = akismet_get_moderator_types(); |
| 135 | } |
| 136 | return count($types); |
| 137 | } |
| 138 | |
| 139 | function _akismet_is_moderator($moderator_types = array(), $type = '') { |
| 140 | if (empty($moderator_types)) { |
| 141 | $moderator_types = akismet_get_moderator_types(); |
| 142 | } |
| 143 | if (_akismet_moderator_types_count($moderator_types) > 0 && (empty($type) || isset($moderator_types[$type]))) { |
| 144 | return TRUE; |
| 145 | } |
| 146 | else { |
| 147 | return FALSE; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | function _akismet_is_node_moderator($moderator_types = array()) { |
| 152 | if (empty($moderator_types)) { |
| 153 | $moderator_types = akismet_get_moderator_types(); |
| 154 | } |
| 155 | if (_akismet_is_moderator($moderator_types) && (!_akismet_is_moderator($moderator_types, $type = 'comments') || _akismet_moderator_types_count($moderator_types) > 1)) { |
| 156 | return TRUE; |
| 157 | } |
| 158 | else { |
| 159 | return FALSE; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | function _akismet_is_moderator_type($type, $types = array()) { |
| 164 | if (empty($types)) { |
| 165 | $types = akismet_get_moderator_types(); |
| 166 | } |
| 167 | if (_akismet_moderator_types_count($types) > 0 && isset($types[$type])) { |
| 168 | return TRUE; |
| 169 | } |
| 170 | else { |
| 171 | return FALSE; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /** |
| 176 | * Implementation of hook_menu(). |
| 177 | */ |
| 178 | function akismet_menu() { |
| 179 | $items = array(); |
| 180 | |
| 181 | $items['admin/settings/akismet'] = array( |
| 182 | 'title' => t('Akismet'), |
| 183 | 'description' => t('Use the Akismet Service to protect your site from spam.'), |
| 184 | 'page callback' => 'drupal_get_form', |
| 185 | 'page arguments' => array('akismet_settings'), |
| 186 | 'access arguments' => array('administer akismet settings'), |
| 187 | 'file' => 'akismet.admin.inc', |
| 188 | ); |
| 189 | |
| 190 | $moderator_types = akismet_get_moderator_types(); |
| 191 | if (_akismet_is_moderator($moderator_types)) { |
| 192 | $items['admin/content/akismet'] = array( |
| 193 | 'title' => t('Akismet moderation queue'), |
| 194 | 'description' => t('Manage the Akismet spam queue, appropving or deleting content in need of moderation.'), |
| 195 | 'page callback' => 'akismet_callback_queue', |
| 196 | 'access callback' => '_akismet_is_moderator', |
| 197 | 'access arguments' => array($moderator_types), |
| 198 | 'file' => 'akismet.admin.inc', |
| 199 | ); |
| 200 | $items['admin/content/akismet/overview'] = array( |
| 201 | 'title' => t('Overview'), |
| 202 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 203 | 'weight' => 0, |
| 204 | 'file' => 'akismet.admin.inc', |
| 205 | ); |
| 206 | if (_akismet_is_node_moderator($moderator_types)) { |
| 207 | $items['admin/content/akismet/nodes'] = array( |
| 208 | 'title' => t('Nodes'), |
| 209 | 'page callback' => 'akismet_callback_queue', |
| 210 | 'page arguments' => array('nodes'), |
| 211 | 'access callback' => '_akismet_is_node_moderator', |
| 212 | 'access arguments' => array($moderator_types), |
| 213 | 'type' => MENU_LOCAL_TASK, |
| 214 | 'weight' => 1, |
| 215 | 'file' => 'akismet.admin.inc', |
| 216 | ); |
| 217 | $items['admin/content/akismet/nodes/spam'] = array( |
| 218 | 'title' => t('Spam'), |
| 219 | 'page arguments' => array('nodes'), |
| 220 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 221 | 'weight' => 0, |
| 222 | 'file' => 'akismet.admin.inc', |
| 223 | ); |
| 224 | $items['admin/content/akismet/nodes/unpublished'] = array( |
| 225 | 'title' => t('Unpublished nodes'), |
| 226 | 'page arguments' => array('nodes', 'unpublished'), |
| 227 | 'type' => MENU_LOCAL_TASK, |
| 228 | 'weight' => 1, |
| 229 | 'file' => 'akismet.admin.inc', |
| 230 | ); |
| 231 | $items['admin/content/akismet/nodes/published'] = array( |
| 232 | 'title' => t('Published nodes'), |
| 233 | 'callback arguments' => array('nodes', 'published'), |
| 234 | 'type' => MENU_LOCAL_TASK, |
| 235 | 'weight' => 2, |
| 236 | 'file' => 'akismet.admin.inc', |
| 237 | ); |
| 238 | } |
| 239 | if (_akismet_is_moderator($moderator_types, 'comments')) { |
| 240 | $items['admin/content/akismet/comments'] = array( |
| 241 | 'title' => t('Comments'), |
| 242 | 'page callback' => 'akismet_callback_queue', |
| 243 | 'page arguments' => array('comments'), |
| 244 | 'access callback' => '_akismet_is_moderator', |
| 245 | 'access arguments' => array($moderator_types, 'comments'), |
| 246 | 'type' => MENU_LOCAL_TASK, |
| 247 | 'weight' => 2, |
| 248 | 'file' => 'akismet.admin.inc', |
| 249 | ); |
| 250 | $items['admin/content/akismet/comments/spam'] = array( |
| 251 | 'title' => t('Spam'), |
| 252 | 'page arguments' => array('comments'), |
| 253 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 254 | 'weight' => 0, |
| 255 | 'file' => 'akismet.admin.inc', |
| 256 | ); |
| 257 | $items['admin/content/akismet/comments/unpublished'] = array( |
| 258 | 'title' => t('Unpublished comments'), |
| 259 | 'page arguments' => array('comments', 'unpublished'), |
| 260 | 'type' => MENU_LOCAL_TASK, |
| 261 | 'weight' => 1, |
| 262 | 'file' => 'akismet.admin.inc', |
| 263 | ); |
| 264 | $items['admin/content/akismet/comments/published'] = array( |
| 265 | 'title' => t('Published comments'), |
| 266 | 'page arguments' => array('comments', 'published'), |
| 267 | 'type' => MENU_LOCAL_TASK, |
| 268 | 'weight' => 2, |
| 269 | 'file' => 'akismet.admin.inc', |
| 270 | ); |
| 271 | } |
| 272 | } |
| 273 | else { |
| 274 | $item = array( |
| 275 | 'title' => 'switch content status', |
| 276 | 'page callback' => 'akismet_page', |
| 277 | 'page arguments' => array(0, 1, 2, 3), |
| 278 | 'load arguments' => array('%map', '%index'), |
| 279 | 'access callback' => 'akismet_access_callback', |
| 280 | 'access argument' => array(0, 1, 2, 3), |
| 281 | ); |
| 282 | foreach (array('publish', 'unpublish', 'submit-spam', 'submit-ham') as $op) { |
| 283 | $items['akismet/%akismet/%/'. $op] = $item; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | return $items; |
| 288 | } |
| 289 | |
| 290 | function akismet_load($arg, &$map, $index) { |
| 291 | if (!is_numeric($map[2])) { |
| 292 | // Node and comment ids are always numeric! |
| 293 | return FALSE; |
| 294 | } |
| 295 | $content_type = $map[1]; |
| 296 | if ($content_type == 'node') { |
| 297 | if (!$map[2] = node_load($map[2])) { |
| 298 | return FALSE; |
| 299 | } |
| 300 | } |
| 301 | if ($content_type == 'comment' && module_exists('comment')) { |
| 302 | if (!$map[2] = comment_load($map[2])) { |
| 303 | return FALSE; |
| 304 | } |
| 305 | } |
| 306 | $op == $map[3]; |
| 307 | if ($op == 'publish' || $op == 'unpublish') { |
| 308 | $map[0] = 'akismet_callback_set_published_status'; |
| 309 | } |
| 310 | else if ($op == 'submit-spam' || $op == 'submit-ham') { |
| 311 | $map[0] = 'akismet_callback_set_spam_status'; |
| 312 | } |
| 313 | return $map[$index]; |
| 314 | } |
| 315 | |
| 316 | function akismet_access_callback($callback, $content_type, $object, $op) { |
| 317 | if ($content_type == 'node' && !node_access($map[2])) { |
| 318 | return FALSE; |
| 319 | } |
| 320 | if (function_exists($callback && !akismet_is_spam_moderator(akismet_content_get_moderator_type($content_type, $object)))) { |
| 321 | return FALSE; |
| 322 | } |
| 323 | // Is there a comment access check we need to run? If yes, then do the same as above. |
| 324 | return TRUE; |
| 325 | } |
| 326 | |
| 327 | function akismet_page($callback, $content_type, $object, $op) { |
| 328 | if (function_exists($callback)) { |
| 329 | return $callback($content_type, $object, $op); |
| 330 | } |
| 331 | drupal_not_found(); |
| 332 | } |
| 333 | |
| 334 | /** |
| 335 | * Implementation of hook_link(). |
| 336 | */ |
| 337 | function akismet_link($type, $content = 0, $main = 0) { |
| 338 | $links = array(); |
| 339 | if ($type == 'node' && akismet_is_spam_moderator($content->type)) { |
| 340 | if (variable_get('akismet_node_publish_links', 0)) { |
| 341 | if ($content->status) { |
| 342 | $links['akismet_node_unpublish'] = array('title' => t('Unpublish'), 'href' => 'akismet/node/'. $content->nid .'/unpublish'); |
| 343 | } |
| 344 | else { |
| 345 | $links['akismet_node_publish'] = array('title' => t('Publish'), 'href' => 'akismet/node/'. $content->nid .'/publish'); |
| 346 | } |
| 347 | } |
| 348 | if (variable_get('akismet_node_spam_links', 0)) { |
| 349 | if (akismet_content_is_spam('node', $content->nid)) { |
| 350 | $links['akismet_node_ham'] = array('title' => (variable_get('akismet_connection_enabled', 1) ? t('Submit ham') : t('Mark as ham')), 'href' => 'akismet/node/'. $content->nid .'/submit-ham'); |
| 351 | } |
| 352 | else { |
| 353 | $links['akismet_node_spam'] = array('title' => (variable_get('akismet_connection_enabled', 1) ? t('Submit spam') : t('Mark as spam')), 'href' => 'akismet/node/'. $content->nid .'/submit-spam'); |
| 354 | } |
| 355 | } |
| 356 | } |
| 357 | else if ($type == 'comment' && akismet_is_spam_moderator('comments')) { |
| 358 | if (variable_get('akismet_comment_publish_links', 1)) { |
| 359 | if ($content->status == COMMENT_PUBLISHED) { |
| 360 | $links['akismet_comment_unpublish'] = array('title' => t('Unpublish'), 'href' => 'akismet/comment/'. $content->cid .'/unpublish'); |
| 361 | } |
| 362 | else if ($content->status == COMMENT_NOT_PUBLISHED) { |
| 363 | $links['akismet_comment_publish'] = array('title' => t('Publish'), 'href' => 'akismet/comment/'. $content->cid .'/publish'); |
| 364 | } |
| 365 | } |
| 366 | if (variable_get('akismet_comment_spam_links', 1)) { |
| 367 | if (akismet_content_is_spam('comment', $content->cid)) { |
| 368 | $links['akismet_comment_ham'] = array('title' => (variable_get('akismet_connection_enabled', 1) ? t('Submit ham') : t('Mark as ham')), 'href' => 'akismet/comment/'. $content->cid .'/submit-ham'); |
| 369 | } |
| 370 | else { |
| 371 | $links['akismet_comment_spam'] = array('title' => (variable_get('akismet_connection_enabled', 1) ? t('Submit spam') : t('Mark as spam')), 'href' => 'akismet/comment/'. $content->cid .'/submit-spam'); |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | return $links; |
| 376 | } |
| 377 | |
| 378 | /** |
| 379 | * Menu callback; publish/unpublish content. |
| 380 | * |
| 381 | * @param string Content type; it can be 'node' or 'comment'. |
| 382 | * @param integer Content ID; can be either a nid or a cid. |
| 383 | * @param string Operation; it can be 'publish' or 'unpublish'. |
| 384 | */ |
| 385 | function akismet_callback_set_published_status($content_type, $object, $op) { |
| 386 | // Load the content (existence has been checked in hook_menu). |
| 387 | $content = akismet_content_load($content_type, $object); |
| 388 | |
| 389 | if ($content_type == 'node') { |
| 390 | $is_published = ($content->status ? TRUE : FALSE); |
| 391 | } |
| 392 | else { // comment |
| 393 | $is_published = ($content->status == COMMENT_PUBLISHED ? TRUE : FALSE); |
| 394 | } |
| 395 | |
| 396 | if ($op == 'publish' && !$is_published) { |
| 397 | akismet_content_publish_operation($content_type, $content, 'publish'); |
| 398 | } |
| 399 | else if ($op == 'unpublish' && $is_published) { |
| 400 | akismet_content_publish_operation($content_type, $content, 'unpublish'); |
| 401 | } |
| 402 | |
| 403 | if ($content_type == 'node') { |
| 404 | drupal_goto('node/'. $content->nid); |
| 405 | } |
| 406 | else { // comment |
| 407 | drupal_goto('node/'. $content->nid, NULL, 'comment-'. $content->cid); |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | /** |
| 412 | * Menu callback; mark/unmark content as spam. |
| 413 | * |
| 414 | * When content is marked as spam, it is also unpublished (if necessary) and vice-versa. |
| 415 | * |
| 416 | * @param string Content type; it can be 'node' or 'comment'. |
| 417 | * @param integer Content ID; can be either a nid or a cid. |
| 418 | * @param string Operation; it can be 'submit-spam' or 'submit-ham'. |
| 419 | */ |
| 420 | function akismet_callback_set_spam_status($content_type, $object, $op) { |
| 421 | $is_spam = akismet_content_is_spam($content_type, $object); |
| 422 | |
| 423 | // Load the content (existence has been checked in hook_menu). |
| 424 | $content = akismet_content_load($content_type, $object); |
| 425 | |
| 426 | if ($content_type == 'node') { |
| 427 | $is_published = ($content->status ? TRUE : FALSE); |
| 428 | } |
| 429 | else { // comment |
| 430 | $is_published = ($content->status == COMMENT_PUBLISHED ? TRUE : FALSE); |
| 431 | } |
| 432 | |
| 433 | // insert or remove the spam marker (publishing/unpublishing if necessary). |
| 434 | if ($op == 'submit-spam') { |
| 435 | if (!$is_spam) { |
| 436 | akismet_content_spam_operation($content_type, $content, 'submit-spam'); |
| 437 | } |
| 438 | if ($is_published) { |
| 439 | akismet_content_publish_operation($content_type, $content, 'unpublish'); |
| 440 | } |
| 441 | } |
| 442 | else if ($op == 'submit-ham') { |
| 443 | if ($is_spam) { |
| 444 | akismet_content_spam_operation($content_type, $content, 'submit-ham'); |
| 445 | } |
| 446 | if (!$is_published) { |
| 447 | akismet_content_publish_operation($content_type, $content, 'publish'); |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | if ($content_type == 'node') { |
| 452 | drupal_goto('node/'. $content->nid); |
| 453 | } |
| 454 | else { // comment |
| 455 | drupal_goto('node/'. $content->nid, NULL, 'comment-'. $content->cid); |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | /** |
| 460 | * Implementation of hook_nodeapi(). |
| 461 | */ |
| 462 | function akismet_nodeapi(&$node, $op, $teaser, $page) { |
| 463 | switch ($op) { |
| 464 | case 'insert': |
| 465 | case 'update': |
| 466 | // If Akismet connections are not enabled, we have nothing else to do here. |
| 467 | if (!variable_get('akismet_connection_enabled', 1)) { |
| 468 | akismet_notify_moderators('node', $node, ($node->status ? TRUE : FALSE), FALSE); |
| 469 | break; |
| 470 | } |
| 471 | |
| 472 | // Also quit asap, if current user has administration permission |
| 473 | // or permission to post without spam checking. |
| 474 | if (akismet_is_spam_moderator($node->type) || user_access('post with no akismet checking')) { |
| 475 | akismet_notify_moderators('node', $node, ($node->status ? TRUE : FALSE), FALSE); |
| 476 | break; |
| 477 | } |
| 478 | |
| 479 | // Now, check if it's about a node type that we have not been explicitly requested to check. |
| 480 | $check_nodetypes = variable_get('akismet_check_nodetypes', array()); |
| 481 | if (!is_array($check_nodetypes) || !isset($check_nodetypes[$node->type]) || !$check_nodetypes[$node->type]) { |
| 482 | akismet_notify_moderators('node', $node, ($node->status ? TRUE : FALSE), FALSE); |
| 483 | break; |
| 484 | } |
| 485 | |
| 486 | // Ok, let's send a query to Akismet. |
| 487 | $akismet_api_result = akismet_api_cmd_comment_check(akismet_prepare_comment_data('node', $node)); |
| 488 | if ($akismet_api_result == AKISMET_API_RESULT_IS_HAM) { |
| 489 | akismet_notify_moderators('node', $node, ($node->status ? TRUE : FALSE), FALSE); |
| 490 | } |
| 491 | else { |
| 492 | if ($akismet_api_result == AKISMET_API_RESULT_IS_SPAM) { |
| 493 | // Oops! Akismet is telling us we got spammed, let's mark the comment as such. |
| 494 | akismet_content_spam_operation('node', $node, 'submit-spam', FALSE); |
| 495 | // Increment Akismet spam counter |
| 496 | variable_set('akismet_counter_spam', akismet_get_spam_counter() + 1); |
| 497 | |
| 498 | akismet_notify_moderators('node', $node, FALSE, TRUE); |
| 499 | } |
| 500 | else { |
| 501 | akismet_notify_moderators('node', $node, FALSE, FALSE); |
| 502 | } |
| 503 | |
| 504 | // Unpublish the node, if necessary. |
| 505 | if ($node->status) { |
| 506 | akismet_content_publish_operation('node', $node, 'unpublish', FALSE); |
| 507 | } |
| 508 | |
| 509 | // Since users won't see their content published, show them a polite explanation on why. |
| 510 | $content_type_name = node_get_types('name', $node); |
| 511 | drupal_set_message(t('Your %content-type-name has been queued for moderation by site administrators and will be published after approval.', array('%content-type-name' => $content_type_name))); |
| 512 | |
| 513 | // Record the event to watchdog. |
| 514 | if ($akismet_api_result == AKISMET_API_RESULT_ERROR) { |
| 515 | watchdog('content', 'Akismet service seems to be down, %content-type-name queued for manual approval: %title', array('%content-type-name' => $content_type_name, '%title' => $node->title), WATCHDOG_WARNING, l(t('view'), 'node/'. $node->nid)); |
| 516 | } |
| 517 | else { |
| 518 | watchdog('content', 'Spam detected by Akismet in %content-type-name: %title', array('%content-type-name' => $content_type_name, '%title' => $node->title), WATCHDOG_WARNING, l(t('view'), 'node/'. $node->nid)); |
| 519 | // If requested to, generate a delay so the spammer has to wait for a while. |
| 520 | if (($seconds = variable_get('akismet_antispambot_delay', 60)) > 0) { |
| 521 | sleep($seconds); |
| 522 | } |
| 523 | } |
| 524 | } |
| 525 | break; |
| 526 | case 'delete': |
| 527 | db_query('DELETE FROM {akismet_spam_marks} WHERE content_type = \'node\' AND content_id = %d', $node->nid); |
| 528 | break; |
| 529 | } |
| 530 | } |
| 531 | |
| 532 | /** |
| 533 | * Implementation of hook_comment(). |
| 534 | */ |
| 535 | function akismet_comment(&$comment, $op) { |
| 536 | switch ($op) { |
| 537 | case 'insert': |
| 538 | case 'update': |
| 539 | if (!variable_get('akismet_check_comments', 0) || akismet_is_spam_moderator('comments') || !variable_get('akismet_connection_enabled', 1)) { |
| 540 | akismet_notify_moderators('comment', $comment, ($comment->status == COMMENT_PUBLISHED ? TRUE : FALSE), FALSE); |
| 541 | } |
| 542 | break; |
| 543 | case 'delete': |
| 544 | db_query('DELETE FROM {akismet_spam_marks} WHERE content_type = \'comment\' AND content_id = %d', $comment->cid); |
| 545 | break; |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | /** |
| 550 | * Implementation of hook_form_alter(). |
| 551 | */ |
| 552 | function akismet_form_alter(&$form, &$form_state, $form_id) { |
| 553 | // Hook into comment edit/reply form. |
| 554 | if ($form_id == 'comment_form' && variable_get('akismet_check_comments', 1)) { |
| 555 | // ...only if current user is not moderator. |
| 556 | if (!akismet_is_spam_moderator('comments')) { |
| 557 | // ...also check if Akismet connections are enabled. |
| 558 | if (variable_get('akismet_connection_enabled', 1)) { |
| 559 | // This is the simple hook method, *if* we already have the $cid. |
| 560 | $form['#submit'][] = '_akismet_comment_form_submit'; |
| 561 | if (!isset($form['cid']) || !isset($form['cid']['#value']) || !is_numeric($form['cid']['#value'])) { |
| 562 | // This is a bit more complex, because the user is creating a new comment, so |
| 563 | // how can we get the $cid? See comments below, within our own submit callback. |
| 564 | $form['#comment_form_param1'] = $form['#submit']; |
| 565 | } |
| 566 | } |
| 567 | // Inject anti-spambot code, if requested to. |
| 568 | if (akismet_is_anti_spambot_enabled()) { |
| 569 | $form['#validate'][] = '_akismet_comment_form_validate'; |
| 570 | } |
| 571 | } |
| 572 | } |
| 573 | // Hook into node edit form. |
| 574 | else if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { |
| 575 | // ...only if current user is not moderator. |
| 576 | if (!akismet_is_spam_moderator('comments')) { |
| 577 | // ...also check if it's about a node type that we have been explicitly requested to check. |
| 578 | $check_nodetypes = variable_get('akismet_check_nodetypes', array()); |
| 579 | $node_type = $form['type']['#value']; |
| 580 | if (is_array($check_nodetypes) && isset($check_nodetypes[$node_type]) && $check_nodetypes[$node_type]) { |
| 581 | // Inject anti-spambot code, if requested to. |
| 582 | if (akismet_is_anti_spambot_enabled()) { |
| 583 | $form['#validate'][] = '_akismet_node_form_validate'; |
| 584 | } |
| 585 | } |
| 586 | } |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | /** |
| 591 | * Node form validate callback; check for spambots. |
| 592 | */ |
| 593 | function _akismet_node_form_validate($form, &$form_state) { |
| 594 | // Quit if there have already been errors in the form. |
| 595 | if (form_get_errors()) { |
| 596 | return; |
| 597 | } |
| 598 | |
| 599 | // Ok, let's build a quick query to see if we can catch a spambot. |
| 600 | global $user; |
| 601 | $antispambot_rules = akismet_get_anti_spambot_rules(); |
| 602 | $sql_where = array(); |
| 603 | $sql_args = array(); |
| 604 | if ($antispambot_rules['ip']) { |
| 605 | $sql_where[] = 's.hostname = \'%s\''; |
| 606 | $sql_args[] = ip_address(); |
| 607 | } |
| 608 | if ($antispambot_rules['body'] && !empty($form_state['values']['body'])) { |
| 609 | $sql_where[] = 'r.body = \'%s\''; |
| 610 | $sql_args[] = $form_state['values']['body']; |
| 611 | } |
| 612 | if ($antispambot_rules['mail'] && !empty($user->mail)) { |
| 613 | $sql_where[] = 's.mail = \'%s\''; |
| 614 | $sql_args[] = $user->mail; |
| 615 | } |
| 616 | |
| 617 | if (count($sql_where) > 0) { |
| 618 | if ($antispambot_rules['body'] || $antispambot_rules['mail']) { |
| 619 | $sql_stmt = 'SELECT 1 FROM {node} n INNER JOIN {node_revisions} r ON r.nid = n.nid INNER JOIN {akismet_spam_marks} s ON s.content_type = \'node\' AND s.content_id = n.nid WHERE (%cond)'; |
| 620 | } |
| 621 | else { |
| 622 | $sql_stmt = 'SELECT 1 FROM {akismet_spam_marks} s WHERE s.content_type = \'node\' AND (%cond)'; |
| 623 | } |
| 624 | $sql_stmt = str_replace('%cond', implode(' OR ', $sql_where), $sql_stmt); |
| 625 | if (db_result(db_query($sql_stmt, $sql_args, 0, 1))) { |
| 626 | akismet_anti_spambot_action(array( |
| 627 | t('SQL') => _akismet_translate_query($sql_stmt, $sql_args), |
| 628 | t('E-mail') => (isset($user->mail) ? $user->mail : ''), |
| 629 | t('Body') => $form_state['values']['body'] |
| 630 | )); |
| 631 | } |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | /** |
| 636 | * Comment form validate callback; check for spambots. |
| 637 | */ |
| 638 | function _akismet_comment_form_validate($form, &$form_state) { |
| 639 | // Quit if there have already been errors in the form. |
| 640 | if (form_get_errors()) { |
| 641 | return; |
| 642 | } |
| 643 | |
| 644 | // Ok, let's build a quick query to see if we can catch a spambot. |
| 645 | $antispambot_rules = akismet_get_anti_spambot_rules(); |
| 646 | $sql_where = array(); |
| 647 | $sql_args = array(); |
| 648 | if ($antispambot_rules['ip']) { |
| 649 | $sql_where[] = 's.hostname = \'%s\''; |
| 650 | $sql_args[] = ip_address(); |
| 651 | } |
| 652 | if ($antispambot_rules['body'] && !empty($form_state['values']['comment'])) { |
| 653 | $sql_where[] = 'c.comment = \'%s\''; |
| 654 | $sql_args[] = $form_state['values']['comment']; |
| 655 | } |
| 656 | if ($antispambot_rules['mail'] && !empty($form_state['values']['mail'])) { |
| 657 | $sql_where[] = 's.mail = \'%s\''; |
| 658 | $sql_args[] = $$form_state['values']['mail']; |
| 659 | } |
| 660 | |
| 661 | if (count($sql_where) > 0) { |
| 662 | if ($antispambot_rules['body'] || $antispambot_rules['mail']) { |
| 663 | $sql_stmt = 'SELECT 1 FROM {comments} c INNER JOIN {akismet_spam_marks} s ON s.content_type = \'comment\' AND s.content_id = c.cid WHERE (%cond)'; |
| 664 | } |
| 665 | else { |
| 666 | $sql_stmt = 'SELECT 1 FROM {akismet_spam_marks} s WHERE s.content_type = \'comment\' AND (%cond)'; |
| 667 | } |
| 668 | $sql_stmt = str_replace('%cond', implode(' OR ', $sql_where), $sql_stmt); |
| 669 | if (db_result(db_query($sql_stmt, $sql_args, 0, 1))) { |
| 670 | akismet_anti_spambot_action(array( |
| 671 | t('SQL') => _akismet_translate_query($sql_stmt, $sql_args), |
| 672 | t('E-mail') => $form_state['values']['mail'], |
| 673 | t('Comment') => $form_state['values']['comment'] |
| 674 | )); |
| 675 | } |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | /** |
| 680 | * Comment form submit callback; check for spam. |
| 681 | */ |
| 682 | function _akismet_comment_form_submit($form, &$form_state, $original_submit_callback = NULL) { |
| 683 | // Our default destination. It doesn't need to override the original. |
| 684 | $goto = NULL; |
| 685 | |
| 686 | // If the comment is being edited, then there's no problem to get the $cid. |
| 687 | // In this case, the original #submit callback has already been called. |
| 688 | if (isset($form_state['values']['cid'])) { |
| 689 | $cid = $form_state['values']['cid']; |
| 690 | } |
| 691 | // However, if the comment is being created, we'll try to get the $cid from the |
| 692 | // return value of the original #submit callback. It's an array that customizes |
| 693 | // the URL the user should be sent when the form is submitted. It contains the |
| 694 | // $cid in the last argument, in the form of "comment-$cid", the hash of the URL. |
| 695 | else { |
| 696 | // Invoke the previous submit callbacks and capture their return values to try |
| 697 | // to get the $cid from there. |
| 698 | |
| 699 | // The first critical part with this approach is that we have to emmulate the |
| 700 | // $form argument that form.inc::drupal_submit_form() expects. At this point in |
| 701 | // time, this function just uses the '#submit' element, but that could change in |
| 702 | // the future. We have to keep an eye here, or think about a completely different |
| 703 | // approach. Hopefully, this one will remain stable during the 4.x lifecycle. |
| 704 | $form = array('#submit' => $original_submit_callback); |
| 705 | |
| 706 | // The second critical part is that we expect to find the $cid in the 3rd element |
| 707 | // of the $goto array, as described above. |
| 708 | $goto = drupal_submit_form($form_id, $form); |
| 709 | if (is_array($goto) && isset($goto[2]) && preg_match('#^comment-([0-9]+)$#', $goto[2], $match)) { |
| 710 | $cid = $match[1]; |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | // Once we have a $cid, we can (try to) load the comment with all relevant |
| 715 | // information that we need to make the Akismet request to check for spam. |
| 716 | if ($cid) { |
| 717 | $comment = akismet_content_load('comment', $cid); |
| 718 | // If we got a comment, send query to Akismet. |
| 719 | if ($comment) { |
| 720 | $akismet_api_result = akismet_api_cmd_comment_check(akismet_prepare_comment_data('comment', $comment)); |
| 721 | if ($akismet_api_result == AKISMET_API_RESULT_IS_HAM) { |
| 722 | akismet_notify_moderators('comment', $comment, ($comment->status == COMMENT_PUBLISHED ? TRUE : FALSE), FALSE); |
| 723 | } |
| 724 | else { |
| 725 | if ($akismet_api_result == AKISMET_API_RESULT_IS_SPAM) { |
| 726 | // Oops! Akismet is telling us we got spammed, let's mark the comment as such. |
| 727 | akismet_content_spam_operation('comment', $comment, 'submit-spam', FALSE); |
| 728 | // Increment Akismet spam counter |
| 729 | variable_set('akismet_counter_spam', akismet_get_spam_counter() + 1); |
| 730 | |
| 731 | akismet_notify_moderators('comment', $comment, FALSE, TRUE); |
| 732 | } |
| 733 | else { |
| 734 | akismet_notify_moderators('comment', $comment, FALSE, FALSE); |
| 735 | } |
| 736 | |
| 737 | // Unpublish the comment, if necessary. |
| 738 | if ($comment->status == COMMENT_PUBLISHED) { |
| 739 | akismet_content_publish_operation('comment', $comment, 'unpublish', FALSE); |
| 740 | } |
| 741 | |
| 742 | // Since users won't see their replies published, show them a polite explanation on why. |
| 743 | drupal_set_message(t('Your comment has been queued for moderation by site administrators and will be published after approval.')); |
| 744 | |
| 745 | // Record the event to watchdog. |
| 746 | if ($akismet_api_result == AKISMET_API_RESULT_ERROR) { |
| 747 | watchdog('content', 'Akismet service seems to be down, comment queued for manual approval: %subject', array('%subject' => $comment->subject), WATCHDOG_WARNING, l(t('view'), 'node/'. $comment->nid, NULL, NULL, 'comment-'. $comment->cid)); |
| 748 | } |
| 749 | else { |
| 750 | watchdog('content', 'Spam detected by Akismet in comment: %subject', array('%subject' => $comment->subject), WATCHDOG_WARNING, l(t('view'), 'node/'. $comment->nid, NULL, NULL, 'comment-'. $comment->cid)); |
| 751 | // If requested to, generate a delay so the spammer has to wait for a while. |
| 752 | if (($seconds = variable_get('akismet_antispambot_delay', 60)) > 0) { |
| 753 | sleep($seconds); |
| 754 | } |
| 755 | } |
| 756 | } |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | // Return NULL or the destination returned by the original #submit callback. |
| 761 | return $goto; |
| 762 | } |
| 763 | |
| 764 | /** |
| 765 | * Get anti-spambot rules. |
| 766 | * |
| 767 | * @return array |
| 768 | */ |
| 769 | function akismet_get_anti_spambot_rules() { |
| 770 | static $antispambot_rules = FALSE; |
| 771 | if (!$antispambot_rules) { |
| 772 | $antispambot_rules = array(); |
| 773 | $options = variable_get('akismet_antispambot_rules', array()); |
| 774 | if (is_array($options)) { |
| 775 | foreach ($options as $key => $value) { |
| 776 | if (is_string($key)) { |
| 777 | $antispambot_rules[$key] = ($key === $value ? TRUE : FALSE); |
| 778 | } |
| 779 | } |
| 780 | } |
| 781 | } |
| 782 | return $antispambot_rules; |
| 783 | } |
| 784 | |
| 785 | /** |
| 786 | * Check if anti-spambot options are enabled. |
| 787 | * |
| 788 | * @return boolean TRUE if enabled; FALSE otherwise. |
| 789 | */ |
| 790 | function akismet_is_anti_spambot_enabled() { |
| 791 | $antispambot_rules = akismet_get_anti_spambot_rules(); |
| 792 | return (count($antispambot_rules) > 0 ? TRUE : FALSE); |
| 793 | } |
| 794 | |
| 795 | /** |
| 796 | * Perform an anti-spambot action based on module settings. |
| 797 | * |
| 798 | * @param array Extra data, used here to enhance the logged information, for debugging purposes. |
| 799 | */ |
| 800 | function akismet_anti_spambot_action($debug_info) { |
| 801 | $antispambot_action = variable_get('akismet_antispambot_action', '503'); |
| 802 | |
| 803 | // First action is generate a delay, if requested to. |
| 804 | if (($seconds = variable_get('akismet_antispambot_delay', 60)) > 0) { |
| 805 | sleep($seconds); |
| 806 | } |
| 807 | |
| 808 | // If no other action was set, we're done. |
| 809 | if ($antispambot_action == 'none') { |
| 810 | return; |
| 811 | } |
| 812 | |
| 813 | $items = array(); |
| 814 | foreach ($debug_info as $label => $value) { |
| 815 | $items[] = '<strong>'. check_plain($label) .'</strong>: '. check_plain($value); |
| 816 | } |
| 817 | |
| 818 | // From here on, the request is killed using different methods. |
| 819 | if ($antispambot_action == '403d') { |
| 820 | drupal_access_denied(); |
| 821 | $message = t('Spambot detected (action: 403 Forbidden).'); |
| 822 | } |
| 823 | else if ($antispambot_action == '403') { |
| 824 | @header('HTTP/1.0 403 Forbidden'); |
| 825 | print t('Access denied'); |
| 826 | $message = t('Spambot detected (action: 403 Forbidden).'); |
| 827 | } |
| 828 | else { // 503 |
| 829 | @header('HTTP/1.0 503 Service unavailable'); |
| 830 | print t('Service unavailable'); |
| 831 | $message = t('Spambot detected (action: 503 Service unavailable).'); |
| 832 | } |
| 833 | |
| 834 | watchdog('akismet', '%message<p>Additional information:</p>%items', array('%message' => $message, '%items' => theme('item_list', $items))); |
| 835 | |
| 836 | module_invoke_all('exit'); |
| 837 | exit; |
| 838 | } |
| 839 | |
| 840 | /** |
| 841 | * Expand query for debugging purposes. |
| 842 | * |
| 843 | * @param string SQL statement. |
| 844 | * @param mixed array or variable list of arguments. |
| 845 | */ |
| 846 | function _akismet_translate_query($query) { |
| 847 | $args = func_get_args(); |
| 848 | array_shift($args); |
| 849 | $query = db_prefix_tables($query); |
| 850 | if (isset($args[0]) && is_array($args[0])) { // 'All arguments in one array' syntax |
| 851 | $args = $args[0]; |
| 852 | } |
| 853 | _db_query_callback($args, TRUE); |
| 854 | $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); |
| 855 | return $query; |
| 856 | } |
| 857 | |
| 858 | /** |
| 859 | * Check if specified content is marked as spam. |
| 860 | * |
| 861 | * @param string Content type; can be either 'node' or 'comment'. |
| 862 | * @param integer Content ID; can be either a nid or a cid. |
| 863 | * @return boolean TRUE if content is marked as spam; FALSE otherwise. |
| 864 | */ |
| 865 | function akismet_content_is_spam($content_type, $content_id) { |
| 866 | return db_result(db_query('SELECT 1 FROM {akismet_spam_marks} WHERE content_type = \'%s\' AND content_id = %d', $content_type, $content_id)); |
| 867 | } |
| 868 | |
| 869 | /** |
| 870 | * Get moderator type required for specified content. |
| 871 | * |
| 872 | * @param string Content type; can be either 'node' or 'comment'. |
| 873 | * @param integer Content ID; can be either a nid or a cid. |
| 874 | * @return string Moderator Type or empty string if content is not found. |
| 875 | */ |
| 876 | function akismet_content_get_moderator_type($content_type, $content_id) { |
| 877 | if ($content_type == 'node') { |
| 878 | $moderator_type = db_result(db_query('SELECT type FROM {node} WHERE nid = %d', $content_id)); |
| 879 | if (!$moderator_type) { |
| 880 | $moderator_type = ''; |
| 881 | } |
| 882 | } |
| 883 | else if ($content_type == 'comment') { |
| 884 | $moderator_type = (db_result(db_query('SELECT 1 FROM {comments} WHERE cid = %d', $content_id)) ? 'comments' : ''); |
| 885 | } |
| 886 | else { |
| 887 | $moderator_type = ''; |
| 888 | } |
| 889 | return $moderator_type; |
| 890 | } |
| 891 | |
| 892 | /** |
| 893 | * Get the types the current user is allowed to moderate. |
| 894 | * |
| 895 | * @param object The account to check; use current user if not given. |
| 896 | * @return array Moderator Types. |
| 897 | */ |
| 898 | function akismet_get_moderator_types($account = NULL) { |
| 899 | global $user; |
| 900 | static $node_types = FALSE; |
| 901 | static $moderator_types = array(); |
| 902 | |
| 903 | if (is_null($account)) { |
| 904 | $account = $user; |
| 905 | } |
| 906 | |
| 907 | if ($node_types === FALSE) { |
| 908 | $node_types = node_get_types('names'); |
| 909 | } |
| 910 | |
| 911 | if (!isset($moderator_types[$account->uid])) { |
| 912 | if (user_access('administer nodes', $account)) { |
| 913 | foreach ($node_types as $type => $name) { |
| 914 | $moderator_types[$account->uid][$type] = $name; |
| 915 | } |
| 916 | } |
| 917 | else { |
| 918 | foreach ($node_types as $type => $name) { |
| 919 | if (user_access('moderate spam in nodes of type '. $node_types[$type], $account)) { |
| 920 | $moderator_types[$account->uid][$type] = $name; |
| 921 | } |
| 922 | } |
| 923 | } |
| 924 | |
| 925 | if (user_access('administer comments', $account) || user_access('moderate spam in comments', $account)) { |
| 926 | $moderator_types[$account->uid]['comments'] = t('comments'); |
| 927 | } |
| 928 | } |
| 929 | |
| 930 | return $moderator_types[$account->uid]; |
| 931 | } |
| 932 | |
| 933 | /** |
| 934 | * Is current user spam moderator? |
| 935 | * |
| 936 | * @param string Moderator Type (comments, node type or NULL). |
| 937 | * @param object The account to check; use current user if not given. |
| 938 | * @return boolean TRUE if current user is moderator of specified type; FALSE otherwise. |
| 939 | */ |
| 940 | function akismet_is_spam_moderator($moderator_type = NULL, $account = NULL) { |
| 941 | global $user; |
| 942 | if (is_null($account)) { |
| 943 | $account = $user; |
| 944 | } |
| 945 | $moderator_types = akismet_get_moderator_types($account); |
| 946 | if (is_null($moderator_type)) { |
| 947 | return (count($moderator_types) > 0 ? TRUE : FALSE); |
| 948 | } |
| 949 | return isset($moderator_types[$moderator_type]); |
| 950 | } |
| 951 | |
| 952 | /** |
| 953 | * Notify moderators of new/updated content, only content needing approval or nothing at all. |
| 954 | * |
| 955 | * @param string Content type; can be either 'node' or 'comment'. |
| 956 | * @param object Content object. |
| 957 | * @param boolean TRUE if content is in published status. |
| 958 | * @param boolean TRUE if content has been marked as spam. |
| 959 | */ |
| 960 | function akismet_notify_moderators($content_type, $content, $is_published, $is_spam) { |
| 961 | global $user, $base_url; |
| 962 | |
| 963 | // Proceed only if e-mail notifications are enabled. |
| 964 | if (!variable_get('akismet_email_enabled', 0)) { |
| 965 | return; |
| 966 | } |
| 967 | |
| 968 | // Make sure we have an object. |
| 969 | $content = (object)$content; |
| 970 | |
| 971 | // Compute the related moderator permission. |
| 972 | if ($content_type == 'comment') { |
| 973 | $moderator_permission = 'moderate spam in comments'; |
| 974 | $administer_permission = 'administer comments'; |
| 975 | } |
| 976 | else { |
| 977 | $moderator_types = akismet_get_moderator_types($account); |
| 978 | $moderator_permission = 'moderate spam in nodes of type '. $moderator_types[$content->type]; |
| 979 | $administer_permission = 'administer nodes'; |
| 980 | } |
| 981 | |
| 982 | // Obtain list of moderators of the specified content type. |
| 983 | $sql = 'SELECT u.uid, u.name, u.mail, m.email_for'. |
| 984 | ' FROM {permission} p'. |
| 985 | ' INNER JOIN {users_roles} r ON r.rid = p.rid'. |
| 986 | ' INNER JOIN {users} u ON u.uid = r.uid OR u.uid = 1'. |
| 987 | ' LEFT JOIN {akismet_moderator} m ON m.uid = u.uid'. |
| 988 | ' WHERE p.perm LIKE \'%%%s%%\''. |
| 989 | ' OR p.perm LIKE \'%%%s%%\''; |
| 990 | $result = db_query($sql, $moderator_permission, $administer_permission); |
| 991 | $moderators = array(); |
| 992 | while ($u = db_fetch_object($result)) { |
| 993 | if ($u->uid != $user->uid) { |
| 994 | $moderators[$u->uid] = array( |
| 995 | 'name' => $u->name, |
| 996 | 'email_to' => $u->mail, |
| 997 | 'email_for' => (!is_null($u->email_for) ? $u->email_for : 'approval') |
| 998 | ); |
| 999 | } |
| 1000 | } |
| 1001 | |
| 1002 | // Extract unique email addresses and ignore those who have requested to not get e-mail notifications. |
| 1003 | $unique_emails = array(); |
| 1004 | foreach ($moderators as $uid => $moderator) { |
| 1005 | if ($moderator['email_for'] == 'all' || ($moderator['email_for'] == 'approval' && !$is_published)) { |
| 1006 | if (!isset($unique_emails[$moderator['email_to']])) { |
| 1007 | $unique_emails[$moderator['email_to']] = $uid; |
| 1008 | } |
| 1009 | } |
| 1010 | } |
| 1011 | if (count($unique_emails) <= 0) { |
| 1012 | return; |
| 1013 | } |
| 1014 | |
| 1015 | // If this is about a comment, try to load the node. |
| 1016 | // Also, prepare arguments for notification message. |
| 1017 | $site_name = variable_get('site_name', t('Drupal')); |
| 1018 | if ($content_type == 'comment') { |
| 1019 | if (!($node = akismet_content_load('node', $content->nid))) { |
| 1020 | watchdog('akismet', 'An error has ocurred while trying to notify moderators about a comment. The associated node could not be loaded.', array(), WATCHDOG_NOTICE, l(t('view'), 'node/'. $content->nid, NULL, NULL, 'comment-'. $content->cid)); |
| 1021 | return; |
| 1022 | } |
| 1023 | $message_args = array( |
| 1024 | '@title-label' => t('Subject'), |
| 1025 | '@content-title' => $content->subject, |
| 1026 | '@content-type' => t('comment'), |
| 1027 | '!content-link' => url('node/'. $content->nid, array('fragment' => 'comment-'. $content->cid, 'absolute' => TRUE)) |
| 1028 | ); |
| 1029 | } |
| 1030 | else { |
| 1031 | $message_args = array( |
| 1032 | '@title-label' => t('Title'), |
| 1033 | '@content-title' => $content->title, |
| 1034 | '@content-type' => $moderator_types[$content->type], |
| 1035 | '!content-link' => url('node/'. $content->nid, array ('absolute' => TRUE)) |
| 1036 | ); |
| 1037 | } |
| 1038 | $message_args['@content-status'] = ($is_published ? t('published') : t('not published')) . ($is_spam ? ' ('. t('marked as spam') .')' : ''); |
| 1039 | $message_args['@site-name'] = $site_name; |
| 1040 | $message_args['!site-link'] = $base_url . base_path(); |
| 1041 | $message_args['@type'] = $moderator_types[$content->type]; |
| 1042 | |
| 1043 | $message_title = t('[@site-name] moderator notification - Posted @content-type \'@content-title\'', $message_args); |
| 1044 | |
| 1045 | $message_body = t(<<<EOT |
| 1046 | Hello @user-name, |
| 1047 | |
| 1048 | You can use the following information to review this @content-type: |
| 1049 | |
| 1050 | @title-label: @content-title |
| 1051 | URL: !content-link |
| 1052 | Status: @content-status |
| 1053 | |
| 1054 | Please, do not reply to this e-mail. It is an automated notification you are receiving because you are a moderator at @site-name. If you no longer wish to receive such notifications, you can change your moderator settings in your user profile. |
| 1055 | |
| 1056 | Thank you |
| 1057 | |
| 1058 | !site-link |
| 1059 | EOT |
| 1060 | , $message_args); |
| 1061 | |
| 1062 | // Log the notification to watchdog. |
| 1063 | watchdog('akismet', 'Trying to notify the following recipients: %emails', array('%emails' => implode(', ', array_keys($unique_emails)))); |
| 1064 | |
| 1065 | // Send e-mails. |
| 1066 | foreach ($unique_emails as $email_to => $uid) { |
| 1067 | drupal_mail( |
| 1068 | 'akismet_moderator_notification', |
| 1069 | $email_to, |
| 1070 | $message_title, |
| 1071 | str_replace('@user-name', check_plain($moderators[$uid]['name']), $message_body) |
| 1072 | ); |
| 1073 | } |
| 1074 | } |
| 1075 | |
| 1076 | /** |
| 1077 | * Implementation of hook_user(). |
| 1078 | */ |
| 1079 | function akismet_user($op, &$edit, &$account, $category = NULL) { |
| 1080 | $moderator_email_for_options = array( |
| 1081 | 'all' => t('All new (or updated) content'), |
| 1082 | 'approval' => t('Only content needing approval'), |
| 1083 | 'never' => t('Never') |
| 1084 | ); |
| 1085 | switch ($op) { |
| 1086 | case 'form': |
| 1087 | if ($category == 'account' |