From 50c34abdca1c205877732ca856cadb35ea61e626 Mon Sep 17 00:00:00 2001 From: "Anthony G. Basile" Date: Wed, 20 Sep 2017 09:51:53 -0400 Subject: akismet 4.0 Signed-off-by: Anthony G. Basile --- plugins/akismet/_inc/akismet.js | 10 +- plugins/akismet/akismet.php | 9 +- plugins/akismet/class.akismet-admin.php | 45 +++-- plugins/akismet/class.akismet-rest-api.php | 271 +++++++++++++++++++++++++++++ plugins/akismet/class.akismet.php | 74 ++++---- plugins/akismet/readme.txt | 13 +- plugins/akismet/views/config.php | 5 +- 7 files changed, 354 insertions(+), 73 deletions(-) create mode 100644 plugins/akismet/class.akismet-rest-api.php diff --git a/plugins/akismet/_inc/akismet.js b/plugins/akismet/_inc/akismet.js index c1ddc8b0..cac4d57f 100644 --- a/plugins/akismet/_inc/akismet.js +++ b/plugins/akismet/_inc/akismet.js @@ -162,8 +162,14 @@ jQuery( function ( $ ) { var recheck_count = 0; function akismet_check_for_spam(offset, limit) { + var check_for_spam_buttons = $( '.checkforspam' ); + + // We show the percentage complete down to one decimal point so even queues with 100k + // pending comments will show some progress pretty quickly. + var percentage_complete = Math.round( ( recheck_count / check_for_spam_buttons.data( 'pending-comment-count' ) ) * 1000 ) / 10; + // Update the progress counter on the "Check for Spam" button. - $( '.checkforspam-progress' ).text( $( '.checkforspam' ).data( 'progress-label-format' ).replace( '%1$s', offset ) ); + $( '.checkforspam-progress' ).text( check_for_spam_buttons.data( 'progress-label-format' ).replace( '%1$s', percentage_complete ) ); $.post( ajaxurl, @@ -177,7 +183,7 @@ jQuery( function ( $ ) { spam_count += result.counts.spam; if (result.counts.processed < limit) { - window.location.href = $( '.checkforspam' ).data( 'success-url' ).replace( '__recheck_count__', recheck_count ).replace( '__spam_count__', spam_count ); + window.location.href = check_for_spam_buttons.data( 'success-url' ).replace( '__recheck_count__', recheck_count ).replace( '__spam_count__', spam_count ); } else { // Account for comments that were caught as spam and moved out of the queue. diff --git a/plugins/akismet/akismet.php b/plugins/akismet/akismet.php index 9894295b..56babcba 100644 --- a/plugins/akismet/akismet.php +++ b/plugins/akismet/akismet.php @@ -6,7 +6,7 @@ Plugin Name: Akismet Anti-Spam Plugin URI: https://akismet.com/ Description: Used by millions, Akismet is quite possibly the best way in the world to protect your blog from spam. It 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. -Version: 3.3.4 +Version: 4.0 Author: Automattic Author URI: https://automattic.com/wordpress-plugins/ License: GPLv2 or later @@ -37,8 +37,8 @@ if ( !function_exists( 'add_action' ) ) { exit; } -define( 'AKISMET_VERSION', '3.3.4' ); -define( 'AKISMET__MINIMUM_WP_VERSION', '3.7' ); +define( 'AKISMET_VERSION', '4.0' ); +define( 'AKISMET__MINIMUM_WP_VERSION', '4.0' ); define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'AKISMET_DELETE_LIMIT', 100000 ); @@ -47,9 +47,12 @@ register_deactivation_hook( __FILE__, array( 'Akismet', 'plugin_deactivation' ) require_once( AKISMET__PLUGIN_DIR . 'class.akismet.php' ); require_once( AKISMET__PLUGIN_DIR . 'class.akismet-widget.php' ); +require_once( AKISMET__PLUGIN_DIR . 'class.akismet-rest-api.php' ); add_action( 'init', array( 'Akismet', 'init' ) ); +add_action( 'rest_api_init', array( 'Akismet_REST_API', 'init' ) ); + if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) { require_once( AKISMET__PLUGIN_DIR . 'class.akismet-admin.php' ); add_action( 'init', array( 'Akismet_Admin', 'init' ) ); diff --git a/plugins/akismet/class.akismet-admin.php b/plugins/akismet/class.akismet-admin.php index aed5e3d5..bc00260f 100644 --- a/plugins/akismet/class.akismet-admin.php +++ b/plugins/akismet/class.akismet-admin.php @@ -91,12 +91,14 @@ class Akismet_Admin { } public static function load_menu() { - if ( class_exists( 'Jetpack' ) ) + if ( class_exists( 'Jetpack' ) ) { $hook = add_submenu_page( 'jetpack', __( 'Akismet' , 'akismet'), __( 'Akismet' , 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) ); - else + } + else { $hook = add_options_page( __('Akismet', 'akismet'), __('Akismet', 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) ); - - if ( version_compare( $GLOBALS['wp_version'], '3.3', '>=' ) ) { + } + + if ( $hook ) { add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) ); } } @@ -248,8 +250,9 @@ class Akismet_Admin { } public static function enter_api_key() { - if ( function_exists('current_user_can') && !current_user_can('manage_options') ) - die(__('Cheatin’ uh?', 'akismet')); + if ( ! current_user_can( 'manage_options' ) ) { + die( __( 'Cheatin’ uh?', 'akismet' ) ); + } if ( !wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) ) return false; @@ -303,8 +306,9 @@ class Akismet_Admin { } public static function dashboard_stats() { - if ( !function_exists('did_action') || did_action( 'rightnow_end' ) ) + if ( did_action( 'rightnow_end' ) ) { return; // We already displayed this info in the "Right Now" section + } if ( !$count = get_option('akismet_spam_count') ) return; @@ -356,19 +360,19 @@ class Akismet_Admin { return; } - if ( function_exists('plugins_url') ) - $link = add_query_arg( array( 'action' => 'akismet_recheck_queue' ), admin_url( 'admin.php' ) ); - else - $link = add_query_arg( array( 'page' => 'akismet-admin', 'recheckqueue' => 'true', 'noheader' => 'true' ), admin_url( 'edit-comments.php' ) ); + $link = add_query_arg( array( 'action' => 'akismet_recheck_queue' ), admin_url( 'admin.php' ) ); + $comments_count = wp_count_comments(); + echo ''; echo '
'; echo ''; echo '' . esc_html__('Check for Spam', 'akismet') . ''; echo ''; @@ -467,11 +471,6 @@ class Akismet_Admin { } public static function comment_row_action( $a, $comment ) { - - // failsafe for old WP versions - if ( !function_exists('add_comment_meta') ) - return $a; - $akismet_result = get_comment_meta( $comment->comment_ID, 'akismet_result', true ); $akismet_error = get_comment_meta( $comment->comment_ID, 'akismet_error', true ); $user_result = get_comment_meta( $comment->comment_ID, 'akismet_user_result', true); @@ -500,7 +499,7 @@ class Akismet_Admin { $b[ $k ] = $item; if ( $k == 'edit' - || ( $k == 'unspam' && $GLOBALS['wp_version'] >= 3.4 ) + || $k == 'unspam' ) { $b['history'] = ' '. esc_html__('History', 'akismet') . ''; } @@ -645,12 +644,8 @@ class Akismet_Admin { if ( !$type ) { // total $count = wp_cache_get( 'akismet_spam_count', 'widget' ); if ( false === $count ) { - if ( function_exists('wp_count_comments') ) { - $count = wp_count_comments(); - $count = $count->spam; - } else { - $count = (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam'"); - } + $count = wp_count_comments(); + $count = $count->spam; wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 ); } return $count; @@ -706,7 +701,7 @@ class Akismet_Admin { update_option('akismet_connectivity_time', time()); } - if ( function_exists( 'wp_http_supports' ) && ( wp_http_supports( array( 'ssl' ) ) ) ) { + if ( wp_http_supports( array( 'ssl' ) ) ) { $response = wp_remote_get( 'https://rest.akismet.com/1.1/test' ); } else { diff --git a/plugins/akismet/class.akismet-rest-api.php b/plugins/akismet/class.akismet-rest-api.php new file mode 100644 index 00000000..f97b710d --- /dev/null +++ b/plugins/akismet/class.akismet-rest-api.php @@ -0,0 +1,271 @@ + WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_key' ), + ), array( + 'methods' => WP_REST_Server::EDITABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'set_key' ), + 'args' => array( + 'key' => array( + 'required' => true, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_key' ), + 'description' => __( 'A 12-character Akismet API key. Available at akismet.com/get/', 'akismet' ), + ), + ), + ), array( + 'methods' => WP_REST_Server::DELETABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'delete_key' ), + ) + ) ); + + register_rest_route( 'akismet/v1', '/settings/', array( + array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_settings' ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'set_boolean_settings' ), + 'args' => array( + 'akismet_strictness' => array( + 'required' => false, + 'type' => 'boolean', + 'description' => __( 'If true, Akismet will automatically discard the worst spam automatically rather than putting it in the spam folder.', 'akismet' ), + ), + 'akismet_show_user_comments_approved' => array( + 'required' => false, + 'type' => 'boolean', + 'description' => __( 'If true, show the number of approved comments beside each comment author in the comments list page.', 'akismet' ), + ), + ), + ) + ) ); + + register_rest_route( 'akismet/v1', '/stats', array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_stats' ), + 'args' => array( + 'interval' => array( + 'required' => false, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_interval' ), + 'description' => __( 'The time period for which to retrieve stats. Options: 60-days, 6-months, all', 'akismet' ), + 'default' => 'all', + ), + ), + ) ); + + register_rest_route( 'akismet/v1', '/stats/(?P[\w+])', array( + 'args' => array( + 'interval' => array( + 'description' => __( 'The time period for which to retrieve stats. Options: 60-days, 6-months, all', 'akismet' ), + 'type' => 'string', + ), + ), + array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_stats' ), + ) + ) ); + } + + /** + * Get the current Akismet API key. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_key( $request = null ) { + return rest_ensure_response( Akismet::get_api_key() ); + } + + /** + * Set the API key, if possible. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function set_key( $request ) { + if ( defined( 'WPCOM_API_KEY' ) ) { + return rest_ensure_response( new WP_Error( 'hardcoded_key', __( 'This site\'s API key is hardcoded and cannot be changed via the API.', 'akismet' ), array( 'status'=> 409 ) ) ); + } + + $new_api_key = $request->get_param( 'key' ); + + if ( ! self::key_is_valid( $new_api_key ) ) { + return rest_ensure_response( new WP_Error( 'invalid_key', __( 'The value provided is not a valid and registered API key.', 'akismet' ), array( 'status' => 400 ) ) ); + } + + update_option( 'wordpress_api_key', $new_api_key ); + + return self::get_key(); + } + + /** + * Unset the API key, if possible. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function delete_key( $request ) { + if ( defined( 'WPCOM_API_KEY' ) ) { + return rest_ensure_response( new WP_Error( 'hardcoded_key', __( 'This site\'s API key is hardcoded and cannot be deleted.', 'akismet' ), array( 'status'=> 409 ) ) ); + } + + delete_option( 'wordpress_api_key' ); + + return rest_ensure_response( true ); + } + + /** + * Get the Akismet settings. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_settings( $request = null ) { + return rest_ensure_response( array( + 'akismet_strictness' => ( get_option( 'akismet_strictness', '1' ) === '1' ), + 'akismet_show_user_comments_approved' => ( get_option( 'akismet_show_user_comments_approved', '1' ) === '1' ), + ) ); + } + + /** + * Update the Akismet settings. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function set_boolean_settings( $request ) { + foreach ( array( + 'akismet_strictness', + 'akismet_show_user_comments_approved', + ) as $setting_key ) { + + $setting_value = $request->get_param( $setting_key ); + if ( is_null( $setting_value ) ) { + // This setting was not specified. + continue; + } + + // From 4.7+, WP core will ensure that these are always boolean + // values because they are registered with 'type' => 'boolean', + // but we need to do this ourselves for prior versions. + $setting_value = Akismet_REST_API::parse_boolean( $setting_value ); + + update_option( $setting_key, $setting_value ? '1' : '0' ); + } + + return self::get_settings(); + } + + /** + * Parse a numeric or string boolean value into a boolean. + * + * @param mixed $value The value to convert into a boolean. + * @return bool The converted value. + */ + public static function parse_boolean( $value ) { + switch ( $value ) { + case true: + case 'true': + case '1': + case 1: + return true; + + case false: + case 'false': + case '0': + case 0: + return false; + + default: + return (bool) $value; + } + } + + /** + * Get the Akismet stats for a given time period. + * + * Possible `interval` values: + * - all + * - 60-days + * - 6-months + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_stats( $request ) { + $api_key = Akismet::get_api_key(); + + $interval = $request->get_param( 'interval' ); + + $stat_totals = array(); + + $response = Akismet::http_post( Akismet::build_query( array( 'blog' => get_option( 'home' ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' ); + + if ( ! empty( $response[1] ) ) { + $stat_totals[$interval] = json_decode( $response[1] ); + } + + return rest_ensure_response( $stat_totals ); + } + + private static function key_is_valid( $key ) { + $response = Akismet::http_post( + Akismet::build_query( + array( + 'key' => $key, + 'blog' => get_option( 'home' ) + ) + ), + 'verify-key' + ); + + if ( $response[1] == 'valid' ) { + return true; + } + + return false; + } + + public static function privileged_permission_callback() { + return current_user_can( 'manage_options' ); + } + + public static function sanitize_interval( $interval, $request, $param ) { + $interval = trim( $interval ); + + $valid_intervals = array( '60-days', '6-months', 'all', ); + + if ( ! in_array( $interval, $valid_intervals ) ) { + $interval = 'all'; + } + + return $interval; + } + + public static function sanitize_key( $key, $request, $param ) { + return trim( $key ); + } +} diff --git a/plugins/akismet/class.akismet.php b/plugins/akismet/class.akismet.php index f4172e03..02d994d1 100644 --- a/plugins/akismet/class.akismet.php +++ b/plugins/akismet/class.akismet.php @@ -10,7 +10,8 @@ class Akismet { private static $prevent_moderation_email_for_these_comments = array(); private static $last_comment_result = null; private static $comment_as_submitted_allowed_keys = array( 'blog' => '', 'blog_charset' => '', 'blog_lang' => '', 'blog_ua' => '', 'comment_agent' => '', 'comment_author' => '', 'comment_author_IP' => '', 'comment_author_email' => '', 'comment_author_url' => '', 'comment_content' => '', 'comment_date_gmt' => '', 'comment_tags' => '', 'comment_type' => '', 'guid' => '', 'is_test' => '', 'permalink' => '', 'reporter' => '', 'site_domain' => '', 'submit_referer' => '', 'submit_uri' => '', 'user_ID' => '', 'user_agent' => '', 'user_id' => '', 'user_ip' => '' ); - + private static $is_rest_api_call = false; + public static function init() { if ( ! self::$initiated ) { self::init_hooks(); @@ -25,6 +26,8 @@ class Akismet { add_action( 'wp_insert_comment', array( 'Akismet', 'auto_check_update_meta' ), 10, 2 ); add_filter( 'preprocess_comment', array( 'Akismet', 'auto_check_comment' ), 1 ); + add_filter( 'rest_pre_insert_comment', array( 'Akismet', 'rest_auto_check_comment' ), 1 ); + add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments' ) ); add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments_meta' ) ); add_action( 'akismet_schedule_cron_recheck', array( 'Akismet', 'cron_recheck' ) ); @@ -104,6 +107,12 @@ class Akismet { self::verify_key( $value ); } } + + public static function rest_auto_check_comment( $commentdata ) { + self::$is_rest_api_call = true; + + return self::auto_check_comment( $commentdata ); + } public static function auto_check_comment( $commentdata ) { self::$last_comment_result = null; @@ -190,14 +199,25 @@ class Akismet { do_action( 'akismet_spam_caught', $discard ); if ( $discard ) { + // The spam is obvious, so we're bailing out early. // akismet_result_spam() won't be called so bump the counter here - if ( $incr = apply_filters('akismet_spam_count_incr', 1) ) - update_option( 'akismet_spam_count', get_option('akismet_spam_count') + $incr ); - // The spam is obvious, so we're bailing out early. Redirect back to the previous page, - // or failing that, the post permalink, or failing that, the homepage of the blog. - $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ( $post ? get_permalink( $post ) : home_url() ); - wp_safe_redirect( esc_url_raw( $redirect_to ) ); - die(); + if ( $incr = apply_filters( 'akismet_spam_count_incr', 1 ) ) { + update_option( 'akismet_spam_count', get_option( 'akismet_spam_count' ) + $incr ); + } + + if ( self::$is_rest_api_call ) { + return new WP_Error( 'akismet_rest_comment_discarded', __( 'Comment discarded.', 'akismet' ) ); + } + else { + // Redirect back to the previous page, or failing that, the post permalink, or failing that, the homepage of the blog. + $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ( $post ? get_permalink( $post ) : home_url() ); + wp_safe_redirect( esc_url_raw( $redirect_to ) ); + die(); + } + } + else if ( self::$is_rest_api_call ) { + // The way the REST API structures its calls, we can set the comment_approved value right away. + $commentdata['comment_approved'] = 'spam'; } } @@ -207,26 +227,20 @@ class Akismet { // Comment status should be moderated self::$last_comment_result = '0'; } - if ( function_exists('wp_next_scheduled') && function_exists('wp_schedule_single_event') ) { - if ( !wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { - wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); - do_action( 'akismet_scheduled_recheck', 'invalid-response-' . $response[1] ); - } + + if ( ! wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + do_action( 'akismet_scheduled_recheck', 'invalid-response-' . $response[1] ); } self::$prevent_moderation_email_for_these_comments[] = $commentdata; } - if ( function_exists('wp_next_scheduled') && function_exists('wp_schedule_event') ) { - // WP 2.1+: delete old comments daily - if ( !wp_next_scheduled( 'akismet_scheduled_delete' ) ) - wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' ); - } - elseif ( (mt_rand(1, 10) == 3) ) { - // WP 2.0: run this one time in ten - self::delete_old_comments(); + // Delete old comments daily + if ( ! wp_next_scheduled( 'akismet_scheduled_delete' ) ) { + wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' ); } - + self::set_last_comment( $commentdata ); self::fix_scheduled_recheck(); @@ -256,11 +270,6 @@ class Akismet { // this fires on wp_insert_comment. we can't update comment_meta when auto_check_comment() runs // because we don't know the comment ID at that point. public static function auto_check_update_meta( $id, $comment ) { - - // failsafe for old WP versions - if ( !function_exists('add_comment_meta') ) - return false; - // wp_insert_comment() might be called in other contexts, so make sure this is the same comment // as was checked by auto_check_comment if ( is_object( $comment ) && !empty( self::$last_comment ) && is_array( self::$last_comment ) ) { @@ -398,11 +407,6 @@ class Akismet { // get the full comment history for a given comment, as an array in reverse chronological order public static function get_comment_history( $comment_id ) { - - // failsafe for old WP versions - if ( !function_exists('add_comment_meta') ) - return false; - $history = get_comment_meta( $comment_id, 'akismet_history', false ); usort( $history, array( 'Akismet', '_cmp_time' ) ); return $history; @@ -419,10 +423,6 @@ class Akismet { public static function update_comment_history( $comment_id, $message, $event=null, $meta=null ) { global $current_user; - // failsafe for old WP versions - if ( !function_exists('add_comment_meta') ) - return false; - $user = ''; $event = array( @@ -1024,7 +1024,7 @@ class Akismet { do_action( 'akismet_ssl_disabled' ); } - if ( ! $ssl_disabled && function_exists( 'wp_http_supports') && ( $ssl = wp_http_supports( array( 'ssl' ) ) ) ) { + if ( ! $ssl_disabled && ( $ssl = wp_http_supports( array( 'ssl' ) ) ) ) { $akismet_url = set_url_scheme( $akismet_url, 'https' ); do_action( 'akismet_https_request_pre' ); diff --git a/plugins/akismet/readme.txt b/plugins/akismet/readme.txt index d56cc7a7..a1bd5b63 100644 --- a/plugins/akismet/readme.txt +++ b/plugins/akismet/readme.txt @@ -1,9 +1,9 @@ === Akismet === Contributors: matt, ryan, andy, mdawaffe, tellyworth, josephscott, lessbloat, eoigal, cfinke, automattic, jgs Tags: akismet, comments, spam, antispam, anti-spam, anti spam, comment moderation, comment spam, contact form spam, spam comments -Requires at least: 3.7 +Requires at least: 4.0 Tested up to: 4.8.1 -Stable tag: 3.3.4 +Stable tag: 4.0 License: GPLv2 or later Akismet checks your comments and contact form submissions against our global database of spam to protect you and your site from malicious content. @@ -30,7 +30,16 @@ Upload the Akismet plugin to your blog, Activate it, then enter your [Akismet.co == Changelog == += 4.0 = +*Release Date - 19 September 2017* + +* Added REST API endpoints for configuring Akismet and retrieving stats. +* Increased the minimum supported WordPress version to 4.0. +* Added compatibility with comments submitted via the REST API. +* Improved the progress indicator on the "Check for Spam" button. + = 3.3.4 = +*Release Date - 3 August 2017* * Disabled Akismet's debug log output by default unless AKISMET_DEBUG is defined. * URL previews now begin preloading when the mouse moves near them in the comments section of wp-admin. diff --git a/plugins/akismet/views/config.php b/plugins/akismet/views/config.php index c12914a0..59dd18c5 100644 --- a/plugins/akismet/views/config.php +++ b/plugins/akismet/views/config.php @@ -82,10 +82,7 @@

wp_http_supports' ); ?>