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