bp-activity-akismet.php 21.9 KB
Newer Older
lechuck's avatar
lechuck committed
1
2
3
4
5
<?php
/**
 * Akismet support for BuddyPress' Activity Stream
 *
 * @package BuddyPress
lechuck's avatar
lechuck committed
6
 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
7
8
9
10
11
12
13
14
15
16
17
18
 * @subpackage Activity
 */

// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) exit;

class BP_Akismet {
	/**
	 * The activity last marked as spam
	 *
	 * @access protected
	 * @var BP_Activity_Activity
lechuck's avatar
lechuck committed
19
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
20
21
22
23
24
25
	 */
	protected $last_activity = null;

	/**
	 * Constructor
	 *
lechuck's avatar
lechuck committed
26
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
27
28
29
30
31
32
33
34
	 */
	public function __construct() {
		$this->setup_actions();
	}

	/**
	 * Hook Akismet into the activity stream
	 *
lechuck's avatar
lechuck committed
35
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	 */
	protected function setup_actions() {
		// Add nonces to activity stream lists
		add_action( 'bp_after_activity_post_form', array( $this, 'add_activity_stream_nonce' ) );
		add_action( 'bp_activity_entry_comments',  array( $this, 'add_activity_stream_nonce' ) );

		// Add a "mark as spam" button to individual activity items
		add_action( 'bp_activity_entry_meta',      array( $this, 'add_activity_spam_button' ) );
		add_action( 'bp_activity_comment_options', array( $this, 'add_activity_comment_spam_button' ) );

		// Check activity for spam
		add_action( 'bp_activity_before_save',     array( $this, 'check_activity' ), 4, 1 );

		// Tidy up member's latest (activity) update
		add_action( 'bp_activity_posted_update',   array( $this, 'check_member_activity_update' ), 1, 3 );

		// Hooks to extend Activity core spam/ham functions for Akismet
		add_action( 'bp_activity_mark_as_spam',    array( $this, 'mark_as_spam' ), 10, 2 );
		add_action( 'bp_activity_mark_as_ham',     array( $this, 'mark_as_ham' ),  10, 2 );

		// Hook into the Activity wp-admin screen
		add_action( 'bp_activity_admin_comment_row_actions', array( $this, 'comment_row_action' ), 10, 2 );
		add_action( 'bp_activity_admin_load',                array( $this, 'add_history_metabox' ) );
	}

	/**
	 * Add a history item to the hover links in an activity's row.
	 *
	 * This function lifted with love from the Akismet WordPress plugin's
	 * akismet_comment_row_action() function. Thanks!
	 *
	 * @param array $actions The hover links
	 * @param array $activity The activity for the current row being processed
	 * @return array The hover links
lechuck's avatar
lechuck committed
70
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	 */
	function comment_row_action( $actions, $activity ) {
		$akismet_result = bp_activity_get_meta( $activity['id'], '_bp_akismet_result' );
		$user_result    = bp_activity_get_meta( $activity['id'], '_bp_akismet_user_result' );
		$desc           = '';

		if ( !$user_result || $user_result == $akismet_result ) {
			// Show the original Akismet result if the user hasn't overridden it, or if their decision was the same
			if ( 'true' == $akismet_result && $activity['is_spam'] )
				$desc = __( 'Flagged as spam by Akismet', 'buddypress' );

			elseif ( 'false' == $akismet_result && !$activity['is_spam'] )
				$desc = __( 'Cleared by Akismet', 'buddypress' );

		} else {
			$who = bp_activity_get_meta( $activity['id'], '_bp_akismet_user' );

			if ( 'true' == $user_result )
				$desc = sprintf( __( 'Flagged as spam by %s', 'buddypress' ), $who );
			else
				$desc = sprintf( __( 'Un-spammed by %s', 'buddypress' ), $who );
		}

		// add a History item to the hover links, just after Edit
		if ( $akismet_result ) {
			$b = array();
			foreach ( $actions as $k => $item ) {
				$b[ $k ] = $item;
				if ( $k == 'edit' )
lechuck's avatar
lechuck committed
100
					$b['history'] = '<a href="' . esc_url( bp_get_admin_url( 'admin.php?page=bp-activity&amp;action=edit&aid=' . $activity['id'] ) ) . '#bp_activity_history"> '. __( 'History', 'buddypress' ) . '</a>';
lechuck's avatar
lechuck committed
101
102
103
104
105
106
			}

			$actions = $b;
		}

		if ( $desc )
lechuck's avatar
lechuck committed
107
			echo '<span class="akismet-status"><a href="' . esc_url( bp_get_admin_url( 'admin.php?page=bp-activity&amp;action=edit&aid=' . $activity['id'] ) ) . '#bp_activity_history">' . htmlspecialchars( $desc ) . '</a></span>';
lechuck's avatar
lechuck committed
108
109
110
111
112
113
114
115
116

		return apply_filters( 'bp_akismet_comment_row_action', $actions );
	}

	/**
	 * Adds a nonce to the member profile status form, and to the reply form of each activity stream item.
	 * This is used by Akismet to help detect spam activity.
	 *
	 * @see http://plugins.trac.wordpress.org/ticket/1232
lechuck's avatar
lechuck committed
117
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
118
119
	 */
	public function add_activity_stream_nonce() {
lechuck's avatar
lechuck committed
120
		$form_id = '_bp_as_nonce';
lechuck's avatar
lechuck committed
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
		$value   = '_bp_as_nonce_' . bp_loggedin_user_id();

		// If we're in the activity stream loop, we can use the current item's ID to make the nonce unique
		if ( 'bp_activity_entry_comments' == current_filter() ) {
			$form_id .= '_' . bp_get_activity_id();
			$value   .= '_' . bp_get_activity_id();
		}

		wp_nonce_field( $value, $form_id, false );
	}

	/**
	 * Check the member's latest (activity) update to see if it's the item that was (just) marked as spam.
	 *
	 * This can't be done in BP_Akismet::check_activity() due to BP-Default's AJAX implementation; see bp_dtheme_post_update().
	 *
	 * @param string $content Activity update text
	 * @param int $user_id User ID
	 * @param int $activity_id Activity ID
	 * @see bp_dtheme_post_update()
lechuck's avatar
lechuck committed
141
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
	 */
	public function check_member_activity_update( $content, $user_id, $activity_id ) {
		// By default, only handle activity updates and activity comments.
		if ( empty( $this->last_activity ) || !in_array( $this->last_activity->type, BP_Akismet::get_activity_types() ) )
			return;

		// Was this $activity_id just marked as spam? If not, bail out.
		if ( !$this->last_activity->id || $activity_id != $this->last_activity->id || 'false' == $this->last_activity->akismet_submission['bp_as_result'] )
			return;

		// It was, so delete the member's latest activity update.
		bp_delete_user_meta( $user_id, 'bp_latest_update' );
	}

	/**
	 * Adds a "mark as spam" button to each activity item for site admins.
	 *
	 * This function is intended to be used inside the activity stream loop.
	 *
lechuck's avatar
lechuck committed
161
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
	 */
	public function add_activity_spam_button() {
		if ( !bp_activity_user_can_mark_spam() )
			return;

		// By default, only handle activity updates and activity comments.
		if ( !in_array( bp_get_activity_type(), BP_Akismet::get_activity_types() ) )
			return;

		bp_button(
			array(
				'block_self' => false,
				'component'  => 'activity',
				'id'         => 'activity_make_spam_' . bp_get_activity_id(),
				'link_class' => 'bp-secondary-action spam-activity confirm button item-button',
				'link_href'  => wp_nonce_url( bp_get_root_domain() . '/' . bp_get_activity_slug() . '/spam/' . bp_get_activity_id() . '/', 'bp_activity_akismet_spam_' . bp_get_activity_id() ),
				'link_text'  => __( 'Spam', 'buddypress' ),
				'wrapper'    => false,
			)
		);
	}

	/**
	 * Adds a "mark as spam" button to each activity COMMENT item for site admins.
	 *
	 * This function is intended to be used inside the activity stream loop.
	 *
lechuck's avatar
lechuck committed
189
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
	 */
	public function add_activity_comment_spam_button() {
		if ( !bp_activity_user_can_mark_spam() )
			return;

		// By default, only handle activity updates and activity comments.
		$current_comment = bp_activity_current_comment();
		if ( empty( $current_comment ) || !in_array( $current_comment->type, BP_Akismet::get_activity_types() ) )
			return;

		bp_button(
			array(
				'block_self' => false,
				'component'  => 'activity',
				'id'         => 'activity_make_spam_' . bp_get_activity_comment_id(),
				'link_class' => 'bp-secondary-action spam-activity-comment confirm',
				'link_href'  => wp_nonce_url( bp_get_root_domain() . '/' . bp_get_activity_slug() . '/spam/' . bp_get_activity_comment_id() . '/?cid=' . bp_get_activity_comment_id(), 'bp_activity_akismet_spam_' . bp_get_activity_comment_id() ),
				'link_text'  => __( 'Spam', 'buddypress' ),
				'wrapper'    => false,
			)
		);
	}

	/**
	 * Get a list of filterable types of activity item that we want Akismet to automatically check for spam.
	 *
	 * @return array List of activity types
lechuck's avatar
lechuck committed
217
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
218
219
220
221
222
223
224
225
226
227
228
	 * @static
	 */
	public static function get_activity_types() {
		return apply_filters( 'bp_akismet_get_activity_types', array( 'activity_comment', 'activity_update' ) );
	}

	/**
	 * Mark activity item as spam
	 *
	 * @param BP_Activity_Activity $activity
	 * @param string $source Either "by_a_person" (e.g. a person has manually marked the activity as spam) or "by_akismet" (automatically spammed).
lechuck's avatar
lechuck committed
229
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
230
231
232
233
234
235
236
237
238
239
240
241
242
	 */
	public function mark_as_spam( $activity, $source ) {
		// Record this item so we can do some tidyup in BP_Akismet::check_member_activity_update()
		$this->last_activity = $activity;

		do_action( 'bp_activity_akismet_mark_as_spam', $activity, $source );
	}

	/**
	 * Mark activity item as ham
	 *
	 * @param BP_Activity_Activity $activity
	 * @param string $source Either "by_a_person" (e.g. a person has manually marked the activity as ham) or "by_akismet" (automatically hammed).
lechuck's avatar
lechuck committed
243
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
244
245
246
247
248
249
250
	 */
	public function mark_as_ham( $activity, $source ) {
		// If the activity was, originally, automatically marked as spam by Akismet, run the @mentions filter as it would have been skipped.
		if ( 'true' == bp_activity_get_meta( $activity->id, '_bp_akismet_result' ) && !bp_activity_get_meta( $activity->id, '_bp_akismet_user_result' ) )
			$activity->content = bp_activity_at_name_filter( $activity->content, $activity->id );

		do_action( 'bp_activity_akismet_mark_as_ham', $activity, $source );
lechuck's avatar
lechuck committed
251
	}
lechuck's avatar
lechuck committed
252
253
254
255
256
257

	/**
	 * Build a data package for the Akismet service to inspect
	 *
	 * @param BP_Activity_Activity $activity
	 * @see http://akismet.com/development/api/#comment-check
lechuck's avatar
lechuck committed
258
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
	 * @static
	 */
	public static function build_akismet_data_package( $activity ) {
		$userdata = get_userdata( $activity->user_id );

		$activity_data                          = array();
		$activity_data['akismet_comment_nonce'] = 'inactive';
		$activity_data['comment_author']        = $userdata->display_name;
		$activity_data['comment_author_email']  = $userdata->user_email;
		$activity_data['comment_author_url']    = bp_core_get_userlink( $userdata->ID, false, true);
		$activity_data['comment_content']       = $activity->content;
		$activity_data['comment_type']          = $activity->type;
		$activity_data['permalink']             = bp_activity_get_permalink( $activity->id, $activity );
		$activity_data['user_ID']               = $userdata->ID;
		$activity_data['user_role']             = akismet_get_user_roles( $userdata->ID );

		/**
		 * Get the nonce if the new activity was submitted through the "what's up, Paul?" form.
		 * This helps Akismet ensure that the update was a valid form submission.
		 */
		if ( !empty( $_POST['_bp_as_nonce'] ) )
			$activity_data['akismet_comment_nonce'] = wp_verify_nonce( $_POST['_bp_as_nonce'], "_bp_as_nonce_{$userdata->ID}" ) ? 'passed' : 'failed';

		/**
		 * If the new activity was a reply to an existing item, check the nonce with the activity parent ID.
		 * This helps Akismet ensure that the update was a valid form submission.
		 */
		elseif ( !empty( $activity->secondary_item_id ) && !empty( $_POST['_bp_as_nonce_' . $activity->secondary_item_id] ) )
			$activity_data['akismet_comment_nonce'] = wp_verify_nonce( $_POST["_bp_as_nonce_{$activity->secondary_item_id}"], "_bp_as_nonce_{$userdata->ID}_{$activity->secondary_item_id}" ) ? 'passed' : 'failed';

		return apply_filters( 'bp_akismet_build_akismet_data_package', $activity_data, $activity );
	}

	/**
	 * Check if the activity item is spam or ham
	 *
	 * @param BP_Activity_Activity $activity The activity item to check
	 * @see http://akismet.com/development/api/
lechuck's avatar
lechuck committed
297
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
	 * @todo Spam counter?
	 * @todo Auto-delete old spam?
	 */
	public function check_activity( $activity ) {
		// By default, only handle activity updates and activity comments.
		if ( !in_array( $activity->type, BP_Akismet::get_activity_types() ) )
			return;

		// Make sure last_activity is clear to avoid any confusion
		$this->last_activity = null;

		// Build data package for Akismet
		$activity_data = BP_Akismet::build_akismet_data_package( $activity );

		// Check with Akismet to see if this is spam
		$activity_data = $this->send_akismet_request( $activity_data, 'check', 'spam' );

		// Record this item
		$this->last_activity = $activity;

		// Store a copy of the data that was submitted to Akismet
		$this->last_activity->akismet_submission = $activity_data;

		// Spam
		if ( 'true' == $activity_data['bp_as_result'] ) {
			// Action for plugin authors
			do_action_ref_array( 'bp_activity_akismet_spam_caught', array( &$activity, $activity_data ) );

			// Mark as spam
			bp_activity_mark_as_spam( $activity, 'by_akismet' );
		}

		// Update activity meta after a spam check
		add_action( 'bp_activity_after_save', array( $this, 'update_activity_akismet_meta' ), 1, 1 );
	}

	/**
	 * Update activity meta after a manual spam change (user initiated)
	 *
	 * @param BP_Activity_Activity $activity The activity to check
lechuck's avatar
lechuck committed
338
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
	 */
	public function update_activity_spam_meta( $activity ) {
		// By default, only handle activity updates and activity comments.
		if ( !in_array( $activity->type, BP_Akismet::get_activity_types() ) )
			return;

		$this->update_activity_history( $activity->id, sprintf( __( '%s reported this activity as spam', 'buddypress' ), bp_get_loggedin_user_username() ), 'report-spam' );
		bp_activity_update_meta( $activity->id, '_bp_akismet_user_result', 'true' );
		bp_activity_update_meta( $activity->id, '_bp_akismet_user', bp_get_loggedin_user_username() );
	}

	/**
	 * Update activity meta after a manual ham change (user initiated)
	 *
	 * @param BP_Activity_Activity $activity The activity to check
lechuck's avatar
lechuck committed
354
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
	 */
	public function update_activity_ham_meta( $activity ) {
		// By default, only handle activity updates and activity comments.
		if ( !in_array( $activity->type, BP_Akismet::get_activity_types() ) )
			return;

		$this->update_activity_history( $activity->id, sprintf( __( '%s reported this activity as not spam', 'buddypress' ), bp_get_loggedin_user_username() ), 'report-ham' );
		bp_activity_update_meta( $activity->id, '_bp_akismet_user_result', 'false' );
		bp_activity_update_meta( $activity->id, '_bp_akismet_user', bp_get_loggedin_user_username() );
	}

	/**
	 * Update activity meta after an automatic spam check (not user initiated)
	 *
	 * @param BP_Activity_Activity $activity The activity to check
lechuck's avatar
lechuck committed
370
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
	 */
	public function update_activity_akismet_meta( $activity ) {
		// Check we're dealing with what was last updated by Akismet
		if ( empty( $this->last_activity ) || !empty( $this->last_activity ) && $activity->id != $this->last_activity->id )
			return;

		// By default, only handle activity updates and activity comments.
		if ( !in_array( $this->last_activity->type, BP_Akismet::get_activity_types() ) )
			return;

		// Spam
		if ( 'true' == $this->last_activity->akismet_submission['bp_as_result'] ) {
			bp_activity_update_meta( $activity->id, '_bp_akismet_result', 'true' );
			$this->update_activity_history( $activity->id, __( 'Akismet caught this item as spam', 'buddypress' ), 'check-spam' );

		// Not spam
		} elseif ( 'false' == $this->last_activity->akismet_submission['bp_as_result'] ) {
			bp_activity_update_meta( $activity->id, '_bp_akismet_result', 'false' );
			$this->update_activity_history( $activity->id, __( 'Akismet cleared this item', 'buddypress' ), 'check-ham' );

		// Uh oh, something's gone horribly wrong. Unexpected result.
		} else {
			bp_activity_update_meta( $activity->id, '_bp_akismet_error', bp_core_current_time() );
			$this->update_activity_history( $activity->id, sprintf( __( 'Akismet was unable to check this item (response: %s), will automatically retry again later.', 'buddypress' ), $this->last_activity->akismet_submission['bp_as_result'] ), 'check-error' );
		}

		// Record the original data which was submitted to Akismet for checking
		bp_activity_update_meta( $activity->id, '_bp_akismet_submission', $this->last_activity->akismet_submission );
	}

	/**
	 * Contact Akismet to check if this is spam or ham
	 *
	 * Props to WordPress core Akismet plugin for alot of this
	 *
	 * @global string $akismet_api_host
	 * @global string $akismet_api_port
	 * @param array $activity_data Packet of information to submit to Akismet
	 * @param string $check "check" or "submit"
	 * @param string $spam "spam" or "ham"
lechuck's avatar
lechuck committed
411
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
	 */
	public function send_akismet_request( $activity_data, $check = 'check', $spam = 'spam' ) {
		global $akismet_api_host, $akismet_api_port;

		// Check that host and port are set, if not, set them
		if ( function_exists( 'akismet_init' ) && ( empty( $akismet_api_host ) || empty( $akismet_api_port ) ) )
			akismet_init();

		$query_string = $path = $response = '';

		$activity_data['blog']         = bp_get_option( 'home' );
		$activity_data['blog_charset'] = bp_get_option( 'blog_charset' );
		$activity_data['blog_lang']    = get_locale();
		$activity_data['referrer']     = $_SERVER['HTTP_REFERER'];
		$activity_data['user_agent']   = bp_core_current_user_ua();
		$activity_data['user_ip']      = bp_core_current_user_ip();

		if ( akismet_test_mode() )
			$activity_data['is_test'] = 'true';

		// Loop through _POST args and rekey strings
		foreach ( $_POST as $key => $value )
			if ( is_string( $value ) && 'cookie' != $key )
				$activity_data['POST_' . $key] = $value;

		// Keys to ignore
		$ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' );

		// Loop through _SERVER args and remove whitelisted keys
		foreach ( $_SERVER as $key => $value ) {

			// Key should not be ignored
			if ( !in_array( $key, $ignore ) && is_string( $value ) ) {
				$activity_data[$key] = $value;

			// Key should be ignored
			} else {
				$activity_data[$key] = '';
			}
		}

		foreach ( $activity_data as $key => $data )
			$query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&';

		if ( 'check' == $check )
			$path = '/1.1/comment-check';
		elseif ( 'submit' == $check )
			$path = '/1.1/submit-' . $spam;

		// Send to Akismet
		add_filter( 'akismet_ua', array( $this, 'buddypress_ua' ) );
		$response = akismet_http_post( $query_string, $akismet_api_host, $path, $akismet_api_port );
		remove_filter( 'akismet_ua', array( $this, 'buddypress_ua' ) );

		// Get the response
		if ( ! empty( $response[1] ) && ! is_wp_error( $response[1] ) )
			$activity_data['bp_as_result'] = $response[1];
		else
			$activity_data['bp_as_result'] = false;

		// Perform a daily tidy up
		if ( ! wp_next_scheduled( 'bp_activity_akismet_delete_old_metadata' ) )
			wp_schedule_event( time(), 'daily', 'bp_activity_akismet_delete_old_metadata' );

		return $activity_data;
	}

	/**
	 * Filters user agent when sending to Akismet.
	 *
	 * @param string $user_agent
lechuck's avatar
lechuck committed
483
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
484
485
486
487
488
489
490
491
492
493
	 */
	public function buddypress_ua( $user_agent ) {
		$user_agent = 'BuddyPress/' . bp_get_version() . ' | Akismet/'. constant( 'AKISMET_VERSION' );
		return $user_agent;
	}

	/**
	 * Adds a "History" meta box to the activity edit screen.
	 *
	 * @param string $screen_action The type of screen that has been requested
lechuck's avatar
lechuck committed
494
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
495
496
497
498
499
500
501
502
503
504
505
506
507
508
	 */
	function add_history_metabox( $screen_action ) {
		// Only proceed if we're on the edit screen
		if ( 'edit' != $screen_action )
			return;

		// Display meta box with a low priority (low position on screen by default)
		add_meta_box( 'bp_activity_history',  __( 'Activity History', 'buddypress' ), array( $this, 'history_metabox' ), get_current_screen()->id, 'normal', 'low' );
	}

	/**
	 * History meta box for the Activity admin edit screen
	 *
	 * @param object $item Activity item
lechuck's avatar
lechuck committed
509
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
	 * @todo Update activity meta to allow >1 record with the same key (iterate through $history).
	 * @see http://buddypress.trac.wordpress.org/ticket/3907
	 */
	function history_metabox( $item ) {
		$history = BP_Akismet::get_activity_history( $item->id );

		if ( empty( $history ) )
			return;

		echo '<div class="akismet-history"><div>';
		printf( _x( '<span>%1$s</span> &mdash; %2$s', 'x hours ago - akismet cleared this item', 'buddypress' ), bp_core_time_since( $history[2] ), esc_html( $history[1] ) );
		echo '</div></div>';
	}

	/**
	 * Update an activity item's Akismet history
	 *
	 * @param int $activity_id Activity item ID
	 * @param string $message Human-readable description of what's changed
	 * @param string $event The type of check we were carrying out
lechuck's avatar
lechuck committed
530
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
	 */
	public function update_activity_history( $activity_id = 0, $message = '', $event = '' ) {
		$event = array(
			'event'   => $event,
			'message' => $message,
			'time'    => akismet_microtime(),
			'user'    => bp_loggedin_user_id(),
		);

		// Save the history data
		bp_activity_update_meta( $activity_id, '_bp_akismet_history', $event );
	}

	/**
	 * Get an activity item's Akismet history
	 *
	 * @param int $activity_id Activity item ID
	 * @return array The activity item's Akismet history
lechuck's avatar
lechuck committed
549
	 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
	 */
	public function get_activity_history( $activity_id = 0 ) {
		$history = bp_activity_get_meta( $activity_id, '_bp_akismet_history' );
		if ( $history === false )
			$history = array();

		// Sort it by the time recorded
		usort( $history, 'akismet_cmp_time' );

		return $history;
	}
}

/**
 * Deletes old spam activity meta data, as _bp_akismet_submission meta can be large.
 *
 * @global object $bp BuddyPress global settings
 * @global wpdb $wpdb WordPress database object
lechuck's avatar
lechuck committed
568
 * @since BuddyPress (1.6)
lechuck's avatar
lechuck committed
569
570
571
572
573
574
575
576
577
 */
function bp_activity_akismet_delete_old_metadata() {
	global $bp, $wpdb;

	$interval = apply_filters( 'bp_activity_akismet_delete_meta_interval', 15 );

	// Enforce a minimum of 1 day
	$interval = max( 1, absint( $interval ) );

lechuck's avatar
lechuck committed
578
	// _bp_akismet_submission meta values are large, so expire them after $interval days regardless of the activity status
lechuck's avatar
lechuck committed
579
580
581
582
583
584
585
586
	$sql          = $wpdb->prepare( "SELECT a.id FROM {$bp->activity->table_name} a LEFT JOIN {$bp->activity->table_name_meta} m ON a.id = m.activity_id WHERE m.meta_key = %s AND DATE_SUB(%s, INTERVAL {$interval} DAY) > a.date_recorded LIMIT 10000", '_bp_akismet_submission', current_time( 'mysql', 1 ) );
	$activity_ids = $wpdb->get_col( $sql );

	if ( ! empty( $activity_ids ) ) {
		foreach ( $activity_ids as $activity_id )
			bp_activity_delete_meta( $activity_id, '_bp_akismet_submission' );
	}
}
lechuck's avatar
lechuck committed
587
add_action( 'bp_activity_akismet_delete_old_metadata', 'bp_activity_akismet_delete_old_metadata' );