const NONCE = 'akismet-update-key';
private static $initiated = false;
private static $notices = array();
private static $allowed = array(
public static function init() {
if ( ! self::$initiated ) {
if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) {
public static function init_hooks() {
// The standalone stats page was removed in 3.0 for an all-in-one config and stats page.
// Redirect any links that might have been bookmarked or in browser history.
if ( isset( $_GET['page'] ) && 'akismet-stats-display' == $_GET['page'] ) {
wp_safe_redirect( esc_url_raw( self::get_page_url( 'stats' ) ), 301 );
add_action( 'admin_init', array( 'Akismet_Admin', 'admin_init' ) );
add_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 ); # Priority 5, so it's called before Jetpack's admin_menu.
add_action( 'admin_notices', array( 'Akismet_Admin', 'display_notice' ) );
add_action( 'admin_enqueue_scripts', array( 'Akismet_Admin', 'load_resources' ) );
add_action( 'activity_box_end', array( 'Akismet_Admin', 'dashboard_stats' ) );
add_action( 'rightnow_end', array( 'Akismet_Admin', 'rightnow_stats' ) );
add_action( 'manage_comments_nav', array( 'Akismet_Admin', 'check_for_spam_button' ) );
add_action( 'admin_action_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) );
add_action( 'wp_ajax_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) );
add_action( 'wp_ajax_comment_author_deurl', array( 'Akismet_Admin', 'remove_comment_author_url' ) );
add_action( 'wp_ajax_comment_author_reurl', array( 'Akismet_Admin', 'add_comment_author_url' ) );
add_action( 'jetpack_auto_activate_akismet', array( 'Akismet_Admin', 'connect_jetpack_user' ) );
add_filter( 'plugin_action_links', array( 'Akismet_Admin', 'plugin_action_links' ), 10, 2 );
add_filter( 'comment_row_actions', array( 'Akismet_Admin', 'comment_row_action' ), 10, 2 );
add_filter( 'plugin_action_links_'.plugin_basename( plugin_dir_path( __FILE__ ) . 'akismet.php'), array( 'Akismet_Admin', 'admin_plugin_settings_link' ) );
add_filter( 'wxr_export_skip_commentmeta', array( 'Akismet_Admin', 'exclude_commentmeta_from_export' ), 10, 3 );
add_filter( 'all_plugins', array( 'Akismet_Admin', 'modify_plugin_description' ) );
// priority=1 because we need ours to run before core's comment anonymizer runs, and that's registered at priority=10
add_filter( 'wp_privacy_personal_data_erasers', array( 'Akismet_Admin', 'register_personal_data_eraser' ), 1 );
public static function admin_init() {
if ( get_option( 'Activated_Akismet' ) ) {
delete_option( 'Activated_Akismet' );
if ( ! headers_sent() ) {
wp_redirect( add_query_arg( array( 'page' => 'akismet-key-config', 'view' => 'start' ), class_exists( 'Jetpack' ) ? admin_url( 'admin.php' ) : admin_url( 'options-general.php' ) ) );
load_plugin_textdomain( 'akismet' );
add_meta_box( 'akismet-status', __('Comment History', 'akismet'), array( 'Akismet_Admin', 'comment_status_meta_box' ), 'comment', 'normal' );
if ( function_exists( 'wp_add_privacy_policy_content' ) ) {
wp_add_privacy_policy_content(
__( 'Akismet', 'akismet' ),
__( 'We collect information about visitors who comment on Sites that use our Akismet anti-spam service. The information we collect depends on how the User sets up Akismet for the Site, but typically includes the commenter\'s IP address, user agent, referrer, and Site URL (along with other information directly provided by the commenter such as their name, username, email address, and the comment itself).', 'akismet' )
public static function admin_menu() {
if ( class_exists( 'Jetpack' ) )
add_action( 'jetpack_admin_menu', array( 'Akismet_Admin', 'load_menu' ) );
public static function admin_head() {
if ( !current_user_can( 'manage_options' ) )
public static function admin_plugin_settings_link( $links ) {
$settings_link = '<a href="'.esc_url( self::get_page_url() ).'">'.__('Settings', 'akismet').'</a>';
array_unshift( $links, $settings_link );
public static function load_menu() {
if ( class_exists( 'Jetpack' ) ) {
$hook = add_submenu_page( 'jetpack', __( 'Akismet Anti-Spam' , 'akismet'), __( 'Akismet Anti-Spam' , 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
$hook = add_options_page( __('Akismet Anti-Spam', 'akismet'), __('Akismet Anti-Spam', 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) );
public static function load_resources() {
if ( in_array( $hook_suffix, apply_filters( 'akismet_admin_page_hook_suffixes', array(
'settings_page_akismet-key-config',
'jetpack_page_akismet-key-config',
wp_register_style( 'akismet.css', plugin_dir_url( __FILE__ ) . '_inc/akismet.css', array(), AKISMET_VERSION );
wp_enqueue_style( 'akismet.css');
wp_register_script( 'akismet.js', plugin_dir_url( __FILE__ ) . '_inc/akismet.js', array('jquery'), AKISMET_VERSION );
wp_enqueue_script( 'akismet.js' );
'comment_author_url_nonce' => wp_create_nonce( 'comment_author_url_nonce' ),
'Remove this URL' => __( 'Remove this URL' , 'akismet'),
'Removing...' => __( 'Removing...' , 'akismet'),
'URL removed' => __( 'URL removed' , 'akismet'),
'(undo)' => __( '(undo)' , 'akismet'),
'Re-adding...' => __( 'Re-adding...' , 'akismet'),
if ( isset( $_GET['akismet_recheck'] ) && wp_verify_nonce( $_GET['akismet_recheck'], 'akismet_recheck' ) ) {
$inline_js['start_recheck'] = true;
if ( apply_filters( 'akismet_enable_mshots', true ) ) {
$inline_js['enable_mshots'] = true;
wp_localize_script( 'akismet.js', 'WPAkismet', $inline_js );
* Add help to the Akismet page
* @return false if not the Akismet page
public static function admin_help() {
$current_screen = get_current_screen();
if ( current_user_can( 'manage_options' ) ) {
if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) {
$current_screen->add_help_tab(
'title' => __( 'Overview' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
'<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.' , 'akismet') . '</p>' .
'<p>' . esc_html__( 'On this page, you are able to set up the Akismet plugin.' , 'akismet') . '</p>',
$current_screen->add_help_tab(
'title' => __( 'New to Akismet' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
'<p>' . esc_html__( 'You need to enter an API key to activate the Akismet service on your site.' , 'akismet') . '</p>' .
'<p>' . sprintf( __( 'Sign up for an account on %s to get an API Key.' , 'akismet'), '<a href="https://akismet.com/plugin-signup/" target="_blank">Akismet.com</a>' ) . '</p>',
$current_screen->add_help_tab(
'title' => __( 'Enter an API Key' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
'<p>' . esc_html__( 'If you already have an API key' , 'akismet') . '</p>' .
'<li>' . esc_html__( 'Copy and paste the API key into the text field.' , 'akismet') . '</li>' .
'<li>' . esc_html__( 'Click the Use this Key button.' , 'akismet') . '</li>' .
elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) {
$current_screen->add_help_tab(
'title' => __( 'Overview' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Stats' , 'akismet') . '</strong></p>' .
'<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.' , 'akismet') . '</p>' .
'<p>' . esc_html__( 'On this page, you are able to view stats on spam filtered on your site.' , 'akismet') . '</p>',
$current_screen->add_help_tab(
'title' => __( 'Overview' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Configuration' , 'akismet') . '</strong></p>' .
'<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.' , 'akismet') . '</p>' .
'<p>' . esc_html__( 'On this page, you are able to update your Akismet settings and view spam stats.' , 'akismet') . '</p>',
$current_screen->add_help_tab(
'title' => __( 'Settings' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Configuration' , 'akismet') . '</strong></p>' .
( Akismet::predefined_api_key() ? '' : '<p><strong>' . esc_html__( 'API Key' , 'akismet') . '</strong> - ' . esc_html__( 'Enter/remove an API key.' , 'akismet') . '</p>' ) .
'<p><strong>' . esc_html__( 'Comments' , 'akismet') . '</strong> - ' . esc_html__( 'Show the number of approved comments beside each comment author in the comments list page.' , 'akismet') . '</p>' .
'<p><strong>' . esc_html__( 'Strictness' , 'akismet') . '</strong> - ' . esc_html__( 'Choose to either discard the worst spam automatically or to always put all spam in spam folder.' , 'akismet') . '</p>',
if ( ! Akismet::predefined_api_key() ) {
$current_screen->add_help_tab(
'title' => __( 'Account' , 'akismet'),
'<p><strong>' . esc_html__( 'Akismet Configuration' , 'akismet') . '</strong></p>' .
'<p><strong>' . esc_html__( 'Subscription Type' , 'akismet') . '</strong> - ' . esc_html__( 'The Akismet subscription plan' , 'akismet') . '</p>' .
'<p><strong>' . esc_html__( 'Status' , 'akismet') . '</strong> - ' . esc_html__( 'The subscription status - active, cancelled or suspended' , 'akismet') . '</p>',
$current_screen->set_help_sidebar(
'<p><strong>' . esc_html__( 'For more information:' , 'akismet') . '</strong></p>' .
'<p><a href="https://akismet.com/faq/" target="_blank">' . esc_html__( 'Akismet FAQ' , 'akismet') . '</a></p>' .
'<p><a href="https://akismet.com/support/" target="_blank">' . esc_html__( 'Akismet Support' , 'akismet') . '</a></p>'
public static function enter_api_key() {
if ( ! current_user_can( 'manage_options' ) ) {
die( __( 'Cheatin’ uh?', 'akismet' ) );
if ( !wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) )
foreach( array( 'akismet_strictness', 'akismet_show_user_comments_approved' ) as $option ) {
update_option( $option, isset( $_POST[$option] ) && (int) $_POST[$option] == 1 ? '1' : '0' );
if ( ! empty( $_POST['akismet_comment_form_privacy_notice'] ) ) {
self::set_form_privacy_notice_option( $_POST['akismet_comment_form_privacy_notice'] );
self::set_form_privacy_notice_option( 'hide' );
if ( Akismet::predefined_api_key() ) {
return false; //shouldn't have option to save key if already defined
$new_key = preg_replace( '/[^a-f0-9]/i', '', $_POST['key'] );
$old_key = Akismet::get_api_key();
if ( empty( $new_key ) ) {
if ( !empty( $old_key ) ) {
delete_option( 'wordpress_api_key' );
self::$notices[] = 'new-key-empty';
elseif ( $new_key != $old_key ) {
self::save_key( $new_key );
public static function save_key( $api_key ) {
$key_status = Akismet::verify_key( $api_key );
if ( $key_status == 'valid' ) {
$akismet_user = self::get_akismet_user( $api_key );
if ( in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ) )
update_option( 'wordpress_api_key', $api_key );
if ( $akismet_user->status == 'active' )
self::$notices['status'] = 'new-key-valid';
elseif ( $akismet_user->status == 'notice' )
self::$notices['status'] = $akismet_user;
self::$notices['status'] = $akismet_user->status;
self::$notices['status'] = 'new-key-invalid';
elseif ( in_array( $key_status, array( 'invalid', 'failed' ) ) )
self::$notices['status'] = 'new-key-'.$key_status;
public static function dashboard_stats() {
if ( did_action( 'rightnow_end' ) ) {
return; // We already displayed this info in the "Right Now" section
if ( !$count = get_option('akismet_spam_count') )
echo '<h3>' . esc_html( _x( 'Spam', 'comments' , 'akismet') ) . '</h3>';
'<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comment</a>.',
'<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comments</a>.',
, 'akismet'), 'https://akismet.com/wordpress/', esc_url( add_query_arg( array( 'page' => 'akismet-admin' ), admin_url( isset( $submenu['edit-comments.php'] ) ? 'edit-comments.php' : 'edit.php' ) ) ), number_format_i18n($count) ).'</p>';
public static function rightnow_stats() {
if ( $count = get_option('akismet_spam_count') ) {
'<a href="%1$s">Akismet</a> has protected your site from %2$s spam comment already. ',
'<a href="%1$s">Akismet</a> has protected your site from %2$s spam comments already. ',
, 'akismet'), 'https://akismet.com/wordpress/', number_format_i18n( $count ) );
$intro = sprintf( __('<a href="%s">Akismet</a> blocks spam from getting to your blog. ', 'akismet'), 'https://akismet.com/wordpress/' );
$link = add_query_arg( array( 'comment_status' => 'spam' ), admin_url( 'edit-comments.php' ) );
if ( $queue_count = self::get_spam_count() ) {
$queue_text = sprintf( _n(
'There’s <a href="%2$s">%1$s comment</a> in your spam queue right now.',
'There are <a href="%2$s">%1$s comments</a> in your spam queue right now.',
, 'akismet'), number_format_i18n( $queue_count ), esc_url( $link ) );
$queue_text = sprintf( __( "There’s nothing in your <a href='%s'>spam queue</a> at the moment." , 'akismet'), esc_url( $link ) );
$text = $intro . '<br />' . $queue_text;
echo "<p class='akismet-right-now'>$text</p>\n";
public static function check_for_spam_button( $comment_status ) {
// The "Check for Spam" button should only appear when the page might be showing
// a comment with comment_approved=0, which means an un-trashed, un-spammed,
// not-yet-moderated comment.
if ( 'all' != $comment_status && 'moderated' != $comment_status ) {
$comments_count = wp_count_comments();
echo '<div class="alignleft actions">';
'button-disabled' // Disable button until the page is loaded
if ( $comments_count->moderated > 0 ) {
$classes[] = 'enable-on-load';
if ( ! Akismet::get_api_key() ) {
$link = add_query_arg( array( 'page' => 'akismet-key-config' ), class_exists( 'Jetpack' ) ? admin_url( 'admin.php' ) : admin_url( 'options-general.php' ) );
$classes[] = 'ajax-disabled';
class="' . esc_attr( implode( ' ', $classes ) ) . '"' .
( ! empty( $link ) ? ' href="' . esc_url( $link ) . '"' : '' ) .
/* translators: The placeholder is for showing how much of the process has completed, as a percent. e.g., "Checking for Spam (40%)" */
' data-progress-label="' . esc_attr( __( 'Checking for Spam (%1$s%)', 'akismet' ) ) . '"
data-success-url="' . esc_attr( remove_query_arg( array( 'akismet_recheck', 'akismet_recheck_error' ), add_query_arg( array( 'akismet_recheck_complete' => 1, 'recheck_count' => urlencode( '__recheck_count__' ), 'spam_count' => urlencode( '__spam_count__' ) ) ) ) ) . '"
data-failure-url="' . esc_attr( remove_query_arg( array( 'akismet_recheck', 'akismet_recheck_complete' ), add_query_arg( array( 'akismet_recheck_error' => 1 ) ) ) ) . '"
data-pending-comment-count="' . esc_attr( $comments_count->moderated ) . '"
data-nonce="' . esc_attr( wp_create_nonce( 'akismet_check_for_spam' ) ) . '"
' . ( ! in_array( 'ajax-disabled', $classes ) ? 'onclick="return false;"' : '' ) . '
>' . esc_html__('Check for Spam', 'akismet') . '</a>';
echo '<span class="checkforspam-spinner"></span>';
public static function recheck_queue() {
Akismet::fix_scheduled_recheck();
if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) ) {
if ( ! wp_verify_nonce( $_POST['nonce'], 'akismet_check_for_spam' ) ) {
'error' => __( "You don't have permission to do that."),
$result_counts = self::recheck_queue_portion( empty( $_POST['offset'] ) ? 0 : $_POST['offset'], empty( $_POST['limit'] ) ? 100 : $_POST['limit'] );
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
'counts' => $result_counts,
$redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' );
wp_safe_redirect( $redirect_to );
public static function recheck_queue_portion( $start = 0, $limit = 100 ) {
$moderation = $wpdb->get_col( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_approved = '0' LIMIT %d OFFSET %d", $limit, $start ) );
'processed' => count( $moderation ),
foreach ( $moderation as $comment_id ) {
$api_response = Akismet::recheck_comment( $comment_id, 'recheck_queue' );
if ( 'true' === $api_response ) {
++$result_counts['spam'];
elseif ( 'false' === $api_response ) {
++$result_counts['error'];
// Adds an 'x' link next to author URLs, clicking will remove the author URL and show an undo link
public static function remove_comment_author_url() {
if ( !empty( $_POST['id'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) {
$comment_id = intval( $_POST['id'] );
$comment = get_comment( $comment_id, ARRAY_A );
if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) {