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