PluginProbe ʕ •ᴥ•ʔ
Akismet Anti-spam: Spam Protection / 3.1.6
Akismet Anti-spam: Spam Protection v3.1.6
5.7 3.0.4 3.0.5 3.1 3.1.1 3.1.10 3.1.11 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2 3.3 3.3.1 3.3.2 3.3.3 3.3.4 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1 4.1.1 4.1.10 4.1.11 4.1.12 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 5.0 5.0.1 5.0.2 5.1 5.2 5.3 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4 5.5 5.6 trunk 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.4.0 2.4.1 2.5.0 2.5.1 2.5.10 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 3.0.0 3.0.0-RC1 3.0.1 3.0.2 3.0.3
akismet / class.akismet-admin.php
akismet Last commit date
_inc 10 years ago views 10 years ago LICENSE.txt 10 years ago akismet.php 10 years ago class.akismet-admin.php 10 years ago class.akismet-widget.php 10 years ago class.akismet.php 10 years ago index.php 10 years ago readme.txt 10 years ago wrapper.php 10 years ago
class.akismet-admin.php
1001 lines
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
68 public static function admin_init() {
69 load_plugin_textdomain( 'akismet' );
70 add_meta_box( 'akismet-status', __('Comment History', 'akismet'), array( 'Akismet_Admin', 'comment_status_meta_box' ), 'comment', 'normal' );
71 }
72
73 public static function admin_menu() {
74 if ( class_exists( 'Jetpack' ) )
75 add_action( 'jetpack_admin_menu', array( 'Akismet_Admin', 'load_menu' ) );
76 else
77 self::load_menu();
78 }
79
80 public static function admin_head() {
81 if ( !current_user_can( 'manage_options' ) )
82 return;
83 }
84
85 public static function admin_plugin_settings_link( $links ) {
86 $settings_link = '<a href="'.esc_url( self::get_page_url() ).'">'.__('Settings', 'akismet').'</a>';
87 array_unshift( $links, $settings_link );
88 return $links;
89 }
90
91 public static function load_menu() {
92 if ( class_exists( 'Jetpack' ) )
93 $hook = add_submenu_page( 'jetpack', __( 'Akismet' , 'akismet'), __( 'Akismet' , 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
94 else
95 $hook = add_options_page( __('Akismet', 'akismet'), __('Akismet', 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
96
97 if ( version_compare( $GLOBALS['wp_version'], '3.3', '>=' ) ) {
98 add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) );
99 }
100 }
101
102 public static function load_resources() {
103 global $hook_suffix;
104
105 if ( in_array( $hook_suffix, array(
106 'index.php', # dashboard
107 'edit-comments.php',
108 'comment.php',
109 'post.php',
110 'settings_page_akismet-key-config',
111 'jetpack_page_akismet-key-config',
112 ) ) ) {
113 wp_register_style( 'akismet.css', AKISMET__PLUGIN_URL . '_inc/akismet.css', array(), AKISMET_VERSION );
114 wp_enqueue_style( 'akismet.css');
115
116 wp_register_script( 'akismet.js', AKISMET__PLUGIN_URL . '_inc/akismet.js', array('jquery','postbox'), AKISMET_VERSION );
117 wp_enqueue_script( 'akismet.js' );
118 wp_localize_script( 'akismet.js', 'WPAkismet', array(
119 'comment_author_url_nonce' => wp_create_nonce( 'comment_author_url_nonce' ),
120 'strings' => array(
121 'Remove this URL' => __( 'Remove this URL' , 'akismet'),
122 'Removing...' => __( 'Removing...' , 'akismet'),
123 'URL removed' => __( 'URL removed' , 'akismet'),
124 '(undo)' => __( '(undo)' , 'akismet'),
125 'Re-adding...' => __( 'Re-adding...' , 'akismet'),
126 )
127 ) );
128 }
129 }
130
131 /**
132 * Add help to the Akismet page
133 *
134 * @return false if not the Akismet page
135 */
136 public static function admin_help() {
137 $current_screen = get_current_screen();
138
139 // Screen Content
140 if ( current_user_can( 'manage_options' ) ) {
141 if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) {
142 //setup page
143 $current_screen->add_help_tab(
144 array(
145 'id' => 'overview',
146 'title' => __( 'Overview' , 'akismet'),
147 'content' =>
148 '<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
149 '<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.' , 'akismet') . '</p>' .
150 '<p>' . esc_html__( 'On this page, you are able to set up the Akismet plugin.' , 'akismet') . '</p>',
151 )
152 );
153
154 $current_screen->add_help_tab(
155 array(
156 'id' => 'setup-signup',
157 'title' => __( 'New to Akismet' , 'akismet'),
158 'content' =>
159 '<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
160 '<p>' . esc_html__( 'You need to enter an API key to activate the Akismet service on your site.' , 'akismet') . '</p>' .
161 '<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>',
162 )
163 );
164
165 $current_screen->add_help_tab(
166 array(
167 'id' => 'setup-manual',
168 'title' => __( 'Enter an API Key' , 'akismet'),
169 'content' =>
170 '<p><strong>' . esc_html__( 'Akismet Setup' , 'akismet') . '</strong></p>' .
171 '<p>' . esc_html__( 'If you already have an API key' , 'akismet') . '</p>' .
172 '<ol>' .
173 '<li>' . esc_html__( 'Copy and paste the API key into the text field.' , 'akismet') . '</li>' .
174 '<li>' . esc_html__( 'Click the Use this Key button.' , 'akismet') . '</li>' .
175 '</ol>',
176 )
177 );
178 }
179 elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) {
180 //stats page
181 $current_screen->add_help_tab(
182 array(
183 'id' => 'overview',
184 'title' => __( 'Overview' , 'akismet'),
185 'content' =>
186 '<p><strong>' . esc_html__( 'Akismet Stats' , 'akismet') . '</strong></p>' .
187 '<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.' , 'akismet') . '</p>' .
188 '<p>' . esc_html__( 'On this page, you are able to view stats on spam filtered on your site.' , 'akismet') . '</p>',
189 )
190 );
191 }
192 else {
193 //configuration page
194 $current_screen->add_help_tab(
195 array(
196 'id' => 'overview',
197 'title' => __( 'Overview' , 'akismet'),
198 'content' =>
199 '<p><strong>' . esc_html__( 'Akismet Configuration' , 'akismet') . '</strong></p>' .
200 '<p>' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.' , 'akismet') . '</p>' .
201 '<p>' . esc_html__( 'On this page, you are able to enter/remove an API key, view account information and view spam stats.' , 'akismet') . '</p>',
202 )
203 );
204
205 $current_screen->add_help_tab(
206 array(
207 'id' => 'settings',
208 'title' => __( 'Settings' , 'akismet'),
209 'content' =>
210 '<p><strong>' . esc_html__( 'Akismet Configuration' , 'akismet') . '</strong></p>' .
211 '<p><strong>' . esc_html__( 'API Key' , 'akismet') . '</strong> - ' . esc_html__( 'Enter/remove an API key.' , 'akismet') . '</p>' .
212 '<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>' .
213 '<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>',
214 )
215 );
216
217 $current_screen->add_help_tab(
218 array(
219 'id' => 'account',
220 'title' => __( 'Account' , 'akismet'),
221 'content' =>
222 '<p><strong>' . esc_html__( 'Akismet Configuration' , 'akismet') . '</strong></p>' .
223 '<p><strong>' . esc_html__( 'Subscription Type' , 'akismet') . '</strong> - ' . esc_html__( 'The Akismet subscription plan' , 'akismet') . '</p>' .
224 '<p><strong>' . esc_html__( 'Status' , 'akismet') . '</strong> - ' . esc_html__( 'The subscription status - active, cancelled or suspended' , 'akismet') . '</p>',
225 )
226 );
227 }
228 }
229
230 // Help Sidebar
231 $current_screen->set_help_sidebar(
232 '<p><strong>' . esc_html__( 'For more information:' , 'akismet') . '</strong></p>' .
233 '<p><a href="https://akismet.com/faq/" target="_blank">' . esc_html__( 'Akismet FAQ' , 'akismet') . '</a></p>' .
234 '<p><a href="https://akismet.com/support/" target="_blank">' . esc_html__( 'Akismet Support' , 'akismet') . '</a></p>'
235 );
236 }
237
238 public static function enter_api_key() {
239 if ( function_exists('current_user_can') && !current_user_can('manage_options') )
240 die(__('Cheatin&#8217; uh?', 'akismet'));
241
242 if ( !wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) )
243 return false;
244
245 foreach( array( 'akismet_strictness', 'akismet_show_user_comments_approved' ) as $option ) {
246 update_option( $option, isset( $_POST[$option] ) && (int) $_POST[$option] == 1 ? '1' : '0' );
247 }
248
249 if ( defined( 'WPCOM_API_KEY' ) )
250 return false; //shouldn't have option to save key if already defined
251
252 $new_key = preg_replace( '/[^a-f0-9]/i', '', $_POST['key'] );
253 $old_key = Akismet::get_api_key();
254
255 if ( empty( $new_key ) ) {
256 if ( !empty( $old_key ) ) {
257 delete_option( 'wordpress_api_key' );
258 self::$notices[] = 'new-key-empty';
259 }
260 }
261 elseif ( $new_key != $old_key ) {
262 self::save_key( $new_key );
263 }
264
265 return true;
266 }
267
268 public static function save_key( $api_key ) {
269 $key_status = Akismet::verify_key( $api_key );
270
271 if ( $key_status == 'valid' ) {
272 $akismet_user = self::get_akismet_user( $api_key );
273
274 if ( $akismet_user ) {
275 if ( in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ) )
276 update_option( 'wordpress_api_key', $api_key );
277
278 if ( $akismet_user->status == 'active' )
279 self::$notices['status'] = 'new-key-valid';
280 elseif ( $akismet_user->status == 'notice' )
281 self::$notices['status'] = $akismet_user;
282 else
283 self::$notices['status'] = $akismet_user->status;
284 }
285 else
286 self::$notices['status'] = 'new-key-invalid';
287 }
288 elseif ( in_array( $key_status, array( 'invalid', 'failed' ) ) )
289 self::$notices['status'] = 'new-key-'.$key_status;
290 }
291
292 public static function dashboard_stats() {
293 if ( !function_exists('did_action') || did_action( 'rightnow_end' ) )
294 return; // We already displayed this info in the "Right Now" section
295
296 if ( !$count = get_option('akismet_spam_count') )
297 return;
298
299 global $submenu;
300
301 echo '<h3>' . esc_html( _x( 'Spam', 'comments' , 'akismet') ) . '</h3>';
302
303 echo '<p>'.sprintf( _n(
304 '<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comment</a>.',
305 '<a href="%1$s">Akismet</a> has protected your site from <a href="%2$s">%3$s spam comments</a>.',
306 $count
307 , '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>';
308 }
309
310 // WP 2.5+
311 public static function rightnow_stats() {
312 if ( $count = get_option('akismet_spam_count') ) {
313 $intro = sprintf( _n(
314 '<a href="%1$s">Akismet</a> has protected your site from %2$s spam comment already. ',
315 '<a href="%1$s">Akismet</a> has protected your site from %2$s spam comments already. ',
316 $count
317 , 'akismet'), 'https://akismet.com/wordpress/', number_format_i18n( $count ) );
318 } else {
319 $intro = sprintf( __('<a href="%s">Akismet</a> blocks spam from getting to your blog. ', 'akismet'), 'https://akismet.com/wordpress/' );
320 }
321
322 $link = add_query_arg( array( 'comment_status' => 'spam' ), admin_url( 'edit-comments.php' ) );
323
324 if ( $queue_count = self::get_spam_count() ) {
325 $queue_text = sprintf( _n(
326 'There&#8217;s <a href="%2$s">%1$s comment</a> in your spam queue right now.',
327 'There are <a href="%2$s">%1$s comments</a> in your spam queue right now.',
328 $queue_count
329 , 'akismet'), number_format_i18n( $queue_count ), esc_url( $link ) );
330 } else {
331 $queue_text = sprintf( __( "There&#8217;s nothing in your <a href='%s'>spam queue</a> at the moment." , 'akismet'), esc_url( $link ) );
332 }
333
334 $text = $intro . '<br />' . $queue_text;
335 echo "<p class='akismet-right-now'>$text</p>\n";
336 }
337
338 public static function check_for_spam_button( $comment_status ) {
339 // The "Check for Spam" button should only appear when the page might be showing
340 // a comment with comment_approved=0, which means an un-trashed, un-spammed,
341 // not-yet-moderated comment.
342 if ( 'all' != $comment_status && 'moderated' != $comment_status ) {
343 return;
344 }
345
346 if ( function_exists('plugins_url') )
347 $link = add_query_arg( array( 'action' => 'akismet_recheck_queue' ), admin_url( 'admin.php' ) );
348 else
349 $link = add_query_arg( array( 'page' => 'akismet-admin', 'recheckqueue' => 'true', 'noheader' => 'true' ), admin_url( 'edit-comments.php' ) );
350
351 echo '</div><div class="alignleft"><a class="button-secondary checkforspam" href="' . esc_url( $link ) . '">' . esc_html__('Check for Spam', 'akismet') . '</a><span class="checkforspam-spinner"></span>';
352 }
353
354 public static function recheck_queue() {
355 global $wpdb;
356
357 Akismet::fix_scheduled_recheck();
358
359 if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) )
360 return;
361
362 $paginate = '';
363 if ( isset( $_POST['limit'] ) && isset( $_POST['offset'] ) ) {
364 $paginate = $wpdb->prepare( " LIMIT %d OFFSET %d", array( $_POST['limit'], $_POST['offset'] ) );
365 }
366 $moderation = $wpdb->get_results( "SELECT * FROM {$wpdb->comments} WHERE comment_approved = '0'{$paginate}", ARRAY_A );
367
368 foreach ( (array) $moderation as $c ) {
369 $c['user_ip'] = $c['comment_author_IP'];
370 $c['user_agent'] = $c['comment_agent'];
371 $c['referrer'] = '';
372 $c['blog'] = get_bloginfo('url');
373 $c['blog_lang'] = get_locale();
374 $c['blog_charset'] = get_option('blog_charset');
375 $c['permalink'] = get_permalink($c['comment_post_ID']);
376
377 $c['user_role'] = '';
378 if ( isset( $c['user_ID'] ) )
379 $c['user_role'] = Akismet::get_user_roles($c['user_ID']);
380
381 if ( Akismet::is_test_mode() )
382 $c['is_test'] = 'true';
383
384 add_comment_meta( $c['comment_ID'], 'akismet_rechecking', true );
385
386 $response = Akismet::http_post( Akismet::build_query( $c ), 'comment-check' );
387
388 if ( 'true' == $response[1] ) {
389 wp_set_comment_status( $c['comment_ID'], 'spam' );
390 update_comment_meta( $c['comment_ID'], 'akismet_result', 'true' );
391 delete_comment_meta( $c['comment_ID'], 'akismet_error' );
392 delete_comment_meta( $c['comment_ID'], 'akismet_delayed_moderation_email' );
393 Akismet::update_comment_history( $c['comment_ID'], '', 'recheck-spam' );
394
395 } elseif ( 'false' == $response[1] ) {
396 update_comment_meta( $c['comment_ID'], 'akismet_result', 'false' );
397 delete_comment_meta( $c['comment_ID'], 'akismet_error' );
398 delete_comment_meta( $c['comment_ID'], 'akismet_delayed_moderation_email' );
399 Akismet::update_comment_history( $c['comment_ID'], '', 'recheck-ham' );
400 // abnormal result: error
401 } else {
402 update_comment_meta( $c['comment_ID'], 'akismet_result', 'error' );
403 Akismet::update_comment_history(
404 $c['comment_ID'],
405 '',
406 'recheck-error',
407 array( 'response' => substr( $response[1], 0, 50 ) )
408 );
409 }
410
411 delete_comment_meta( $c['comment_ID'], 'akismet_rechecking' );
412 }
413 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
414 wp_send_json( array(
415 'processed' => count((array) $moderation),
416 ));
417 }
418 else {
419 $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' );
420 wp_safe_redirect( $redirect_to );
421 exit;
422 }
423 }
424
425 // Adds an 'x' link next to author URLs, clicking will remove the author URL and show an undo link
426 public static function remove_comment_author_url() {
427 if ( !empty( $_POST['id'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) {
428 $comment = get_comment( intval( $_POST['id'] ), ARRAY_A );
429 if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) {
430 $comment['comment_author_url'] = '';
431 do_action( 'comment_remove_author_url' );
432 print( wp_update_comment( $comment ) );
433 die();
434 }
435 }
436 }
437
438 public static function add_comment_author_url() {
439 if ( !empty( $_POST['id'] ) && !empty( $_POST['url'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) {
440 $comment = get_comment( intval( $_POST['id'] ), ARRAY_A );
441 if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) {
442 $comment['comment_author_url'] = esc_url( $_POST['url'] );
443 do_action( 'comment_add_author_url' );
444 print( wp_update_comment( $comment ) );
445 die();
446 }
447 }
448 }
449
450 public static function comment_row_action( $a, $comment ) {
451
452 // failsafe for old WP versions
453 if ( !function_exists('add_comment_meta') )
454 return $a;
455
456 $akismet_result = get_comment_meta( $comment->comment_ID, 'akismet_result', true );
457 $akismet_error = get_comment_meta( $comment->comment_ID, 'akismet_error', true );
458 $user_result = get_comment_meta( $comment->comment_ID, 'akismet_user_result', true);
459 $comment_status = wp_get_comment_status( $comment->comment_ID );
460 $desc = null;
461 if ( $akismet_error ) {
462 $desc = __( 'Awaiting spam check' , 'akismet');
463 } elseif ( !$user_result || $user_result == $akismet_result ) {
464 // Show the original Akismet result if the user hasn't overridden it, or if their decision was the same
465 if ( $akismet_result == 'true' && $comment_status != 'spam' && $comment_status != 'trash' )
466 $desc = __( 'Flagged as spam by Akismet' , 'akismet');
467 elseif ( $akismet_result == 'false' && $comment_status == 'spam' )
468 $desc = __( 'Cleared by Akismet' , 'akismet');
469 } else {
470 $who = get_comment_meta( $comment->comment_ID, 'akismet_user', true );
471 if ( $user_result == 'true' )
472 $desc = sprintf( __('Flagged as spam by %s', 'akismet'), $who );
473 else
474 $desc = sprintf( __('Un-spammed by %s', 'akismet'), $who );
475 }
476
477 // add a History item to the hover links, just after Edit
478 if ( $akismet_result ) {
479 $b = array();
480 foreach ( $a as $k => $item ) {
481 $b[ $k ] = $item;
482 if (
483 $k == 'edit'
484 || ( $k == 'unspam' && $GLOBALS['wp_version'] >= 3.4 )
485 ) {
486 $b['history'] = '<a href="comment.php?action=editcomment&amp;c='.$comment->comment_ID.'#akismet-status" title="'. esc_attr__( 'View comment history' , 'akismet') . '"> '. esc_html__('History', 'akismet') . '</a>';
487 }
488 }
489
490 $a = $b;
491 }
492
493 if ( $desc )
494 echo '<span class="akismet-status" commentid="'.$comment->comment_ID.'"><a href="comment.php?action=editcomment&amp;c='.$comment->comment_ID.'#akismet-status" title="' . esc_attr__( 'View comment history' , 'akismet') . '">'.esc_html( $desc ).'</a></span>';
495
496 $show_user_comments = apply_filters( 'akismet_show_user_comments_approved', get_option('akismet_show_user_comments_approved') );
497 $show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; //option used to be saved as 'false' / 'true'
498
499 if ( $show_user_comments ) {
500 $comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url );
501 $comment_count = intval( $comment_count );
502 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>';
503 }
504
505 return $a;
506 }
507
508 public static function comment_status_meta_box( $comment ) {
509 $history = Akismet::get_comment_history( $comment->comment_ID );
510
511 if ( $history ) {
512 echo '<div class="akismet-history" style="margin: 13px;">';
513
514 foreach ( $history as $row ) {
515 $time = date( 'D d M Y @ h:i:m a', $row['time'] ) . ' GMT';
516
517 $message = '';
518
519 if ( ! empty( $row['message'] ) ) {
520 // Old versions of Akismet stored the message as a literal string in the commentmeta.
521 // New versions don't do that for two reasons:
522 // 1) Save space.
523 // 2) The message can be translated into the current language of the blog, not stuck
524 // in the language of the blog when the comment was made.
525 $message = $row['message'];
526 }
527
528 // If possible, use a current translation.
529 switch ( $row['event'] ) {
530 case 'recheck-spam';
531 $message = __( 'Akismet re-checked and caught this comment as spam.', 'akismet' );
532 break;
533 case 'check-spam':
534 $message = __( 'Akismet caught this comment as spam.', 'akismet' );
535 break;
536 case 'recheck-ham':
537 $message = __( 'Akismet re-checked and cleared this comment.', 'akismet' );
538 break;
539 case 'check-ham':
540 $message = __( 'Akismet cleared this comment.', 'akismet' );
541 break;
542 case 'wp-blacklisted':
543 $message = __( 'Comment was caught by wp_blacklist_check.', 'akismet' );
544 break;
545 case 'report-spam':
546 if ( isset( $row['user'] ) ) {
547 $message = sprintf( __( '%s reported this comment as spam.', 'akismet' ), $row['user'] );
548 }
549 else if ( ! $message ) {
550 $message = __( 'This comment was reported as spam.', 'akismet' );
551 }
552 break;
553 case 'report-ham':
554 if ( isset( $row['user'] ) ) {
555 $message = sprintf( __( '%s reported this comment as not spam.', 'akismet' ), $row['user'] );
556 }
557 else if ( ! $message ) {
558 $message = __( 'This comment was reported as not spam.', 'akismet' );
559 }
560 break;
561 case 'cron-retry-spam':
562 $message = __( 'Akismet caught this comment as spam during an automatic retry.' , 'akismet');
563 break;
564 case 'cron-retry-ham':
565 $message = __( 'Akismet cleared this comment during an automatic retry.', 'akismet');
566 break;
567 case 'check-error':
568 if ( isset( $row['meta'], $row['meta']['response'] ) ) {
569 $message = sprintf( __( 'Akismet was unable to check this comment (response: %s) but will automatically retry later.', 'akismet'), $row['meta']['response'] );
570 }
571 break;
572 case 'recheck-error':
573 if ( isset( $row['meta'], $row['meta']['response'] ) ) {
574 $message = sprintf( __( 'Akismet was unable to recheck this comment (response: %s).', 'akismet'), $row['meta']['response'] );
575 }
576 break;
577 default:
578 if ( preg_match( '/^status-changed/', $row['event'] ) ) {
579 // Half of these used to be saved without the dash after 'status-changed'.
580 // See https://plugins.trac.wordpress.org/changeset/1150658/akismet/trunk
581 $new_status = preg_replace( '/^status-changed-?/', '', $row['event'] );
582 $message = sprintf( __( 'Comment status was changed to %s', 'akismet' ), $new_status );
583 }
584 else if ( preg_match( '/^status-/', $row['event'] ) ) {
585 $new_status = preg_replace( '/^status-/', '', $row['event'] );
586
587 if ( isset( $row['user'] ) ) {
588 $message = sprintf( __( '%1$s changed the comment status to %2$s.', 'akismet' ), $row['user'], $new_status );
589 }
590 }
591 break;
592
593 }
594
595 echo '<div style="margin-bottom: 13px;">';
596 echo '<span style="color: #999;" alt="' . $time . '" title="' . $time . '">' . sprintf( esc_html__('%s ago', 'akismet'), human_time_diff( $row['time'] ) ) . '</span>';
597 echo ' - ';
598 echo esc_html( $message );
599 echo '</div>';
600 }
601
602 echo '</div>';
603 }
604 }
605
606 public static function plugin_action_links( $links, $file ) {
607 if ( $file == plugin_basename( AKISMET__PLUGIN_URL . '/akismet.php' ) ) {
608 $links[] = '<a href="' . esc_url( self::get_page_url() ) . '">'.esc_html__( 'Settings' , 'akismet').'</a>';
609 }
610
611 return $links;
612 }
613
614 // Total spam in queue
615 // get_option( 'akismet_spam_count' ) is the total caught ever
616 public static function get_spam_count( $type = false ) {
617 global $wpdb;
618
619 if ( !$type ) { // total
620 $count = wp_cache_get( 'akismet_spam_count', 'widget' );
621 if ( false === $count ) {
622 if ( function_exists('wp_count_comments') ) {
623 $count = wp_count_comments();
624 $count = $count->spam;
625 } else {
626 $count = (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam'");
627 }
628 wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 );
629 }
630 return $count;
631 } elseif ( 'comments' == $type || 'comment' == $type ) { // comments
632 $type = '';
633 }
634
635 return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) );
636 }
637
638 // Check connectivity between the WordPress blog and Akismet's servers.
639 // 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).
640 public static function check_server_ip_connectivity() {
641
642 $servers = $ips = array();
643
644 // Some web hosts may disable this function
645 if ( function_exists('gethostbynamel') ) {
646
647 $ips = gethostbynamel( 'rest.akismet.com' );
648 if ( $ips && is_array($ips) && count($ips) ) {
649 $api_key = Akismet::get_api_key();
650
651 foreach ( $ips as $ip ) {
652 $response = Akismet::verify_key( $api_key, $ip );
653 // even if the key is invalid, at least we know we have connectivity
654 if ( $response == 'valid' || $response == 'invalid' )
655 $servers[$ip] = 'connected';
656 else
657 $servers[$ip] = $response ? $response : 'unable to connect';
658 }
659 }
660 }
661
662 return $servers;
663 }
664
665 // Simpler connectivity check
666 public static function check_server_connectivity($cache_timeout = 86400) {
667
668 $debug = array();
669 $debug[ 'PHP_VERSION' ] = PHP_VERSION;
670 $debug[ 'WORDPRESS_VERSION' ] = $GLOBALS['wp_version'];
671 $debug[ 'AKISMET_VERSION' ] = AKISMET_VERSION;
672 $debug[ 'AKISMET__PLUGIN_DIR' ] = AKISMET__PLUGIN_DIR;
673 $debug[ 'SITE_URL' ] = site_url();
674 $debug[ 'HOME_URL' ] = home_url();
675
676 $servers = get_option('akismet_available_servers');
677 if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) {
678 $servers = self::check_server_ip_connectivity();
679 update_option('akismet_available_servers', $servers);
680 update_option('akismet_connectivity_time', time());
681 }
682
683 $response = wp_remote_get( 'http://rest.akismet.com/1.1/test' );
684
685 $debug[ 'gethostbynamel' ] = function_exists('gethostbynamel') ? 'exists' : 'not here';
686 $debug[ 'Servers' ] = $servers;
687 $debug[ 'Test Connection' ] = $response;
688
689 Akismet::log( $debug );
690
691 if ( $response && 'connected' == wp_remote_retrieve_body( $response ) )
692 return true;
693
694 return false;
695 }
696
697 // Check the server connectivity and store the available servers in an option.
698 public static function get_server_connectivity($cache_timeout = 86400) {
699 return self::check_server_connectivity( $cache_timeout );
700 }
701
702 public static function get_number_spam_waiting() {
703 global $wpdb;
704 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" );
705 }
706
707 public static function get_page_url( $page = 'config' ) {
708
709 $args = array( 'page' => 'akismet-key-config' );
710
711 if ( $page == 'stats' )
712 $args = array( 'page' => 'akismet-key-config', 'view' => 'stats' );
713 elseif ( $page == 'delete_key' )
714 $args = array( 'page' => 'akismet-key-config', 'view' => 'start', 'action' => 'delete-key', '_wpnonce' => wp_create_nonce( self::NONCE ) );
715
716 $url = add_query_arg( $args, class_exists( 'Jetpack' ) ? admin_url( 'admin.php' ) : admin_url( 'options-general.php' ) );
717
718 return $url;
719 }
720
721 public static function get_akismet_user( $api_key ) {
722 $akismet_user = false;
723
724 $subscription_verification = Akismet::http_post( Akismet::build_query( array( 'key' => $api_key, 'blog' => get_bloginfo( 'url' ) ) ), 'get-subscription' );
725
726 if ( ! empty( $subscription_verification[1] ) ) {
727 if ( 'invalid' !== $subscription_verification[1] ) {
728 $akismet_user = json_decode( $subscription_verification[1] );
729 }
730 }
731
732 return $akismet_user;
733 }
734
735 public static function get_stats( $api_key ) {
736 $stat_totals = array();
737
738 foreach( array( '6-months', 'all' ) as $interval ) {
739 $response = Akismet::http_post( Akismet::build_query( array( 'blog' => get_bloginfo( 'url' ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' );
740
741 if ( ! empty( $response[1] ) ) {
742 $stat_totals[$interval] = json_decode( $response[1] );
743 }
744 }
745
746 return $stat_totals;
747 }
748
749 public static function verify_wpcom_key( $api_key, $user_id, $extra = array() ) {
750 $akismet_account = Akismet::http_post( Akismet::build_query( array_merge( array(
751 'user_id' => $user_id,
752 'api_key' => $api_key,
753 'get_account_type' => 'true'
754 ), $extra ) ), 'verify-wpcom-key' );
755
756 if ( ! empty( $akismet_account[1] ) )
757 $akismet_account = json_decode( $akismet_account[1] );
758
759 Akismet::log( compact( 'akismet_account' ) );
760
761 return $akismet_account;
762 }
763
764 public static function connect_jetpack_user() {
765
766 if ( $jetpack_user = self::get_jetpack_user() ) {
767 if ( isset( $jetpack_user['user_id'] ) && isset( $jetpack_user['api_key'] ) ) {
768 $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'], array( 'action' => 'connect_jetpack_user' ) );
769
770 if ( is_object( $akismet_user ) ) {
771 self::save_key( $akismet_user->api_key );
772 return in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) );
773 }
774 }
775 }
776
777 return false;
778 }
779
780 public static function display_alert() {
781 Akismet::view( 'notice', array(
782 'type' => 'alert',
783 'code' => (int) get_option( 'akismet_alert_code' ),
784 'msg' => get_option( 'akismet_alert_msg' )
785 ) );
786 }
787
788 public static function display_spam_check_warning() {
789 Akismet::fix_scheduled_recheck();
790
791 if ( wp_next_scheduled('akismet_schedule_cron_recheck') > time() && self::get_number_spam_waiting() > 0 ) {
792 $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() ) ) );
793 Akismet::view( 'notice', array( 'type' => 'spam-check', 'link_text' => $link_text ) );
794 }
795 }
796
797 public static function display_invalid_version() {
798 Akismet::view( 'notice', array( 'type' => 'version' ) );
799 }
800
801 public static function display_api_key_warning() {
802 Akismet::view( 'notice', array( 'type' => 'plugin' ) );
803 }
804
805 public static function display_page() {
806 if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) )
807 self::display_start_page();
808 elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' )
809 self::display_stats_page();
810 else
811 self::display_configuration_page();
812 }
813
814 public static function display_start_page() {
815 if ( isset( $_GET['action'] ) ) {
816 if ( $_GET['action'] == 'delete-key' ) {
817 if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], self::NONCE ) )
818 delete_option( 'wordpress_api_key' );
819 }
820 }
821
822 if ( $api_key = Akismet::get_api_key() && ( empty( self::$notices['status'] ) || 'existing-key-invalid' != self::$notices['status'] ) ) {
823 self::display_configuration_page();
824 return;
825 }
826
827 //the user can choose to auto connect their API key by clicking a button on the akismet done page
828 //if jetpack, get verified api key by using connected wpcom user id
829 //if no jetpack, get verified api key by using an akismet token
830
831 $akismet_user = false;
832
833 if ( isset( $_GET['token'] ) && preg_match('/^(\d+)-[0-9a-f]{20}$/', $_GET['token'] ) )
834 $akismet_user = self::verify_wpcom_key( '', '', array( 'token' => $_GET['token'] ) );
835 elseif ( $jetpack_user = self::get_jetpack_user() )
836 $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'] );
837
838 if ( isset( $_GET['action'] ) ) {
839 if ( $_GET['action'] == 'save-key' ) {
840 if ( is_object( $akismet_user ) ) {
841 self::save_key( $akismet_user->api_key );
842 self::display_notice();
843 self::display_configuration_page();
844 return;
845 }
846 }
847 }
848
849 echo '<h2 class="ak-header">'.esc_html__('Akismet', 'akismet').'</h2>';
850
851 self::display_status();
852
853 Akismet::view( 'start', compact( 'akismet_user' ) );
854 }
855
856 public static function display_stats_page() {
857 Akismet::view( 'stats' );
858 }
859
860 public static function display_configuration_page() {
861 $api_key = Akismet::get_api_key();
862 $akismet_user = self::get_akismet_user( $api_key );
863
864 if ( ! $akismet_user ) {
865 // This could happen if the user's key became invalid after it was previously valid and successfully set up.
866 self::$notices['status'] = 'existing-key-invalid';
867 self::display_start_page();
868 return;
869 }
870
871 $stat_totals = self::get_stats( $api_key );
872
873 // If unset, create the new strictness option using the old discard option to determine its default
874 if ( get_option( 'akismet_strictness' ) === false )
875 add_option( 'akismet_strictness', (get_option('akismet_discard_month') === 'true' ? '1' : '0') );
876
877 if ( empty( self::$notices ) ) {
878 //show status
879 if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) {
880
881 $time_saved = false;
882
883 if ( $stat_totals['all']->time_saved > 1800 ) {
884 $total_in_minutes = round( $stat_totals['all']->time_saved / 60 );
885 $total_in_hours = round( $total_in_minutes / 60 );
886 $total_in_days = round( $total_in_hours / 8 );
887 $cleaning_up = __( 'Cleaning up spam takes time.' , 'akismet');
888
889 if ( $total_in_days > 1 )
890 $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 ) );
891 elseif ( $total_in_hours > 1 )
892 $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 );
893 elseif ( $total_in_minutes >= 30 )
894 $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 );
895 }
896
897 Akismet::view( 'notice', array( 'type' => 'active-notice', 'time_saved' => $time_saved ) );
898 }
899
900 if ( !empty( $akismet_user->limit_reached ) && in_array( $akismet_user->limit_reached, array( 'yellow', 'red' ) ) ) {
901 Akismet::view( 'notice', array( 'type' => 'limit-reached', 'level' => $akismet_user->limit_reached ) );
902 }
903 }
904
905 if ( !isset( self::$notices['status'] ) && in_array( $akismet_user->status, array( 'cancelled', 'suspended', 'missing', 'no-sub' ) ) )
906 Akismet::view( 'notice', array( 'type' => $akismet_user->status ) );
907
908 Akismet::log( compact( 'stat_totals', 'akismet_user' ) );
909 Akismet::view( 'config', compact( 'api_key', 'akismet_user', 'stat_totals' ) );
910 }
911
912 public static function display_notice() {
913 global $hook_suffix;
914
915 if ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config', 'edit-comments.php' ) ) && (int) get_option( 'akismet_alert_code' ) > 0 ) {
916 Akismet::verify_key( Akismet::get_api_key() ); //verify that the key is still in alert state
917
918 if ( get_option( 'akismet_alert_code' ) > 0 )
919 self::display_alert();
920 }
921 elseif ( $hook_suffix == 'plugins.php' && !Akismet::get_api_key() ) {
922 self::display_api_key_warning();
923 }
924 elseif ( $hook_suffix == 'edit-comments.php' && wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) {
925 self::display_spam_check_warning();
926 }
927 elseif ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config' ) ) && Akismet::get_api_key() ) {
928 self::display_status();
929 }
930 }
931
932 public static function display_status() {
933 $type = '';
934
935 if ( !self::get_server_connectivity() )
936 $type = 'servers-be-down';
937
938 if ( !empty( $type ) )
939 Akismet::view( 'notice', compact( 'type' ) );
940 elseif ( !empty( self::$notices ) ) {
941 foreach ( self::$notices as $type ) {
942 if ( is_object( $type ) ) {
943 $notice_header = $notice_text = '';
944
945 if ( property_exists( $type, 'notice_header' ) )
946 $notice_header = wp_kses( $type->notice_header, self::$allowed );
947
948 if ( property_exists( $type, 'notice_text' ) )
949 $notice_text = wp_kses( $type->notice_text, self::$allowed );
950
951 if ( property_exists( $type, 'status' ) ) {
952 $type = wp_kses( $type->status, self::$allowed );
953 Akismet::view( 'notice', compact( 'type', 'notice_header', 'notice_text' ) );
954 }
955 }
956 else
957 Akismet::view( 'notice', compact( 'type' ) );
958 }
959 }
960 }
961
962 private static function get_jetpack_user() {
963 if ( !class_exists('Jetpack') )
964 return false;
965
966 Jetpack::load_xml_rpc_client();
967 $xml = new Jetpack_IXR_ClientMulticall( array( 'user_id' => get_current_user_id() ) );
968
969 $xml->addCall( 'wpcom.getUserID' );
970 $xml->addCall( 'akismet.getAPIKey' );
971 $xml->query();
972
973 Akismet::log( compact( 'xml' ) );
974
975 if ( !$xml->isError() ) {
976 $responses = $xml->getResponse();
977 if ( count( $responses ) > 1 ) {
978 $api_key = array_shift( $responses[0] );
979 $user_id = (int) array_shift( $responses[1] );
980 return compact( 'api_key', 'user_id' );
981 }
982 }
983 return false;
984 }
985
986 /**
987 * Some commentmeta isn't useful in an export file. Suppress it (when supported).
988 *
989 * @param bool $exclude
990 * @param string $key The meta key
991 * @param object $meta The meta object
992 * @return bool Whether to exclude this meta entry from the export.
993 */
994 public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) {
995 if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) {
996 return true;
997 }
998
999 return $exclude;
1000 }
1001 }