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