[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-content/plugins/akismet/ -> class.akismet-admin.php (source)

   1  <?php
   2  
   3  // We plan to gradually remove all of the disabled lint rules below.
   4  // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
   5  // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
   6  // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
   7  // phpcs:disable Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
   8  // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
   9  
  10  class Akismet_Admin {
  11  
  12      const NONCE = 'akismet-update-key';
  13  
  14      const NOTICE_EXISTING_KEY_INVALID = 'existing-key-invalid';
  15  
  16      private static $initiated = false;
  17      private static $notices   = array();
  18      private static $allowed   = array(
  19          'a'      => array(
  20              'href'  => true,
  21              'title' => true,
  22          ),
  23          'b'      => array(),
  24          'code'   => array(),
  25          'del'    => array(
  26              'datetime' => true,
  27          ),
  28          'em'     => array(),
  29          'i'      => array(),
  30          'q'      => array(
  31              'cite' => true,
  32          ),
  33          'strike' => array(),
  34          'strong' => array(),
  35      );
  36  
  37      /**
  38       * List of pages where activation banner should be displayed.
  39       *
  40       * @var array
  41       */
  42      private static $activation_banner_pages = array(
  43          'edit-comments.php',
  44          'options-discussion.php',
  45          'plugins.php',
  46      );
  47  
  48  	public static function init() {
  49          if ( ! self::$initiated ) {
  50              self::init_hooks();
  51          }
  52  
  53          if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) {
  54              self::enter_api_key();
  55          }
  56      }
  57  
  58  	public static function init_hooks() {
  59          // The standalone stats page was removed in 3.0 for an all-in-one config and stats page.
  60          // Redirect any links that might have been bookmarked or in browser history.
  61          if ( isset( $_GET['page'] ) && 'akismet-stats-display' == $_GET['page'] ) {
  62              wp_safe_redirect( esc_url_raw( self::get_page_url( 'stats' ) ), 301 );
  63              die;
  64          }
  65  
  66          self::$initiated = true;
  67  
  68          add_action( 'admin_init', array( 'Akismet_Admin', 'admin_init' ) );
  69          add_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 ); // Priority 5, so it's called before Jetpack's admin_menu.
  70          add_action( 'admin_notices', array( 'Akismet_Admin', 'display_notice' ) );
  71          add_action( 'admin_enqueue_scripts', array( 'Akismet_Admin', 'load_resources' ) );
  72          add_action( 'activity_box_end', array( 'Akismet_Admin', 'dashboard_stats' ) );
  73          add_action( 'rightnow_end', array( 'Akismet_Admin', 'rightnow_stats' ) );
  74          add_action( 'manage_comments_nav', array( 'Akismet_Admin', 'check_for_spam_button' ) );
  75          add_action( 'admin_action_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) );
  76          add_action( 'wp_ajax_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) );
  77          add_action( 'wp_ajax_comment_author_deurl', array( 'Akismet_Admin', 'remove_comment_author_url' ) );
  78          add_action( 'wp_ajax_comment_author_reurl', array( 'Akismet_Admin', 'add_comment_author_url' ) );
  79          add_action( 'jetpack_auto_activate_akismet', array( 'Akismet_Admin', 'connect_jetpack_user' ) );
  80  
  81          add_filter( 'plugin_action_links', array( 'Akismet_Admin', 'plugin_action_links' ), 10, 2 );
  82          add_filter( 'comment_row_actions', array( 'Akismet_Admin', 'comment_row_action' ), 10, 2 );
  83  
  84          add_filter( 'plugin_action_links_' . plugin_basename( plugin_dir_path( __FILE__ ) . 'akismet.php' ), array( 'Akismet_Admin', 'admin_plugin_settings_link' ) );
  85  
  86          add_filter( 'wxr_export_skip_commentmeta', array( 'Akismet_Admin', 'exclude_commentmeta_from_export' ), 10, 3 );
  87  
  88          add_filter( 'all_plugins', array( 'Akismet_Admin', 'modify_plugin_description' ) );
  89  
  90          // priority=1 because we need ours to run before core's comment anonymizer runs, and that's registered at priority=10
  91          add_filter( 'wp_privacy_personal_data_erasers', array( 'Akismet_Admin', 'register_personal_data_eraser' ), 1 );
  92      }
  93  
  94  	public static function admin_init() {
  95          if ( get_option( 'Activated_Akismet' ) ) {
  96              delete_option( 'Activated_Akismet' );
  97              if ( ! headers_sent() ) {
  98                  $admin_url = self::get_page_url( 'init' );
  99                  wp_redirect( $admin_url );
 100              }
 101          }
 102  
 103          add_meta_box( 'akismet-status', __( 'Comment History', 'akismet' ), array( 'Akismet_Admin', 'comment_status_meta_box' ), 'comment', 'normal' );
 104  
 105          if ( function_exists( 'wp_add_privacy_policy_content' ) ) {
 106              wp_add_privacy_policy_content(
 107                  __( 'Akismet', 'akismet' ),
 108                  __( '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' )
 109              );
 110          }
 111      }
 112  
 113  	public static function admin_menu() {
 114          if ( class_exists( 'Jetpack' ) ) {
 115              add_action( 'jetpack_admin_menu', array( 'Akismet_Admin', 'load_menu' ) );
 116          } else {
 117              self::load_menu();
 118          }
 119      }
 120  
 121  	public static function admin_head() {
 122          if ( ! current_user_can( 'manage_options' ) ) {
 123              return;
 124          }
 125      }
 126  
 127  	public static function admin_plugin_settings_link( $links ) {
 128          $settings_link = '<a href="' . esc_url( self::get_page_url() ) . '">' . __( 'Settings', 'akismet' ) . '</a>';
 129          array_unshift( $links, $settings_link );
 130          return $links;
 131      }
 132  
 133  	public static function load_menu() {
 134          if ( class_exists( 'Jetpack' ) ) {
 135              $hook = add_submenu_page( 'jetpack', __( 'Akismet Anti-spam', 'akismet' ), __( 'Akismet Anti-spam', 'akismet' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
 136          } else {
 137              $hook = add_options_page( __( 'Akismet Anti-spam', 'akismet' ), __( 'Akismet Anti-spam', 'akismet' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
 138          }
 139  
 140          if ( $hook ) {
 141              add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) );
 142          }
 143      }
 144  
 145  	public static function load_resources() {
 146          global $hook_suffix;
 147  
 148          if ( in_array(
 149              $hook_suffix,
 150              apply_filters(
 151                  'akismet_admin_page_hook_suffixes',
 152                  array_merge(
 153                      array(
 154                          'index.php', // dashboard
 155                          'comment.php',
 156                          'post.php',
 157                          'settings_page_akismet-key-config',
 158                          'jetpack_page_akismet-key-config',
 159                      ),
 160                      self::$activation_banner_pages
 161                  )
 162              )
 163          ) ) {
 164              $akismet_css_path = is_rtl() ? '_inc/rtl/akismet-rtl.css' : '_inc/akismet.css';
 165              wp_register_style( 'akismet', plugin_dir_url( __FILE__ ) . $akismet_css_path, array(), self::get_asset_file_version( $akismet_css_path ) );
 166              wp_enqueue_style( 'akismet' );
 167  
 168              wp_register_style( 'akismet-font-inter', plugin_dir_url( __FILE__ ) . '_inc/fonts/inter.css', array(), self::get_asset_file_version( '_inc/fonts/inter.css' ) );
 169              wp_enqueue_style( 'akismet-font-inter' );
 170  
 171              $akismet_admin_css_path = is_rtl() ? '_inc/rtl/akismet-admin-rtl.css' : '_inc/akismet-admin.css';
 172              wp_register_style( 'akismet-admin', plugin_dir_url( __FILE__ ) . $akismet_admin_css_path, array(), self::get_asset_file_version( $akismet_admin_css_path ) );
 173              wp_enqueue_style( 'akismet-admin' );
 174  
 175              wp_add_inline_style( 'akismet-admin', self::get_inline_css() );
 176  
 177              wp_register_script( 'akismet.js', plugin_dir_url( __FILE__ ) . '_inc/akismet.js', array( 'jquery' ), self::get_asset_file_version( '_inc/akismet.js' ) );
 178              wp_enqueue_script( 'akismet.js' );
 179  
 180              wp_register_script( 'akismet-admin.js', plugin_dir_url( __FILE__ ) . '_inc/akismet-admin.js', array(), self::get_asset_file_version( '/_inc/akismet-admin.js' ) );
 181              wp_enqueue_script( 'akismet-admin.js' );
 182  
 183              $inline_js = array(
 184                  'comment_author_url_nonce' => wp_create_nonce( 'comment_author_url_nonce' ),
 185                  'strings'                  => array(
 186                      'Remove this URL' => __( 'Remove this URL', 'akismet' ),
 187                      'Removing...'     => __( 'Removing...', 'akismet' ),
 188                      'URL removed'     => __( 'URL removed', 'akismet' ),
 189                      '(undo)'          => __( '(undo)', 'akismet' ),
 190                      'Re-adding...'    => __( 'Re-adding...', 'akismet' ),
 191                  ),
 192              );
 193  
 194              if ( isset( $_GET['akismet_recheck'] ) && wp_verify_nonce( $_GET['akismet_recheck'], 'akismet_recheck' ) ) {
 195                  $inline_js['start_recheck'] = true;
 196              }
 197  
 198              if ( apply_filters( 'akismet_enable_mshots', true ) ) {
 199                  $inline_js['enable_mshots'] = true;
 200              }
 201  
 202              wp_localize_script( 'akismet.js', 'WPAkismet', $inline_js );
 203          }
 204      }
 205  
 206      /**
 207       * Add help to the Akismet page
 208       *
 209       * @return false if not the Akismet page
 210       */
 211  	public static function admin_help() {
 212          $current_screen = get_current_screen();
 213  
 214          // Screen Content
 215          if ( current_user_can( 'manage_options' ) ) {
 216              if ( ! Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) {
 217                  // setup page
 218                  $current_screen->add_help_tab(
 219                      array(
 220                          'id'      => 'overview',
 221                          'title'   => __( 'Overview', 'akismet' ),
 222                          'content' =>
 223                              '<p><strong>' . esc_html__( 'Akismet Setup', 'akismet' ) . '</strong></p>' .
 224                              '<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.', 'akismet' ) . '</p>' .
 225                              '<p>' . esc_html__( 'On this page, you are able to set up the Akismet plugin.', 'akismet' ) . '</p>',
 226                      )
 227                  );
 228  
 229                  $current_screen->add_help_tab(
 230                      array(
 231                          'id'      => 'setup-signup',
 232                          'title'   => __( 'New to Akismet', 'akismet' ),
 233                          'content' =>
 234                              '<p><strong>' . esc_html__( 'Akismet Setup', 'akismet' ) . '</strong></p>' .
 235                              '<p>' . esc_html__( 'You need to enter an API key to activate the Akismet service on your site.', 'akismet' ) . '</p>' .
 236                              /* translators: %s: a link to the signup page with the text 'Akismet.com'. */
 237                              '<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>',
 238                      )
 239                  );
 240  
 241                  $current_screen->add_help_tab(
 242                      array(
 243                          'id'      => 'setup-manual',
 244                          'title'   => __( 'Enter an API Key', 'akismet' ),
 245                          'content' =>
 246                              '<p><strong>' . esc_html__( 'Akismet Setup', 'akismet' ) . '</strong></p>' .
 247                              '<p>' . esc_html__( 'If you already have an API key', 'akismet' ) . '</p>' .
 248                              '<ol>' .
 249                              '<li>' . esc_html__( 'Copy and paste the API key into the text field.', 'akismet' ) . '</li>' .
 250                              '<li>' . esc_html__( 'Click the Use this Key button.', 'akismet' ) . '</li>' .
 251                              '</ol>',
 252                      )
 253                  );
 254              } elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) {
 255                  // stats page
 256                  $current_screen->add_help_tab(
 257                      array(
 258                          'id'      => 'overview',
 259                          'title'   => __( 'Overview', 'akismet' ),
 260                          'content' =>
 261                              '<p><strong>' . esc_html__( 'Akismet Stats', 'akismet' ) . '</strong></p>' .
 262                              '<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.', 'akismet' ) . '</p>' .
 263                              '<p>' . esc_html__( 'On this page, you are able to view stats on spam filtered on your site.', 'akismet' ) . '</p>',
 264                      )
 265                  );
 266              } else {
 267                  // configuration page
 268                  $current_screen->add_help_tab(
 269                      array(
 270                          'id'      => 'overview',
 271                          'title'   => __( 'Overview', 'akismet' ),
 272                          'content' =>
 273                              '<p><strong>' . esc_html__( 'Akismet Configuration', 'akismet' ) . '</strong></p>' .
 274                              '<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.', 'akismet' ) . '</p>' .
 275                              '<p>' . esc_html__( 'On this page, you are able to update your Akismet settings and view spam stats.', 'akismet' ) . '</p>',
 276                      )
 277                  );
 278  
 279                  $current_screen->add_help_tab(
 280                      array(
 281                          'id'      => 'settings',
 282                          'title'   => __( 'Settings', 'akismet' ),
 283                          'content' =>
 284                              '<p><strong>' . esc_html__( 'Akismet Configuration', 'akismet' ) . '</strong></p>' .
 285                              ( Akismet::predefined_api_key() ? '' : '<p><strong>' . esc_html__( 'API Key', 'akismet' ) . '</strong> - ' . esc_html__( 'Enter/remove an API key.', 'akismet' ) . '</p>' ) .
 286                              '<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>' .
 287                              '<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>',
 288                      )
 289                  );
 290  
 291                  if ( ! Akismet::predefined_api_key() ) {
 292                      $current_screen->add_help_tab(
 293                          array(
 294                              'id'      => 'account',
 295                              'title'   => __( 'Account', 'akismet' ),
 296                              'content' =>
 297                                  '<p><strong>' . esc_html__( 'Akismet Configuration', 'akismet' ) . '</strong></p>' .
 298                                  '<p><strong>' . esc_html__( 'Subscription Type', 'akismet' ) . '</strong> - ' . esc_html__( 'The Akismet subscription plan', 'akismet' ) . '</p>' .
 299                                  '<p><strong>' . esc_html__( 'Status', 'akismet' ) . '</strong> - ' . esc_html__( 'The subscription status - active, cancelled or suspended', 'akismet' ) . '</p>',
 300                          )
 301                      );
 302                  }
 303              }
 304          }
 305  
 306          // Help Sidebar
 307          $current_screen->set_help_sidebar(
 308              '<p><strong>' . esc_html__( 'For more information:', 'akismet' ) . '</strong></p>' .
 309              '<p><a href="https://akismet.com/faq/" target="_blank">' . esc_html__( 'Akismet FAQ', 'akismet' ) . '</a></p>' .
 310              '<p><a href="https://akismet.com/support/" target="_blank">' . esc_html__( 'Akismet Support', 'akismet' ) . '</a></p>'
 311          );
 312      }
 313  
 314  	public static function enter_api_key() {
 315          if ( ! current_user_can( 'manage_options' ) ) {
 316              die( __( 'Cheatin&#8217; uh?', 'akismet' ) );
 317          }
 318  
 319          if ( ! wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) ) {
 320              return false;
 321          }
 322  
 323          foreach ( array( 'akismet_strictness', 'akismet_show_user_comments_approved' ) as $option ) {
 324              update_option( $option, isset( $_POST[ $option ] ) && (int) $_POST[ $option ] == 1 ? '1' : '0' );
 325          }
 326  
 327          if ( ! empty( $_POST['akismet_comment_form_privacy_notice'] ) ) {
 328              self::set_form_privacy_notice_option( $_POST['akismet_comment_form_privacy_notice'] );
 329          } else {
 330              self::set_form_privacy_notice_option( 'hide' );
 331          }
 332  
 333          if ( Akismet::predefined_api_key() ) {
 334              return false; // shouldn't have option to save key if already defined
 335          }
 336  
 337          $new_key = preg_replace( '/[^a-f0-9]/i', '', $_POST['key'] );
 338          $old_key = Akismet::get_api_key();
 339  
 340          if ( empty( $new_key ) ) {
 341              if ( ! empty( $old_key ) ) {
 342                  delete_option( 'wordpress_api_key' );
 343                  self::$notices[] = 'new-key-empty';
 344              }
 345          } elseif ( $new_key != $old_key ) {
 346              self::save_key( $new_key );
 347          }
 348  
 349          return true;
 350      }
 351  
 352  	public static function save_key( $api_key ) {
 353          $key_status = Akismet::verify_key( $api_key );
 354  
 355          if ( $key_status == 'valid' ) {
 356              $akismet_user = self::get_akismet_user( $api_key );
 357  
 358              if ( $akismet_user ) {
 359                  if ( in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ) ) {
 360                      update_option( 'wordpress_api_key', $api_key );
 361                  }
 362  
 363                  if ( $akismet_user->status == 'active' ) {
 364                      self::$notices['status'] = 'new-key-valid';
 365                  } elseif ( $akismet_user->status == 'notice' ) {
 366                      self::$notices['status'] = $akismet_user;
 367                  } else {
 368                      self::$notices['status'] = $akismet_user->status;
 369                  }
 370              } else {
 371                  self::$notices['status'] = 'new-key-invalid';
 372              }
 373          } elseif ( in_array( $key_status, array( 'invalid', 'failed' ) ) ) {
 374              self::$notices['status'] = 'new-key-' . $key_status;
 375          }
 376      }
 377  
 378  	public static function dashboard_stats() {
 379          if ( did_action( 'rightnow_end' ) ) {
 380              return; // We already displayed this info in the "Right Now" section
 381          }
 382  
 383          if ( ! $count = get_option( 'akismet_spam_count' ) ) {
 384              return;
 385          }
 386  
 387          global $submenu;
 388  
 389          echo '<h3>' . esc_html( _x( 'Spam', 'comments', 'akismet' ) ) . '</h3>';
 390  
 391          echo '<p>' . sprintf(
 392              /* translators: 1: Akismet website URL, 2: Comments page URL, 3: Number of spam comments. */
 393              _n(
 394                  '<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comment</a>.',
 395                  '<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comments</a>.',
 396                  $count,
 397                  'akismet'
 398              ),
 399              'https://akismet.com/wordpress/',
 400              esc_url( add_query_arg( array( 'page' => 'akismet-admin' ), admin_url( isset( $submenu['edit-comments.php'] ) ? 'edit-comments.php' : 'edit.php' ) ) ),
 401              number_format_i18n( $count )
 402          ) . '</p>';
 403      }
 404  
 405      // WP 2.5+
 406  	public static function rightnow_stats() {
 407          if ( $count = get_option( 'akismet_spam_count' ) ) {
 408              $intro = sprintf(
 409              /* translators: 1: Akismet website URL, 2: Number of spam comments. */
 410                  _n(
 411                      '<a href="%1$s">Akismet</a> has protected your site from %2$s spam comment already. ',
 412                      '<a href="%1$s">Akismet</a> has protected your site from %2$s spam comments already. ',
 413                      $count,
 414                      'akismet'
 415                  ),
 416                  'https://akismet.com/wordpress/',
 417                  number_format_i18n( $count )
 418              );
 419          } else {
 420              /* translators: %s: Akismet website URL. */
 421              $intro = sprintf( __( '<a href="%s">Akismet</a> blocks spam from getting to your blog. ', 'akismet' ), 'https://akismet.com/wordpress/' );
 422          }
 423  
 424          $link = add_query_arg( array( 'comment_status' => 'spam' ), admin_url( 'edit-comments.php' ) );
 425  
 426          if ( $queue_count = self::get_spam_count() ) {
 427              $queue_text = sprintf(
 428              /* translators: 1: Number of comments, 2: Comments page URL. */
 429                  _n(
 430                      'There&#8217;s <a href="%2$s">%1$s comment</a> in your spam queue right now.',
 431                      'There are <a href="%2$s">%1$s comments</a> in your spam queue right now.',
 432                      $queue_count,
 433                      'akismet'
 434                  ),
 435                  number_format_i18n( $queue_count ),
 436                  esc_url( $link )
 437              );
 438          } else {
 439              /* translators: %s: Comments page URL. */
 440              $queue_text = sprintf( __( "There&#8217;s nothing in your <a href='%s'>spam queue</a> at the moment.", 'akismet' ), esc_url( $link ) );
 441          }
 442  
 443          $text = $intro . '<br />' . $queue_text;
 444          echo "<p class='akismet-right-now'>$text</p>\n";
 445      }
 446  
 447  	public static function check_for_spam_button( $comment_status ) {
 448          // The "Check for Spam" button should only appear when the page might be showing
 449          // a comment with comment_approved=0, which means an un-trashed, un-spammed,
 450          // not-yet-moderated comment.
 451          if ( 'all' != $comment_status && 'moderated' != $comment_status ) {
 452              return;
 453          }
 454  
 455          $link = '';
 456  
 457          $comments_count = wp_count_comments();
 458  
 459          echo '</div>';
 460          echo '<div class="alignleft actions">';
 461  
 462          $classes = array(
 463              'button-secondary',
 464              'checkforspam',
 465              'button-disabled',   // Disable button until the page is loaded
 466          );
 467  
 468          if ( $comments_count->moderated > 0 ) {
 469              $classes[] = 'enable-on-load';
 470  
 471              if ( ! Akismet::get_api_key() ) {
 472                  $link      = self::get_page_url();
 473                  $classes[] = 'ajax-disabled';
 474              }
 475          }
 476  
 477          echo '<a
 478                  class="' . esc_attr( implode( ' ', $classes ) ) . '"' .
 479              ( ! empty( $link ) ? ' href="' . esc_url( $link ) . '"' : '' ) .
 480              /* translators: The placeholder is for showing how much of the process has completed, as a percent. e.g., "Checking for Spam (40%)" */
 481              ' data-progress-label="' . esc_attr( __( 'Checking for Spam (%1$s%)', 'akismet' ) ) . '"
 482                  data-success-url="' . esc_attr(
 483                  remove_query_arg(
 484                      array( 'akismet_recheck', 'akismet_recheck_error' ),
 485                      add_query_arg(
 486                          array(
 487                              'akismet_recheck_complete' => 1,
 488                              'recheck_count'            => urlencode( '__recheck_count__' ),
 489                              'spam_count'               => urlencode( '__spam_count__' ),
 490                          )
 491                      )
 492                  )
 493              ) . '"
 494                  data-failure-url="' . esc_attr( remove_query_arg( array( 'akismet_recheck', 'akismet_recheck_complete' ), add_query_arg( array( 'akismet_recheck_error' => 1 ) ) ) ) . '"
 495                  data-pending-comment-count="' . esc_attr( $comments_count->moderated ) . '"
 496                  data-nonce="' . esc_attr( wp_create_nonce( 'akismet_check_for_spam' ) ) . '"
 497                  ' . ( ! in_array( 'ajax-disabled', $classes ) ? 'onclick="return false;"' : '' ) . '
 498                  >' . esc_html__( 'Check for Spam', 'akismet' ) . '</a>';
 499          echo '<span class="checkforspam-spinner"></span>';
 500      }
 501  
 502  	public static function recheck_queue() {
 503          global $wpdb;
 504  
 505          Akismet::fix_scheduled_recheck();
 506  
 507          if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) ) {
 508              return;
 509          }
 510  
 511          if ( ! wp_verify_nonce( $_POST['nonce'], 'akismet_check_for_spam' ) ) {
 512              wp_send_json(
 513                  array(
 514                      'error' => __( 'You don&#8217;t have permission to do that.', 'akismet' ),
 515                  )
 516              );
 517              return;
 518          }
 519  
 520          $result_counts = self::recheck_queue_portion( empty( $_POST['offset'] ) ? 0 : $_POST['offset'], empty( $_POST['limit'] ) ? 100 : $_POST['limit'] );
 521  
 522          if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
 523              wp_send_json(
 524                  array(
 525                      'counts' => $result_counts,
 526                  )
 527              );
 528          } else {
 529              $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' );
 530              wp_safe_redirect( $redirect_to );
 531              exit;
 532          }
 533      }
 534  
 535  	public static function recheck_queue_portion( $start = 0, $limit = 100 ) {
 536          global $wpdb;
 537  
 538          $paginate = '';
 539  
 540          if ( $limit <= 0 ) {
 541              $limit = 100;
 542          }
 543  
 544          if ( $start < 0 ) {
 545              $start = 0;
 546          }
 547  
 548          $moderation = $wpdb->get_col( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_approved = '0' LIMIT %d OFFSET %d", $limit, $start ) );
 549  
 550          $result_counts = array(
 551              'processed' => is_countable( $moderation ) ? count( $moderation ) : 0,
 552              'spam'      => 0,
 553              'ham'       => 0,
 554              'error'     => 0,
 555          );
 556  
 557          foreach ( $moderation as $comment_id ) {
 558              $api_response = Akismet::recheck_comment( $comment_id, 'recheck_queue' );
 559  
 560              if ( 'true' === $api_response ) {
 561                  ++$result_counts['spam'];
 562              } elseif ( 'false' === $api_response ) {
 563                  ++$result_counts['ham'];
 564              } else {
 565                  ++$result_counts['error'];
 566              }
 567          }
 568  
 569          return $result_counts;
 570      }
 571  
 572      // Adds an 'x' link next to author URLs, clicking will remove the author URL and show an undo link
 573  	public static function remove_comment_author_url() {
 574          if ( ! empty( $_POST['id'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) {
 575              $comment_id = intval( $_POST['id'] );
 576              $comment    = get_comment( $comment_id, ARRAY_A );
 577              if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) {
 578                  $comment['comment_author_url'] = '';
 579                  do_action( 'comment_remove_author_url' );
 580                  print( wp_update_comment( $comment ) );
 581                  die();
 582              }
 583          }
 584      }
 585  
 586  	public static function add_comment_author_url() {
 587          if ( ! empty( $_POST['id'] ) && ! empty( $_POST['url'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) {
 588              $comment_id = intval( $_POST['id'] );
 589              $comment    = get_comment( $comment_id, ARRAY_A );
 590              if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) {
 591                  $comment['comment_author_url'] = esc_url( $_POST['url'] );
 592                  do_action( 'comment_add_author_url' );
 593                  print( wp_update_comment( $comment ) );
 594                  die();
 595              }
 596          }
 597      }
 598  
 599  	public static function comment_row_action( $a, $comment ) {
 600          $akismet_result = get_comment_meta( $comment->comment_ID, 'akismet_result', true );
 601          if ( ! $akismet_result && get_comment_meta( $comment->comment_ID, 'akismet_skipped', true ) ) {
 602              $akismet_result = 'skipped'; // Akismet chose to skip the comment-check request.
 603          }
 604  
 605          $akismet_error  = get_comment_meta( $comment->comment_ID, 'akismet_error', true );
 606          $user_result    = get_comment_meta( $comment->comment_ID, 'akismet_user_result', true );
 607          $comment_status = wp_get_comment_status( $comment->comment_ID );
 608          $desc           = null;
 609          if ( $akismet_error ) {
 610              $desc = __( 'Awaiting spam check', 'akismet' );
 611          } elseif ( ! $user_result || $user_result == $akismet_result ) {
 612              // Show the original Akismet result if the user hasn't overridden it, or if their decision was the same
 613              if ( $akismet_result == 'true' && $comment_status != 'spam' && $comment_status != 'trash' ) {
 614                  $desc = __( 'Flagged as spam by Akismet', 'akismet' );
 615              } elseif ( $akismet_result == 'false' && $comment_status == 'spam' ) {
 616                  $desc = __( 'Cleared by Akismet', 'akismet' );
 617              }
 618          } else {
 619              $who = get_comment_meta( $comment->comment_ID, 'akismet_user', true );
 620              if ( $user_result == 'true' ) {
 621                  /* translators: %s: Username. */
 622                  $desc = sprintf( __( 'Flagged as spam by %s', 'akismet' ), $who );
 623              } else {
 624                  /* translators: %s: Username. */
 625                  $desc = sprintf( __( 'Un-spammed by %s', 'akismet' ), $who );
 626              }
 627          }
 628  
 629          // add a History item to the hover links, just after Edit
 630          if ( $akismet_result && is_array( $a ) ) {
 631              $b = array();
 632              foreach ( $a as $k => $item ) {
 633                  $b[ $k ] = $item;
 634                  if (
 635                      $k == 'edit'
 636                      || $k == 'unspam'
 637                  ) {
 638                      $b['history'] = '<a href="comment.php?action=editcomment&amp;c=' . $comment->comment_ID . '#akismet-status" title="' . esc_attr__( 'View comment history', 'akismet' ) . '"> ' . esc_html__( 'History', 'akismet' ) . '</a>';
 639                  }
 640              }
 641  
 642              $a = $b;
 643          }
 644  
 645          if ( $desc ) {
 646              echo '<span class="akismet-status" commentid="' . $comment->comment_ID . '"><a href="comment.php?action=editcomment&amp;c=' . $comment->comment_ID . '#akismet-status" title="' . esc_attr__( 'View comment history', 'akismet' ) . '">' . esc_html( $desc ) . '</a></span>';
 647          }
 648  
 649          $show_user_comments_option = get_option( 'akismet_show_user_comments_approved' );
 650  
 651          if ( $show_user_comments_option === false ) {
 652              // Default to active if the user hasn't made a decision.
 653              $show_user_comments_option = '1';
 654          }
 655  
 656          $show_user_comments = apply_filters( 'akismet_show_user_comments_approved', $show_user_comments_option );
 657          $show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; // option used to be saved as 'false' / 'true'
 658  
 659          if ( $show_user_comments ) {
 660              $comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url );
 661              $comment_count = intval( $comment_count );
 662              echo '<span class="akismet-user-comment-count" commentid="' . $comment->comment_ID . '" style="display:none;"><br><span class="akismet-user-comment-counts">';
 663              /* translators: %s: Number of comments. */
 664              echo sprintf( esc_html( _n( '%s approved', '%s approved', $comment_count, 'akismet' ) ), number_format_i18n( $comment_count ) ) . '</span></span>';
 665          }
 666  
 667          return $a;
 668      }
 669  
 670  	public static function comment_status_meta_box( $comment ) {
 671          $history = Akismet::get_comment_history( $comment->comment_ID );
 672  
 673          if ( $history ) {
 674              foreach ( $history as $row ) {
 675                  $message = '';
 676  
 677                  if ( ! empty( $row['message'] ) ) {
 678                      // Old versions of Akismet stored the message as a literal string in the commentmeta.
 679                      // New versions don't do that for two reasons:
 680                      // 1) Save space.
 681                      // 2) The message can be translated into the current language of the blog, not stuck
 682                      // in the language of the blog when the comment was made.
 683                      $message = esc_html( $row['message'] );
 684                  } elseif ( ! empty( $row['event'] ) ) {
 685                      // If possible, use a current translation.
 686                      switch ( $row['event'] ) {
 687                          case 'recheck-spam':
 688                              $message = esc_html( __( 'Akismet re-checked and caught this comment as spam.', 'akismet' ) );
 689                              break;
 690                          case 'check-spam':
 691                              $message = esc_html( __( 'Akismet caught this comment as spam.', 'akismet' ) );
 692                              break;
 693                          case 'recheck-ham':
 694                              $message = esc_html( __( 'Akismet re-checked and cleared this comment.', 'akismet' ) );
 695                              break;
 696                          case 'check-ham':
 697                              $message = esc_html( __( 'Akismet cleared this comment.', 'akismet' ) );
 698                              break;
 699                          case 'wp-blacklisted':
 700                          case 'wp-disallowed':
 701                              $message = sprintf(
 702                              /* translators: The placeholder is a WordPress PHP function name. */
 703                                  esc_html( __( 'Comment was caught by %s.', 'akismet' ) ),
 704                                  function_exists( 'wp_check_comment_disallowed_list' ) ? '<code>wp_check_comment_disallowed_list</code>' : '<code>wp_blacklist_check</code>'
 705                              );
 706                              break;
 707                          case 'report-spam':
 708                              if ( isset( $row['user'] ) ) {
 709                                  /* translators: The placeholder is a username. */
 710                                  $message = esc_html( sprintf( __( '%s reported this comment as spam.', 'akismet' ), $row['user'] ) );
 711                              } elseif ( ! $message ) {
 712                                  $message = esc_html( __( 'This comment was reported as spam.', 'akismet' ) );
 713                              }
 714                              break;
 715                          case 'report-ham':
 716                              if ( isset( $row['user'] ) ) {
 717                                  /* translators: The placeholder is a username. */
 718                                  $message = esc_html( sprintf( __( '%s reported this comment as not spam.', 'akismet' ), $row['user'] ) );
 719                              } elseif ( ! $message ) {
 720                                  $message = esc_html( __( 'This comment was reported as not spam.', 'akismet' ) );
 721                              }
 722                              break;
 723                          case 'cron-retry-spam':
 724                              $message = esc_html( __( 'Akismet caught this comment as spam during an automatic retry.', 'akismet' ) );
 725                              break;
 726                          case 'cron-retry-ham':
 727                              $message = esc_html( __( 'Akismet cleared this comment during an automatic retry.', 'akismet' ) );
 728                              break;
 729                          case 'check-error':
 730                              if ( isset( $row['meta'], $row['meta']['response'] ) ) {
 731                                  /* translators: The placeholder is an error response returned by the API server. */
 732                                  $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>' );
 733                              } else {
 734                                  $message = esc_html( __( 'Akismet was unable to check this comment but will automatically retry later.', 'akismet' ) );
 735                              }
 736                              break;
 737                          case 'recheck-error':
 738                              if ( isset( $row['meta'], $row['meta']['response'] ) ) {
 739                                  /* translators: The placeholder is an error response returned by the API server. */
 740                                  $message = sprintf( esc_html( __( 'Akismet was unable to recheck this comment (response: %s).', 'akismet' ) ), '<code>' . esc_html( $row['meta']['response'] ) . '</code>' );
 741                              } else {
 742                                  $message = esc_html( __( 'Akismet was unable to recheck this comment.', 'akismet' ) );
 743                              }
 744                              break;
 745                          case 'webhook-spam':
 746                              $message = esc_html( __( 'Akismet caught this comment as spam and updated its status via webhook.', 'akismet' ) );
 747                              break;
 748                          case 'webhook-ham':
 749                              $message = esc_html( __( 'Akismet cleared this comment and updated its status via webhook.', 'akismet' ) );
 750                              break;
 751                          case 'webhook-spam-noaction':
 752                              $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' ) );
 753                              break;
 754                          case 'webhook-ham-noaction':
 755                              $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' ) );
 756                              break;
 757                          case 'akismet-skipped':
 758                              $message = esc_html( __( 'This comment was not sent to Akismet when it was submitted because it was caught by something else.', 'akismet' ) );
 759                              break;
 760                          case 'akismet-skipped-disallowed':
 761                              $message = esc_html( __( 'This comment was not sent to Akismet when it was submitted because it was caught by the comment disallowed list.', 'akismet' ) );
 762                              break;
 763                          default:
 764                              if ( preg_match( '/^status-changed/', $row['event'] ) ) {
 765                                  // Half of these used to be saved without the dash after 'status-changed'.
 766                                  // See https://plugins.trac.wordpress.org/changeset/1150658/akismet/trunk
 767                                  $new_status = preg_replace( '/^status-changed-?/', '', $row['event'] );
 768                                  /* translators: The placeholder is a short string (like 'spam' or 'approved') denoting the new comment status. */
 769                                  $message = sprintf( esc_html( __( 'Comment status was changed to %s', 'akismet' ) ), '<code>' . esc_html( $new_status ) . '</code>' );
 770                              } elseif ( preg_match( '/^status-/', $row['event'] ) ) {
 771                                  $new_status = preg_replace( '/^status-/', '', $row['event'] );
 772  
 773                                  if ( isset( $row['user'] ) ) {
 774                                      /* translators: %1$s is a username; %2$s is a short string (like 'spam' or 'approved') denoting the new comment status. */
 775                                      $message = sprintf( esc_html( __( '%1$s changed the comment status to %2$s.', 'akismet' ) ), $row['user'], '<code>' . esc_html( $new_status ) . '</code>' );
 776                                  }
 777                              }
 778                              break;
 779                      }
 780                  }
 781  
 782                  if ( ! empty( $message ) ) {
 783                      echo '<p>';
 784  
 785                      if ( isset( $row['time'] ) ) {
 786                          $time = gmdate( 'D d M Y @ h:i:s a', (int) $row['time'] ) . ' GMT';
 787  
 788                          /* translators: The placeholder is an amount of time, like "7 seconds" or "3 days" returned by the function human_time_diff(). */
 789                          $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>';
 790  
 791                          printf(
 792                          /* 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." */
 793                              esc_html( __( '%1$s - %2$s', 'akismet' ) ),
 794                              // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 795                              $time_html,
 796                              // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 797                              $message
 798                          ); // esc_html() is done above so that we can use HTML in $message.
 799                      } else {
 800                          // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 801                          echo $message; // esc_html() is done above so that we can use HTML in $message.
 802                      }
 803  
 804                      echo '</p>';
 805                  }
 806              }
 807          } else {
 808              echo '<p>';
 809              echo esc_html( __( 'No comment history.', 'akismet' ) );
 810              echo '</p>';
 811          }
 812      }
 813  
 814  	public static function plugin_action_links( $links, $file ) {
 815          if ( $file == plugin_basename( plugin_dir_url( __FILE__ ) . '/akismet.php' ) ) {
 816              $links[] = '<a href="' . esc_url( self::get_page_url() ) . '">' . esc_html__( 'Settings', 'akismet' ) . '</a>';
 817          }
 818  
 819          return $links;
 820      }
 821  
 822      // Total spam in queue
 823      // get_option( 'akismet_spam_count' ) is the total caught ever
 824  	public static function get_spam_count( $type = false ) {
 825          global $wpdb;
 826  
 827          if ( ! $type ) { // total
 828              $count = wp_cache_get( 'akismet_spam_count', 'widget' );
 829              if ( false === $count ) {
 830                  $count = wp_count_comments();
 831                  $count = $count->spam;
 832                  wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 );
 833              }
 834              return $count;
 835          } elseif ( 'comments' == $type || 'comment' == $type ) { // comments
 836              $type = '';
 837          }
 838  
 839          return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) );
 840      }
 841  
 842      // Check connectivity between the WordPress blog and Akismet's servers.
 843      // 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).
 844  	public static function check_server_ip_connectivity() {
 845  
 846          $servers = $ips = array();
 847  
 848          // Some web hosts may disable this function
 849          if ( function_exists( 'gethostbynamel' ) ) {
 850  
 851              $ips = gethostbynamel( 'rest.akismet.com' );
 852              if ( $ips && is_array( $ips ) && count( $ips ) ) {
 853                  $api_key = Akismet::get_api_key();
 854  
 855                  foreach ( $ips as $ip ) {
 856                      $response = Akismet::verify_key( $api_key, $ip );
 857                      // even if the key is invalid, at least we know we have connectivity
 858                      if ( $response == 'valid' || $response == 'invalid' ) {
 859                          $servers[ $ip ] = 'connected';
 860                      } else {
 861                          $servers[ $ip ] = $response ? $response : 'unable to connect';
 862                      }
 863                  }
 864              }
 865          }
 866  
 867          return $servers;
 868      }
 869  
 870      // Simpler connectivity check
 871  	public static function check_server_connectivity( $cache_timeout = 86400 ) {
 872  
 873          $debug                        = array();
 874          $debug['PHP_VERSION']         = PHP_VERSION;
 875          $debug['WORDPRESS_VERSION']   = $GLOBALS['wp_version'];
 876          $debug['AKISMET_VERSION']     = AKISMET_VERSION;
 877          $debug['AKISMET__PLUGIN_DIR'] = AKISMET__PLUGIN_DIR;
 878          $debug['SITE_URL']            = site_url();
 879          $debug['HOME_URL']            = home_url();
 880  
 881          $servers = get_option( 'akismet_available_servers' );
 882          if ( ( time() - get_option( 'akismet_connectivity_time' ) < $cache_timeout ) && $servers !== false ) {
 883              $servers = self::check_server_ip_connectivity();
 884              update_option( 'akismet_available_servers', $servers );
 885              update_option( 'akismet_connectivity_time', time() );
 886          }
 887  
 888          if ( wp_http_supports( array( 'ssl' ) ) ) {
 889              $response = wp_remote_get( 'https://rest.akismet.com/1.1/test' );
 890          } else {
 891              $response = wp_remote_get( 'http://rest.akismet.com/1.1/test' );
 892          }
 893  
 894          $debug['gethostbynamel']  = function_exists( 'gethostbynamel' ) ? 'exists' : 'not here';
 895          $debug['Servers']         = $servers;
 896          $debug['Test Connection'] = $response;
 897  
 898          Akismet::log( $debug );
 899  
 900          if ( $response && 'connected' == wp_remote_retrieve_body( $response ) ) {
 901              return true;
 902          }
 903  
 904          return false;
 905      }
 906  
 907      // Check the server connectivity and store the available servers in an option.
 908  	public static function get_server_connectivity( $cache_timeout = 86400 ) {
 909          return self::check_server_connectivity( $cache_timeout );
 910      }
 911  
 912      /**
 913       * Find out whether any comments in the Pending queue have not yet been checked by Akismet.
 914       *
 915       * @return bool
 916       */
 917  	public static function are_any_comments_waiting_to_be_checked() {
 918          return ! ! get_comments(
 919              array(
 920                  // Exclude comments that are not pending. This would happen if someone manually approved or spammed a comment
 921                  // that was waiting to be checked. The akismet_error meta entry will eventually be removed by the cron recheck job.
 922                  'status'   => 'hold',
 923  
 924                  // This is the commentmeta that is saved when a comment couldn't be checked.
 925                  'meta_key' => 'akismet_error',
 926  
 927                  // We only need to know whether at least one comment is waiting for a check.
 928                  'number'   => 1,
 929              )
 930          );
 931      }
 932  
 933  	public static function get_page_url( $page = 'config' ) {
 934  
 935          $args = array( 'page' => 'akismet-key-config' );
 936  
 937          if ( $page == 'stats' ) {
 938              $args = array(
 939                  'page' => 'akismet-key-config',
 940                  'view' => 'stats',
 941              );
 942          } elseif ( $page == 'delete_key' ) {
 943              $args = array(
 944                  'page'     => 'akismet-key-config',
 945                  'view'     => 'start',
 946                  'action'   => 'delete-key',
 947                  '_wpnonce' => wp_create_nonce( self::NONCE ),
 948              );
 949          } elseif ( $page === 'init' ) {
 950              $args = array(
 951                  'page' => 'akismet-key-config',
 952                  'view' => 'start',
 953              );
 954          }
 955  
 956          return add_query_arg( $args, menu_page_url( 'akismet-key-config', false ) );
 957      }
 958  
 959  	public static function get_akismet_user( $api_key ) {
 960          $akismet_user = false;
 961  
 962          $request_args = array(
 963              'key'  => $api_key,
 964              'blog' => get_option( 'home' ),
 965          );
 966  
 967          $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-subscription' );
 968  
 969          $subscription_verification = Akismet::http_post( Akismet::build_query( $request_args ), 'get-subscription' );
 970  
 971          if ( ! empty( $subscription_verification[1] ) ) {
 972              if ( 'invalid' !== $subscription_verification[1] ) {
 973                  $akismet_user = json_decode( $subscription_verification[1] );
 974              }
 975          }
 976  
 977          return $akismet_user;
 978      }
 979  
 980  	public static function get_stats( $api_key ) {
 981          $stat_totals = array();
 982  
 983          foreach ( array( '6-months', 'all' ) as $interval ) {
 984              $request_args = array(
 985                  'blog' => get_option( 'home' ),
 986                  'key'  => $api_key,
 987                  'from' => $interval,
 988              );
 989  
 990              $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' );
 991  
 992              $response = Akismet::http_post( Akismet::build_query( $request_args ), 'get-stats' );
 993  
 994              if ( ! empty( $response[1] ) ) {
 995                  $data = json_decode( $response[1] );
 996                  /*
 997                   * The json decoded response should be an object. If it's not an object, something's wrong, and the data
 998                   * shouldn't be added to the stats_totals array.
 999                   */
1000                  if ( is_object( $data ) ) {
1001                      $stat_totals[ $interval ] = $data;
1002                  }
1003              }
1004          }
1005  
1006          return $stat_totals;
1007      }
1008  
1009  	public static function verify_wpcom_key( $api_key, $user_id, $extra = array() ) {
1010          $request_args = array_merge(
1011              array(
1012                  'user_id'          => $user_id,
1013                  'api_key'          => $api_key,
1014                  'get_account_type' => 'true',
1015              ),
1016              $extra
1017          );
1018  
1019          $request_args = apply_filters( 'akismet_request_args', $request_args, 'verify-wpcom-key' );
1020  
1021          $akismet_account = Akismet::http_post( Akismet::build_query( $request_args ), 'verify-wpcom-key' );
1022  
1023          if ( ! empty( $akismet_account[1] ) ) {
1024              $akismet_account = json_decode( $akismet_account[1] );
1025          }
1026  
1027          Akismet::log( compact( 'akismet_account' ) );
1028  
1029          return $akismet_account;
1030      }
1031  
1032  	public static function connect_jetpack_user() {
1033  
1034          if ( $jetpack_user = self::get_jetpack_user() ) {
1035              if ( isset( $jetpack_user['user_id'] ) && isset( $jetpack_user['api_key'] ) ) {
1036                  $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'], array( 'action' => 'connect_jetpack_user' ) );
1037  
1038                  if ( is_object( $akismet_user ) ) {
1039                      self::save_key( $akismet_user->api_key );
1040                      return in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) );
1041                  }
1042              }
1043          }
1044  
1045          return false;
1046      }
1047  
1048  	public static function display_alert() {
1049          Akismet::view(
1050              'notice',
1051              array(
1052                  'type' => 'alert',
1053                  'code' => (int) get_option( 'akismet_alert_code' ),
1054                  'msg'  => get_option( 'akismet_alert_msg' ),
1055              )
1056          );
1057      }
1058  
1059  	public static function get_usage_limit_alert_data() {
1060          return array(
1061              'type'                => 'usage-limit',
1062              'code'                => (int) get_option( 'akismet_alert_code' ),
1063              'msg'                 => get_option( 'akismet_alert_msg' ),
1064              'api_calls'           => get_option( 'akismet_alert_api_calls' ),
1065              'usage_limit'         => get_option( 'akismet_alert_usage_limit' ),
1066              'upgrade_plan'        => get_option( 'akismet_alert_upgrade_plan' ),
1067              'upgrade_url'         => get_option( 'akismet_alert_upgrade_url' ),
1068              'upgrade_type'        => get_option( 'akismet_alert_upgrade_type' ),
1069              'upgrade_via_support' => get_option( 'akismet_alert_upgrade_via_support' ) === 'true',
1070          );
1071      }
1072  
1073  	public static function display_usage_limit_alert() {
1074          Akismet::view( 'notice', self::get_usage_limit_alert_data() );
1075      }
1076  
1077  	public static function display_spam_check_warning() {
1078          Akismet::fix_scheduled_recheck();
1079  
1080          if ( wp_next_scheduled( 'akismet_schedule_cron_recheck' ) > time() && self::are_any_comments_waiting_to_be_checked() ) {
1081              /*
1082               * The 'akismet_display_cron_disabled_notice' filter can be used to control whether the WP-Cron disabled notice is displayed.
1083               */
1084              if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON && apply_filters( 'akismet_display_cron_disabled_notice', true ) ) {
1085                  Akismet::view( 'notice', array( 'type' => 'spam-check-cron-disabled' ) );
1086              } else {
1087                  /* translators: The Akismet configuration page URL. */
1088                  $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() ) ) );
1089                  Akismet::view(
1090                      'notice',
1091                      array(
1092                          'type'      => 'spam-check',
1093                          'link_text' => $link_text,
1094                      )
1095                  );
1096              }
1097          }
1098      }
1099  
1100  	public static function display_api_key_warning() {
1101          Akismet::view( 'notice', array( 'type' => 'plugin' ) );
1102      }
1103  
1104  	public static function display_page() {
1105          if ( ! Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) {
1106              self::display_start_page();
1107          } elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) {
1108              self::display_stats_page();
1109          } else {
1110              self::display_configuration_page();
1111          }
1112      }
1113  
1114  	public static function display_start_page() {
1115          if ( isset( $_GET['action'] ) ) {
1116              if ( $_GET['action'] == 'delete-key' ) {
1117                  if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], self::NONCE ) ) {
1118                      delete_option( 'wordpress_api_key' );
1119                  }
1120              }
1121          }
1122  
1123          $api_key               = Akismet::get_api_key();
1124          $existing_key_is_valid = ! (
1125              self::get_notice_by_key( 'status' ) === self::NOTICE_EXISTING_KEY_INVALID
1126          );
1127  
1128          if ( $api_key && $existing_key_is_valid ) {
1129              self::display_configuration_page();
1130              return;
1131          }
1132  
1133          // the user can choose to auto connect their API key by clicking a button on the akismet done page
1134          // if jetpack, get verified api key by using connected wpcom user id
1135          // if no jetpack, get verified api key by using an akismet token
1136  
1137          $akismet_user = false;
1138  
1139          if ( isset( $_GET['token'] ) && preg_match( '/^(\d+)-[0-9a-f]{20}$/', $_GET['token'] ) ) {
1140              $akismet_user = self::verify_wpcom_key( '', '', array( 'token' => $_GET['token'] ) );
1141          }
1142  
1143          if ( false === $akismet_user ) {
1144              $jetpack_user = self::get_jetpack_user();
1145  
1146              if ( is_array( $jetpack_user ) ) {
1147                  $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'] );
1148              }
1149          }
1150  
1151          if ( isset( $_GET['action'] ) ) {
1152              if ( $_GET['action'] == 'save-key' ) {
1153                  if ( is_object( $akismet_user ) ) {
1154                      self::save_key( $akismet_user->api_key );
1155                      self::display_configuration_page();
1156                      return;
1157                  }
1158              }
1159          }
1160  
1161          Akismet::view( 'start', compact( 'akismet_user' ) );
1162  
1163          /*
1164          // To see all variants when testing.
1165          $akismet_user->status = 'no-sub';
1166          Akismet::view( 'start', compact( 'akismet_user' ) );
1167          $akismet_user->status = 'cancelled';
1168          Akismet::view( 'start', compact( 'akismet_user' ) );
1169          $akismet_user->status = 'suspended';
1170          Akismet::view( 'start', compact( 'akismet_user' ) );
1171          $akismet_user->status = 'other';
1172          Akismet::view( 'start', compact( 'akismet_user' ) );
1173          $akismet_user = false;
1174          */
1175      }
1176  
1177  	public static function display_stats_page() {
1178          Akismet::view( 'stats' );
1179      }
1180  
1181  	public static function display_configuration_page() {
1182          $api_key      = Akismet::get_api_key();
1183          $akismet_user = self::get_akismet_user( $api_key );
1184  
1185          if ( ! $akismet_user ) {
1186              // This could happen if the user's key became invalid after it was previously valid and successfully set up.
1187              self::$notices['status'] = self::NOTICE_EXISTING_KEY_INVALID;
1188              self::display_start_page();
1189              return;
1190          }
1191  
1192          $stat_totals = self::get_stats( $api_key );
1193  
1194          // If unset, create the new strictness option using the old discard option to determine its default.
1195          // If the old option wasn't set, default to discarding the blatant spam.
1196          if ( get_option( 'akismet_strictness' ) === false ) {
1197              add_option( 'akismet_strictness', ( get_option( 'akismet_discard_month' ) === 'false' ? '0' : '1' ) );
1198          }
1199  
1200          // Sync the local "Total spam blocked" count with the authoritative count from the server.
1201          if ( isset( $stat_totals['all'], $stat_totals['all']->spam ) ) {
1202              update_option( 'akismet_spam_count', $stat_totals['all']->spam );
1203          }
1204  
1205          $notices = array();
1206  
1207          if ( empty( self::$notices ) ) {
1208              if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) {
1209  
1210                  $time_saved = false;
1211  
1212                  if ( $stat_totals['all']->time_saved > 1800 ) {
1213                      $total_in_minutes = round( $stat_totals['all']->time_saved / 60 );
1214                      $total_in_hours   = round( $total_in_minutes / 60 );
1215                      $total_in_days    = round( $total_in_hours / 8 );
1216                      $cleaning_up      = __( 'Cleaning up spam takes time.', 'akismet' );
1217  
1218                      if ( $total_in_days > 1 ) {
1219                          /* translators: %s: Number of days. */
1220                          $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 ) );
1221                      } elseif ( $total_in_hours > 1 ) {
1222                          /* translators: %s: Number of hours. */
1223                          $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 );
1224                      } elseif ( $total_in_minutes >= 30 ) {
1225                          /* translators: %s: Number of minutes. */
1226                          $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 );
1227                      }
1228                  }
1229  
1230                  $notices[] = array(
1231                      'type'       => 'active-notice',
1232                      'time_saved' => $time_saved,
1233                  );
1234              }
1235          }
1236  
1237          if ( ! Akismet::predefined_api_key() && ! isset( self::$notices['status'] ) && in_array( $akismet_user->status, array( 'cancelled', 'suspended', 'missing', 'no-sub' ) ) ) {
1238              $notices[] = array( 'type' => $akismet_user->status );
1239          }
1240  
1241          $alert_code = get_option( 'akismet_alert_code' );
1242          if ( isset( Akismet::$limit_notices[ $alert_code ] ) ) {
1243              $notices[] = self::get_usage_limit_alert_data();
1244          } elseif ( $alert_code > 0 ) {
1245              $notices[] = array(
1246                  'type' => 'alert',
1247                  'code' => (int) get_option( 'akismet_alert_code' ),
1248                  'msg'  => get_option( 'akismet_alert_msg' ),
1249              );
1250          }
1251  
1252          /*
1253           *  To see all variants when testing.
1254           *
1255           *  You may also want to comment out the akismet_view_arguments filter in Akismet::view()
1256           *  to ensure that you can see all of the notices (e.g. suspended, active-notice).
1257          */
1258          // $notices[] = array( 'type' => 'active-notice', 'time_saved' => 'Cleaning up spam takes time. Akismet has saved you 1 minute!' );
1259          // $notices[] = array( 'type' => 'plugin' );
1260          // $notices[] = array( 'type' => 'notice', 'notice_header' => 'This is the notice header.', 'notice_text' => 'This is the notice text.' );
1261          // $notices[] = array( 'type' => 'missing-functions' );
1262          // $notices[] = array( 'type' => 'servers-be-down' );
1263          // $notices[] = array( 'type' => 'active-dunning' );
1264          // $notices[] = array( 'type' => 'cancelled' );
1265          // $notices[] = array( 'type' => 'suspended' );
1266          // $notices[] = array( 'type' => 'missing' );
1267          // $notices[] = array( 'type' => 'no-sub' );
1268          // $notices[] = array( 'type' => 'new-key-valid' );
1269          // $notices[] = array( 'type' => 'new-key-invalid' );
1270          // $notices[] = array( 'type' => 'existing-key-invalid' );
1271          // $notices[] = array( 'type' => 'new-key-failed' );
1272          // $notices[] = array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_plan' => 'Enterprise', 'upgrade_url' => 'https://akismet.com/account/', 'code' => 10502 );
1273          // $notices[] = array( 'type' => 'spam-check', 'link_text' => 'Link text.' );
1274          // $notices[] = array( 'type' => 'spam-check-cron-disabled' );
1275          // $notices[] = array( 'type' => 'alert', 'code' => 123 );
1276          // $notices[] = array( 'type' => 'alert', 'code' => Akismet::ALERT_CODE_COMMERCIAL );
1277  
1278          Akismet::log( compact( 'stat_totals', 'akismet_user' ) );
1279          Akismet::view( 'config', compact( 'api_key', 'akismet_user', 'stat_totals', 'notices' ) );
1280      }
1281  
1282  	public static function display_notice() {
1283          global $hook_suffix;
1284  
1285          if ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config' ) ) ) {
1286              // This page manages the notices and puts them inline where they make sense.
1287              return;
1288          }
1289  
1290          // To see notice variants while testing.
1291          // Akismet::view( 'notice', array( 'type' => 'spam-check-cron-disabled' ) );
1292          // Akismet::view( 'notice', array( 'type' => 'spam-check' ) );
1293          // Akismet::view( 'notice', array( 'type' => 'alert', 'code' => 123, 'msg' => 'Message' ) );
1294  
1295          if ( in_array( $hook_suffix, array( 'edit-comments.php' ) ) && (int) get_option( 'akismet_alert_code' ) > 0 ) {
1296              Akismet::verify_key( Akismet::get_api_key() ); // verify that the key is still in alert state
1297  
1298              $alert_code = get_option( 'akismet_alert_code' );
1299              if ( isset( Akismet::$limit_notices[ $alert_code ] ) ) {
1300                  self::display_usage_limit_alert();
1301              } elseif ( $alert_code > 0 ) {
1302                  self::display_alert();
1303              }
1304          } elseif ( in_array( $hook_suffix, self::$activation_banner_pages, true ) && ! Akismet::get_api_key() ) {
1305              // Show the "Set Up Akismet" banner on the comments and plugin pages if no API key has been set.
1306              self::display_api_key_warning();
1307          } elseif ( $hook_suffix == 'edit-comments.php' && wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) {
1308              self::display_spam_check_warning();
1309          }
1310  
1311          if ( isset( $_GET['akismet_recheck_complete'] ) ) {
1312              $recheck_count = (int) $_GET['recheck_count'];
1313              $spam_count    = (int) $_GET['spam_count'];
1314  
1315              if ( $recheck_count === 0 ) {
1316                  $message = __( 'There were no comments to check. Akismet will only check comments awaiting moderation.', 'akismet' );
1317              } else {
1318                  /* translators: %s: Number of comments. */
1319                  $message  = sprintf( _n( 'Akismet checked %s comment.', 'Akismet checked %s comments.', $recheck_count, 'akismet' ), number_format( $recheck_count ) );
1320                  $message .= ' ';
1321  
1322                  if ( $spam_count === 0 ) {
1323                      $message .= __( 'No comments were caught as spam.', 'akismet' );
1324                  } else {
1325                      /* translators: %s: Number of comments. */
1326                      $message .= sprintf( _n( '%s comment was caught as spam.', '%s comments were caught as spam.', $spam_count, 'akismet' ), number_format( $spam_count ) );
1327                  }
1328              }
1329  
1330              echo '<div class="notice notice-success"><p>' . esc_html( $message ) . '</p></div>';
1331          } elseif ( isset( $_GET['akismet_recheck_error'] ) ) {
1332              echo '<div class="notice notice-error"><p>' . esc_html( __( 'Akismet could not recheck your comments for spam.', 'akismet' ) ) . '</p></div>';
1333          }
1334      }
1335  
1336  	public static function display_status() {
1337          if ( ! self::get_server_connectivity() ) {
1338              Akismet::view( 'notice', array( 'type' => 'servers-be-down' ) );
1339          } elseif ( ! empty( self::$notices ) ) {
1340              foreach ( self::$notices as $index => $type ) {
1341                  if ( is_object( $type ) ) {
1342                      $notice_header = $notice_text = '';
1343  
1344                      if ( property_exists( $type, 'notice_header' ) ) {
1345                          $notice_header = wp_kses( $type->notice_header, self::$allowed );
1346                      }
1347  
1348                      if ( property_exists( $type, 'notice_text' ) ) {
1349                          $notice_text = wp_kses( $type->notice_text, self::$allowed );
1350                      }
1351  
1352                      if ( property_exists( $type, 'status' ) ) {
1353                          $type = wp_kses( $type->status, self::$allowed );
1354                          Akismet::view( 'notice', compact( 'type', 'notice_header', 'notice_text' ) );
1355  
1356                          unset( self::$notices[ $index ] );
1357                      }
1358                  } else {
1359                      Akismet::view( 'notice', compact( 'type' ) );
1360  
1361                      unset( self::$notices[ $index ] );
1362                  }
1363              }
1364          }
1365      }
1366  
1367      /**
1368       * Gets a specific notice by key.
1369       *
1370       * @param $key
1371       * @return mixed
1372       */
1373  	private static function get_notice_by_key( $key ) {
1374          return self::$notices[ $key ] ?? null;
1375      }
1376  
1377      /**
1378       * Gets a Jetpack user.
1379       *
1380       * @return array|false
1381       */
1382  	private static function get_jetpack_user() {
1383          if ( ! class_exists( 'Jetpack' ) ) {
1384              return false;
1385          }
1386  
1387          if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) ) {
1388              // For version of Jetpack prior to 7.7.
1389              Jetpack::load_xml_rpc_client();
1390          }
1391  
1392          $xml = new Jetpack_IXR_ClientMulticall( array( 'user_id' => get_current_user_id() ) );
1393  
1394          $xml->addCall( 'wpcom.getUserID' );
1395          $xml->addCall( 'akismet.getAPIKey' );
1396          $xml->query();
1397  
1398          Akismet::log( compact( 'xml' ) );
1399  
1400          if ( ! $xml->isError() ) {
1401              $responses = $xml->getResponse();
1402              if ( ( is_countable( $responses ) ? count( $responses ) : 0 ) > 1 ) {
1403                  // Due to a quirk in how Jetpack does multi-calls, the response order
1404                  // can't be trusted to match the call order. It's a good thing our
1405                  // return values can be mostly differentiated from each other.
1406                  $first_response_value  = array_shift( $responses[0] );
1407                  $second_response_value = array_shift( $responses[1] );
1408  
1409                  // If WPCOM ever reaches 100 billion users, this will fail. :-)
1410                  if ( preg_match( '/^[a-f0-9]{12}$/i', $first_response_value ) ) {
1411                      $api_key = $first_response_value;
1412                      $user_id = (int) $second_response_value;
1413                  } else {
1414                      $api_key = $second_response_value;
1415                      $user_id = (int) $first_response_value;
1416                  }
1417  
1418                  return compact( 'api_key', 'user_id' );
1419              }
1420          }
1421          return false;
1422      }
1423  
1424      /**
1425       * Some commentmeta isn't useful in an export file. Suppress it (when supported).
1426       *
1427       * @param bool   $exclude
1428       * @param string $key The meta key
1429       * @param object $meta The meta object
1430       * @return bool Whether to exclude this meta entry from the export.
1431       */
1432  	public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) {
1433          if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) {
1434              return true;
1435          }
1436  
1437          return $exclude;
1438      }
1439  
1440      /**
1441       * When Akismet is active, remove the "Activate Akismet" step from the plugin description.
1442       */
1443  	public static function modify_plugin_description( $all_plugins ) {
1444          if ( isset( $all_plugins['akismet/akismet.php'] ) ) {
1445              if ( Akismet::get_api_key() ) {
1446                  $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' );
1447              } else {
1448                  $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' );
1449              }
1450          }
1451  
1452          return $all_plugins;
1453      }
1454  
1455  	private static function set_form_privacy_notice_option( $state ) {
1456          if ( in_array( $state, array( 'display', 'hide' ) ) ) {
1457              update_option( 'akismet_comment_form_privacy_notice', $state );
1458          }
1459      }
1460  
1461  	public static function register_personal_data_eraser( $erasers ) {
1462          $erasers['akismet'] = array(
1463              'eraser_friendly_name' => __( 'Akismet', 'akismet' ),
1464              'callback'             => array( 'Akismet_Admin', 'erase_personal_data' ),
1465          );
1466  
1467          return $erasers;
1468      }
1469  
1470      /**
1471       * When a user requests that their personal data be removed, Akismet has a duty to discard
1472       * any personal data we store outside of the comment itself. Right now, that is limited
1473       * to the copy of the comment we store in the akismet_as_submitted commentmeta.
1474       *
1475       * FWIW, this information would be automatically deleted after 15 days.
1476       *
1477       * @param $email_address string The email address of the user who has requested erasure.
1478       * @param $page int This function can (and will) be called multiple times to prevent timeouts,
1479       *                  so this argument is used for pagination.
1480       * @return array
1481       * @see https://developer.wordpress.org/plugins/privacy/adding-the-personal-data-eraser-to-your-plugin/
1482       */
1483  	public static function erase_personal_data( $email_address, $page = 1 ) {
1484          $items_removed = false;
1485  
1486          $number = 50;
1487          $page   = (int) $page;
1488  
1489          $comments = get_comments(
1490              array(
1491                  'author_email' => $email_address,
1492                  'number'       => $number,
1493                  'paged'        => $page,
1494                  'order_by'     => 'comment_ID',
1495                  'order'        => 'ASC',
1496              )
1497          );
1498  
1499          foreach ( (array) $comments as $comment ) {
1500              $comment_as_submitted = get_comment_meta( $comment->comment_ID, 'akismet_as_submitted', true );
1501  
1502              if ( $comment_as_submitted ) {
1503                  delete_comment_meta( $comment->comment_ID, 'akismet_as_submitted' );
1504                  $items_removed = true;
1505              }
1506          }
1507  
1508          // Tell core if we have more comments to work on still
1509          $done = ( is_countable( $comments ) ? count( $comments ) : 0 ) < $number;
1510  
1511          return array(
1512              'items_removed'  => $items_removed,
1513              'items_retained' => false, // always false in this example
1514              'messages'       => array(), // no messages in this example
1515              'done'           => $done,
1516          );
1517      }
1518  
1519      /**
1520       * Return an array of HTML elements that are allowed in a notice.
1521       *
1522       * @return array
1523       */
1524  	public static function get_notice_kses_allowed_elements() {
1525          return self::$allowed;
1526      }
1527  
1528      /**
1529       * Return a version to append to the URL of an asset file (e.g. CSS and images).
1530       *
1531       * @param string $relative_path Relative path to asset file
1532       * @return string
1533       */
1534  	public static function get_asset_file_version( $relative_path ) {
1535  
1536          $full_path = AKISMET__PLUGIN_DIR . $relative_path;
1537  
1538          // If the AKISMET_VERSION contains a lower-case letter, it's a development version (e.g. 5.3.1a2).
1539          // Use the file modified time in development.
1540          if ( preg_match( '/[a-z]/', AKISMET_VERSION ) && file_exists( $full_path ) ) {
1541              return filemtime( $full_path );
1542          }
1543  
1544          // Otherwise, use the AKISMET_VERSION.
1545          return AKISMET_VERSION;
1546      }
1547  
1548      /**
1549       * Return inline CSS for Akismet admin.
1550       *
1551       * @return string
1552       */
1553  	protected static function get_inline_css(): string {
1554          global $hook_suffix;
1555  
1556          // Hide excess compatible plugins when there are lots.
1557          $inline_css = '
1558              .akismet-compatible-plugins__card:nth-child(n+' . esc_attr( Akismet_Compatible_Plugins::DEFAULT_VISIBLE_PLUGIN_COUNT + 1 ) . ') {
1559                  display: none;
1560              }
1561  
1562              .akismet-compatible-plugins__list.is-expanded .akismet-compatible-plugins__card:nth-child(n+' . esc_attr( Akismet_Compatible_Plugins::DEFAULT_VISIBLE_PLUGIN_COUNT + 1 ) . ') {
1563                  display: flex;
1564              }
1565          ';
1566  
1567          // Enqueue the Akismet activation banner background separately so we can
1568          // include the right path to the image. Shown on edit-comments.php and plugins.php.
1569          if ( in_array( $hook_suffix, self::$activation_banner_pages, true ) ) {
1570              $activation_banner_url = esc_url(
1571                  plugin_dir_url( __FILE__ ) . '_inc/img/akismet-activation-banner-elements.png'
1572              );
1573              $inline_css           .= '.akismet-activate {' . PHP_EOL .
1574                  'background-image: url(' . $activation_banner_url . ');' . PHP_EOL .
1575                  '}';
1576          }
1577  
1578          return $inline_css;
1579      }
1580  }


Generated : Wed May 14 08:20:01 2025 Cross-referenced by PHPXref