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