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