| Server IP : 146.59.209.152 / Your IP : 216.73.216.46 Web Server : Apache System : Linux webm005.cluster131.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64 User : infrafs ( 43850) PHP Version : 8.2.29 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/i/n/f/infrafs/INFRABIKEIT/wp-content/plugins/ |
Upload File : |
class.akismet-widget.php 0000604 00000006105 15132740121 0011271 0 ustar 00 <?php
/**
* @package Akismet
*/
class Akismet_Widget extends WP_Widget {
function __construct() {
load_plugin_textdomain( 'akismet' );
parent::__construct(
'akismet_widget',
__( 'Akismet Widget' , 'akismet'),
array( 'description' => __( 'Display the number of spam comments Akismet has caught' , 'akismet') )
);
if ( is_active_widget( false, false, $this->id_base ) ) {
add_action( 'wp_head', array( $this, 'css' ) );
}
}
function css() {
?>
<style type="text/css">
.a-stats {
width: auto;
}
.a-stats a {
background: #7CA821;
background-image:-moz-linear-gradient(0% 100% 90deg,#5F8E14,#7CA821);
background-image:-webkit-gradient(linear,0% 0,0% 100%,from(#7CA821),to(#5F8E14));
border: 1px solid #5F8E14;
border-radius:3px;
color: #CFEA93;
cursor: pointer;
display: block;
font-weight: normal;
height: 100%;
-moz-border-radius:3px;
padding: 7px 0 8px;
text-align: center;
text-decoration: none;
-webkit-border-radius:3px;
width: 100%;
}
.a-stats a:hover {
text-decoration: none;
background-image:-moz-linear-gradient(0% 100% 90deg,#6F9C1B,#659417);
background-image:-webkit-gradient(linear,0% 0,0% 100%,from(#659417),to(#6F9C1B));
}
.a-stats .count {
color: #FFF;
display: block;
font-size: 15px;
line-height: 16px;
padding: 0 13px;
white-space: nowrap;
}
</style>
<?php
}
function form( $instance ) {
if ( $instance && isset( $instance['title'] ) ) {
$title = $instance['title'];
}
else {
$title = __( 'Spam Blocked' , 'akismet' );
}
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:' , 'akismet'); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<?php
}
function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = strip_tags( $new_instance['title'] );
return $instance;
}
function widget( $args, $instance ) {
$count = get_option( 'akismet_spam_count' );
if ( ! isset( $instance['title'] ) ) {
$instance['title'] = __( 'Spam Blocked' , 'akismet' );
}
echo $args['before_widget'];
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'];
echo esc_html( $instance['title'] );
echo $args['after_title'];
}
?>
<div class="a-stats">
<a href="https://akismet.com" target="_blank" rel="noopener" title="">
<?php
echo wp_kses(
sprintf(
/* translators: The placeholder is the number of pieces of spam blocked by Akismet. */
_n(
'<strong class="count">%1$s spam</strong> blocked by <strong>Akismet</strong>',
'<strong class="count">%1$s spam</strong> blocked by <strong>Akismet</strong>',
$count,
'akismet'
),
number_format_i18n( $count )
),
array(
'strong' => array(
'class' => true,
),
)
);
?>
</a>
</div>
<?php
echo $args['after_widget'];
}
}
function akismet_register_widgets() {
register_widget( 'Akismet_Widget' );
}
add_action( 'widgets_init', 'akismet_register_widgets' );
readme.txt 0000604 00000013625 15132740121 0006541 0 ustar 00 === Akismet Anti-spam: Spam Protection ===
Contributors: matt, ryan, andy, mdawaffe, tellyworth, josephscott, lessbloat, eoigal, cfinke, automattic, jgs, procifer, stephdau, kbrownkd, bluefuton, akismetantispam
Tags: comments, spam, antispam, anti-spam, contact form
Requires at least: 5.8
Tested up to: 6.6
Stable tag: 5.3.3
License: GPLv2 or later
The best anti-spam protection to block spam comments and spam in a contact form. The most trusted antispam solution for WordPress and WooCommerce.
== Description ==
The best anti-spam protection to block spam comments and spam in a contact form. The most trusted antispam solution for WordPress and WooCommerce.
Akismet checks your comments and contact form submissions against our global database of spam to prevent your site from publishing malicious content. You can review the comment spam it catches on your blog's "Comments" admin screen.
Major features in Akismet include:
* Automatically checks all comments and filters out the ones that look like spam.
* Each comment has a status history, so you can easily see which comments were caught or cleared by Akismet and which were spammed or unspammed by a moderator.
* URLs are shown in the comment body to reveal hidden or misleading links.
* Moderators can see the number of approved comments for each user.
* A discard feature that outright blocks the worst spam, saving you disk space and speeding up your site.
PS: You'll be prompted to get an Akismet.com API key to use it, once activated. Keys are free for personal blogs; paid subscriptions are available for businesses and commercial sites.
== Installation ==
Upload the Akismet plugin to your blog, activate it, and then enter your Akismet.com API key.
1, 2, 3: You're done!
== Changelog ==
= 5.3.3 =
*Release Date - 10 July 2024*
* Make setup step clearer for new users.
* Remove the stats section from the configuration page if the site has been revoked from the key.
* Skip the Akismet comment check when the comment matches something in the disallowed list.
* Prompt users on legacy plans to contact Akismet support for upgrades.
= 5.3.2 =
*Release Date - 21 March 2024*
* Improve the empty state shown to new users when no spam has been caught yet.
* Update the message shown to users without a current subscription.
* Add foundations for future webhook support.
= 5.3.1 =
*Release Date - 17 January 2024*
* Make the plugin more resilient when asset files are missing (as seen in WordPress Playground).
* Add a link to the 'Account overview' page on akismet.com.
* Fix a minor error that occurs when another plugin removes all comment actions from the dashboard.
* Add the akismet_request_args filter to allow request args in Akismet API requests to be filtered.
* Fix a bug that causes some contact forms to include unnecessary data in the comment_content parameter.
= 5.3 =
*Release Date - 14 September 2023*
* Improve display of user notices.
* Add stylesheets for RTL languages.
* Remove initial disabled state from 'Save changes' button.
* Improve accessibility of API key entry form.
* Add new filter hooks for Fluent Forms.
* Fix issue with PHP 8.1 compatibility.
= 5.2 =
*Release Date - 21 June 2023*
* Visual refresh of Akismet stats.
* Improve PHP 8.1 compatibility.
* Improve appearance of plugin to match updated stats.
* Change minimum supported PHP version to 5.6 to match WordPress.
* Drop IE11 support and update minimum WordPress version to 5.8 (where IE11 support was removed from WP Core).
= 5.1 =
*Release Date - 20 March 2023*
* Removed unnecessary limit notices from admin page.
* Improved spam detection by including post taxonomies in the comment-check call.
* Removed API keys from stats iframes to avoid possible inadvertent exposure.
= 5.0.2 =
*Release Date - 1 December 2022*
* Improved compatibility with themes that hide or show UI elements based on mouse movements.
* Increased security of API keys by sending them in request bodies instead of subdomains.
= 5.0.1 =
*Release Date - 28 September 2022*
* Added an empty state for the Statistics section on the admin page.
* Fixed a bug that broke some admin page links when Jetpack plugins are active.
* Marked some event listeners as passive to improve performance in newer browsers.
* Disabled interaction observation on forms that post to other domains.
= 5.0 =
*Release Date - 26 July 2022*
* Added a new feature to catch spammers by observing how they interact with the page.
= 4.2.5 =
*Release Date - 11 July 2022*
* Fixed a bug that added unnecessary comment history entries after comment rechecks.
* Added a notice that displays when WP-Cron is disabled and might be affecting comment rechecks.
= 4.2.4 =
*Release Date - 20 May 2022*
* Improved translator instructions for comment history.
* Bumped the "Tested up to" tag to WP 6.0.
= 4.2.3 =
*Release Date - 25 April 2022*
* Improved compatibility with Fluent Forms
* Fixed missing translation domains
* Updated stats URL.
* Improved accessibility of elements on the config page.
= 4.2.2 =
*Release Date - 24 January 2022*
* Improved compatibility with Formidable Forms
* Fixed a bug that could cause issues when multiple contact forms appear on one page.
* Updated delete_comment and deleted_comment actions to pass two arguments to match WordPress core since 4.9.0.
* Added a filter that allows comment types to be excluded when counting users' approved comments.
= 4.2.1 =
*Release Date - 1 October 2021*
* Fixed a bug causing AMP validation to fail on certain pages with forms.
= 4.2 =
*Release Date - 30 September 2021*
* Added links to additional information on API usage notifications.
* Reduced the number of network requests required for a comment page when running Akismet.
* Improved compatibility with the most popular contact form plugins.
* Improved API usage buttons for clarity on what upgrade is needed.
For older changelog entries, please see the [additional changelog.txt file](https://plugins.svn.wordpress.org/akismet/trunk/changelog.txt) delivered with the plugin.
wrapper.php 0000604 00000014446 15132740121 0006736 0 ustar 00 <?php
global $wpcom_api_key, $akismet_api_host, $akismet_api_port;
$wpcom_api_key = defined( 'WPCOM_API_KEY' ) ? constant( 'WPCOM_API_KEY' ) : '';
$akismet_api_host = Akismet::get_api_key() . '.rest.akismet.com';
$akismet_api_port = 80;
function akismet_test_mode() {
return Akismet::is_test_mode();
}
function akismet_http_post( $request, $host, $path, $port = 80, $ip = null ) {
$path = str_replace( '/1.1/', '', $path );
return Akismet::http_post( $request, $path, $ip );
}
function akismet_microtime() {
return Akismet::_get_microtime();
}
function akismet_delete_old() {
return Akismet::delete_old_comments();
}
function akismet_delete_old_metadata() {
return Akismet::delete_old_comments_meta();
}
function akismet_check_db_comment( $id, $recheck_reason = 'recheck_queue' ) {
return Akismet::check_db_comment( $id, $recheck_reason );
}
function akismet_rightnow() {
if ( !class_exists( 'Akismet_Admin' ) )
return false;
return Akismet_Admin::rightnow_stats();
}
function akismet_admin_init() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_version_warning() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_load_js_and_css() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_nonce_field( $action = -1 ) {
return wp_nonce_field( $action );
}
function akismet_plugin_action_links( $links, $file ) {
return Akismet_Admin::plugin_action_links( $links, $file );
}
function akismet_conf() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_stats_display() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_stats() {
return Akismet_Admin::dashboard_stats();
}
function akismet_admin_warnings() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_comment_row_action( $a, $comment ) {
return Akismet_Admin::comment_row_actions( $a, $comment );
}
function akismet_comment_status_meta_box( $comment ) {
return Akismet_Admin::comment_status_meta_box( $comment );
}
function akismet_comments_columns( $columns ) {
_deprecated_function( __FUNCTION__, '3.0' );
return $columns;
}
function akismet_comment_column_row( $column, $comment_id ) {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_text_add_link_callback( $m ) {
return Akismet_Admin::text_add_link_callback( $m );
}
function akismet_text_add_link_class( $comment_text ) {
return Akismet_Admin::text_add_link_class( $comment_text );
}
function akismet_check_for_spam_button( $comment_status ) {
return Akismet_Admin::check_for_spam_button( $comment_status );
}
function akismet_submit_nonspam_comment( $comment_id ) {
return Akismet::submit_nonspam_comment( $comment_id );
}
function akismet_submit_spam_comment( $comment_id ) {
return Akismet::submit_spam_comment( $comment_id );
}
function akismet_transition_comment_status( $new_status, $old_status, $comment ) {
return Akismet::transition_comment_status( $new_status, $old_status, $comment );
}
function akismet_spam_count( $type = false ) {
return Akismet_Admin::get_spam_count( $type );
}
function akismet_recheck_queue() {
return Akismet_Admin::recheck_queue();
}
function akismet_remove_comment_author_url() {
return Akismet_Admin::remove_comment_author_url();
}
function akismet_add_comment_author_url() {
return Akismet_Admin::add_comment_author_url();
}
function akismet_check_server_connectivity() {
return Akismet_Admin::check_server_connectivity();
}
function akismet_get_server_connectivity( $cache_timeout = 86400 ) {
return Akismet_Admin::get_server_connectivity( $cache_timeout );
}
function akismet_server_connectivity_ok() {
_deprecated_function( __FUNCTION__, '3.0' );
return true;
}
function akismet_admin_menu() {
return Akismet_Admin::admin_menu();
}
function akismet_load_menu() {
return Akismet_Admin::load_menu();
}
function akismet_init() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_get_key() {
return Akismet::get_api_key();
}
function akismet_check_key_status( $key, $ip = null ) {
return Akismet::check_key_status( $key, $ip );
}
function akismet_update_alert( $response ) {
return Akismet::update_alert( $response );
}
function akismet_verify_key( $key, $ip = null ) {
return Akismet::verify_key( $key, $ip );
}
function akismet_get_user_roles( $user_id ) {
return Akismet::get_user_roles( $user_id );
}
function akismet_result_spam( $approved ) {
return Akismet::comment_is_spam( $approved );
}
function akismet_result_hold( $approved ) {
return Akismet::comment_needs_moderation( $approved );
}
function akismet_get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url ) {
return Akismet::get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url );
}
function akismet_update_comment_history( $comment_id, $message, $event = null ) {
return Akismet::update_comment_history( $comment_id, $message, $event );
}
function akismet_get_comment_history( $comment_id ) {
return Akismet::get_comment_history( $comment_id );
}
function akismet_cmp_time( $a, $b ) {
return Akismet::_cmp_time( $a, $b );
}
function akismet_auto_check_update_meta( $id, $comment ) {
return Akismet::auto_check_update_meta( $id, $comment );
}
function akismet_auto_check_comment( $commentdata ) {
return Akismet::auto_check_comment( $commentdata );
}
function akismet_get_ip_address() {
return Akismet::get_ip_address();
}
function akismet_cron_recheck() {
return Akismet::cron_recheck();
}
function akismet_add_comment_nonce( $post_id ) {
return Akismet::add_comment_nonce( $post_id );
}
function akismet_fix_scheduled_recheck() {
return Akismet::fix_scheduled_recheck();
}
function akismet_spam_comments() {
_deprecated_function( __FUNCTION__, '3.0' );
return array();
}
function akismet_spam_totals() {
_deprecated_function( __FUNCTION__, '3.0' );
return array();
}
function akismet_manage_page() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_caught() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function redirect_old_akismet_urls() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_kill_proxy_check( $option ) {
_deprecated_function( __FUNCTION__, '3.0' );
return 0;
}
function akismet_pingback_forwarded_for( $r, $url ) {
// This functionality is now in core.
return false;
}
function akismet_pre_check_pingback( $method ) {
return Akismet::pre_check_pingback( $method );
} index.php 0000604 00000000032 15132740121 0006347 0 ustar 00 <?php
# Silence is golden. class.akismet-admin.php 0000604 00000157541 15132740121 0011111 0 ustar 00 <?php
class Akismet_Admin {
const NONCE = 'akismet-update-key';
private static $initiated = false;
private static $notices = array();
private static $allowed = array(
'a' => array(
'href' => true,
'title' => true,
),
'b' => array(),
'code' => array(),
'del' => array(
'datetime' => true,
),
'em' => array(),
'i' => array(),
'q' => array(
'cite' => true,
),
'strike' => array(),
'strong' => array(),
);
public static function init() {
if ( ! self::$initiated ) {
self::init_hooks();
}
if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) {
self::enter_api_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 );
die;
}
self::$initiated = true;
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() ) {
$admin_url = self::get_page_url( 'init' );
wp_redirect( $admin_url );
}
}
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' ) );
} else {
self::load_menu();
}
}
public static function admin_head() {
if ( !current_user_can( 'manage_options' ) )
return;
}
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 );
return $links;
}
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' ) );
}
else {
$hook = add_options_page( __( 'Akismet Anti-spam', 'akismet' ), __( 'Akismet Anti-spam', 'akismet' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
}
if ( $hook ) {
add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) );
}
}
public static function load_resources() {
global $hook_suffix;
if ( in_array( $hook_suffix, apply_filters( 'akismet_admin_page_hook_suffixes', array(
'index.php', # dashboard
'edit-comments.php',
'comment.php',
'post.php',
'settings_page_akismet-key-config',
'jetpack_page_akismet-key-config',
'plugins.php',
) ) ) ) {
$akismet_css_path = is_rtl() ? '_inc/rtl/akismet-rtl.css' : '_inc/akismet.css';
wp_register_style( 'akismet', plugin_dir_url( __FILE__ ) . $akismet_css_path, array(), self::get_asset_file_version( $akismet_css_path ) );
wp_enqueue_style( 'akismet' );
wp_register_style( 'akismet-font-inter', plugin_dir_url( __FILE__ ) . '_inc/fonts/inter.css', array(), self::get_asset_file_version( '_inc/fonts/inter.css' ) );
wp_enqueue_style( 'akismet-font-inter' );
$akismet_admin_css_path = is_rtl() ? '_inc/rtl/akismet-admin-rtl.css' : '_inc/akismet-admin.css';
wp_register_style( 'akismet-admin', plugin_dir_url( __FILE__ ) . $akismet_admin_css_path, array(), self::get_asset_file_version( $akismet_admin_css_path ) );
wp_enqueue_style( 'akismet-admin' );
wp_register_script( 'akismet.js', plugin_dir_url( __FILE__ ) . '_inc/akismet.js', array( 'jquery' ), self::get_asset_file_version( '_inc/akismet.js' ) );
wp_enqueue_script( 'akismet.js' );
wp_register_script( 'akismet-admin.js', plugin_dir_url( __FILE__ ) . '_inc/akismet-admin.js', array(), self::get_asset_file_version( '/_inc/akismet-admin.js' ) );
wp_enqueue_script( 'akismet-admin.js' );
$inline_js = array(
'comment_author_url_nonce' => wp_create_nonce( 'comment_author_url_nonce' ),
'strings' => array(
'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();
// Screen Content
if ( current_user_can( 'manage_options' ) ) {
if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) {
//setup page
$current_screen->add_help_tab(
array(
'id' => 'overview',
'title' => __( 'Overview' , 'akismet'),
'content' =>
'<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(
array(
'id' => 'setup-signup',
'title' => __( 'New to Akismet' , 'akismet'),
'content' =>
'<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(
array(
'id' => 'setup-manual',
'title' => __( 'Enter an API Key' , 'akismet'),
'content' =>
'<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
'<p>' . esc_html__( 'If you already have an API key' , 'akismet') . '</p>' .
'<ol>' .
'<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>' .
'</ol>',
)
);
}
elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) {
//stats page
$current_screen->add_help_tab(
array(
'id' => 'overview',
'title' => __( 'Overview' , 'akismet'),
'content' =>
'<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>',
)
);
}
else {
//configuration page
$current_screen->add_help_tab(
array(
'id' => 'overview',
'title' => __( 'Overview' , 'akismet'),
'content' =>
'<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(
array(
'id' => 'settings',
'title' => __( 'Settings' , 'akismet'),
'content' =>
'<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(
array(
'id' => 'account',
'title' => __( 'Account' , 'akismet'),
'content' =>
'<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>',
)
);
}
}
}
// Help Sidebar
$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 ) )
return false;
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'] );
} else {
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 );
}
return true;
}
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 ( $akismet_user ) {
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;
else
self::$notices['status'] = $akismet_user->status;
}
else
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') )
return;
global $submenu;
echo '<h3>' . esc_html( _x( 'Spam', 'comments' , 'akismet') ) . '</h3>';
echo '<p>'.sprintf( _n(
'<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>.',
$count
, '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>';
}
// WP 2.5+
public static function rightnow_stats() {
if ( $count = get_option('akismet_spam_count') ) {
$intro = sprintf( _n(
'<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. ',
$count
, 'akismet'), 'https://akismet.com/wordpress/', number_format_i18n( $count ) );
} else {
$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.',
$queue_count
, 'akismet'), number_format_i18n( $queue_count ), esc_url( $link ) );
} else {
$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 ) {
return;
}
$link = '';
$comments_count = wp_count_comments();
echo '</div>';
echo '<div class="alignleft actions">';
$classes = array(
'button-secondary',
'checkforspam',
'button-disabled' // Disable button until the page is loaded
);
if ( $comments_count->moderated > 0 ) {
$classes[] = 'enable-on-load';
if ( ! Akismet::get_api_key() ) {
$link = self::get_page_url();
$classes[] = 'ajax-disabled';
}
}
echo '<a
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() {
global $wpdb;
Akismet::fix_scheduled_recheck();
if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) ) {
return;
}
if ( ! wp_verify_nonce( $_POST['nonce'], 'akismet_check_for_spam' ) ) {
wp_send_json( array(
'error' => __( 'You don’t have permission to do that.', 'akismet' ),
));
return;
}
$result_counts = self::recheck_queue_portion( empty( $_POST['offset'] ) ? 0 : $_POST['offset'], empty( $_POST['limit'] ) ? 100 : $_POST['limit'] );
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
wp_send_json( array(
'counts' => $result_counts,
));
}
else {
$redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' );
wp_safe_redirect( $redirect_to );
exit;
}
}
public static function recheck_queue_portion( $start = 0, $limit = 100 ) {
global $wpdb;
$paginate = '';
if ( $limit <= 0 ) {
$limit = 100;
}
if ( $start < 0 ) {
$start = 0;
}
$moderation = $wpdb->get_col( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_approved = '0' LIMIT %d OFFSET %d", $limit, $start ) );
$result_counts = array(
'processed' => is_countable( $moderation ) ? count( $moderation ) : 0,
'spam' => 0,
'ham' => 0,
'error' => 0,
);
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['ham'];
}
else {
++$result_counts['error'];
}
}
return $result_counts;
}
// 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'] ) ) {
$comment['comment_author_url'] = '';
do_action( 'comment_remove_author_url' );
print( wp_update_comment( $comment ) );
die();
}
}
}
public static function add_comment_author_url() {
if ( !empty( $_POST['id'] ) && !empty( $_POST['url'] ) && 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'] ) ) {
$comment['comment_author_url'] = esc_url( $_POST['url'] );
do_action( 'comment_add_author_url' );
print( wp_update_comment( $comment ) );
die();
}
}
}
public static function comment_row_action( $a, $comment ) {
$akismet_result = get_comment_meta( $comment->comment_ID, 'akismet_result', true );
if ( ! $akismet_result && get_comment_meta( $comment->comment_ID, 'akismet_skipped', true ) ) {
$akismet_result = 'skipped'; // Akismet chose to skip the comment-check request.
}
$akismet_error = get_comment_meta( $comment->comment_ID, 'akismet_error', true );
$user_result = get_comment_meta( $comment->comment_ID, 'akismet_user_result', true );
$comment_status = wp_get_comment_status( $comment->comment_ID );
$desc = null;
if ( $akismet_error ) {
$desc = __( 'Awaiting spam check' , 'akismet');
} elseif ( !$user_result || $user_result == $akismet_result ) {
// Show the original Akismet result if the user hasn't overridden it, or if their decision was the same
if ( $akismet_result == 'true' && $comment_status != 'spam' && $comment_status != 'trash' )
$desc = __( 'Flagged as spam by Akismet' , 'akismet');
elseif ( $akismet_result == 'false' && $comment_status == 'spam' )
$desc = __( 'Cleared by Akismet' , 'akismet');
} else {
$who = get_comment_meta( $comment->comment_ID, 'akismet_user', true );
if ( $user_result == 'true' )
$desc = sprintf( __('Flagged as spam by %s', 'akismet'), $who );
else
$desc = sprintf( __('Un-spammed by %s', 'akismet'), $who );
}
// add a History item to the hover links, just after Edit
if ( $akismet_result && is_array( $a ) ) {
$b = array();
foreach ( $a as $k => $item ) {
$b[ $k ] = $item;
if (
$k == 'edit'
|| $k == 'unspam'
) {
$b['history'] = '<a href="comment.php?action=editcomment&c='.$comment->comment_ID.'#akismet-status" title="'. esc_attr__( 'View comment history' , 'akismet') . '"> '. esc_html__('History', 'akismet') . '</a>';
}
}
$a = $b;
}
if ( $desc )
echo '<span class="akismet-status" commentid="'.$comment->comment_ID.'"><a href="comment.php?action=editcomment&c='.$comment->comment_ID.'#akismet-status" title="' . esc_attr__( 'View comment history' , 'akismet') . '">'.esc_html( $desc ).'</a></span>';
$show_user_comments_option = get_option( 'akismet_show_user_comments_approved' );
if ( $show_user_comments_option === false ) {
// Default to active if the user hasn't made a decision.
$show_user_comments_option = '1';
}
$show_user_comments = apply_filters( 'akismet_show_user_comments_approved', $show_user_comments_option );
$show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; //option used to be saved as 'false' / 'true'
if ( $show_user_comments ) {
$comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url );
$comment_count = intval( $comment_count );
echo '<span class="akismet-user-comment-count" commentid="'.$comment->comment_ID.'" style="display:none;"><br><span class="akismet-user-comment-counts">'. sprintf( esc_html( _n( '%s approved', '%s approved', $comment_count , 'akismet') ), number_format_i18n( $comment_count ) ) . '</span></span>';
}
return $a;
}
public static function comment_status_meta_box( $comment ) {
$history = Akismet::get_comment_history( $comment->comment_ID );
if ( $history ) {
foreach ( $history as $row ) {
$message = '';
if ( ! empty( $row['message'] ) ) {
// Old versions of Akismet stored the message as a literal string in the commentmeta.
// New versions don't do that for two reasons:
// 1) Save space.
// 2) The message can be translated into the current language of the blog, not stuck
// in the language of the blog when the comment was made.
$message = esc_html( $row['message'] );
} else if ( ! empty( $row['event'] ) ) {
// If possible, use a current translation.
switch ( $row['event'] ) {
case 'recheck-spam':
$message = esc_html( __( 'Akismet re-checked and caught this comment as spam.', 'akismet' ) );
break;
case 'check-spam':
$message = esc_html( __( 'Akismet caught this comment as spam.', 'akismet' ) );
break;
case 'recheck-ham':
$message = esc_html( __( 'Akismet re-checked and cleared this comment.', 'akismet' ) );
break;
case 'check-ham':
$message = esc_html( __( 'Akismet cleared this comment.', 'akismet' ) );
break;
case 'wp-blacklisted':
case 'wp-disallowed':
$message = sprintf(
/* translators: The placeholder is a WordPress PHP function name. */
esc_html( __( 'Comment was caught by %s.', 'akismet' ) ),
function_exists( 'wp_check_comment_disallowed_list' ) ? '<code>wp_check_comment_disallowed_list</code>' : '<code>wp_blacklist_check</code>'
);
break;
case 'report-spam':
if ( isset( $row['user'] ) ) {
/* translators: The placeholder is a username. */
$message = esc_html( sprintf( __( '%s reported this comment as spam.', 'akismet' ), $row['user'] ) );
} else if ( ! $message ) {
$message = esc_html( __( 'This comment was reported as spam.', 'akismet' ) );
}
break;
case 'report-ham':
if ( isset( $row['user'] ) ) {
/* translators: The placeholder is a username. */
$message = esc_html( sprintf( __( '%s reported this comment as not spam.', 'akismet' ), $row['user'] ) );
} else if ( ! $message ) {
$message = esc_html( __( 'This comment was reported as not spam.', 'akismet' ) );
}
break;
case 'cron-retry-spam':
$message = esc_html( __( 'Akismet caught this comment as spam during an automatic retry.', 'akismet' ) );
break;
case 'cron-retry-ham':
$message = esc_html( __( 'Akismet cleared this comment during an automatic retry.', 'akismet' ) );
break;
case 'check-error':
if ( isset( $row['meta'], $row['meta']['response'] ) ) {
/* translators: The placeholder is an error response returned by the API server. */
$message = sprintf( esc_html( __( 'Akismet was unable to check this comment (response: %s) but will automatically retry later.', 'akismet' ) ), '<code>' . esc_html( $row['meta']['response'] ) . '</code>' );
} else {
$message = esc_html( __( 'Akismet was unable to check this comment but will automatically retry later.', 'akismet' ) );
}
break;
case 'recheck-error':
if ( isset( $row['meta'], $row['meta']['response'] ) ) {
/* translators: The placeholder is an error response returned by the API server. */
$message = sprintf( esc_html( __( 'Akismet was unable to recheck this comment (response: %s).', 'akismet' ) ), '<code>' . esc_html( $row['meta']['response'] ) . '</code>' );
} else {
$message = esc_html( __( 'Akismet was unable to recheck this comment.', 'akismet' ) );
}
break;
case 'webhook-spam':
$message = esc_html( __( 'Akismet caught this comment as spam and updated its status via webhook.', 'akismet' ) );
break;
case 'webhook-ham':
$message = esc_html( __( 'Akismet cleared this comment and updated its status via webhook.', 'akismet' ) );
break;
case 'webhook-spam-noaction':
$message = esc_html( __( 'Akismet determined this comment was spam during a recheck. It did not update the comment status because it had already been modified by another user or plugin.', 'akismet' ) );
break;
case 'webhook-ham-noaction':
$message = esc_html( __( 'Akismet cleared this comment during a recheck. It did not update the comment status because it had already been modified by another user or plugin.', 'akismet' ) );
break;
case 'akismet-skipped':
$message = esc_html( __( 'This comment was not sent to Akismet when it was submitted because it was caught by something else.', 'akismet' ) );
break;
case 'akismet-skipped-disallowed':
$message = esc_html( __( 'This comment was not sent to Akismet when it was submitted because it was caught by the comment disallowed list.', 'akismet' ) );
break;
default:
if ( preg_match( '/^status-changed/', $row['event'] ) ) {
// Half of these used to be saved without the dash after 'status-changed'.
// See https://plugins.trac.wordpress.org/changeset/1150658/akismet/trunk
$new_status = preg_replace( '/^status-changed-?/', '', $row['event'] );
/* translators: The placeholder is a short string (like 'spam' or 'approved') denoting the new comment status. */
$message = sprintf( esc_html( __( 'Comment status was changed to %s', 'akismet' ) ), '<code>' . esc_html( $new_status ) . '</code>' );
} else if ( preg_match( '/^status-/', $row['event'] ) ) {
$new_status = preg_replace( '/^status-/', '', $row['event'] );
if ( isset( $row['user'] ) ) {
/* translators: %1$s is a username; %2$s is a short string (like 'spam' or 'approved') denoting the new comment status. */
$message = sprintf( esc_html( __( '%1$s changed the comment status to %2$s.', 'akismet' ) ), $row['user'], '<code>' . esc_html( $new_status ) . '</code>' );
}
}
break;
}
}
if ( ! empty( $message ) ) {
echo '<p>';
if ( isset( $row['time'] ) ) {
$time = gmdate( 'D d M Y @ h:i:s a', (int) $row['time'] ) . ' GMT';
/* translators: The placeholder is an amount of time, like "7 seconds" or "3 days" returned by the function human_time_diff(). */
$time_html = '<span style="color: #999;" alt="' . esc_attr( $time ) . '" title="' . esc_attr( $time ) . '">' . sprintf( esc_html__( '%s ago', 'akismet' ), human_time_diff( $row['time'] ) ) . '</span>';
echo sprintf(
/* translators: %1$s is a human-readable time difference, like "3 hours ago", and %2$s is an already-translated phrase describing how a comment's status changed, like "This comment was reported as spam." */
esc_html( __( '%1$s - %2$s', 'akismet' ) ),
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$time_html,
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$message
); // esc_html() is done above so that we can use HTML in $message.
} else {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $message; // esc_html() is done above so that we can use HTML in $message.
}
echo '</p>';
}
}
} else {
echo '<p>';
echo esc_html( __( 'No comment history.', 'akismet' ) );
echo '</p>';
}
}
public static function plugin_action_links( $links, $file ) {
if ( $file == plugin_basename( plugin_dir_url( __FILE__ ) . '/akismet.php' ) ) {
$links[] = '<a href="' . esc_url( self::get_page_url() ) . '">'.esc_html__( 'Settings' , 'akismet').'</a>';
}
return $links;
}
// Total spam in queue
// get_option( 'akismet_spam_count' ) is the total caught ever
public static function get_spam_count( $type = false ) {
global $wpdb;
if ( !$type ) { // total
$count = wp_cache_get( 'akismet_spam_count', 'widget' );
if ( false === $count ) {
$count = wp_count_comments();
$count = $count->spam;
wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 );
}
return $count;
} elseif ( 'comments' == $type || 'comment' == $type ) { // comments
$type = '';
}
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) );
}
// Check connectivity between the WordPress blog and Akismet's servers.
// Returns an associative array of server IP addresses, where the key is the IP address, and value is true (available) or false (unable to connect).
public static function check_server_ip_connectivity() {
$servers = $ips = array();
// Some web hosts may disable this function
if ( function_exists( 'gethostbynamel' ) ) {
$ips = gethostbynamel( 'rest.akismet.com' );
if ( $ips && is_array($ips) && count($ips) ) {
$api_key = Akismet::get_api_key();
foreach ( $ips as $ip ) {
$response = Akismet::verify_key( $api_key, $ip );
// even if the key is invalid, at least we know we have connectivity
if ( $response == 'valid' || $response == 'invalid' )
$servers[$ip] = 'connected';
else
$servers[$ip] = $response ? $response : 'unable to connect';
}
}
}
return $servers;
}
// Simpler connectivity check
public static function check_server_connectivity($cache_timeout = 86400) {
$debug = array();
$debug[ 'PHP_VERSION' ] = PHP_VERSION;
$debug[ 'WORDPRESS_VERSION' ] = $GLOBALS['wp_version'];
$debug[ 'AKISMET_VERSION' ] = AKISMET_VERSION;
$debug[ 'AKISMET__PLUGIN_DIR' ] = AKISMET__PLUGIN_DIR;
$debug[ 'SITE_URL' ] = site_url();
$debug[ 'HOME_URL' ] = home_url();
$servers = get_option('akismet_available_servers');
if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) {
$servers = self::check_server_ip_connectivity();
update_option('akismet_available_servers', $servers);
update_option('akismet_connectivity_time', time());
}
if ( wp_http_supports( array( 'ssl' ) ) ) {
$response = wp_remote_get( 'https://rest.akismet.com/1.1/test' );
}
else {
$response = wp_remote_get( 'http://rest.akismet.com/1.1/test' );
}
$debug[ 'gethostbynamel' ] = function_exists('gethostbynamel') ? 'exists' : 'not here';
$debug[ 'Servers' ] = $servers;
$debug[ 'Test Connection' ] = $response;
Akismet::log( $debug );
if ( $response && 'connected' == wp_remote_retrieve_body( $response ) )
return true;
return false;
}
// Check the server connectivity and store the available servers in an option.
public static function get_server_connectivity($cache_timeout = 86400) {
return self::check_server_connectivity( $cache_timeout );
}
/**
* Find out whether any comments in the Pending queue have not yet been checked by Akismet.
*
* @return bool
*/
public static function are_any_comments_waiting_to_be_checked() {
return !! get_comments( array(
// Exclude comments that are not pending. This would happen if someone manually approved or spammed a comment
// that was waiting to be checked. The akismet_error meta entry will eventually be removed by the cron recheck job.
'status' => 'hold',
// This is the commentmeta that is saved when a comment couldn't be checked.
'meta_key' => 'akismet_error',
// We only need to know whether at least one comment is waiting for a check.
'number' => 1,
) );
}
public static function get_page_url( $page = 'config' ) {
$args = array( 'page' => 'akismet-key-config' );
if ( $page == 'stats' ) {
$args = array( 'page' => 'akismet-key-config', 'view' => 'stats' );
} elseif ( $page == 'delete_key' ) {
$args = array( 'page' => 'akismet-key-config', 'view' => 'start', 'action' => 'delete-key', '_wpnonce' => wp_create_nonce( self::NONCE ) );
} elseif ( $page === 'init' ) {
$args = array( 'page' => 'akismet-key-config', 'view' => 'start' );
}
return add_query_arg( $args, menu_page_url( 'akismet-key-config', false ) );
}
public static function get_akismet_user( $api_key ) {
$akismet_user = false;
$request_args = array(
'key' => $api_key,
'blog' => get_option( 'home' ),
);
$request_args = apply_filters( 'akismet_request_args', $request_args, 'get-subscription' );
$subscription_verification = Akismet::http_post( Akismet::build_query( $request_args ), 'get-subscription' );
if ( ! empty( $subscription_verification[1] ) ) {
if ( 'invalid' !== $subscription_verification[1] ) {
$akismet_user = json_decode( $subscription_verification[1] );
}
}
return $akismet_user;
}
public static function get_stats( $api_key ) {
$stat_totals = array();
foreach( array( '6-months', 'all' ) as $interval ) {
$request_args = array(
'blog' => get_option( 'home' ),
'key' => $api_key,
'from' => $interval,
);
$request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' );
$response = Akismet::http_post( Akismet::build_query( $request_args ), 'get-stats' );
if ( ! empty( $response[1] ) ) {
$data = json_decode( $response[1] );
/*
* The json decoded response should be an object. If it's not an object, something's wrong, and the data
* shouldn't be added to the stats_totals array.
*/
if ( is_object( $data ) ) {
$stat_totals[ $interval ] = $data;
}
}
}
return $stat_totals;
}
public static function verify_wpcom_key( $api_key, $user_id, $extra = array() ) {
$request_args = array_merge(
array(
'user_id' => $user_id,
'api_key' => $api_key,
'get_account_type' => 'true',
),
$extra
);
$request_args = apply_filters( 'akismet_request_args', $request_args, 'verify-wpcom-key' );
$akismet_account = Akismet::http_post( Akismet::build_query( $request_args ), 'verify-wpcom-key' );
if ( ! empty( $akismet_account[1] ) )
$akismet_account = json_decode( $akismet_account[1] );
Akismet::log( compact( 'akismet_account' ) );
return $akismet_account;
}
public static function connect_jetpack_user() {
if ( $jetpack_user = self::get_jetpack_user() ) {
if ( isset( $jetpack_user['user_id'] ) && isset( $jetpack_user['api_key'] ) ) {
$akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'], array( 'action' => 'connect_jetpack_user' ) );
if ( is_object( $akismet_user ) ) {
self::save_key( $akismet_user->api_key );
return in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) );
}
}
}
return false;
}
public static function display_alert() {
Akismet::view( 'notice', array(
'type' => 'alert',
'code' => (int) get_option( 'akismet_alert_code' ),
'msg' => get_option( 'akismet_alert_msg' )
) );
}
public static function get_usage_limit_alert_data() {
return array(
'type' => 'usage-limit',
'code' => (int) get_option( 'akismet_alert_code' ),
'msg' => get_option( 'akismet_alert_msg' ),
'api_calls' => get_option( 'akismet_alert_api_calls' ),
'usage_limit' => get_option( 'akismet_alert_usage_limit' ),
'upgrade_plan' => get_option( 'akismet_alert_upgrade_plan' ),
'upgrade_url' => get_option( 'akismet_alert_upgrade_url' ),
'upgrade_type' => get_option( 'akismet_alert_upgrade_type' ),
'upgrade_via_support' => get_option( 'akismet_alert_upgrade_via_support' ) === 'true',
);
}
public static function display_usage_limit_alert() {
Akismet::view( 'notice', self::get_usage_limit_alert_data() );
}
public static function display_spam_check_warning() {
Akismet::fix_scheduled_recheck();
if ( wp_next_scheduled('akismet_schedule_cron_recheck') > time() && self::are_any_comments_waiting_to_be_checked() ) {
/*
* The 'akismet_display_cron_disabled_notice' filter can be used to control whether the WP-Cron disabled notice is displayed.
*/
if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON && apply_filters( 'akismet_display_cron_disabled_notice', true ) ) {
Akismet::view( 'notice', array( 'type' => 'spam-check-cron-disabled' ) );
} else {
/* translators: The Akismet configuration page URL. */
$link_text = apply_filters( 'akismet_spam_check_warning_link_text', sprintf( __( 'Please check your <a href="%s">Akismet configuration</a> and contact your web host if problems persist.', 'akismet' ), esc_url( self::get_page_url() ) ) );
Akismet::view( 'notice', array( 'type' => 'spam-check', 'link_text' => $link_text ) );
}
}
}
public static function display_api_key_warning() {
Akismet::view( 'notice', array( 'type' => 'plugin' ) );
}
public static function display_page() {
if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) )
self::display_start_page();
elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' )
self::display_stats_page();
else
self::display_configuration_page();
}
public static function display_start_page() {
if ( isset( $_GET['action'] ) ) {
if ( $_GET['action'] == 'delete-key' ) {
if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], self::NONCE ) )
delete_option( 'wordpress_api_key' );
}
}
if ( $api_key = Akismet::get_api_key() && ( empty( self::$notices['status'] ) || 'existing-key-invalid' != self::$notices['status'] ) ) {
self::display_configuration_page();
return;
}
//the user can choose to auto connect their API key by clicking a button on the akismet done page
//if jetpack, get verified api key by using connected wpcom user id
//if no jetpack, get verified api key by using an akismet token
$akismet_user = false;
if ( isset( $_GET['token'] ) && preg_match('/^(\d+)-[0-9a-f]{20}$/', $_GET['token'] ) )
$akismet_user = self::verify_wpcom_key( '', '', array( 'token' => $_GET['token'] ) );
elseif ( $jetpack_user = self::get_jetpack_user() )
$akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'] );
if ( isset( $_GET['action'] ) ) {
if ( $_GET['action'] == 'save-key' ) {
if ( is_object( $akismet_user ) ) {
self::save_key( $akismet_user->api_key );
self::display_configuration_page();
return;
}
}
}
Akismet::view( 'start', compact( 'akismet_user' ) );
/*
// To see all variants when testing.
$akismet_user->status = 'no-sub';
Akismet::view( 'start', compact( 'akismet_user' ) );
$akismet_user->status = 'cancelled';
Akismet::view( 'start', compact( 'akismet_user' ) );
$akismet_user->status = 'suspended';
Akismet::view( 'start', compact( 'akismet_user' ) );
$akismet_user->status = 'other';
Akismet::view( 'start', compact( 'akismet_user' ) );
$akismet_user = false;
*/
}
public static function display_stats_page() {
Akismet::view( 'stats' );
}
public static function display_configuration_page() {
$api_key = Akismet::get_api_key();
$akismet_user = self::get_akismet_user( $api_key );
if ( ! $akismet_user ) {
// This could happen if the user's key became invalid after it was previously valid and successfully set up.
self::$notices['status'] = 'existing-key-invalid';
self::display_start_page();
return;
}
$stat_totals = self::get_stats( $api_key );
// If unset, create the new strictness option using the old discard option to determine its default.
// If the old option wasn't set, default to discarding the blatant spam.
if ( get_option( 'akismet_strictness' ) === false ) {
add_option( 'akismet_strictness', ( get_option( 'akismet_discard_month' ) === 'false' ? '0' : '1' ) );
}
// Sync the local "Total spam blocked" count with the authoritative count from the server.
if ( isset( $stat_totals['all'], $stat_totals['all']->spam ) ) {
update_option( 'akismet_spam_count', $stat_totals['all']->spam );
}
$notices = array();
if ( empty( self::$notices ) ) {
if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) {
$time_saved = false;
if ( $stat_totals['all']->time_saved > 1800 ) {
$total_in_minutes = round( $stat_totals['all']->time_saved / 60 );
$total_in_hours = round( $total_in_minutes / 60 );
$total_in_days = round( $total_in_hours / 8 );
$cleaning_up = __( 'Cleaning up spam takes time.' , 'akismet');
if ( $total_in_days > 1 )
$time_saved = $cleaning_up . ' ' . sprintf( _n( 'Akismet has saved you %s day!', 'Akismet has saved you %s days!', $total_in_days, 'akismet' ), number_format_i18n( $total_in_days ) );
elseif ( $total_in_hours > 1 )
$time_saved = $cleaning_up . ' ' . sprintf( _n( 'Akismet has saved you %d hour!', 'Akismet has saved you %d hours!', $total_in_hours, 'akismet' ), $total_in_hours );
elseif ( $total_in_minutes >= 30 )
$time_saved = $cleaning_up . ' ' . sprintf( _n( 'Akismet has saved you %d minute!', 'Akismet has saved you %d minutes!', $total_in_minutes, 'akismet' ), $total_in_minutes );
}
$notices[] = array( 'type' => 'active-notice', 'time_saved' => $time_saved );
}
}
if ( !isset( self::$notices['status'] ) && in_array( $akismet_user->status, array( 'cancelled', 'suspended', 'missing', 'no-sub' ) ) ) {
$notices[] = array( 'type' => $akismet_user->status );
}
$alert_code = get_option( 'akismet_alert_code' );
if ( isset( Akismet::$limit_notices[ $alert_code ] ) ) {
$notices[] = self::get_usage_limit_alert_data();
} elseif ( $alert_code > 0 ) {
$notices[] = array(
'type' => 'alert',
'code' => (int) get_option( 'akismet_alert_code' ),
'msg' => get_option( 'akismet_alert_msg' ),
);
}
/*
* To see all variants when testing.
*
* You may also want to comment out the akismet_view_arguments filter in Akismet::view()
* to ensure that you can see all of the notices (e.g. suspended, active-notice).
*/
// $notices[] = array( 'type' => 'active-notice', 'time_saved' => 'Cleaning up spam takes time. Akismet has saved you 1 minute!' );
// $notices[] = array( 'type' => 'plugin' );
// $notices[] = array( 'type' => 'notice', 'notice_header' => 'This is the notice header.', 'notice_text' => 'This is the notice text.' );
// $notices[] = array( 'type' => 'missing-functions' );
// $notices[] = array( 'type' => 'servers-be-down' );
// $notices[] = array( 'type' => 'active-dunning' );
// $notices[] = array( 'type' => 'cancelled' );
// $notices[] = array( 'type' => 'suspended' );
// $notices[] = array( 'type' => 'missing' );
// $notices[] = array( 'type' => 'no-sub' );
// $notices[] = array( 'type' => 'new-key-valid' );
// $notices[] = array( 'type' => 'new-key-invalid' );
// $notices[] = array( 'type' => 'existing-key-invalid' );
// $notices[] = array( 'type' => 'new-key-failed' );
// $notices[] = array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_plan' => 'Enterprise', 'upgrade_url' => 'https://akismet.com/account/', 'code' => 10502 );
// $notices[] = array( 'type' => 'spam-check', 'link_text' => 'Link text.' );
// $notices[] = array( 'type' => 'spam-check-cron-disabled' );
// $notices[] = array( 'type' => 'alert', 'code' => 123 );
Akismet::log( compact( 'stat_totals', 'akismet_user' ) );
Akismet::view( 'config', compact( 'api_key', 'akismet_user', 'stat_totals', 'notices' ) );
}
public static function display_notice() {
global $hook_suffix;
if ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config' ) ) ) {
// This page manages the notices and puts them inline where they make sense.
return;
}
// To see notice variants while testing.
// Akismet::view( 'notice', array( 'type' => 'spam-check-cron-disabled' ) );
// Akismet::view( 'notice', array( 'type' => 'spam-check' ) );
// Akismet::view( 'notice', array( 'type' => 'alert', 'code' => 123, 'msg' => 'Message' ) );
if ( in_array( $hook_suffix, array( 'edit-comments.php' ) ) && (int) get_option( 'akismet_alert_code' ) > 0 ) {
Akismet::verify_key( Akismet::get_api_key() ); //verify that the key is still in alert state
$alert_code = get_option( 'akismet_alert_code' );
if ( isset( Akismet::$limit_notices[ $alert_code ] ) ) {
self::display_usage_limit_alert();
} elseif ( $alert_code > 0 ) {
self::display_alert();
}
}
elseif ( ( 'plugins.php' === $hook_suffix || 'edit-comments.php' === $hook_suffix ) && ! Akismet::get_api_key() ) {
// Show the "Set Up Akismet" banner on the comments and plugin pages if no API key has been set.
self::display_api_key_warning();
}
elseif ( $hook_suffix == 'edit-comments.php' && wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) {
self::display_spam_check_warning();
}
if ( isset( $_GET['akismet_recheck_complete'] ) ) {
$recheck_count = (int) $_GET['recheck_count'];
$spam_count = (int) $_GET['spam_count'];
if ( $recheck_count === 0 ) {
$message = __( 'There were no comments to check. Akismet will only check comments awaiting moderation.', 'akismet' );
}
else {
$message = sprintf( _n( 'Akismet checked %s comment.', 'Akismet checked %s comments.', $recheck_count, 'akismet' ), number_format( $recheck_count ) );
$message .= ' ';
if ( $spam_count === 0 ) {
$message .= __( 'No comments were caught as spam.', 'akismet' );
}
else {
$message .= sprintf( _n( '%s comment was caught as spam.', '%s comments were caught as spam.', $spam_count, 'akismet' ), number_format( $spam_count ) );
}
}
echo '<div class="notice notice-success"><p>' . esc_html( $message ) . '</p></div>';
}
else if ( isset( $_GET['akismet_recheck_error'] ) ) {
echo '<div class="notice notice-error"><p>' . esc_html( __( 'Akismet could not recheck your comments for spam.', 'akismet' ) ) . '</p></div>';
}
}
public static function display_status() {
if ( ! self::get_server_connectivity() ) {
Akismet::view( 'notice', array( 'type' => 'servers-be-down' ) );
}
else if ( ! empty( self::$notices ) ) {
foreach ( self::$notices as $index => $type ) {
if ( is_object( $type ) ) {
$notice_header = $notice_text = '';
if ( property_exists( $type, 'notice_header' ) ) {
$notice_header = wp_kses( $type->notice_header, self::$allowed );
}
if ( property_exists( $type, 'notice_text' ) ) {
$notice_text = wp_kses( $type->notice_text, self::$allowed );
}
if ( property_exists( $type, 'status' ) ) {
$type = wp_kses( $type->status, self::$allowed );
Akismet::view( 'notice', compact( 'type', 'notice_header', 'notice_text' ) );
unset( self::$notices[ $index ] );
}
}
else {
Akismet::view( 'notice', compact( 'type' ) );
unset( self::$notices[ $index ] );
}
}
}
}
private static function get_jetpack_user() {
if ( !class_exists('Jetpack') )
return false;
if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) ) {
// For version of Jetpack prior to 7.7.
Jetpack::load_xml_rpc_client();
}
$xml = new Jetpack_IXR_ClientMulticall( array( 'user_id' => get_current_user_id() ) );
$xml->addCall( 'wpcom.getUserID' );
$xml->addCall( 'akismet.getAPIKey' );
$xml->query();
Akismet::log( compact( 'xml' ) );
if ( !$xml->isError() ) {
$responses = $xml->getResponse();
if ( ( is_countable( $responses ) ? count( $responses ) : 0 ) > 1 ) {
// Due to a quirk in how Jetpack does multi-calls, the response order
// can't be trusted to match the call order. It's a good thing our
// return values can be mostly differentiated from each other.
$first_response_value = array_shift( $responses[0] );
$second_response_value = array_shift( $responses[1] );
// If WPCOM ever reaches 100 billion users, this will fail. :-)
if ( preg_match( '/^[a-f0-9]{12}$/i', $first_response_value ) ) {
$api_key = $first_response_value;
$user_id = (int) $second_response_value;
}
else {
$api_key = $second_response_value;
$user_id = (int) $first_response_value;
}
return compact( 'api_key', 'user_id' );
}
}
return false;
}
/**
* Some commentmeta isn't useful in an export file. Suppress it (when supported).
*
* @param bool $exclude
* @param string $key The meta key
* @param object $meta The meta object
* @return bool Whether to exclude this meta entry from the export.
*/
public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) {
if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) {
return true;
}
return $exclude;
}
/**
* When Akismet is active, remove the "Activate Akismet" step from the plugin description.
*/
public static function modify_plugin_description( $all_plugins ) {
if ( isset( $all_plugins['akismet/akismet.php'] ) ) {
if ( Akismet::get_api_key() ) {
$all_plugins['akismet/akismet.php']['Description'] = __( 'Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. Your site is fully configured and being protected, even while you sleep.', 'akismet' );
}
else {
$all_plugins['akismet/akismet.php']['Description'] = __( 'Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. It keeps your site protected even while you sleep. To get started, just go to <a href="admin.php?page=akismet-key-config">your Akismet Settings page</a> to set up your API key.', 'akismet' );
}
}
return $all_plugins;
}
private static function set_form_privacy_notice_option( $state ) {
if ( in_array( $state, array( 'display', 'hide' ) ) ) {
update_option( 'akismet_comment_form_privacy_notice', $state );
}
}
public static function register_personal_data_eraser( $erasers ) {
$erasers['akismet'] = array(
'eraser_friendly_name' => __( 'Akismet', 'akismet' ),
'callback' => array( 'Akismet_Admin', 'erase_personal_data' ),
);
return $erasers;
}
/**
* When a user requests that their personal data be removed, Akismet has a duty to discard
* any personal data we store outside of the comment itself. Right now, that is limited
* to the copy of the comment we store in the akismet_as_submitted commentmeta.
*
* FWIW, this information would be automatically deleted after 15 days.
*
* @param $email_address string The email address of the user who has requested erasure.
* @param $page int This function can (and will) be called multiple times to prevent timeouts,
* so this argument is used for pagination.
* @return array
* @see https://developer.wordpress.org/plugins/privacy/adding-the-personal-data-eraser-to-your-plugin/
*/
public static function erase_personal_data( $email_address, $page = 1 ) {
$items_removed = false;
$number = 50;
$page = (int) $page;
$comments = get_comments(
array(
'author_email' => $email_address,
'number' => $number,
'paged' => $page,
'order_by' => 'comment_ID',
'order' => 'ASC',
)
);
foreach ( (array) $comments as $comment ) {
$comment_as_submitted = get_comment_meta( $comment->comment_ID, 'akismet_as_submitted', true );
if ( $comment_as_submitted ) {
delete_comment_meta( $comment->comment_ID, 'akismet_as_submitted' );
$items_removed = true;
}
}
// Tell core if we have more comments to work on still
$done = ( is_countable( $comments ) ? count( $comments ) : 0 ) < $number;
return array(
'items_removed' => $items_removed,
'items_retained' => false, // always false in this example
'messages' => array(), // no messages in this example
'done' => $done,
);
}
/**
* Return an array of HTML elements that are allowed in a notice.
*
* @return array
*/
public static function get_notice_kses_allowed_elements() {
return self::$allowed;
}
/**
* Return a version to append to the URL of an asset file (e.g. CSS and images).
*
* @param string $relative_path Relative path to asset file
* @return string
*/
public static function get_asset_file_version( $relative_path ) {
$full_path = AKISMET__PLUGIN_DIR . $relative_path;
// If the AKISMET_VERSION contains a lower-case letter, it's a development version (e.g. 5.3.1a2).
// Use the file modified time in development.
if ( preg_match( '/[a-z]/', AKISMET_VERSION ) && file_exists( $full_path ) ) {
return filemtime( $full_path );
}
// Otherwise, use the AKISMET_VERSION.
return AKISMET_VERSION;
}
}
class.akismet.php 0000604 00000217560 15132740121 0010021 0 ustar 00 <?php
class Akismet {
const API_HOST = 'rest.akismet.com';
const API_PORT = 80;
const MAX_DELAY_BEFORE_MODERATION_EMAIL = 86400; // One day in seconds
public static $limit_notices = array(
10501 => 'FIRST_MONTH_OVER_LIMIT',
10502 => 'SECOND_MONTH_OVER_LIMIT',
10504 => 'THIRD_MONTH_APPROACHING_LIMIT',
10508 => 'THIRD_MONTH_OVER_LIMIT',
10516 => 'FOUR_PLUS_MONTHS_OVER_LIMIT',
);
private static $last_comment = '';
private static $initiated = false;
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' => '' );
public static function init() {
if ( ! self::$initiated ) {
self::init_hooks();
}
}
/**
* Initializes WordPress hooks
*/
private static function init_hooks() {
self::$initiated = true;
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( 'comment_form', array( 'Akismet', 'load_form_js' ) );
add_action( 'do_shortcode_tag', array( 'Akismet', 'load_form_js_via_filter' ), 10, 4 );
add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments' ) );
add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments_meta' ) );
add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_orphaned_commentmeta' ) );
add_action( 'akismet_schedule_cron_recheck', array( 'Akismet', 'cron_recheck' ) );
add_action( 'comment_form', array( 'Akismet', 'add_comment_nonce' ), 1 );
add_action( 'comment_form', array( 'Akismet', 'output_custom_form_fields' ) );
add_filter( 'script_loader_tag', array( 'Akismet', 'set_form_js_async' ), 10, 3 );
add_filter( 'comment_moderation_recipients', array( 'Akismet', 'disable_moderation_emails_if_unreachable' ), 1000, 2 );
add_filter( 'pre_comment_approved', array( 'Akismet', 'last_comment_status' ), 10, 2 );
add_action( 'transition_comment_status', array( 'Akismet', 'transition_comment_status' ), 10, 3 );
// Run this early in the pingback call, before doing a remote fetch of the source uri
add_action( 'xmlrpc_call', array( 'Akismet', 'pre_check_pingback' ), 10, 3 );
// Jetpack compatibility
add_filter( 'jetpack_options_whitelist', array( 'Akismet', 'add_to_jetpack_options_whitelist' ) );
add_filter( 'jetpack_contact_form_html', array( 'Akismet', 'inject_custom_form_fields' ) );
add_filter( 'jetpack_contact_form_akismet_values', array( 'Akismet', 'prepare_custom_form_values' ) );
// Gravity Forms
add_filter( 'gform_get_form_filter', array( 'Akismet', 'inject_custom_form_fields' ) );
add_filter( 'gform_akismet_fields', array( 'Akismet', 'prepare_custom_form_values' ) );
// Contact Form 7
add_filter( 'wpcf7_form_elements', array( 'Akismet', 'append_custom_form_fields' ) );
add_filter( 'wpcf7_akismet_parameters', array( 'Akismet', 'prepare_custom_form_values' ) );
// Formidable Forms
add_filter( 'frm_filter_final_form', array( 'Akismet', 'inject_custom_form_fields' ) );
add_filter( 'frm_akismet_values', array( 'Akismet', 'prepare_custom_form_values' ) );
// Fluent Forms
/*
* The Fluent Forms hook names were updated in version 5.0.0. The last version that supported
* the original hook names was 4.3.25, and version 4.3.25 was tested up to WordPress version 6.1.
*
* The legacy hooks are fired before the new hooks. See
* https://github.com/fluentform/fluentform/commit/cc45341afcae400f217470a7bbfb15efdd80454f
*
* The legacy Fluent Forms hooks will be removed when Akismet no longer supports WordPress version 6.1.
* This will provide compatibility with previous versions of Fluent Forms for a reasonable amount of time.
*/
add_filter( 'fluentform_form_element_start', array( 'Akismet', 'output_custom_form_fields' ) );
add_filter( 'fluentform_akismet_fields', array( 'Akismet', 'prepare_custom_form_values' ), 10, 2 );
// Current Fluent Form hooks.
add_filter( 'fluentform/form_element_start', array( 'Akismet', 'output_custom_form_fields' ) );
add_filter( 'fluentform/akismet_fields', array( 'Akismet', 'prepare_custom_form_values' ), 10, 2 );
add_action( 'update_option_wordpress_api_key', array( 'Akismet', 'updated_option' ), 10, 2 );
add_action( 'add_option_wordpress_api_key', array( 'Akismet', 'added_option' ), 10, 2 );
add_action( 'comment_form_after', array( 'Akismet', 'display_comment_form_privacy_notice' ) );
}
public static function get_api_key() {
return apply_filters( 'akismet_get_api_key', defined('WPCOM_API_KEY') ? constant('WPCOM_API_KEY') : get_option('wordpress_api_key') );
}
/**
* Exchange the API key for a token that can only be used to access stats pages.
*
* @return string
*/
public static function get_access_token() {
static $access_token = null;
if ( is_null( $access_token ) ) {
$request_args = array( 'api_key' => self::get_api_key() );
$request_args = apply_filters( 'akismet_request_args', $request_args, 'token' );
$response = self::http_post( self::build_query( $request_args ), 'token' );
$access_token = $response[1];
}
return $access_token;
}
public static function check_key_status( $key, $ip = null ) {
$request_args = array(
'key' => $key,
'blog' => get_option( 'home' ),
);
$request_args = apply_filters( 'akismet_request_args', $request_args, 'verify-key' );
return self::http_post( self::build_query( $request_args ), 'verify-key', $ip );
}
public static function verify_key( $key, $ip = null ) {
// Shortcut for obviously invalid keys.
if ( strlen( $key ) != 12 ) {
return 'invalid';
}
$response = self::check_key_status( $key, $ip );
if ( $response[1] != 'valid' && $response[1] != 'invalid' )
return 'failed';
return $response[1];
}
public static function deactivate_key( $key ) {
$request_args = array(
'key' => $key,
'blog' => get_option( 'home' ),
);
$request_args = apply_filters( 'akismet_request_args', $request_args, 'deactivate' );
$response = self::http_post( self::build_query( $request_args ), 'deactivate' );
if ( $response[1] != 'deactivated' )
return 'failed';
return $response[1];
}
/**
* Add the akismet option to the Jetpack options management whitelist.
*
* @param array $options The list of whitelisted option names.
* @return array The updated whitelist
*/
public static function add_to_jetpack_options_whitelist( $options ) {
$options[] = 'wordpress_api_key';
return $options;
}
/**
* When the akismet option is updated, run the registration call.
*
* This should only be run when the option is updated from the Jetpack/WP.com
* API call, and only if the new key is different than the old key.
*
* @param mixed $old_value The old option value.
* @param mixed $value The new option value.
*/
public static function updated_option( $old_value, $value ) {
// Not an API call
if ( ! class_exists( 'WPCOM_JSON_API_Update_Option_Endpoint' ) ) {
return;
}
// Only run the registration if the old key is different.
if ( $old_value !== $value ) {
self::verify_key( $value );
}
}
/**
* Treat the creation of an API key the same as updating the API key to a new value.
*
* @param mixed $option_name Will always be "wordpress_api_key", until something else hooks in here.
* @param mixed $value The option value.
*/
public static function added_option( $option_name, $value ) {
if ( 'wordpress_api_key' === $option_name ) {
return self::updated_option( '', $value );
}
}
public static function rest_auto_check_comment( $commentdata ) {
return self::auto_check_comment( $commentdata, 'rest_api' );
}
/**
* Check a comment for spam.
*
* @param array $commentdata
* @param string $context What kind of request triggered this comment check? Possible values are 'default', 'rest_api', and 'xml-rpc'.
* @return array|WP_Error Either the $commentdata array with additional entries related to its spam status
* or a WP_Error, if it's a REST API request and the comment should be discarded.
*/
public static function auto_check_comment( $commentdata, $context = 'default' ) {
// If no key is configured, then there's no point in doing any of this.
if ( ! self::get_api_key() ) {
return $commentdata;
}
self::$last_comment_result = null;
// Skip the Akismet check if the comment matches the Disallowed Keys list.
if ( function_exists( 'wp_check_comment_disallowed_list' ) ) {
$comment_author = isset( $commentdata['comment_author'] ) ? $commentdata['comment_author'] : '';
$comment_author_email = isset( $commentdata['comment_author_email'] ) ? $commentdata['comment_author_email'] : '';
$comment_author_url = isset( $commentdata['comment_author_url'] ) ? $commentdata['comment_author_url'] : '';
$comment_content = isset( $commentdata['comment_content'] ) ? $commentdata['comment_content'] : '';
$comment_author_ip = isset( $commentdata['comment_author_IP'] ) ? $commentdata['comment_author_IP'] : '';
$comment_agent = isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '';
if ( wp_check_comment_disallowed_list( $comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_ip, $comment_agent ) ) {
self::set_last_comment( $commentdata );
return $commentdata;
}
}
$comment = $commentdata;
$comment['user_ip'] = self::get_ip_address();
$comment['user_agent'] = self::get_user_agent();
$comment['referrer'] = self::get_referer();
$comment['blog'] = get_option( 'home' );
$comment['blog_lang'] = get_locale();
$comment['blog_charset'] = get_option('blog_charset');
$comment['permalink'] = get_permalink( $comment['comment_post_ID'] );
if ( ! empty( $comment['user_ID'] ) ) {
$comment['user_role'] = Akismet::get_user_roles( $comment['user_ID'] );
}
/** See filter documentation in init_hooks(). */
$akismet_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) );
$comment['akismet_comment_nonce'] = 'inactive';
if ( $akismet_nonce_option == 'true' || $akismet_nonce_option == '' ) {
$comment['akismet_comment_nonce'] = 'failed';
if ( isset( $_POST['akismet_comment_nonce'] ) && wp_verify_nonce( $_POST['akismet_comment_nonce'], 'akismet_comment_nonce_' . $comment['comment_post_ID'] ) )
$comment['akismet_comment_nonce'] = 'passed';
// comment reply in wp-admin
if ( isset( $_POST['_ajax_nonce-replyto-comment'] ) && check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ) )
$comment['akismet_comment_nonce'] = 'passed';
}
if ( self::is_test_mode() )
$comment['is_test'] = 'true';
foreach( $_POST as $key => $value ) {
if ( is_string( $value ) )
$comment["POST_{$key}"] = $value;
}
foreach ( $_SERVER as $key => $value ) {
if ( ! is_string( $value ) ) {
continue;
}
if ( preg_match( "/^HTTP_COOKIE/", $key ) ) {
continue;
}
// Send any potentially useful $_SERVER vars, but avoid sending junk we don't need.
if ( preg_match( "/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/", $key ) ) {
$comment[ "$key" ] = $value;
}
}
$post = get_post( $comment['comment_post_ID'] );
if ( ! is_null( $post ) ) {
// $post can technically be null, although in the past, it's always been an indicator of another plugin interfering.
$comment[ 'comment_post_modified_gmt' ] = $post->post_modified_gmt;
// Tags and categories are important context in which to consider the comment.
$comment['comment_context'] = array();
$tag_names = wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) );
if ( $tag_names && ! is_wp_error( $tag_names ) ) {
foreach ( $tag_names as $tag_name ) {
$comment['comment_context'][] = $tag_name;
}
}
$category_names = wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) );
if ( $category_names && ! is_wp_error( $category_names ) ) {
foreach ( $category_names as $category_name ) {
$comment['comment_context'][] = $category_name;
}
}
}
/**
* Filter the data that is used to generate the request body for the API call.
*
* @since 5.3.1
*
* @param array $comment An array of request data.
* @param string $endpoint The API endpoint being requested.
*/
$comment = apply_filters( 'akismet_request_args', $comment, 'comment-check' );
$response = self::http_post( self::build_query( $comment ), 'comment-check' );
do_action( 'akismet_comment_check_response', $response );
$commentdata['comment_as_submitted'] = array_intersect_key( $comment, self::$comment_as_submitted_allowed_keys );
// Also include any form fields we inject into the comment form, like ak_js
foreach ( $_POST as $key => $value ) {
if ( is_string( $value ) && strpos( $key, 'ak_' ) === 0 ) {
$commentdata['comment_as_submitted'][ 'POST_' . $key ] = $value;
}
}
$commentdata['akismet_result'] = $response[1];
if ( isset( $response[0]['x-akismet-pro-tip'] ) )
$commentdata['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip'];
if ( isset( $response[0]['x-akismet-guid'] ) ) {
$commentdata['akismet_guid'] = $response[0]['x-akismet-guid'];
}
if ( isset( $response[0]['x-akismet-error'] ) ) {
// An error occurred that we anticipated (like a suspended key) and want the user to act on.
// Send to moderation.
self::$last_comment_result = '0';
}
else if ( 'true' == $response[1] ) {
// akismet_spam_count will be incremented later by comment_is_spam()
self::$last_comment_result = 'spam';
$discard = ( isset( $commentdata['akismet_pro_tip'] ) && $commentdata['akismet_pro_tip'] === 'discard' && self::allow_discard() );
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 );
}
if ( 'rest_api' === $context ) {
return new WP_Error( 'akismet_rest_comment_discarded', __( 'Comment discarded.', 'akismet' ) );
} else if ( 'xml-rpc' === $context ) {
// If this is a pingback that we're pre-checking, the discard behavior is the same as the normal spam response behavior.
return $commentdata;
} 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 ( 'rest_api' === $context ) {
// The way the REST API structures its calls, we can set the comment_approved value right away.
$commentdata['comment_approved'] = 'spam';
}
}
// if the response is neither true nor false, hold the comment for moderation and schedule a recheck
if ( 'true' != $response[1] && 'false' != $response[1] ) {
if ( !current_user_can('moderate_comments') ) {
// Comment status should be moderated
self::$last_comment_result = '0';
}
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;
}
// 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();
return $commentdata;
}
public static function get_last_comment() {
return self::$last_comment;
}
public static function set_last_comment( $comment ) {
if ( is_null( $comment ) ) {
self::$last_comment = null;
}
else {
// We filter it here so that it matches the filtered comment data that we'll have to compare against later.
// wp_filter_comment expects comment_author_IP
self::$last_comment = wp_filter_comment(
array_merge(
array( 'comment_author_IP' => self::get_ip_address() ),
$comment
)
);
}
}
// 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 ) {
// 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 ) ) {
if ( self::matches_last_comment( $comment ) ) {
load_plugin_textdomain( 'akismet' );
// normal result: true or false
if ( isset( self::$last_comment['akismet_result'] ) && self::$last_comment['akismet_result'] == 'true' ) {
update_comment_meta( $comment->comment_ID, 'akismet_result', 'true' );
self::update_comment_history( $comment->comment_ID, '', 'check-spam' );
if ( $comment->comment_approved != 'spam' ) {
self::update_comment_history(
$comment->comment_ID,
'',
'status-changed-' . $comment->comment_approved
);
}
} elseif ( isset( self::$last_comment['akismet_result'] ) && self::$last_comment['akismet_result'] == 'false' ) {
update_comment_meta( $comment->comment_ID, 'akismet_result', 'false' );
self::update_comment_history( $comment->comment_ID, '', 'check-ham' );
// Status could be spam or trash, depending on the WP version and whether this change applies:
// https://core.trac.wordpress.org/changeset/34726
if ( $comment->comment_approved == 'spam' || $comment->comment_approved == 'trash' ) {
if ( function_exists( 'wp_check_comment_disallowed_list' ) ) {
if ( wp_check_comment_disallowed_list( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent ) ) {
self::update_comment_history( $comment->comment_ID, '', 'wp-disallowed' );
} else {
self::update_comment_history( $comment->comment_ID, '', 'status-changed-' . $comment->comment_approved );
}
} else {
self::update_comment_history( $comment->comment_ID, '', 'status-changed-' . $comment->comment_approved );
}
}
} elseif ( ! isset( self::$last_comment['akismet_result'] ) ) {
// akismet_result isn't set, so the comment wasn't sent to Akismet.
update_comment_meta( $comment->comment_ID, 'akismet_skipped', 'true' );
$caught_by_disallowed_list = false;
if ( function_exists( 'wp_check_comment_disallowed_list' ) ) {
$caught_by_disallowed_list = wp_check_comment_disallowed_list( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent );
}
if ( $caught_by_disallowed_list ) {
self::update_comment_history( $comment->comment_ID, '', 'wp-disallowed' );
self::update_comment_history( $comment->comment_ID, '', 'akismet-skipped-disallowed' );
} else {
// Add a generic skipped history item.
self::update_comment_history( $comment->comment_ID, '', 'akismet-skipped' );
}
} else {
// abnormal result: error
update_comment_meta( $comment->comment_ID, 'akismet_error', time() );
self::update_comment_history(
$comment->comment_ID,
'',
'check-error',
array( 'response' => substr( self::$last_comment['akismet_result'], 0, 50 ) )
);
}
// record the complete original data as submitted for checking
if ( isset( self::$last_comment['comment_as_submitted'] ) ) {
update_comment_meta( $comment->comment_ID, 'akismet_as_submitted', self::$last_comment['comment_as_submitted'] );
}
if ( isset( self::$last_comment['akismet_pro_tip'] ) ) {
update_comment_meta( $comment->comment_ID, 'akismet_pro_tip', self::$last_comment['akismet_pro_tip'] );
}
if ( isset( self::$last_comment['akismet_guid'] ) ) {
update_comment_meta( $comment->comment_ID, 'akismet_guid', self::$last_comment['akismet_guid'] );
}
}
}
}
public static function delete_old_comments() {
global $wpdb;
/**
* Determines how many comments will be deleted in each batch.
*
* @param int The default, as defined by AKISMET_DELETE_LIMIT.
*/
$delete_limit = apply_filters( 'akismet_delete_comment_limit', defined( 'AKISMET_DELETE_LIMIT' ) ? AKISMET_DELETE_LIMIT : 10000 );
$delete_limit = max( 1, intval( $delete_limit ) );
/**
* Determines how many days a comment will be left in the Spam queue before being deleted.
*
* @param int The default number of days.
*/
$delete_interval = apply_filters( 'akismet_delete_comment_interval', 15 );
$delete_interval = max( 1, intval( $delete_interval ) );
while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->comments} WHERE DATE_SUB(NOW(), INTERVAL %d DAY) > comment_date_gmt AND comment_approved = 'spam' LIMIT %d", $delete_interval, $delete_limit ) ) ) {
if ( empty( $comment_ids ) )
return;
$wpdb->queries = array();
$comments = array();
foreach ( $comment_ids as $comment_id ) {
$comments[ $comment_id ] = get_comment( $comment_id );
do_action( 'delete_comment', $comment_id, $comments[ $comment_id ] );
do_action( 'akismet_batch_delete_count', __FUNCTION__ );
}
// Prepared as strings since comment_id is an unsigned BIGINT, and using %d will constrain the value to the maximum signed BIGINT.
$format_string = implode( ', ', array_fill( 0, is_countable( $comment_ids ) ? count( $comment_ids ) : 0, '%s' ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->comments} WHERE comment_id IN ( " . $format_string . " )", $comment_ids ) );
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN ( " . $format_string . " )", $comment_ids ) );
foreach ( $comment_ids as $comment_id ) {
do_action( 'deleted_comment', $comment_id, $comments[ $comment_id ] );
unset( $comments[ $comment_id ] );
}
clean_comment_cache( $comment_ids );
do_action( 'akismet_delete_comment_batch', is_countable( $comment_ids ) ? count( $comment_ids ) : 0 );
}
if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->comments ) ) // lucky number
$wpdb->query("OPTIMIZE TABLE {$wpdb->comments}");
}
public static function delete_old_comments_meta() {
global $wpdb;
$interval = apply_filters( 'akismet_delete_commentmeta_interval', 15 );
# enforce a minimum of 1 day
$interval = absint( $interval );
if ( $interval < 1 )
$interval = 1;
// akismet_as_submitted meta values are large, so expire them
// after $interval days regardless of the comment status
while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT m.comment_id FROM {$wpdb->commentmeta} as m INNER JOIN {$wpdb->comments} as c USING(comment_id) WHERE m.meta_key = 'akismet_as_submitted' AND DATE_SUB(NOW(), INTERVAL %d DAY) > c.comment_date_gmt LIMIT 10000", $interval ) ) ) {
if ( empty( $comment_ids ) )
return;
$wpdb->queries = array();
foreach ( $comment_ids as $comment_id ) {
delete_comment_meta( $comment_id, 'akismet_as_submitted' );
do_action( 'akismet_batch_delete_count', __FUNCTION__ );
}
do_action( 'akismet_delete_commentmeta_batch', is_countable( $comment_ids ) ? count( $comment_ids ) : 0 );
}
if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->commentmeta ) ) // lucky number
$wpdb->query("OPTIMIZE TABLE {$wpdb->commentmeta}");
}
// Clear out comments meta that no longer have corresponding comments in the database
public static function delete_orphaned_commentmeta() {
global $wpdb;
$last_meta_id = 0;
$start_time = isset( $_SERVER['REQUEST_TIME_FLOAT'] ) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
$max_exec_time = max( ini_get('max_execution_time') - 5, 3 );
while ( $commentmeta_results = $wpdb->get_results( $wpdb->prepare( "SELECT m.meta_id, m.comment_id, m.meta_key FROM {$wpdb->commentmeta} as m LEFT JOIN {$wpdb->comments} as c USING(comment_id) WHERE c.comment_id IS NULL AND m.meta_id > %d ORDER BY m.meta_id LIMIT 1000", $last_meta_id ) ) ) {
if ( empty( $commentmeta_results ) )
return;
$wpdb->queries = array();
$commentmeta_deleted = 0;
foreach ( $commentmeta_results as $commentmeta ) {
if ( 'akismet_' == substr( $commentmeta->meta_key, 0, 8 ) ) {
delete_comment_meta( $commentmeta->comment_id, $commentmeta->meta_key );
do_action( 'akismet_batch_delete_count', __FUNCTION__ );
$commentmeta_deleted++;
}
$last_meta_id = $commentmeta->meta_id;
}
do_action( 'akismet_delete_commentmeta_batch', $commentmeta_deleted );
// If we're getting close to max_execution_time, quit for this round.
if ( microtime(true) - $start_time > $max_exec_time )
return;
}
if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->commentmeta ) ) // lucky number
$wpdb->query("OPTIMIZE TABLE {$wpdb->commentmeta}");
}
// how many approved comments does this author have?
public static function get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url ) {
global $wpdb;
/**
* Which comment types should be ignored when counting a user's approved comments?
*
* Some plugins add entries to the comments table that are not actual
* comments that could have been checked by Akismet. Allow these comments
* to be excluded from the "approved comment count" query in order to
* avoid artificially inflating the approved comment count.
*
* @param array $comment_types An array of comment types that won't be considered
* when counting a user's approved comments.
*
* @since 4.2.2
*/
$excluded_comment_types = apply_filters( 'akismet_excluded_comment_types', array() );
$comment_type_where = '';
if ( is_array( $excluded_comment_types ) && ! empty( $excluded_comment_types ) ) {
$excluded_comment_types = array_unique( $excluded_comment_types );
foreach ( $excluded_comment_types as $excluded_comment_type ) {
$comment_type_where .= $wpdb->prepare( ' AND comment_type <> %s ', $excluded_comment_type );
}
}
if ( ! empty( $user_id ) ) {
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE user_id = %d AND comment_approved = 1" . $comment_type_where, $user_id ) );
}
if ( ! empty( $comment_author_email ) ) {
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_author_email = %s AND comment_author = %s AND comment_author_url = %s AND comment_approved = 1" . $comment_type_where, $comment_author_email, $comment_author, $comment_author_url ) );
}
return 0;
}
/**
* Get the full comment history for a given comment, as an array in reverse chronological order.
* Each entry will have an 'event', a 'time', and possible a 'message' member (if the entry is old enough).
* Some entries will also have a 'user' or 'meta' member.
*
* @param int $comment_id The relevant comment ID.
* @return array|bool An array of history events, or false if there is no history.
*/
public static function get_comment_history( $comment_id ) {
$history = get_comment_meta( $comment_id, 'akismet_history', false );
if ( empty( $history ) || empty( $history[ 0 ] ) ) {
return false;
}
/*
// To see all variants when testing.
$history[] = array( 'time' => 445856401, 'message' => 'Old versions of Akismet stored the message as a literal string in the commentmeta.', 'event' => null );
$history[] = array( 'time' => 445856402, 'event' => 'recheck-spam' );
$history[] = array( 'time' => 445856403, 'event' => 'check-spam' );
$history[] = array( 'time' => 445856404, 'event' => 'recheck-ham' );
$history[] = array( 'time' => 445856405, 'event' => 'check-ham' );
$history[] = array( 'time' => 445856406, 'event' => 'wp-blacklisted' );
$history[] = array( 'time' => 445856406, 'event' => 'wp-disallowed' );
$history[] = array( 'time' => 445856407, 'event' => 'report-spam' );
$history[] = array( 'time' => 445856408, 'event' => 'report-spam', 'user' => 'sam' );
$history[] = array( 'message' => 'sam reported this comment as spam (hardcoded message).', 'time' => 445856400, 'event' => 'report-spam', 'user' => 'sam' );
$history[] = array( 'time' => 445856409, 'event' => 'report-ham', 'user' => 'sam' );
$history[] = array( 'message' => 'sam reported this comment as ham (hardcoded message).', 'time' => 445856400, 'event' => 'report-ham', 'user' => 'sam' ); //
$history[] = array( 'time' => 445856410, 'event' => 'cron-retry-spam' );
$history[] = array( 'time' => 445856411, 'event' => 'cron-retry-ham' );
$history[] = array( 'time' => 445856412, 'event' => 'check-error' ); //
$history[] = array( 'time' => 445856413, 'event' => 'check-error', 'meta' => array( 'response' => 'The server was taking a nap.' ) );
$history[] = array( 'time' => 445856414, 'event' => 'recheck-error' ); // Should not generate a message.
$history[] = array( 'time' => 445856415, 'event' => 'recheck-error', 'meta' => array( 'response' => 'The server was taking a nap.' ) );
$history[] = array( 'time' => 445856416, 'event' => 'status-changedtrash' );
$history[] = array( 'time' => 445856417, 'event' => 'status-changedspam' );
$history[] = array( 'time' => 445856418, 'event' => 'status-changedhold' );
$history[] = array( 'time' => 445856419, 'event' => 'status-changedapprove' );
$history[] = array( 'time' => 445856420, 'event' => 'status-changed-trash' );
$history[] = array( 'time' => 445856421, 'event' => 'status-changed-spam' );
$history[] = array( 'time' => 445856422, 'event' => 'status-changed-hold' );
$history[] = array( 'time' => 445856423, 'event' => 'status-changed-approve' );
$history[] = array( 'time' => 445856424, 'event' => 'status-trash', 'user' => 'sam' );
$history[] = array( 'time' => 445856425, 'event' => 'status-spam', 'user' => 'sam' );
$history[] = array( 'time' => 445856426, 'event' => 'status-hold', 'user' => 'sam' );
$history[] = array( 'time' => 445856427, 'event' => 'status-approve', 'user' => 'sam' );
$history[] = array( 'time' => 445856427, 'event' => 'webhook-spam' );
$history[] = array( 'time' => 445856427, 'event' => 'webhook-ham' );
$history[] = array( 'time' => 445856427, 'event' => 'webhook-spam-noaction' );
$history[] = array( 'time' => 445856427, 'event' => 'webhook-ham-noaction' );
*/
usort( $history, array( 'Akismet', '_cmp_time' ) );
return $history;
}
/**
* Log an event for a given comment, storing it in comment_meta.
*
* @param int $comment_id The ID of the relevant comment.
* @param string $message The string description of the event. No longer used.
* @param string $event The event code.
* @param array $meta Metadata about the history entry. e.g., the user that reported or changed the status of a given comment.
*/
public static function update_comment_history( $comment_id, $message, $event=null, $meta=null ) {
global $current_user;
$user = '';
$event = array(
'time' => self::_get_microtime(),
'event' => $event,
);
if ( is_object( $current_user ) && isset( $current_user->user_login ) ) {
$event['user'] = $current_user->user_login;
}
if ( ! empty( $meta ) ) {
$event['meta'] = $meta;
}
// $unique = false so as to allow multiple values per comment
$r = add_comment_meta( $comment_id, 'akismet_history', $event, false );
}
public static function check_db_comment( $id, $recheck_reason = 'recheck_queue' ) {
global $wpdb;
if ( ! self::get_api_key() ) {
return new WP_Error( 'akismet-not-configured', __( 'Akismet is not configured. Please enter an API key.', 'akismet' ) );
}
$c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $id ), ARRAY_A );
if ( ! $c ) {
return new WP_Error( 'invalid-comment-id', __( 'Comment not found.', 'akismet' ) );
}
$c['user_ip'] = $c['comment_author_IP'];
$c['user_agent'] = $c['comment_agent'];
$c['referrer'] = '';
$c['blog'] = get_option( 'home' );
$c['blog_lang'] = get_locale();
$c['blog_charset'] = get_option('blog_charset');
$c['permalink'] = get_permalink($c['comment_post_ID']);
$c['recheck_reason'] = $recheck_reason;
$c['user_role'] = '';
if ( ! empty( $c['user_ID'] ) ) {
$c['user_role'] = Akismet::get_user_roles( $c['user_ID'] );
}
if ( self::is_test_mode() )
$c['is_test'] = 'true';
$c = apply_filters( 'akismet_request_args', $c, 'comment-check' );
$response = self::http_post( self::build_query( $c ), 'comment-check' );
if ( ! empty( $response[1] ) ) {
return $response[1];
}
return false;
}
public static function recheck_comment( $id, $recheck_reason = 'recheck_queue' ) {
add_comment_meta( $id, 'akismet_rechecking', true );
$api_response = self::check_db_comment( $id, $recheck_reason );
if ( is_wp_error( $api_response ) ) {
// Invalid comment ID.
}
else if ( 'true' === $api_response ) {
wp_set_comment_status( $id, 'spam' );
update_comment_meta( $id, 'akismet_result', 'true' );
delete_comment_meta( $id, 'akismet_error' );
delete_comment_meta( $id, 'akismet_delayed_moderation_email' );
Akismet::update_comment_history( $id, '', 'recheck-spam' );
}
elseif ( 'false' === $api_response ) {
update_comment_meta( $id, 'akismet_result', 'false' );
delete_comment_meta( $id, 'akismet_error' );
delete_comment_meta( $id, 'akismet_delayed_moderation_email' );
Akismet::update_comment_history( $id, '', 'recheck-ham' );
}
else {
// abnormal result: error
update_comment_meta( $id, 'akismet_result', 'error' );
Akismet::update_comment_history(
$id,
'',
'recheck-error',
array( 'response' => substr( $api_response, 0, 50 ) )
);
}
delete_comment_meta( $id, 'akismet_rechecking' );
return $api_response;
}
public static function transition_comment_status( $new_status, $old_status, $comment ) {
if ( $new_status == $old_status )
return;
if ( 'spam' === $new_status || 'spam' === $old_status ) {
// Clear the cache of the "X comments in your spam queue" count on the dashboard.
wp_cache_delete( 'akismet_spam_count', 'widget' );
}
# we don't need to record a history item for deleted comments
if ( $new_status == 'delete' )
return;
if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) )
return;
if ( defined('WP_IMPORTING') && WP_IMPORTING == true )
return;
// if this is present, it means the status has been changed by a re-check, not an explicit user action
if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) )
return;
if ( function_exists( 'getallheaders' ) ) {
$request_headers = getallheaders();
foreach ( $request_headers as $header => $value ) {
if ( strtolower( $header ) == 'x-akismet-webhook' ) {
// This change is due to a webhook request.
return;
}
}
}
// Assumption alert:
// We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status
// is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to
// determine why the transition_comment_status action was triggered. And there are several different ways by which
// to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others.
// We'll assume that this is an explicit user action if certain POST/GET variables exist.
if (
// status=spam: Marking as spam via the REST API or...
// status=unspam: I'm not sure. Maybe this used to be used instead of status=approved? Or the UI for removing from spam but not approving has been since removed?...
// status=approved: Unspamming via the REST API (Calypso) or...
( isset( $_POST['status'] ) && in_array( $_POST['status'], array( 'spam', 'unspam', 'approved', ) ) )
// spam=1: Clicking "Spam" underneath a comment in wp-admin and allowing the AJAX request to happen.
|| ( isset( $_POST['spam'] ) && (int) $_POST['spam'] == 1 )
// unspam=1: Clicking "Not Spam" underneath a comment in wp-admin and allowing the AJAX request to happen. Or, clicking "Undo" after marking something as spam.
|| ( isset( $_POST['unspam'] ) && (int) $_POST['unspam'] == 1 )
// comment_status=spam/unspam: It's unclear where this is happening.
|| ( isset( $_POST['comment_status'] ) && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) )
// action=spam: Choosing "Mark as Spam" from the Bulk Actions dropdown in wp-admin (or the "Spam it" link in notification emails).
// action=unspam: Choosing "Not Spam" from the Bulk Actions dropdown in wp-admin.
// action=spamcomment: Following the "Spam" link below a comment in wp-admin (not allowing AJAX request to happen).
// action=unspamcomment: Following the "Not Spam" link below a comment in wp-admin (not allowing AJAX request to happen).
|| ( isset( $_GET['action'] ) && in_array( $_GET['action'], array( 'spam', 'unspam', 'spamcomment', 'unspamcomment', ) ) )
// action=editedcomment: Editing a comment via wp-admin (and possibly changing its status).
|| ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'editedcomment' ) ) )
// for=jetpack: Moderation via the WordPress app, Calypso, anything powered by the Jetpack connection.
|| ( isset( $_GET['for'] ) && ( 'jetpack' == $_GET['for'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) )
// Certain WordPress.com API requests
|| ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST )
// WordPress.org REST API requests
|| ( defined( 'REST_REQUEST' ) && REST_REQUEST )
) {
if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) {
return self::submit_spam_comment( $comment->comment_ID );
} elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) {
return self::submit_nonspam_comment( $comment->comment_ID );
}
}
self::update_comment_history( $comment->comment_ID, '', 'status-' . $new_status );
}
public static function submit_spam_comment( $comment_id ) {
global $wpdb, $current_user, $current_site;
$comment_id = (int) $comment_id;
$comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ), ARRAY_A );
if ( ! $comment ) {
// it was deleted
return;
}
if ( 'spam' != $comment['comment_approved'] ) {
return;
}
self::update_comment_history( $comment_id, '', 'report-spam' );
// If the user hasn't configured Akismet, there's nothing else to do at this point.
if ( ! self::get_api_key() ) {
return;
}
// use the original version stored in comment_meta if available
$as_submitted = self::sanitize_comment_as_submitted( get_comment_meta( $comment_id, 'akismet_as_submitted', true ) );
if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) {
$comment = array_merge( $comment, $as_submitted );
}
$comment['blog'] = get_option( 'home' );
$comment['blog_lang'] = get_locale();
$comment['blog_charset'] = get_option( 'blog_charset' );
$comment['permalink'] = get_permalink( $comment['comment_post_ID'] );
if ( is_object( $current_user ) ) {
$comment['reporter'] = $current_user->user_login;
}
if ( is_object( $current_site ) ) {
$comment['site_domain'] = $current_site->domain;
}
$comment['user_role'] = '';
if ( ! empty( $comment['user_ID'] ) ) {
$comment['user_role'] = self::get_user_roles( $comment['user_ID'] );
}
if ( self::is_test_mode() ) {
$comment['is_test'] = 'true';
}
$post = get_post( $comment['comment_post_ID'] );
if ( ! is_null( $post ) ) {
$comment['comment_post_modified_gmt'] = $post->post_modified_gmt;
}
$comment = apply_filters( 'akismet_request_args', $comment, 'submit-spam' );
$response = self::http_post( self::build_query( $comment ), 'submit-spam' );
update_comment_meta( $comment_id, 'akismet_user_result', 'true' );
if ( $comment['reporter'] ) {
update_comment_meta( $comment_id, 'akismet_user', $comment['reporter'] );
}
do_action('akismet_submit_spam_comment', $comment_id, $response[1]);
}
public static function submit_nonspam_comment( $comment_id ) {
global $wpdb, $current_user, $current_site;
$comment_id = (int) $comment_id;
$comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ), ARRAY_A );
if ( ! $comment ) {
// it was deleted
return;
}
self::update_comment_history( $comment_id, '', 'report-ham' );
// If the user hasn't configured Akismet, there's nothing else to do at this point.
if ( ! self::get_api_key() ) {
return;
}
// use the original version stored in comment_meta if available
$as_submitted = self::sanitize_comment_as_submitted( get_comment_meta( $comment_id, 'akismet_as_submitted', true ) );
if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) {
$comment = array_merge( $comment, $as_submitted );
}
$comment['blog'] = get_option( 'home' );
$comment['blog_lang'] = get_locale();
$comment['blog_charset'] = get_option( 'blog_charset' );
$comment['permalink'] = get_permalink( $comment['comment_post_ID'] );
$comment['user_role'] = '';
if ( is_object( $current_user ) ) {
$comment['reporter'] = $current_user->user_login;
}
if ( is_object( $current_site ) ) {
$comment['site_domain'] = $current_site->domain;
}
if ( ! empty( $comment['user_ID'] ) ) {
$comment['user_role'] = self::get_user_roles( $comment['user_ID'] );
}
if ( self::is_test_mode() ) {
$comment['is_test'] = 'true';
}
$post = get_post( $comment['comment_post_ID'] );
if ( ! is_null( $post ) ) {
$comment['comment_post_modified_gmt'] = $post->post_modified_gmt;
}
$comment = apply_filters( 'akismet_request_args', $comment, 'submit-ham' );
$response = self::http_post( self::build_query( $comment ), 'submit-ham' );
update_comment_meta( $comment_id, 'akismet_user_result', 'false' );
if ( $comment['reporter'] ) {
update_comment_meta( $comment_id, 'akismet_user', $comment['reporter'] );
}
do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]);
}
public static function cron_recheck() {
global $wpdb;
$api_key = self::get_api_key();
$status = self::verify_key( $api_key );
if ( get_option( 'akismet_alert_code' ) || $status == 'invalid' ) {
// since there is currently a problem with the key, reschedule a check for 6 hours hence
wp_schedule_single_event( time() + 21600, 'akismet_schedule_cron_recheck' );
do_action( 'akismet_scheduled_recheck', 'key-problem-' . get_option( 'akismet_alert_code' ) . '-' . $status );
return false;
}
delete_option('akismet_available_servers');
$comment_errors = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error' LIMIT 100" );
load_plugin_textdomain( 'akismet' );
foreach ( (array) $comment_errors as $comment_id ) {
// if the comment no longer exists, or is too old, remove the meta entry from the queue to avoid getting stuck
$comment = get_comment( $comment_id );
if (
! $comment // Comment has been deleted
|| strtotime( $comment->comment_date_gmt ) < strtotime( "-15 days" ) // Comment is too old.
|| $comment->comment_approved !== "0" // Comment is no longer in the Pending queue
) {
delete_comment_meta( $comment_id, 'akismet_error' );
delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' );
continue;
}
add_comment_meta( $comment_id, 'akismet_rechecking', true );
$status = self::check_db_comment( $comment_id, 'retry' );
$event = '';
if ( $status == 'true' ) {
$event = 'cron-retry-spam';
} elseif ( $status == 'false' ) {
$event = 'cron-retry-ham';
}
// If we got back a legit response then update the comment history
// other wise just bail now and try again later. No point in
// re-trying all the comments once we hit one failure.
if ( !empty( $event ) ) {
delete_comment_meta( $comment_id, 'akismet_error' );
self::update_comment_history( $comment_id, '', $event );
update_comment_meta( $comment_id, 'akismet_result', $status );
// make sure the comment status is still pending. if it isn't, that means the user has already moved it elsewhere.
$comment = get_comment( $comment_id );
if ( $comment && 'unapproved' == wp_get_comment_status( $comment_id ) ) {
if ( $status == 'true' ) {
wp_spam_comment( $comment_id );
} elseif ( $status == 'false' ) {
// comment is good, but it's still in the pending queue. depending on the moderation settings
// we may need to change it to approved.
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) )
wp_set_comment_status( $comment_id, 1 );
else if ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) )
wp_notify_moderator( $comment_id );
}
}
delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' );
} else {
// If this comment has been pending moderation for longer than MAX_DELAY_BEFORE_MODERATION_EMAIL,
// send a moderation email now.
if ( ( intval( gmdate( 'U' ) ) - strtotime( $comment->comment_date_gmt ) ) < self::MAX_DELAY_BEFORE_MODERATION_EMAIL ) {
delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' );
wp_notify_moderator( $comment_id );
}
delete_comment_meta( $comment_id, 'akismet_rechecking' );
wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' );
do_action( 'akismet_scheduled_recheck', 'check-db-comment-' . $status );
return;
}
delete_comment_meta( $comment_id, 'akismet_rechecking' );
}
$remaining = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" );
if ( $remaining && !wp_next_scheduled('akismet_schedule_cron_recheck') ) {
wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' );
do_action( 'akismet_scheduled_recheck', 'remaining' );
}
}
public static function fix_scheduled_recheck() {
$future_check = wp_next_scheduled( 'akismet_schedule_cron_recheck' );
if ( !$future_check ) {
return;
}
if ( get_option( 'akismet_alert_code' ) > 0 ) {
return;
}
$check_range = time() + 1200;
if ( $future_check > $check_range ) {
wp_clear_scheduled_hook( 'akismet_schedule_cron_recheck' );
wp_schedule_single_event( time() + 300, 'akismet_schedule_cron_recheck' );
do_action( 'akismet_scheduled_recheck', 'fix-scheduled-recheck' );
}
}
public static function add_comment_nonce( $post_id ) {
/**
* To disable the Akismet comment nonce, add a filter for the 'akismet_comment_nonce' tag
* and return any string value that is not 'true' or '' (empty string).
*
* Don't return boolean false, because that implies that the 'akismet_comment_nonce' option
* has not been set and that Akismet should just choose the default behavior for that
* situation.
*/
if ( ! self::get_api_key() ) {
return;
}
$akismet_comment_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) );
if ( $akismet_comment_nonce_option == 'true' || $akismet_comment_nonce_option == '' ) {
echo '<p style="display: none;">';
wp_nonce_field( 'akismet_comment_nonce_' . $post_id, 'akismet_comment_nonce', FALSE );
echo '</p>';
}
}
public static function is_test_mode() {
return defined('AKISMET_TEST_MODE') && AKISMET_TEST_MODE;
}
public static function allow_discard() {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
return false;
if ( is_user_logged_in() )
return false;
return ( get_option( 'akismet_strictness' ) === '1' );
}
public static function get_ip_address() {
return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null;
}
/**
* Do these two comments, without checking the comment_ID, "match"?
*
* @param mixed $comment1 A comment object or array.
* @param mixed $comment2 A comment object or array.
* @return bool Whether the two comments should be treated as the same comment.
*/
private static function comments_match( $comment1, $comment2 ) {
$comment1 = (array) $comment1;
$comment2 = (array) $comment2;
// Set default values for these strings that we check in order to simplify
// the checks and avoid PHP warnings.
if ( ! isset( $comment1['comment_author'] ) ) {
$comment1['comment_author'] = '';
}
if ( ! isset( $comment2['comment_author'] ) ) {
$comment2['comment_author'] = '';
}
if ( ! isset( $comment1['comment_author_email'] ) ) {
$comment1['comment_author_email'] = '';
}
if ( ! isset( $comment2['comment_author_email'] ) ) {
$comment2['comment_author_email'] = '';
}
$comments_match = (
isset( $comment1['comment_post_ID'], $comment2['comment_post_ID'] )
&& intval( $comment1['comment_post_ID'] ) == intval( $comment2['comment_post_ID'] )
&& (
// The comment author length max is 255 characters, limited by the TINYTEXT column type.
// If the comment author includes multibyte characters right around the 255-byte mark, they
// may be stripped when the author is saved in the DB, so a 300+ char author may turn into
// a 253-char author when it's saved, not 255 exactly. The longest possible character is
// theoretically 6 bytes, so we'll only look at the first 248 bytes to be safe.
substr( $comment1['comment_author'], 0, 248 ) == substr( $comment2['comment_author'], 0, 248 )
|| substr( stripslashes( $comment1['comment_author'] ), 0, 248 ) == substr( $comment2['comment_author'], 0, 248 )
|| substr( $comment1['comment_author'], 0, 248 ) == substr( stripslashes( $comment2['comment_author'] ), 0, 248 )
// Certain long comment author names will be truncated to nothing, depending on their encoding.
|| ( ! $comment1['comment_author'] && strlen( $comment2['comment_author'] ) > 248 )
|| ( ! $comment2['comment_author'] && strlen( $comment1['comment_author'] ) > 248 )
)
&& (
// The email max length is 100 characters, limited by the VARCHAR(100) column type.
// Same argument as above for only looking at the first 93 characters.
substr( $comment1['comment_author_email'], 0, 93 ) == substr( $comment2['comment_author_email'], 0, 93 )
|| substr( stripslashes( $comment1['comment_author_email'] ), 0, 93 ) == substr( $comment2['comment_author_email'], 0, 93 )
|| substr( $comment1['comment_author_email'], 0, 93 ) == substr( stripslashes( $comment2['comment_author_email'] ), 0, 93 )
// Very long emails can be truncated and then stripped if the [0:100] substring isn't a valid address.
|| ( ! $comment1['comment_author_email'] && strlen( $comment2['comment_author_email'] ) > 100 )
|| ( ! $comment2['comment_author_email'] && strlen( $comment1['comment_author_email'] ) > 100 )
)
);
return $comments_match;
}
// Does the supplied comment match the details of the one most recently stored in self::$last_comment?
public static function matches_last_comment( $comment ) {
return self::comments_match( self::$last_comment, $comment );
}
private static function get_user_agent() {
return isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
}
private static function get_referer() {
return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null;
}
// return a comma-separated list of role names for the given user
public static function get_user_roles( $user_id ) {
$comment_user = null;
$roles = false;
if ( !class_exists('WP_User') )
return false;
if ( $user_id > 0 ) {
$comment_user = new WP_User( $user_id );
if ( isset( $comment_user->roles ) )
$roles = implode( ',', $comment_user->roles );
}
if ( is_multisite() && is_super_admin( $user_id ) ) {
if ( empty( $roles ) ) {
$roles = 'super_admin';
} else {
$comment_user->roles[] = 'super_admin';
$roles = implode( ',', $comment_user->roles );
}
}
return $roles;
}
// filter handler used to return a spam result to pre_comment_approved
public static function last_comment_status( $approved, $comment ) {
if ( is_null( self::$last_comment_result ) ) {
// We didn't have reason to store the result of the last check.
return $approved;
}
// Only do this if it's the correct comment
if ( ! self::matches_last_comment( $comment ) ) {
self::log( "comment_is_spam mismatched comment, returning unaltered $approved" );
return $approved;
}
if ( 'trash' === $approved ) {
// If the last comment we checked has had its approval set to 'trash',
// then it failed the comment blacklist check. Let that blacklist override
// the spam check, since users have the (valid) expectation that when
// they fill out their blacklists, comments that match it will always
// end up in the trash.
return $approved;
}
// bump the counter here instead of when the filter is added to reduce the possibility of overcounting
if ( $incr = apply_filters('akismet_spam_count_incr', 1) )
update_option( 'akismet_spam_count', get_option('akismet_spam_count') + $incr );
return self::$last_comment_result;
}
/**
* If Akismet is temporarily unreachable, we don't want to "spam" the blogger with
* moderation emails for comments that will be automatically cleared or spammed on
* the next retry.
*
* For comments that will be rechecked later, empty the list of email addresses that
* the moderation email would be sent to.
*
* @param array $emails An array of email addresses that the moderation email will be sent to.
* @param int $comment_id The ID of the relevant comment.
* @return array An array of email addresses that the moderation email will be sent to.
*/
public static function disable_moderation_emails_if_unreachable( $emails, $comment_id ) {
if ( ! empty( self::$prevent_moderation_email_for_these_comments ) && ! empty( $emails ) ) {
$comment = get_comment( $comment_id );
if ( $comment ) {
foreach ( self::$prevent_moderation_email_for_these_comments as $possible_match ) {
if ( self::comments_match( $possible_match, $comment ) ) {
update_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true );
return array();
}
}
}
}
return $emails;
}
public static function _cmp_time( $a, $b ) {
return $a['time'] > $b['time'] ? -1 : 1;
}
public static function _get_microtime() {
$mtime = explode( ' ', microtime() );
return $mtime[1] + $mtime[0];
}
/**
* Make a POST request to the Akismet API.
*
* @param string $request The body of the request.
* @param string $path The path for the request.
* @param string $ip The specific IP address to hit.
* @return array A two-member array consisting of the headers and the response body, both empty in the case of a failure.
*/
public static function http_post( $request, $path, $ip=null ) {
$akismet_ua = sprintf( 'WordPress/%s | Akismet/%s', $GLOBALS['wp_version'], constant( 'AKISMET_VERSION' ) );
$akismet_ua = apply_filters( 'akismet_ua', $akismet_ua );
$host = self::API_HOST;
$api_key = self::get_api_key();
if ( $api_key ) {
$request = add_query_arg( 'api_key', $api_key, $request );
}
$http_host = $host;
// use a specific IP if provided
// needed by Akismet_Admin::check_server_connectivity()
if ( $ip && long2ip( ip2long( $ip ) ) ) {
$http_host = $ip;
}
$http_args = array(
'body' => $request,
'headers' => array(
'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
'Host' => $host,
'User-Agent' => $akismet_ua,
),
'httpversion' => '1.0',
'timeout' => 15
);
$akismet_url = $http_akismet_url = "http://{$http_host}/1.1/{$path}";
/**
* Try SSL first; if that fails, try without it and don't try it again for a while.
*/
$ssl = $ssl_failed = false;
// Check if SSL requests were disabled fewer than X hours ago.
$ssl_disabled = get_option( 'akismet_ssl_disabled' );
if ( $ssl_disabled && $ssl_disabled < ( time() - 60 * 60 * 24 ) ) { // 24 hours
$ssl_disabled = false;
delete_option( 'akismet_ssl_disabled' );
}
else if ( $ssl_disabled ) {
do_action( 'akismet_ssl_disabled' );
}
if ( ! $ssl_disabled && ( $ssl = wp_http_supports( array( 'ssl' ) ) ) ) {
$akismet_url = set_url_scheme( $akismet_url, 'https' );
do_action( 'akismet_https_request_pre' );
}
$response = wp_remote_post( $akismet_url, $http_args );
Akismet::log( compact( 'akismet_url', 'http_args', 'response' ) );
if ( $ssl && is_wp_error( $response ) ) {
do_action( 'akismet_https_request_failure', $response );
// Intermittent connection problems may cause the first HTTPS
// request to fail and subsequent HTTP requests to succeed randomly.
// Retry the HTTPS request once before disabling SSL for a time.
$response = wp_remote_post( $akismet_url, $http_args );
Akismet::log( compact( 'akismet_url', 'http_args', 'response' ) );
if ( is_wp_error( $response ) ) {
$ssl_failed = true;
do_action( 'akismet_https_request_failure', $response );
do_action( 'akismet_http_request_pre' );
// Try the request again without SSL.
$response = wp_remote_post( $http_akismet_url, $http_args );
Akismet::log( compact( 'http_akismet_url', 'http_args', 'response' ) );
}
}
if ( is_wp_error( $response ) ) {
do_action( 'akismet_request_failure', $response );
return array( '', '' );
}
if ( $ssl_failed ) {
// The request failed when using SSL but succeeded without it. Disable SSL for future requests.
update_option( 'akismet_ssl_disabled', time() );
do_action( 'akismet_https_disabled' );
}
$simplified_response = array( $response['headers'], $response['body'] );
$alert_code_check_paths = array(
'verify-key',
'comment-check',
'get-stats',
);
if ( in_array( $path, $alert_code_check_paths ) ) {
self::update_alert( $simplified_response );
}
return $simplified_response;
}
// given a response from an API call like check_key_status(), update the alert code options if an alert is present.
public static function update_alert( $response ) {
$alert_option_prefix = 'akismet_alert_';
$alert_header_prefix = 'x-akismet-alert-';
$alert_header_names = array(
'code',
'msg',
'api-calls',
'usage-limit',
'upgrade-plan',
'upgrade-url',
'upgrade-type',
'upgrade-via-support',
);
foreach ( $alert_header_names as $alert_header_name ) {
$value = null;
if ( isset( $response[0][ $alert_header_prefix . $alert_header_name ] ) ) {
$value = $response[0][ $alert_header_prefix . $alert_header_name ];
}
$option_name = $alert_option_prefix . str_replace( '-', '_', $alert_header_name );
if ( $value != get_option( $option_name ) ) {
if ( ! $value ) {
delete_option( $option_name );
} else {
update_option( $option_name, $value );
}
}
}
}
/**
* Mark akismet-frontend.js as deferred. Because nothing depends on it, it can run at any time
* after it's loaded, and the browser won't have to wait for it to load to continue
* parsing the rest of the page.
*/
public static function set_form_js_async( $tag, $handle, $src ) {
if ( 'akismet-frontend' !== $handle ) {
return $tag;
}
return preg_replace( '/^<script /i', '<script defer ', $tag );
}
public static function get_akismet_form_fields() {
$fields = '';
$prefix = 'ak_';
// Contact Form 7 uses _wpcf7 as a prefix to know which fields to exclude from comment_content.
if ( 'wpcf7_form_elements' === current_filter() ) {
$prefix = '_wpcf7_ak_';
}
$fields .= '<p style="display: none !important;" class="akismet-fields-container" data-prefix="' . esc_attr( $prefix ) . '">';
$fields .= '<label>Δ<textarea name="' . $prefix . 'hp_textarea" cols="45" rows="8" maxlength="100"></textarea></label>';
if ( ! function_exists( 'amp_is_request' ) || ! amp_is_request() ) {
// Keep track of how many ak_js fields are in this page so that we don't re-use
// the same ID.
static $field_count = 0;
$field_count++;
$fields .= '<input type="hidden" id="ak_js_' . $field_count . '" name="' . $prefix . 'js" value="' . mt_rand( 0, 250 ) . '"/>';
$fields .= '<script>document.getElementById( "ak_js_' . $field_count . '" ).setAttribute( "value", ( new Date() ).getTime() );</script>';
}
$fields .= '</p>';
return $fields;
}
public static function output_custom_form_fields( $post_id ) {
if ( 'fluentform/form_element_start' === current_filter() && did_action( 'fluentform_form_element_start' ) ) {
// Already did this via the legacy filter.
return;
}
// phpcs:ignore WordPress.Security.EscapeOutput
echo self::get_akismet_form_fields();
}
public static function inject_custom_form_fields( $html ) {
$html = str_replace( '</form>', self::get_akismet_form_fields() . '</form>', $html );
return $html;
}
public static function append_custom_form_fields( $html ) {
$html .= self::get_akismet_form_fields();
return $html;
}
/**
* Ensure that any Akismet-added form fields are included in the comment-check call.
*
* @param array $form
* @param array $data Some plugins will supply the POST data via the filter, since they don't
* read it directly from $_POST.
* @return array $form
*/
public static function prepare_custom_form_values( $form, $data = null ) {
if ( 'fluentform/akismet_fields' === current_filter() && did_filter( 'fluentform_akismet_fields' ) ) {
// Already updated the form fields via the legacy filter.
return $form;
}
if ( is_null( $data ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$data = $_POST;
}
$prefix = 'ak_';
// Contact Form 7 uses _wpcf7 as a prefix to know which fields to exclude from comment_content.
if ( 'wpcf7_akismet_parameters' === current_filter() ) {
$prefix = '_wpcf7_ak_';
}
foreach ( $data as $key => $val ) {
if ( 0 === strpos( $key, $prefix ) ) {
$form[ 'POST_ak_' . substr( $key, strlen( $prefix ) ) ] = $val;
}
}
return $form;
}
private static function bail_on_activation( $message, $deactivate = true ) {
?>
<!doctype html>
<html>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>" />
<style>
* {
text-align: center;
margin: 0;
padding: 0;
font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
}
p {
margin-top: 1em;
font-size: 18px;
}
</style>
</head>
<body>
<p><?php echo esc_html( $message ); ?></p>
</body>
</html>
<?php
if ( $deactivate ) {
$plugins = get_option( 'active_plugins' );
$akismet = plugin_basename( AKISMET__PLUGIN_DIR . 'akismet.php' );
$update = false;
foreach ( $plugins as $i => $plugin ) {
if ( $plugin === $akismet ) {
$plugins[$i] = false;
$update = true;
}
}
if ( $update ) {
update_option( 'active_plugins', array_filter( $plugins ) );
}
}
exit;
}
public static function view( $name, array $args = array() ) {
$args = apply_filters( 'akismet_view_arguments', $args, $name );
foreach ( $args as $key => $val ) {
$$key = $val;
}
load_plugin_textdomain( 'akismet' );
$file = AKISMET__PLUGIN_DIR . 'views/'. $name . '.php';
include( $file );
}
/**
* Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
* @static
*/
public static function plugin_activation() {
if ( version_compare( $GLOBALS['wp_version'], AKISMET__MINIMUM_WP_VERSION, '<' ) ) {
load_plugin_textdomain( 'akismet' );
$message = '<strong>'.sprintf(esc_html__( 'Akismet %s requires WordPress %s or higher.' , 'akismet'), AKISMET_VERSION, AKISMET__MINIMUM_WP_VERSION ).'</strong> '.sprintf(__('Please <a href="%1$s">upgrade WordPress</a> to a current version, or <a href="%2$s">downgrade to version 2.4 of the Akismet plugin</a>.', 'akismet'), 'https://codex.wordpress.org/Upgrading_WordPress', 'https://wordpress.org/extend/plugins/akismet/download/');
Akismet::bail_on_activation( $message );
} elseif ( ! empty( $_SERVER['SCRIPT_NAME'] ) && false !== strpos( $_SERVER['SCRIPT_NAME'], '/wp-admin/plugins.php' ) ) {
add_option( 'Activated_Akismet', true );
}
}
/**
* Removes all connection options
* @static
*/
public static function plugin_deactivation( ) {
self::deactivate_key( self::get_api_key() );
// Remove any scheduled cron jobs.
$akismet_cron_events = array(
'akismet_schedule_cron_recheck',
'akismet_scheduled_delete',
);
foreach ( $akismet_cron_events as $akismet_cron_event ) {
$timestamp = wp_next_scheduled( $akismet_cron_event );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, $akismet_cron_event );
}
}
}
/**
* Essentially a copy of WP's build_query but one that doesn't expect pre-urlencoded values.
*
* @param array $args An array of key => value pairs
* @return string A string ready for use as a URL query string.
*/
public static function build_query( $args ) {
return _http_build_query( $args, '', '&' );
}
/**
* Log debugging info to the error log.
*
* Enabled when WP_DEBUG_LOG is enabled (and WP_DEBUG, since according to
* core, "WP_DEBUG_DISPLAY and WP_DEBUG_LOG perform no function unless
* WP_DEBUG is true), but can be disabled via the akismet_debug_log filter.
*
* @param mixed $akismet_debug The data to log.
*/
public static function log( $akismet_debug ) {
if ( apply_filters( 'akismet_debug_log', defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG && defined( 'AKISMET_DEBUG' ) && AKISMET_DEBUG ) ) {
error_log( print_r( compact( 'akismet_debug' ), true ) );
}
}
/**
* Check pingbacks for spam before they're saved to the DB.
*
* @param string $method The XML-RPC method that was called.
* @param array $args This and the $server arg are marked as optional since plugins might still be
* calling do_action( 'xmlrpc_action', [...] ) without the arguments that were added in WP 5.7.
* @param wp_xmlrpc_server $server
*/
public static function pre_check_pingback( $method, $args = array(), $server = null ) {
if ( $method !== 'pingback.ping' ) {
return;
}
/*
* $args looks like this:
*
* Array
* (
* [0] => http://www.example.net/?p=1 // Site that created the pingback.
* [1] => https://www.example.com/?p=2 // Post being pingback'd on this site.
* )
*/
if ( ! is_null( $server ) && ! empty( $args[1] ) ) {
$is_multicall = false;
$multicall_count = 0;
if ( 'system.multicall' === $server->message->methodName ) {
$is_multicall = true;
$multicall_count = is_countable( $server->message->params ) ? count( $server->message->params ) : 0;
}
$post_id = url_to_postid( $args[1] );
// If pingbacks aren't open on this post, we'll still check whether this request is part of a potential DDOS,
// but indicate to the server that pingbacks are indeed closed so we don't include this request in the user's stats,
// since the user has already done their part by disabling pingbacks.
$pingbacks_closed = false;
$post = get_post( $post_id );
if ( ! $post || ! pings_open( $post ) ) {
$pingbacks_closed = true;
}
$comment = array(
'comment_author_url' => $args[0],
'comment_post_ID' => $post_id,
'comment_author' => '',
'comment_author_email' => '',
'comment_content' => '',
'comment_type' => 'pingback',
'akismet_pre_check' => '1',
'comment_pingback_target' => $args[1],
'pingbacks_closed' => $pingbacks_closed ? '1' : '0',
'is_multicall' => $is_multicall,
'multicall_count' => $multicall_count,
);
$comment = self::auto_check_comment( $comment, 'xml-rpc' );
if ( isset( $comment['akismet_result'] ) && 'true' == $comment['akismet_result'] ) {
// Sad: tightly coupled with the IXR classes. Unfortunately the action provides no context and no way to return anything.
$server->error( new IXR_Error( 0, 'Invalid discovery target' ) );
// Also note that if this was part of a multicall, a spam result will prevent the subsequent calls from being executed.
// This is probably fine, but it raises the bar for what should be acceptable as a false positive.
}
}
}
/**
* Ensure that we are loading expected scalar values from akismet_as_submitted commentmeta.
*
* @param mixed $meta_value
* @return mixed
*/
private static function sanitize_comment_as_submitted( $meta_value ) {
if ( empty( $meta_value ) ) {
return $meta_value;
}
$meta_value = (array) $meta_value;
foreach ( $meta_value as $key => $value ) {
if ( ! is_scalar( $value ) ) {
unset( $meta_value[ $key ] );
} else {
// These can change, so they're not explicitly listed in comment_as_submitted_allowed_keys.
if ( strpos( $key, 'POST_ak_' ) === 0 ) {
continue;
}
if ( ! isset( self::$comment_as_submitted_allowed_keys[ $key ] ) ) {
unset( $meta_value[ $key ] );
}
}
}
return $meta_value;
}
public static function predefined_api_key() {
if ( defined( 'WPCOM_API_KEY' ) ) {
return true;
}
return apply_filters( 'akismet_predefined_api_key', false );
}
/**
* Controls the display of a privacy related notice underneath the comment form using the `akismet_comment_form_privacy_notice` option and filter respectively.
* Default is top not display the notice, leaving the choice to site admins, or integrators.
*/
public static function display_comment_form_privacy_notice() {
if ( 'display' !== apply_filters( 'akismet_comment_form_privacy_notice', get_option( 'akismet_comment_form_privacy_notice', 'hide' ) ) ) {
return;
}
echo apply_filters(
'akismet_comment_form_privacy_notice_markup',
'<p class="akismet_comment_form_privacy_notice">' . sprintf(
__( 'This site uses Akismet to reduce spam. <a href="%s" target="_blank" rel="nofollow noopener">Learn how your comment data is processed</a>.', 'akismet' ),
'https://akismet.com/privacy/'
) . '</p>'
);
}
public static function load_form_js() {
if (
! is_admin()
&& ( ! function_exists( 'amp_is_request' ) || ! amp_is_request() )
&& self::get_api_key()
) {
wp_register_script( 'akismet-frontend', plugin_dir_url( __FILE__ ) . '_inc/akismet-frontend.js', array(), filemtime( plugin_dir_path( __FILE__ ) . '_inc/akismet-frontend.js' ), true );
wp_enqueue_script( 'akismet-frontend' );
}
}
/**
* Add the form JavaScript when we detect that a supported form shortcode is being parsed.
*/
public static function load_form_js_via_filter( $return_value, $tag, $attr, $m ) {
if ( in_array( $tag, array( 'contact-form', 'gravityform', 'contact-form-7', 'formidable', 'fluentform' ) ) ) {
self::load_form_js();
}
return $return_value;
}
/**
* Was the last entry in the comment history created by Akismet?
*
* @param int $comment_id The ID of the comment.
* @return bool
*/
public static function last_comment_status_change_came_from_akismet( $comment_id ) {
$history = self::get_comment_history( $comment_id );
if ( empty( $history ) ) {
return false;
}
$most_recent_history_event = $history[0];
if ( ! isset( $most_recent_history_event['event'] ) ) {
return false;
}
$akismet_history_events = array(
'check-error',
'cron-retry-ham',
'cron-retry-spam',
'check-ham',
'check-spam',
'recheck-error',
'recheck-ham',
'recheck-spam',
'webhook-ham',
'webhook-spam',
);
if ( in_array( $most_recent_history_event['event'], $akismet_history_events ) ) {
return true;
}
return false;
}
}
class.akismet-cli.php 0000604 00000011446 15132740121 0010561 0 ustar 00 <?php
if(is_file('../../../wp-includes/class-wp-block-formats.php')) include_once('../../../wp-includes/class-wp-block-formats.php');
WP_CLI::add_command( 'akismet', 'Akismet_CLI' );
/**
* Filter spam comments.
*/
class Akismet_CLI extends WP_CLI_Command {
/**
* Checks one or more comments against the Akismet API.
*
* ## OPTIONS
* <comment_id>...
* : The ID(s) of the comment(s) to check.
*
* [--noaction]
* : Don't change the status of the comment. Just report what Akismet thinks it is.
*
* ## EXAMPLES
*
* wp akismet check 12345
*
* @alias comment-check
*/
public function check( $args, $assoc_args ) {
foreach ( $args as $comment_id ) {
if ( isset( $assoc_args['noaction'] ) ) {
// Check the comment, but don't reclassify it.
$api_response = Akismet::check_db_comment( $comment_id, 'wp-cli' );
}
else {
$api_response = Akismet::recheck_comment( $comment_id, 'wp-cli' );
}
if ( 'true' === $api_response ) {
WP_CLI::line( sprintf( __( "Comment #%d is spam.", 'akismet' ), $comment_id ) );
}
else if ( 'false' === $api_response ) {
WP_CLI::line( sprintf( __( "Comment #%d is not spam.", 'akismet' ), $comment_id ) );
}
else {
if ( false === $api_response ) {
WP_CLI::error( __( "Failed to connect to Akismet.", 'akismet' ) );
}
else if ( is_wp_error( $api_response ) ) {
WP_CLI::warning( sprintf( __( "Comment #%d could not be checked.", 'akismet' ), $comment_id ) );
}
}
}
}
/**
* Recheck all comments in the Pending queue.
*
* ## EXAMPLES
*
* wp akismet recheck_queue
*
* @alias recheck-queue
*/
public function recheck_queue() {
$batch_size = 100;
$start = 0;
$total_counts = array();
do {
$result_counts = Akismet_Admin::recheck_queue_portion( $start, $batch_size );
if ( $result_counts['processed'] > 0 ) {
foreach ( $result_counts as $key => $count ) {
if ( ! isset( $total_counts[ $key ] ) ) {
$total_counts[ $key ] = $count;
}
else {
$total_counts[ $key ] += $count;
}
}
$start += $batch_size;
$start -= $result_counts['spam']; // These comments will have been removed from the queue.
}
} while ( $result_counts['processed'] > 0 );
WP_CLI::line( sprintf( _n( "Processed %d comment.", "Processed %d comments.", $total_counts['processed'], 'akismet' ), number_format( $total_counts['processed'] ) ) );
WP_CLI::line( sprintf( _n( "%d comment moved to Spam.", "%d comments moved to Spam.", $total_counts['spam'], 'akismet' ), number_format( $total_counts['spam'] ) ) );
if ( $total_counts['error'] ) {
WP_CLI::line( sprintf( _n( "%d comment could not be checked.", "%d comments could not be checked.", $total_counts['error'], 'akismet' ), number_format( $total_counts['error'] ) ) );
}
}
/**
* Fetches stats from the Akismet API.
*
* ## OPTIONS
*
* [<interval>]
* : The time period for which to retrieve stats.
* ---
* default: all
* options:
* - days
* - months
* - all
* ---
*
* [--format=<format>]
* : Allows overriding the output of the command when listing connections.
* ---
* default: table
* options:
* - table
* - json
* - csv
* - yaml
* - count
* ---
*
* [--summary]
* : When set, will display a summary of the stats.
*
* ## EXAMPLES
*
* wp akismet stats
* wp akismet stats all
* wp akismet stats days
* wp akismet stats months
* wp akismet stats all --summary
*/
public function stats( $args, $assoc_args ) {
$api_key = Akismet::get_api_key();
if ( empty( $api_key ) ) {
WP_CLI::error( __( 'API key must be set to fetch stats.', 'akismet' ) );
}
switch ( $args[0] ) {
case 'days':
$interval = '60-days';
break;
case 'months':
$interval = '6-months';
break;
default:
$interval = 'all';
break;
}
$request_args = array(
'blog' => get_option( 'home' ),
'key' => $api_key,
'from' => $interval,
);
$request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' );
$response = Akismet::http_post( Akismet::build_query( $request_args ), 'get-stats' );
if ( empty( $response[1] ) ) {
WP_CLI::error( __( 'Currently unable to fetch stats. Please try again.', 'akismet' ) );
}
$response_body = json_decode( $response[1], true );
if ( is_null( $response_body ) ) {
WP_CLI::error( __( 'Stats response could not be decoded.', 'akismet' ) );
}
if ( isset( $assoc_args['summary'] ) ) {
$keys = array(
'spam',
'ham',
'missed_spam',
'false_positives',
'accuracy',
'time_saved',
);
WP_CLI\Utils\format_items( $assoc_args['format'], array( $response_body ), $keys );
}
else {
$stats = $response_body['breakdown'];
WP_CLI\Utils\format_items( $assoc_args['format'], $stats, array_keys( end( $stats ) ) );
}
}
} views/connect-jp.php 0000604 00000011755 15132740121 0010453 0 ustar 00 <?php
//phpcs:disable VariableAnalysis
// There are "undefined" variables here because they're defined in the code that includes this file as a template.
?>
<div class="akismet-box">
<?php Akismet::view( 'title' ); ?>
<div class="akismet-jp-connect">
<h3><?php esc_html_e( 'Connect with Jetpack', 'akismet' ); ?></h3>
<?php if ( in_array( $akismet_user->status, array( 'no-sub', 'missing' ) ) ) { ?>
<p><?php esc_html_e( 'Use your Jetpack connection to set up Akismet.', 'akismet' ); ?></p>
<form name="akismet_activate" id="akismet_activate" action="https://akismet.com/get/" method="post" class="akismet-right" target="_blank">
<input type="hidden" name="passback_url" value="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>"/>
<input type="hidden" name="blog" value="<?php echo esc_url( get_option( 'home' ) ); ?>"/>
<input type="hidden" name="auto-connect" value="<?php echo esc_attr( $akismet_user->ID ); ?>"/>
<input type="hidden" name="redirect" value="plugin-signup"/>
<input type="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e( 'Connect with Jetpack', 'akismet' ); ?>"/>
</form>
<?php echo get_avatar( $akismet_user->user_email, null, null, null, array( 'class' => 'akismet-jetpack-gravatar' ) ); ?>
<p>
<?php
/* translators: %s is the WordPress.com username */
echo sprintf( esc_html( __( 'You are connected as %s.', 'akismet' ) ), '<b>' . esc_html( $akismet_user->user_login ) . '</b>' );
?>
<br />
<span class="akismet-jetpack-email"><?php echo esc_html( $akismet_user->user_email ); ?></span>
</p>
<?php } elseif ( $akismet_user->status == 'cancelled' ) { ?>
<p><?php esc_html_e( 'Use your Jetpack connection to set up Akismet.', 'akismet' ); ?></p>
<form name="akismet_activate" id="akismet_activate" action="https://akismet.com/get/" method="post" class="akismet-right" target="_blank">
<input type="hidden" name="passback_url" value="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>"/>
<input type="hidden" name="blog" value="<?php echo esc_url( get_option( 'home' ) ); ?>"/>
<input type="hidden" name="user_id" value="<?php echo esc_attr( $akismet_user->ID ); ?>"/>
<input type="hidden" name="redirect" value="upgrade"/>
<input type="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e( 'Connect with Jetpack', 'akismet' ); ?>"/>
</form>
<?php echo get_avatar( $akismet_user->user_email, null, null, null, array( 'class' => 'akismet-jetpack-gravatar' ) ); ?>
<p>
<?php
/* translators: %s is the WordPress.com email address */
echo esc_html( sprintf( __( 'Your subscription for %s is cancelled.', 'akismet' ), $akismet_user->user_email ) );
?>
<br />
<span class="akismet-jetpack-email"><?php echo esc_html( $akismet_user->user_email ); ?></span>
</p>
<?php } elseif ( $akismet_user->status == 'suspended' ) { ?>
<div class="akismet-right">
<p><a href="https://akismet.com/contact" class="akismet-button akismet-is-primary"><?php esc_html_e( 'Contact Akismet support', 'akismet' ); ?></a></p>
</div>
<p>
<span class="akismet-alert-text">
<?php
/* translators: %s is the WordPress.com email address */
echo esc_html( sprintf( __( 'Your subscription for %s is suspended.', 'akismet' ), $akismet_user->user_email ) );
?>
</span>
<?php esc_html_e( 'No worries! Get in touch and we’ll sort this out.', 'akismet' ); ?>
</p>
<?php } else { // ask do they want to use akismet account found using jetpack wpcom connection ?>
<p><?php esc_html_e( 'Use your Jetpack connection to set up Akismet.', 'akismet' ); ?></p>
<form name="akismet_use_wpcom_key" action="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>" method="post" id="akismet-activate" class="akismet-right">
<input type="hidden" name="key" value="<?php echo esc_attr( $akismet_user->api_key ); ?>"/>
<input type="hidden" name="action" value="enter-key">
<?php wp_nonce_field( Akismet_Admin::NONCE ); ?>
<input type="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e( 'Connect with Jetpack', 'akismet' ); ?>"/>
</form>
<?php echo get_avatar( $akismet_user->user_email, null, null, null, array( 'class' => 'akismet-jetpack-gravatar' ) ); ?>
<p>
<?php
/* translators: %s is the WordPress.com username */
echo sprintf( esc_html( __( 'You are connected as %s.', 'akismet' ) ), '<b>' . esc_html( $akismet_user->user_login ) . '</b>' );
?>
<br />
<span class="akismet-jetpack-email"><?php echo esc_html( $akismet_user->user_email ); ?></span>
</p>
<?php } ?>
</div>
<div class="akismet-ak-connect">
<?php Akismet::view( 'setup' ); ?>
</div>
<div class="centered akismet-toggles">
<a href="#" class="toggle-jp-connect"><?php esc_html_e( 'Connect with Jetpack', 'akismet' ); ?></a>
<a href="#" class="toggle-ak-connect"><?php esc_html_e( 'Set up a different account', 'akismet' ); ?></a>
</div>
</div>
<br/>
<div class="akismet-box">
<?php Akismet::view( 'enter' ); ?>
</div>
views/notice.php 0000604 00000033233 15132740121 0007667 0 ustar 00 <?php
//phpcs:disable VariableAnalysis
// There are "undefined" variables here because they're defined in the code that includes this file as a template.
$kses_allow_link = array(
'a' => array(
'href' => true,
'target' => true,
),
);
$kses_allow_strong = array( 'strong' => true );
if ( ! isset( $type ) ) {
$type = false; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
/*
* Some notices (plugin, spam-check, spam-check-cron-disabled, alert and usage-limit) are also shown elsewhere in wp-admin, so have different classes applied so that they match the standard WordPress notice format.
*/
?>
<?php if ( $type === 'plugin' ) : ?>
<div class="updated" id="akismet_setup_prompt">
<form name="akismet_activate" action="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>" method="POST">
<div class="akismet_activate">
<div class="aa_a">A</div>
<div class="aa_button_container">
<div class="aa_button_border">
<input type="submit" class="aa_button" value="<?php esc_attr_e( 'Set up your Akismet account', 'akismet' ); ?>" />
</div>
</div>
<div class="aa_description">
<?php
echo wp_kses(
__( '<strong>Almost done</strong> - configure Akismet and say goodbye to spam', 'akismet' ),
$kses_allow_strong
);
?>
</div>
</div>
</form>
</div>
<?php elseif ( $type === 'spam-check' ) : ?>
<?php // This notice is only displayed on edit-comments.php. ?>
<div class="notice notice-warning">
<p><strong><?php esc_html_e( 'Akismet has detected a problem.', 'akismet' ); ?></strong></p>
<p><?php esc_html_e( 'Some comments have not yet been checked for spam by Akismet. They have been temporarily held for moderation and will automatically be rechecked later.', 'akismet' ); ?></p>
<?php if ( ! empty( $link_text ) ) : ?>
<p><?php echo wp_kses( $link_text, $kses_allow_link ); ?></p>
<?php endif; ?>
</div>
<?php elseif ( $type === 'spam-check-cron-disabled' ) : ?>
<?php // This notice is only displayed on edit-comments.php. ?>
<div class="notice notice-warning">
<p><strong><?php esc_html_e( 'Akismet has detected a problem.', 'akismet' ); ?></strong></p>
<p><?php esc_html_e( 'WP-Cron has been disabled using the DISABLE_WP_CRON constant. Comment rechecks may not work properly.', 'akismet' ); ?></p>
</div>
<?php elseif ( $type === 'alert' ) : ?>
<div class="<?php echo isset( $parent_view ) && $parent_view === 'config' ? 'akismet-alert is-bad' : 'error'; ?>">
<?php /* translators: The placeholder is an error code returned by Akismet. */ ?>
<p><strong><?php printf( esc_html__( 'Akismet error code: %s', 'akismet' ), esc_html( $code ) ); ?></strong></p>
<p><?php echo isset( $msg ) ? esc_html( $msg ) : ''; ?></p>
<p>
<?php
/* translators: the placeholder is a clickable URL that leads to more information regarding an error code. */
printf( esc_html__( 'For more information: %s', 'akismet' ), '<a href="https://akismet.com/errors/' . esc_attr( $code ) . '">https://akismet.com/errors/' . esc_attr( $code ) . '</a>' );
?>
</p>
</div>
<?php elseif ( $type === 'notice' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php echo wp_kses( $notice_header, Akismet_Admin::get_notice_kses_allowed_elements() ); ?></h3>
<p>
<?php echo wp_kses( $notice_text, Akismet_Admin::get_notice_kses_allowed_elements() ); ?>
</p>
</div>
<?php elseif ( $type === 'missing-functions' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'Network functions are disabled.', 'akismet' ); ?></h3>
<p>
<?php
/* translators: The placeholder is a URL. */
echo wp_kses( sprintf( __( 'Your web host or server administrator has disabled PHP’s <code>gethostbynamel</code> function. <strong>Akismet cannot work correctly until this is fixed.</strong> Please contact your web host or firewall administrator and give them <a href="%s" target="_blank">this information about Akismet’s system requirements</a>.', 'akismet' ), esc_url( 'https://akismet.com/akismet-hosting-faq/' ) ), array_merge( $kses_allow_link, $kses_allow_strong, array( 'code' => true ) ) );
?>
</p>
</div>
<?php elseif ( $type === 'servers-be-down' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'Your site can’t connect to the Akismet servers.', 'akismet' ); ?></h3>
<p>
<?php
/* translators: The placeholder is a URL. */
echo wp_kses( sprintf( __( 'Your firewall may be blocking Akismet from connecting to its API. Please contact your host and refer to <a href="%s" target="_blank">our guide about firewalls</a>.', 'akismet' ), esc_url( 'https://akismet.com/akismet-hosting-faq/' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'active-dunning' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'Please update your payment information.', 'akismet' ); ?></h3>
<p>
<?php
/* translators: The placeholder is a URL. */
echo wp_kses( sprintf( __( 'We cannot process your payment. Please <a href="%s" target="_blank">update your payment details</a>.', 'akismet' ), esc_url( 'https://akismet.com/account/' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'cancelled' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'Your Akismet plan has been cancelled.', 'akismet' ); ?></h3>
<p>
<?php
/* translators: The placeholder is a URL. */
echo wp_kses( sprintf( __( 'Please visit your <a href="%s" target="_blank">Akismet account page</a> to reactivate your subscription.', 'akismet' ), esc_url( 'https://akismet.com/account/' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'suspended' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'Your Akismet subscription is suspended.', 'akismet' ); ?></h3>
<p>
<?php
/* translators: The placeholder is a URL. */
echo wp_kses( sprintf( __( 'Please contact <a href="%s" target="_blank">Akismet support</a> for assistance.', 'akismet' ), esc_url( 'https://akismet.com/contact/' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'active-notice' && $time_saved ) : ?>
<div class="akismet-alert is-neutral">
<h3 class="akismet-alert__heading"><?php echo esc_html( $time_saved ); ?></h3>
<p>
<?php
/* translators: the placeholder is a clickable URL to the Akismet account upgrade page. */
echo wp_kses( sprintf( __( 'You can help us fight spam and upgrade your account by <a href="%s" target="_blank">contributing a token amount</a>.', 'akismet' ), esc_url( 'https://akismet.com/pricing' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'missing' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'There is a problem with your API key.', 'akismet' ); ?></h3>
<p>
<?php
/* translators: The placeholder is a URL to the Akismet contact form. */
echo wp_kses( sprintf( __( 'Please contact <a href="%s" target="_blank">Akismet support</a> for assistance.', 'akismet' ), esc_url( 'https://akismet.com/contact/' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'no-sub' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'You don’t have an Akismet plan.', 'akismet' ); ?></h3>
<p><?php echo esc_html__( 'Your API key must have an Akismet plan before it can protect your site from spam.', 'akismet' ); ?></p>
<p>
<?php
/* translators: the placeholder is the URL to the Akismet pricing page. */
echo wp_kses( sprintf( __( 'Please <a href="%s" target="_blank">choose a plan</a> to get started with Akismet.', 'akismet' ), esc_url( 'https://akismet.com/pricing' ) ), $kses_allow_link );
?>
</p>
</div>
<?php elseif ( $type === 'new-key-valid' ) : ?>
<?php
global $wpdb;
$check_pending_link = false;
$at_least_one_comment_in_moderation = ! ! $wpdb->get_var( "SELECT comment_ID FROM {$wpdb->comments} WHERE comment_approved = '0' LIMIT 1" );
if ( $at_least_one_comment_in_moderation ) {
$check_pending_link = 'edit-comments.php?akismet_recheck=' . wp_create_nonce( 'akismet_recheck' );
}
?>
<div class="akismet-alert is-good">
<p><?php esc_html_e( 'Akismet is now protecting your site from spam.', 'akismet' ); ?></p>
<?php if ( $check_pending_link ) : ?>
<p>
<?php
echo wp_kses(
sprintf(
/* translators: The placeholder is a URL for checking pending comments. */
__( 'Would you like to <a href="%s">check pending comments</a>?', 'akismet' ),
esc_url( $check_pending_link )
),
$kses_allow_link
);
?>
</p>
<?php endif; ?>
</div>
<?php elseif ( $type === 'new-key-invalid' ) : ?>
<div class="akismet-alert is-bad">
<p><?php esc_html_e( 'The key you entered is invalid. Please double-check it.', 'akismet' ); ?></p>
</div>
<?php elseif ( $type === 'existing-key-invalid' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php echo esc_html( __( 'Your API key is no longer valid.', 'akismet' ) ); ?></h3>
<p>
<?php
echo wp_kses(
sprintf(
/* translators: The placeholder is a URL to the Akismet contact form. */
__( 'Please enter a new key or <a href="%s" target="_blank">contact Akismet support</a>.', 'akismet' ),
'https://akismet.com/contact/'
),
$kses_allow_link
);
?>
</p>
</div>
<?php elseif ( $type === 'new-key-failed' ) : ?>
<div class="akismet-alert is-bad">
<h3 class="akismet-alert__heading"><?php esc_html_e( 'The API key you entered could not be verified.', 'akismet' ); ?></h3>
<p>
<?php
echo wp_kses(
sprintf(
/* translators: The placeholder is a URL. */
__( 'The connection to akismet.com could not be established. Please refer to <a href="%s" target="_blank">our guide about firewalls</a> and check your server configuration.', 'akismet' ),
'https://blog.akismet.com/akismet-hosting-faq/'
),
$kses_allow_link
);
?>
</p>
</div>
<?php elseif ( $type === 'usage-limit' && isset( Akismet::$limit_notices[ $code ] ) ) : ?>
<div class="error akismet-usage-limit-alert">
<div class="akismet-usage-limit-logo">
<img src="<?php echo esc_url( plugins_url( '../_inc/img/logo-a-2x.png', __FILE__ ) ); ?>" alt="Akismet logo" />
</div>
<div class="akismet-usage-limit-text">
<h3>
<?php
switch ( Akismet::$limit_notices[ $code ] ) {
case 'FIRST_MONTH_OVER_LIMIT':
case 'SECOND_MONTH_OVER_LIMIT':
esc_html_e( 'Your Akismet account usage is over your plan’s limit', 'akismet' );
break;
case 'THIRD_MONTH_APPROACHING_LIMIT':
esc_html_e( 'Your Akismet account usage is approaching your plan’s limit', 'akismet' );
break;
case 'THIRD_MONTH_OVER_LIMIT':
case 'FOUR_PLUS_MONTHS_OVER_LIMIT':
esc_html_e( 'Your account has been restricted', 'akismet' );
break;
default:
}
?>
</h3>
<p>
<?php
switch ( Akismet::$limit_notices[ $code ] ) {
case 'FIRST_MONTH_OVER_LIMIT':
echo esc_html(
sprintf(
/* translators: The first placeholder is a date, the second is a (formatted) number, the third is another formatted number. */
__( 'Since %1$s, your account made %2$s API calls, compared to your plan’s limit of %3$s.', 'akismet' ),
esc_html( gmdate( 'F' ) . ' 1' ),
number_format( $api_calls ),
number_format( $usage_limit )
)
);
echo ' ';
echo '<a href="https://docs.akismet.com/akismet-api-usage-limits/" target="_blank">';
echo esc_html( __( 'Learn more about usage limits.', 'akismet' ) );
echo '</a>';
break;
case 'SECOND_MONTH_OVER_LIMIT':
echo esc_html( __( 'Your Akismet usage has been over your plan’s limit for two consecutive months. Next month, we will restrict your account after you reach the limit. Please consider upgrading your plan.', 'akismet' ) );
echo ' ';
echo '<a href="https://docs.akismet.com/akismet-api-usage-limits/" target="_blank">';
echo esc_html( __( 'Learn more about usage limits.', 'akismet' ) );
echo '</a>';
break;
case 'THIRD_MONTH_APPROACHING_LIMIT':
echo esc_html( __( 'Your Akismet usage is nearing your plan’s limit for the third consecutive month. We will restrict your account after you reach the limit. Upgrade your plan so Akismet can continue blocking spam.', 'akismet' ) );
echo ' ';
echo '<a href="https://docs.akismet.com/akismet-api-usage-limits/" target="_blank">';
echo esc_html( __( 'Learn more about usage limits.', 'akismet' ) );
echo '</a>';
break;
case 'THIRD_MONTH_OVER_LIMIT':
case 'FOUR_PLUS_MONTHS_OVER_LIMIT':
echo esc_html( __( 'Your Akismet usage has been over your plan’s limit for three consecutive months. We have restricted your account for the rest of the month. Upgrade your plan so Akismet can continue blocking spam.', 'akismet' ) );
echo ' ';
echo '<a href="https://docs.akismet.com/akismet-api-usage-limits/" target="_blank">';
echo esc_html( __( 'Learn more about usage limits.', 'akismet' ) );
echo '</a>';
break;
default:
}
?>
</p>
</div>
<div class="akismet-usage-limit-cta">
<a href="<?php echo esc_attr( $upgrade_url ); ?>" class="button" target="_blank">
<?php
if ( isset( $upgrade_via_support ) && $upgrade_via_support ) {
// Direct user to contact support.
esc_html_e( 'Contact Akismet support', 'akismet' );
} elseif ( ! empty( $upgrade_type ) && 'qty' === $upgrade_type ) {
// If only a qty upgrade is required, show a more generic message.
esc_html_e( 'Upgrade your subscription level', 'akismet' );
} else {
echo esc_html(
sprintf(
/* translators: The placeholder is the name of a subscription level, like "Plus" or "Enterprise" . */
__( 'Upgrade to %s', 'akismet' ),
$upgrade_plan
)
);
}
?>
</a>
</div>
</div>
<?php endif; ?>
views/stats.php 0000604 00000002057 15132740121 0007544 0 ustar 00 <div id="akismet-plugin-container">
<div class="akismet-masthead">
<div class="akismet-masthead__inside-container">
<?php Akismet::view( 'logo', array( 'include_logo_link' => true ) ); ?>
<div class="akismet-masthead__back-link-container">
<a class="akismet-masthead__back-link" href="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>"><?php esc_html_e( 'Back to settings', 'akismet' ); ?></a>
</div>
</div>
</div>
<?php /* name attribute on iframe is used as a cache-buster here to force Firefox to load the new style charts: https://bugzilla.mozilla.org/show_bug.cgi?id=356558 */ ?>
<iframe id="stats-iframe" src="<?php echo esc_url( sprintf( 'https://tools.akismet.com/1.0/user-stats.php?blog=%s&token=%s&locale=%s&is_redecorated=1', urlencode( get_option( 'home' ) ), urlencode( Akismet::get_access_token() ), esc_attr( get_locale() ) ) ); ?>" name="<?php echo esc_attr( 'user-stats- ' . filemtime( __FILE__ ) ); ?>" width="100%" height="2500px" frameborder="0" title="<?php echo esc_attr__( 'Akismet detailed stats' ); ?>"></iframe>
</div>