Changeset 3323859
- Timestamp:
- 07/07/2025 07:02:26 PM (9 months ago)
- Location:
- akismet/trunk
- Files:
-
- 5 edited
-
akismet.php (modified) (2 diffs)
-
class.akismet-admin.php (modified) (2 diffs)
-
class.akismet-rest-api.php (modified) (3 diffs)
-
class.akismet.php (modified) (16 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
akismet/trunk/akismet.php
r3309445 r3323859 7 7 Plugin URI: https://akismet.com/ 8 8 Description: Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. Akismet Anti-spam keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key. 9 Version: 5.5 9 Version: 5.5a1 10 10 Requires at least: 5.8 11 11 Requires PHP: 7.2 … … 40 40 } 41 41 42 define( 'AKISMET_VERSION', '5.5 ' );42 define( 'AKISMET_VERSION', '5.5a1' ); 43 43 define( 'AKISMET__MINIMUM_WP_VERSION', '5.8' ); 44 44 define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
akismet/trunk/class.akismet-admin.php
r3277808 r3323859 697 697 $message = esc_html( __( 'Akismet cleared this comment.', 'akismet' ) ); 698 698 break; 699 case 'check-ham-pending': 700 $message = esc_html( __( 'Akismet provisionally cleared this comment.', 'akismet' ) ); 701 break; 699 702 case 'wp-blacklisted': 700 703 case 'wp-disallowed': … … 1431 1434 */ 1432 1435 public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) { 1433 if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) { 1436 if ( 1437 in_array( 1438 $key, 1439 array( 1440 'akismet_as_submitted', 1441 'akismet_delay_moderation_email', 1442 'akismet_delayed_moderation_email', 1443 'akismet_rechecking', 1444 'akismet_schedule_approval_fallback', 1445 'akismet_schedule_email_fallback', 1446 'akismet_skipped_microtime', 1447 ) 1448 ) 1449 ) { 1434 1450 return true; 1435 1451 } -
akismet/trunk/class.akismet-rest-api.php
r3182785 r3323859 515 515 Akismet::log( 'Found matching comment.', $comments ); 516 516 517 $current_status = wp_get_comment_status( $comments[0] ); 517 $comment = $comments[0]; 518 519 $current_status = wp_get_comment_status( $comment ); 518 520 519 521 $result = $webhook_comment['result']; … … 525 527 if ( 'spam' != $current_status ) { 526 528 // The comment is not classified as spam. If Akismet was the one to act on it, move it to spam. 527 if ( Akismet::last_comment_status_change_came_from_akismet( $comment s[0]->comment_ID ) ) {529 if ( Akismet::last_comment_status_change_came_from_akismet( $comment->comment_ID ) ) { 528 530 Akismet::log( 'Comment is not spam; marking as spam.' ); 529 531 530 wp_spam_comment( $comment s[0]);531 Akismet::update_comment_history( $comment s[0]->comment_ID, '', 'webhook-spam' );532 wp_spam_comment( $comment ); 533 Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-spam' ); 532 534 } else { 533 535 Akismet::log( 'Comment is not spam, but it has already been manually handled by some other process.' ); 534 Akismet::update_comment_history( $comment s[0]->comment_ID, '', 'webhook-spam-noaction' );536 Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-spam-noaction' ); 535 537 } 536 538 } … … 543 545 544 546 // The comment is classified as spam. If Akismet was the one to label it as spam, unspam it. 545 if ( Akismet::last_comment_status_change_came_from_akismet( $comment s[0]->comment_ID ) ) {547 if ( Akismet::last_comment_status_change_came_from_akismet( $comment->comment_ID ) ) { 546 548 Akismet::log( 'Akismet marked it as spam; unspamming.' ); 547 549 548 wp_unspam_comment( $comments[0] ); 549 akismet::update_comment_history( $comments[0]->comment_ID, '', 'webhook-ham' ); 550 wp_unspam_comment( $comment ); 551 552 akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham' ); 550 553 } else { 551 554 Akismet::log( 'Comment is not spam, but it has already been manually handled by some other process.' ); 552 Akismet::update_comment_history( $comments[0]->comment_ID, '', 'webhook-ham-noaction' ); 555 Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham-noaction' ); 556 } 557 } else if ( 'unapproved' == $current_status ) { 558 Akismet::log( 'Comment is pending.' ); 559 560 // The comment is in Pending. If Akismet was the one to put it there, approve it (but only if the site 561 // settings dictate that). 562 if ( Akismet::last_comment_status_change_came_from_akismet( $comment->comment_ID ) ) { 563 Akismet::log( 'Akismet marked it as Pending; approving.' ); 564 565 if ( check_comment( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type ) ) { 566 wp_set_comment_status( $comment->comment_ID, 1 ); 567 } 568 569 akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham' ); 570 } else { 571 Akismet::log( 'Comment is not spam, but it has already been manually handled by some other process.' ); 572 Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham-noaction' ); 553 573 } 554 574 } 575 576 $moderation_email_was_delayed = get_comment_meta( $comment->comment_ID, 'akismet_delayed_moderation_email', true ); 577 578 if ( $moderation_email_was_delayed ) { 579 Akismet::log( 'Moderation email was delayed for comment #' . $comment->comment_ID . '; sending now.' ); 580 581 delete_comment_meta( $comment->comment_ID, 'akismet_delayed_moderation_email' ); 582 wp_new_comment_notify_moderator( $comment->comment_ID ); 583 wp_new_comment_notify_postauthor( $comment->comment_ID ); 584 } 585 586 delete_comment_meta( $comment->comment_ID, 'akismet_delay_moderation_email' ); 555 587 } 556 588 -
akismet/trunk/class.akismet.php
r3277808 r3323859 23 23 private static $last_comment = ''; 24 24 private static $initiated = false; 25 private static $prevent_moderation_email_for_these_comments = array();26 25 private static $last_comment_result = null; 27 26 private static $comment_as_submitted_allowed_keys = array( … … 65 64 66 65 add_action( 'wp_insert_comment', array( 'Akismet', 'auto_check_update_meta' ), 10, 2 ); 66 add_action( 'wp_insert_comment', array( 'Akismet', 'schedule_email_fallback' ), 10, 2 ); 67 add_action( 'wp_insert_comment', array( 'Akismet', 'schedule_approval_fallback' ), 10, 2 ); 68 67 69 add_filter( 'preprocess_comment', array( 'Akismet', 'auto_check_comment' ), 1 ); 68 70 add_filter( 'rest_pre_insert_comment', array( 'Akismet', 'rest_auto_check_comment' ), 1 ); … … 76 78 add_action( 'akismet_schedule_cron_recheck', array( 'Akismet', 'cron_recheck' ) ); 77 79 80 add_action( 'akismet_email_fallback', array( 'Akismet', 'email_fallback' ), 10, 3 ); 81 add_action( 'akismet_approval_fallback', array( 'Akismet', 'approval_fallback' ), 10, 3 ); 82 78 83 add_action( 'comment_form', array( 'Akismet', 'add_comment_nonce' ), 1 ); 79 84 add_action( 'comment_form', array( 'Akismet', 'output_custom_form_fields' ) ); 80 85 add_filter( 'script_loader_tag', array( 'Akismet', 'set_form_js_async' ), 10, 3 ); 81 86 82 add_filter( 'comment_moderation_recipients', array( 'Akismet', 'disable_moderation_emails_if_unreachable' ), 1000, 2 ); 87 add_filter( 'notify_moderator', array( 'Akismet', 'disable_emails_if_unreachable' ), 1000, 2 ); 88 add_filter( 'notify_post_author', array( 'Akismet', 'disable_emails_if_unreachable' ), 1000, 2 ); 89 83 90 add_filter( 'pre_comment_approved', array( 'Akismet', 'last_comment_status' ), 10, 2 ); 84 91 … … 365 372 } 366 373 374 // Set the webhook callback URL. The Akismet servers may make a request to this URL 375 // if a comment's spam status changes. 376 $comment['callback'] = get_rest_url( null, 'akismet/v1/webhook' ); 377 367 378 /** 368 379 * Filter the data that is used to generate the request body for the API call. … … 404 415 $commentdata['akismet_guid'] = $response[0]['x-akismet-guid']; 405 416 $commentdata['comment_meta']['akismet_guid'] = $response[0]['x-akismet-guid']; 417 418 if ( 'false' === $response[1] ) { 419 // If Akismet has indicated that there is more processing to be done before this comment 420 // can be fully classified, delay moderation emails until that processing is complete. 421 if ( isset( $response[0]['X-akismet-recheck-after'] ) ) { 422 // Prevent this comment from reaching Active status (keep in Pending) until 423 // it's finished being checked. 424 $commentdata['comment_approved'] = '0'; 425 self::$last_comment_result = '0'; 426 427 // Indicate that we should schedule a fallback so that if the site never receives a 428 // followup from Akismet, the emails will still be sent. We don't schedule it here 429 // because we don't yet have the comment ID. Add an extra minute to ensure that the 430 // fallback email isn't sent while the recheck or webhook call is happening. 431 $delay = $response[0]['X-akismet-recheck-after'] * 2; 432 433 $commentdata['comment_meta']['akismet_schedule_approval_fallback'] = $delay; 434 435 // If this commentmeta is present, we'll prevent the moderation email from sending once. 436 $commentdata['comment_meta']['akismet_delay_moderation_email'] = true; 437 438 self::log( 'Delaying moderation email for comment from ' . $commentdata['comment_author'] . ' for ' . $delay . ' seconds' ); 439 440 $commentdata['comment_meta']['akismet_schedule_email_fallback'] = $delay; 441 } 442 } 406 443 } 407 444 … … 451 488 } 452 489 490 $commentdata['comment_meta']['akismet_delay_moderation_email'] = true; 491 453 492 if ( ! wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { 454 493 wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); 455 494 do_action( 'akismet_scheduled_recheck', 'invalid-response-' . $response[1] ); 456 495 } 457 458 self::$prevent_moderation_email_for_these_comments[] = $commentdata;459 496 } 460 497 … … 508 545 } 509 546 } elseif ( isset( self::$last_comment['akismet_result'] ) && self::$last_comment['akismet_result'] == 'false' ) { 510 self::update_comment_history( $comment->comment_ID, '', 'check-ham' ); 547 if ( get_comment_meta( $comment->comment_ID, 'akismet_schedule_approval_fallback', true ) ) { 548 self::update_comment_history( $comment->comment_ID, '', 'check-ham-pending' ); 549 } else { 550 self::update_comment_history( $comment->comment_ID, '', 'check-ham' ); 551 } 552 511 553 // Status could be spam or trash, depending on the WP version and whether this change applies: 512 554 // https://core.trac.wordpress.org/changeset/34726 … … 542 584 } 543 585 586 /** 587 * After the comment has been inserted, we have access to the comment ID. Now, we can 588 * schedule the fallback moderation/notification emails using the comment ID instead 589 * of relying on a lookup of the GUID in the commentmeta table. 590 * 591 * @param int $id The comment ID. 592 * @param object $comment The comment object. 593 */ 594 public static function schedule_email_fallback( $id, $comment ) { 595 self::log( 'Checking whether to schedule_email_fallback for comment #' . $id ); 596 597 // If the moderation/notification emails for this comment were delayed 598 $email_delay = get_comment_meta( $id, 'akismet_schedule_email_fallback', true ); 599 600 if ( $email_delay ) { 601 delete_comment_meta( $id, 'akismet_schedule_email_fallback' ); 602 603 wp_schedule_single_event( time() + $email_delay, 'akismet_email_fallback', array( $id ) ); 604 605 self::log( 'Scheduled email fallback for ' . ( time() + $email_delay ) . ' for comment #' . $id ); 606 } else { 607 self::log( 'No need to schedule_email_fallback for comment #' . $id ); 608 } 609 } 610 611 /** 612 * Send out the notification emails if they were previously delayed while waiting 613 * for a recheck or webhook. 614 * 615 * @param int $comment_ID The comment ID. 616 */ 617 public static function email_fallback( $comment_id ) { 618 self::log( 'In email fallback for comment #' . $comment_id ); 619 620 if ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) ) { 621 self::log( 'Triggering notification emails for comment #' . $comment_id . '. They will be sent if comment is not spam.' ); 622 623 delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); 624 wp_new_comment_notify_moderator( $comment_id ); 625 wp_new_comment_notify_postauthor( $comment_id ); 626 } else { 627 self::log( 'No need to send fallback email for comment #' . $comment_id ); 628 } 629 630 delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); 631 } 632 633 /** 634 * After the comment has been inserted, we have access to the comment ID. Now, we can 635 * schedule the fallback moderation/notification emails using the comment ID instead 636 * of relying on a lookup of the GUID in the commentmeta table. 637 * 638 * @param int $id The comment ID. 639 * @param object $comment The comment object. 640 */ 641 public static function schedule_approval_fallback( $id, $comment ) { 642 self::log( 'Checking whether to schedule_approval_fallback for comment #' . $id ); 643 644 // If the moderation/notification emails for this comment were delayed 645 $approval_delay = get_comment_meta( $id, 'akismet_schedule_approval_fallback', true ); 646 647 if ( $approval_delay ) { 648 delete_comment_meta( $id, 'akismet_schedule_approval_fallback' ); 649 650 wp_schedule_single_event( time() + $approval_delay, 'akismet_approval_fallback', array( $id ) ); 651 652 self::log( 'Scheduled approval fallback for ' . ( time() + $approval_delay ) . ' for comment #' . $id ); 653 } else { 654 self::log( 'No need to schedule_approval_fallback for comment #' . $id ); 655 } 656 } 657 658 /** 659 * If no other process has approved or spammed this comment since it was put in pending, approve it. 660 * 661 * @param int $comment_ID The comment ID. 662 */ 663 public static function approval_fallback( $comment_id ) { 664 self::log( 'In approval fallback for comment #' . $comment_id ); 665 666 if ( wp_get_comment_status( $comment_id ) == 'unapproved' ) { 667 if ( self::last_comment_status_change_came_from_akismet( $comment_id ) ) { 668 $comment = get_comment( $comment_id ); 669 670 if ( ! $comment ) { 671 self::log( 'Comment #' . $comment_id . ' no longer exists.' ); 672 } else if ( check_comment( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type ) ) { 673 self::log( 'Approving comment #' . $comment_id ); 674 675 wp_set_comment_status( $comment_id, 1 ); 676 } else { 677 self::log( 'Not approving comment #' . $comment_id . ' because it does not pass check_comment()' ); 678 } 679 680 self::update_comment_history( $comment->comment_ID, '', 'check-ham' ); 681 } else { 682 self::log( 'No need to fallback approve comment #' . $comment_id . ' because it was not last modified by Akismet.' ); 683 684 $history = self::get_comment_history( $comment_id ); 685 686 if ( ! empty( $history ) ) { 687 $most_recent_history_event = $history[0]; 688 689 error_log( 'Comment history: ' . print_r( $history, true ) ); 690 } 691 } 692 } else { 693 self::log( 'No need to fallback approve comment #' . $comment_id . ' because it is not pending.' ); 694 } 695 } 696 544 697 public static function delete_old_comments() { 545 698 global $wpdb; … … 731 884 $history[] = array( 'time' => 445856404, 'event' => 'recheck-ham' ); 732 885 $history[] = array( 'time' => 445856405, 'event' => 'check-ham' ); 886 $history[] = array( 'time' => 445856405, 'event' => 'check-ham-pending' ); 733 887 $history[] = array( 'time' => 445856406, 'event' => 'wp-blacklisted' ); 734 888 $history[] = array( 'time' => 445856406, 'event' => 'wp-disallowed' ); … … 849 1003 update_comment_meta( $id, 'akismet_result', 'true' ); 850 1004 delete_comment_meta( $id, 'akismet_error' ); 1005 delete_comment_meta( $id, 'akismet_delay_moderation_email' ); 851 1006 delete_comment_meta( $id, 'akismet_delayed_moderation_email' ); 1007 delete_comment_meta( $id, 'akismet_schedule_approval_fallback' ); 1008 delete_comment_meta( $id, 'akismet_schedule_email_fallback' ); 852 1009 self::update_comment_history( $id, '', 'recheck-spam' ); 853 1010 } elseif ( 'false' === $api_response ) { 854 1011 update_comment_meta( $id, 'akismet_result', 'false' ); 855 1012 delete_comment_meta( $id, 'akismet_error' ); 1013 delete_comment_meta( $id, 'akismet_delay_moderation_email' ); 856 1014 delete_comment_meta( $id, 'akismet_delayed_moderation_email' ); 1015 delete_comment_meta( $id, 'akismet_schedule_approval_fallback' ); 1016 delete_comment_meta( $id, 'akismet_schedule_email_fallback' ); 857 1017 self::update_comment_history( $id, '', 'recheck-ham' ); 858 1018 } else { … … 1122 1282 ) { 1123 1283 delete_comment_meta( $comment_id, 'akismet_error' ); 1284 delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); 1124 1285 delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); 1286 delete_comment_meta( $comment_id, 'akismet_schedule_approval_fallback' ); 1287 delete_comment_meta( $comment_id, 'akismet_schedule_email_fallback' ); 1125 1288 continue; 1126 1289 } … … 1154 1317 wp_set_comment_status( $comment_id, 1 ); 1155 1318 } elseif ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) ) { 1156 wp_notify_moderator( $comment_id ); 1319 wp_new_comment_notify_moderator( $comment_id ); 1320 wp_new_comment_notify_postauthor( $comment_id ); 1157 1321 } 1158 1322 } 1159 1323 } 1160 1324 1325 delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); 1161 1326 delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); 1162 1327 } else { … … 1164 1329 // send a moderation email now. 1165 1330 if ( ( intval( gmdate( 'U' ) ) - strtotime( $comment->comment_date_gmt ) ) < self::MAX_DELAY_BEFORE_MODERATION_EMAIL ) { 1331 delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); 1166 1332 delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); 1167 wp_notify_moderator( $comment_id ); 1333 1334 wp_new_comment_notify_moderator( $comment_id ); 1335 wp_new_comment_notify_postauthor( $comment_id ); 1168 1336 } 1169 1337 … … 1171 1339 wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); 1172 1340 do_action( 'akismet_scheduled_recheck', 'check-db-comment-' . $status ); 1341 1173 1342 return; 1174 1343 } 1344 1175 1345 delete_comment_meta( $comment_id, 'akismet_rechecking' ); 1176 1346 } 1177 1347 1178 1348 $remaining = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" ); 1349 1179 1350 if ( $remaining && ! wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { 1180 1351 wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); … … 1377 1548 1378 1549 /** 1379 * If Akismet is temporarily unreachable, we don't want to "spam" the blogger with 1380 * moderation emails for comments that will be automatically cleared or spammed on 1381 * the next retry. 1382 * 1383 * For comments that will be rechecked later, empty the list of email addresses that 1384 * the moderation email would be sent to. 1385 * 1386 * @param array $emails An array of email addresses that the moderation email will be sent to. 1550 * If Akismet is temporarily unreachable, we don't want to "spam" the blogger or post author 1551 * with emails for comments that will be automatically cleared or spammed on the next retry. 1552 * 1553 * @param bool $maybe_notify Whether the notification email will be sent. 1387 1554 * @param int $comment_id The ID of the relevant comment. 1388 * @return array An array of email addresses that the moderation email will be sent to. 1389 */ 1390 public static function disable_moderation_emails_if_unreachable( $emails, $comment_id ) { 1391 if ( ! empty( self::$prevent_moderation_email_for_these_comments ) && ! empty( $emails ) ) { 1392 $matching_fields = self::get_fields_for_comment_matching( $comment_id ); 1393 1394 // self::$prevent_moderation_email_for_these_comments is an array of $commentdata objects 1395 // saved immediately after the comment-check request completes. 1396 foreach ( self::$prevent_moderation_email_for_these_comments as $possible_match ) { 1397 if ( self::comments_match( $possible_match, $matching_fields ) ) { 1398 update_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ); 1399 return array(); 1400 } 1401 } 1402 } 1403 1404 return $emails; 1555 * @return bool Whether the notification email should still be sent. 1556 */ 1557 public static function disable_emails_if_unreachable( $maybe_notify, $comment_id ) { 1558 if ( $maybe_notify ) { 1559 if ( get_comment_meta( $comment_id, 'akismet_delay_moderation_email', true ) ) { 1560 self::log( 'Disabling notification email for comment #' . $comment_id ); 1561 1562 update_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ); 1563 delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); 1564 1565 // If we want to prevent the email from sending another time, we'll have to reset 1566 // the akismet_delay_moderation_email commentmeta. 1567 1568 return false; 1569 } 1570 } 1571 1572 return $maybe_notify; 1405 1573 } 1406 1574 … … 1976 2144 'cron-retry-spam', 1977 2145 'check-ham', 2146 'check-ham-pending', 1978 2147 'check-spam', 1979 2148 'recheck-error', -
akismet/trunk/readme.txt
r3289308 r3323859 32 32 33 33 == Changelog == 34 35 = 5.5 = 36 *Release Date - TBD* 37 38 * Enable webhooks so that Akismet can process comments asynchronously to detect more types of spam. 34 39 35 40 = 5.4 =
Note: See TracChangeset
for help on using the changeset viewer.