BBP_Akismet
Loads Akismet extension
Description
Source
File: bp-forums/extend/akismet.php
class BBP_Akismet { /** * The main bbPress Akismet loader * * @since bbPress (r3277) * * @uses add_filter() */ public function __construct() { $this->setup_actions(); } /** * Setup the admin hooks * * @since bbPress (r3277) * @access private * * @uses add_filter() To add various filters * @uses add_action() To add various actions */ private function setup_actions() { // Prevent debug notices $checks = array(); // bbPress functions to check for spam $checks['check'] = array( 'bbp_new_topic_pre_insert' => 1, // New topic check 'bbp_new_reply_pre_insert' => 1, // New reply check 'bbp_edit_topic_pre_insert' => 1, // Edit topic check 'bbp_edit_reply_pre_insert' => 1 // Edit reply check ); // bbPress functions for spam and ham submissions $checks['submit'] = array( 'bbp_spammed_topic' => 10, // Spammed topic 'bbp_unspammed_topic' => 10, // Unspammed reply 'bbp_spammed_reply' => 10, // Spammed reply 'bbp_unspammed_reply' => 10, // Unspammed reply ); // Add the checks foreach ( $checks as $type => $functions ) foreach ( $functions as $function => $priority ) add_filter( $function, array( $this, $type . '_post' ), $priority ); // Update post meta add_action( 'wp_insert_post', array( $this, 'update_post_meta' ), 10, 2 ); // Admin if ( is_admin() ) { add_action( 'add_meta_boxes', array( $this, 'add_metaboxes' ) ); } } /** * Converts topic/reply data into Akismet comment checking format * * @since bbPress (r3277) * * @param string $post_data * * @uses get_userdata() To get the user data * @uses bbp_filter_anonymous_user_data() To get anonymous user data * @uses bbp_get_topic_permalink() To get the permalink of the topic * @uses bbp_get_reply_url() To get the permalink of the reply * @uses bbp_current_author_ip() To get the IP address of the current user * @uses BBP_Akismet::maybe_spam() To check if post is spam * @uses BBP_Akismet::get_user_roles() To get the role(s) of the current user * @uses do_action() To call the 'bbp_akismet_spam_caught' hook * @uses add_filter() To call the 'bbp_new_reply_pre_set_terms' hook * * @return array Array of post data */ public function check_post( $post_data ) { // Define local variables $user_data = array(); $post_permalink = ''; // Post is not published if ( bbp_get_public_status_id() !== $post_data['post_status'] ) return $post_data; // Cast the post_author to 0 if it's empty if ( empty( $post_data['post_author'] ) ) $post_data['post_author'] = 0; /** Author ************************************************************/ // Get user data $userdata = get_userdata( $post_data['post_author'] ); $anonymous_data = bbp_filter_anonymous_post_data(); // Author is anonymous if ( !empty( $anonymous_data ) ) { $user_data['name'] = $anonymous_data['bbp_anonymous_name']; $user_data['email'] = $anonymous_data['bbp_anonymous_email']; $user_data['website'] = $anonymous_data['bbp_anonymous_website']; // Author is logged in } elseif ( !empty( $userdata ) ) { $user_data['name'] = $userdata->display_name; $user_data['email'] = $userdata->user_email; $user_data['website'] = $userdata->user_url; // Missing author data, so set some empty strings } else { $user_data['name'] = ''; $user_data['email'] = ''; $user_data['website'] = ''; } /** Post **************************************************************/ // Use post parent for permalink if ( !empty( $post_data['post_parent'] ) ) $post_permalink = get_permalink( $post_data['post_parent'] ); // Put post_data back into usable array $_post = array( 'comment_author' => $user_data['name'], 'comment_author_email' => $user_data['email'], 'comment_author_url' => $user_data['website'], 'comment_content' => $post_data['post_content'], 'comment_post_ID' => $post_data['post_parent'], 'comment_type' => $post_data['post_type'], 'permalink' => $post_permalink, 'referrer' => $_SERVER['HTTP_REFERER'], 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 'user_ID' => $post_data['post_author'], 'user_ip' => bbp_current_author_ip(), 'user_role' => $this->get_user_roles( $post_data['post_author'] ), ); // Check the post_data $_post = $this->maybe_spam( $_post ); // Get the result $post_data['bbp_akismet_result'] = $_post['bbp_akismet_result']; unset( $_post['bbp_akismet_result'] ); // Store the data as submitted $post_data['bbp_post_as_submitted'] = $_post; // Allow post_data to be manipulated do_action_ref_array( 'bbp_akismet_check_post', $post_data ); // Spam if ( 'true' === $post_data['bbp_akismet_result'] ) { // Let plugins do their thing do_action( 'bbp_akismet_spam_caught' ); // This is spam $post_data['post_status'] = bbp_get_spam_status_id(); // We don't want your spam tags here add_filter( 'bbp_new_reply_pre_set_terms', array( $this, 'filter_post_terms' ), 1, 3 ); // @todo Spam counter? } // @todo Topic/reply moderation? No true/false response - 'pending' or 'draft' // @todo Auto-delete old spam? // Log the last post $this->last_post = $post_data; // Pass the data back to the filter return $post_data; } /** * Submit a post for spamming or hamming * * @since bbPress (r3277) * * @param int $post_id * * @global wpdb $wpdb WordPress database abstraction object. * @global string $akismet_api_host * @global string $akismet_api_port * @global object $current_user * @global object $current_site * * @uses current_filter() To get the reply_id * @uses get_post() To get the post object * @uses get_the_author_meta() To get the author meta * @uses get_post_meta() To get the post meta * @uses bbp_get_user_profile_url() To get a user's profile url * @uses get_permalink() To get the permalink of the post_parent * @uses BBP_Akismet::get_user_roles() To get the role(s) of the post_author * @uses bbp_current_author_ip() To get the IP address of the current user * @uses BBP_Akismet::maybe_spam() To submit the post as ham or spam * @uses update_post_meta() To update the post meta with some Akismet data * @uses do_action() To call the 'bbp_akismet_submit_spam_post' and 'bbp_akismet_submit_ham_post' hooks * * @return array Array of existing topic terms */ public function submit_post( $post_id = 0 ) { global $current_user, $current_site; // Innocent until proven guilty $request_type = 'ham'; $current_filter = current_filter(); // Check this filter and adjust the $request_type accordingly switch ( $current_filter ) { // Mysterious, and straight from the can case 'bbp_spammed_topic' : case 'bbp_spammed_reply' : $request_type = 'spam'; break; // Honey-glazed, a straight off the bone case 'bbp_unspammed_topic' : case 'bbp_unspammed_reply' : $request_type = 'ham'; break; // Possibly poison... default : return; } // Setup some variables $post_id = (int) $post_id; // Make sure we have a post $_post = get_post( $post_id ); // Bail if get_post() fails if ( empty( $_post ) ) return; // Bail if we're spamming, but the post_status isn't spam if ( ( 'spam' === $request_type ) && ( bbp_get_spam_status_id() !== $_post->post_status ) ) return; // Set some default post_data $post_data = array( 'comment_approved' => $_post->post_status, 'comment_author' => $_post->post_author ? get_the_author_meta( 'display_name', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_name', true ), 'comment_author_email' => $_post->post_author ? get_the_author_meta( 'email', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_email', true ), 'comment_author_url' => $_post->post_author ? bbp_get_user_profile_url( $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_website', true ), 'comment_content' => $_post->post_content, 'comment_date' => $_post->post_date, 'comment_ID' => $post_id, 'comment_post_ID' => $_post->post_parent, 'comment_type' => $_post->post_type, 'permalink' => get_permalink( $post_id ), 'user_ID' => $_post->post_author, 'user_ip' => get_post_meta( $post_id, '_bbp_author_ip', true ), 'user_role' => $this->get_user_roles( $_post->post_author ), ); // Use the original version stored in post_meta if available $as_submitted = get_post_meta( $post_id, '_bbp_akismet_as_submitted', true ); if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) $post_data = array_merge( $post_data, $as_submitted ); // Add the reporter IP address $post_data['reporter_ip'] = bbp_current_author_ip(); // Add some reporter info if ( is_object( $current_user ) ) $post_data['reporter'] = $current_user->user_login; // Add the current site domain if ( is_object( $current_site ) ) $post_data['site_domain'] = $current_site->domain; // Place your slide beneath the microscope $post_data = $this->maybe_spam( $post_data, 'submit', $request_type ); // Manual user action if ( isset( $post_data['reporter'] ) ) { // What kind of action switch ( $request_type ) { // Spammy case 'spam' : $this->update_post_history( $post_id, sprintf( esc_html__( '%1$s reported this %2$s as spam', 'buddyboss' ), $post_data['reporter'], $post_data['comment_type'] ), 'report-spam' ); update_post_meta( $post_id, '_bbp_akismet_user_result', 'true' ); update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] ); break; // Hammy case 'ham' : $this->update_post_history( $post_id, sprintf( esc_html__( '%1$s reported this %2$s as not spam', 'buddyboss' ), $post_data['reporter'], $post_data['comment_type'] ), 'report-ham' ); update_post_meta( $post_id, '_bbp_akismet_user_result', 'false' ); update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] ); // @todo Topic term revision history break; // Possible other actions default : break; } } do_action( 'bbp_akismet_submit_' . $request_type . '_post', $post_id, $post_data['bbp_akismet_result'] ); } /** * Ping Akismet service and check for spam/ham response * * @since bbPress (r3277) * * @param array $post_data * @param string $check Accepts check|submit * @param string $spam Accepts spam|ham * * @global string $akismet_api_host * @global string $akismet_api_port * * @uses akismet_test_mode() To determine if Akismet is in test mode * @uses akismet_http_post() To send data to the mothership for processing * * @return array Array of post data */ private function maybe_spam( $post_data, $check = 'check', $spam = 'spam' ) { global $akismet_api_host, $akismet_api_port; // Define variables $query_string = $path = $response = ''; // Populate post data $post_data['blog'] = get_option( 'home' ); $post_data['blog_charset'] = get_option( 'blog_charset' ); $post_data['blog_lang'] = get_locale(); $post_data['referrer'] = $_SERVER['HTTP_REFERER']; $post_data['user_agent'] = $_SERVER['HTTP_USER_AGENT']; // Akismet Test Mode if ( akismet_test_mode() ) $post_data['is_test'] = 'true'; // Loop through _POST args and rekey strings foreach ( $_POST as $key => $value ) if ( is_string( $value ) ) $post_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 ) ) { $post_data[$key] = $value; // Key should be ignored } else { $post_data[$key] = ''; } } // Ready... foreach ( $post_data as $key => $data ) $query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&'; // Aim... if ( 'check' === $check ) { $path = '/1.1/comment-check'; } elseif ( 'submit' === $check ) { $path = '/1.1/submit-' . $spam; } // Fire! $response = $this->http_post( $query_string, $akismet_api_host, $path, $akismet_api_port ); // Check the high-speed cam if ( !empty( $response[1] ) ) { $post_data['bbp_akismet_result'] = $response[1]; } else { $post_data['bbp_akismet_result'] = esc_html__( 'No response', 'buddyboss' ); } // This is ham return $post_data; } /** * Update post meta after a spam check * * @since bbPress (r3308) * * @param int $post_id * @param object $_post * * @global object $this->last_post * * @uses get_post() To get the post object * @uses get_userdata() To get the user data * @uses bbp_filter_anonymous_user_data() To get anonymous user data * @uses update_post_meta() To update post meta with Akismet data * @uses BBP_Akismet::update_post_history() To update post Akismet history */ public function update_post_meta( $post_id = 0, $_post = false ) { // Define local variable(s) $as_submitted = false; // Setup some variables $post_id = (int) $post_id; // Ensure we have a post object if ( empty( $_post ) ) $_post = get_post( $post_id ); // Set up Akismet last post data if ( !empty( $this->last_post ) ) $as_submitted = $this->last_post['bbp_post_as_submitted']; // wp_insert_post() might be called in other contexts. Ensure this is // the same topic/reply as was checked by BBP_Akismet::check_post() if ( is_object( $_post ) && !empty( $this->last_post ) && is_array( $as_submitted ) ) { // Get user data $userdata = get_userdata( $_post->post_author ); $anonymous_data = bbp_filter_anonymous_post_data(); // More checks if ( intval( $as_submitted['comment_post_ID'] ) === intval( $_post->post_parent ) && $as_submitted['comment_author'] === ( $anonymous_data ? $anonymous_data['bbp_anonymous_name'] : $userdata->display_name ) && $as_submitted['comment_author_email'] === ( $anonymous_data ? $anonymous_data['bbp_anonymous_email'] : $userdata->user_email ) ) { // Normal result: true if ( $this->last_post['bbp_akismet_result'] === 'true' ) { // Leave a trail so other's know what we did update_post_meta( $post_id, '_bbp_akismet_result', 'true' ); $this->update_post_history( $post_id, esc_html__( 'Akismet caught this post as spam', 'buddyboss' ), 'check-spam' ); // If post_status isn't the spam status, as expected, leave a note if ( bbp_get_spam_status_id() !== $_post->post_status ) { $this->update_post_history( $post_id, sprintf( esc_html__( 'Post status was changed to %s', 'buddyboss' ), $_post->post_status ), 'status-changed-' . $_post->post_status ); } // Normal result: false } elseif ( $this->last_post['bbp_akismet_result'] === 'false' ) { // Leave a trail so other's know what we did update_post_meta( $post_id, '_bbp_akismet_result', 'false' ); $this->update_post_history( $post_id, esc_html__( 'Akismet cleared this post as not spam', 'buddyboss' ), 'check-ham' ); // If post_status is the spam status, which isn't expected, leave a note if ( bbp_get_spam_status_id() === $_post->post_status ) { // @todo Use wp_blacklist_check() $this->update_post_history( $post_id, sprintf( esc_html__( 'Post status was changed to %s', 'buddyboss' ), $_post->post_status ), 'status-changed-' . $_post->post_status ); } // Abnormal result: error } else { // Leave a trail so other's know what we did update_post_meta( $post_id, '_bbp_akismet_error', time() ); $this->update_post_history( $post_id, sprintf( esc_html__( 'Akismet was unable to check this post (response: %s), will automatically retry again later.', 'buddyboss' ), $this->last_post['bbp_akismet_result'] ), 'check-error' ); } // Record the complete original data as submitted for checking if ( isset( $this->last_post['bbp_post_as_submitted'] ) ) { update_post_meta( $post_id, '_bbp_akismet_as_submitted', $this->last_post['bbp_post_as_submitted'] ); } } } } /** * Update a post's Akismet history * * @since bbPress (r3308) * * @param int $post_id * @param string $message * @param string $event * * @uses wp_get_current_user() To get the current_user object * @uses add_post_meta() Add Akismet post history */ private function update_post_history( $post_id = 0, $message = null, $event = null ) { // Define local variable(s) $user = ''; // Get the current user $current_user = wp_get_current_user(); // Get the user's login name if possible if ( is_object( $current_user ) && isset( $current_user->user_login ) ) $user = $current_user->user_login; // Setup the event to be saved $event = array( 'time' => akismet_microtime(), 'message' => $message, 'event' => $event, 'user' => $user, ); // Save the event data add_post_meta( $post_id, '_bbp_akismet_history', $event ); } /** * Get a post's Akismet history * * @since bbPress (r3308) * * @param int $post_id * * @uses wp_get_current_user() To get the current_user object * @uses get_post_meta() Get a post's Akismet history * * @return array Array of a post's Akismet history */ public function get_post_history( $post_id = 0 ) { // Retrieve any previous history $history = get_post_meta( $post_id, '_bbp_akismet_history' ); // Sort it by the time recorded usort( $history, 'akismet_cmp_time' ); return $history; } /** * Handle any terms submitted with a post flagged as spam * * @since bbPress (r3308) * * @param string $terms Comma-separated list of terms * @param int $topic_id * @param int $reply_id * * @uses bbp_get_reply_id() To get the reply_id * @uses bbp_get_topic_id() To get the topic_id * @uses wp_get_object_terms() To a post's current terms * @uses update_post_meta() To add spam terms to post meta * * @return array Array of existing topic terms */ public function filter_post_terms( $terms = '', $topic_id = 0, $reply_id = 0 ) { // Validate the reply_id and topic_id $reply_id = bbp_get_reply_id( $reply_id ); $topic_id = bbp_get_topic_id( $topic_id ); // Get any pre-existing terms $existing_terms = wp_get_object_terms( $topic_id, bbp_get_topic_tag_tax_id(), array( 'fields' => 'names' ) ); // Save the terms for later in case the reply gets hammed if ( !empty( $terms ) ) update_post_meta( $reply_id, '_bbp_akismet_spam_terms', $terms ); // Keep the topic tags the same for now return $existing_terms; } /** * Submit data to Akismet service with unique bbPress User Agent * * This code is directly taken from the akismet_http_post() function and * documented to bbPress 2.0 standard. * * @since bbPress (r3466) * * @param string $request The request we are sending * @param string $host The host to send our request to * @param string $path The path from the host * @param string $port The port to use * @param string $ip Optional Override $host with an IP address * @uses bbp_get_version() To get the current bbPress version * @return mixed WP_Error on error, array on success, empty on failure */ private function http_post( $request, $host, $path, $port = 80, $ip = '' ) { // Preload required variables $bbp_version = bbp_get_version(); $content_length = strlen( $request ); $http_host = $host; $blog_charset = get_option( 'blog_charset' ); $response = ''; $errno = null; $errstr = null; // Untque User Agent $akismet_ua = "bbPress/{$bbp_version} | "; $akismet_ua .= 'Akismet/' . constant( 'AKISMET_VERSION' ); // Use specific IP (if provided) if ( !empty( $ip ) && long2ip( ip2long( $ip ) ) ) $http_host = $ip; // WP HTTP class is available if ( function_exists( 'wp_remote_post' ) ) { // Setup the arguments $http_args = array( 'body' => $request, 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded; charset=' . $blog_charset, 'Host' => $host, 'User-Agent' => $akismet_ua ), 'httpversion' => '1.0', 'timeout' => 15 ); // Where we are sending our request $akismet_url = 'http://' . $http_host . $path; // Send the request $response = wp_remote_post( $akismet_url, $http_args ); // Bail if the response is an error if ( is_wp_error( $response ) ) return ''; // No errors so return response return array( $response['headers'], $response['body'] ); // WP HTTP class is not available (Why not?) } else { // Header info to use with our socket $http_request = "POST {$path} HTTP/1.0\r\n"; $http_request .= "Host: {$host}\r\n"; $http_request .= "Content-Type: application/x-www-form-urlencoded; charset={$blog_charset}\r\n"; $http_request .= "Content-Length: {$content_length}\r\n"; $http_request .= "User-Agent: {$akismet_ua}\r\n"; $http_request .= "\r\n"; $http_request .= $request; // Open a socket connection if ( false !== ( $fs = @fsockopen( $http_host, $port, $errno, $errstr, 10 ) ) ) { // Write our request to the pointer fwrite( $fs, $http_request ); // Loop through pointer and compile a response while ( !feof( $fs ) ) { // One TCP-IP packet at a time $response .= fgets( $fs, 1160 ); } // Close our socket fclose( $fs ); // Explode the response into usable data $response = explode( "\r\n\r\n", $response, 2 ); } // Return the response ('' if error/empty) return $response; } } /** * Return a user's roles on this site (including super_admin) * * @since bbPress (r4812) * * @param type $user_id * @return boolean */ private function get_user_roles( $user_id = 0 ) { // Default return value $roles = array(); // Bail if cannot query the user if ( ! class_exists( 'WP_User' ) || empty( $user_id ) ) { return false; } // User ID $user = new WP_User( $user_id ); if ( isset( $user->roles ) ) { $roles = (array) $user->roles; } // Super admin if ( is_multisite() && is_super_admin( $user_id ) ) { $roles[] = 'super_admin'; } return implode( ',', $roles ); } /** Admin *****************************************************************/ /** * Add Aksimet History metaboxes to topics and replies * * @since bbPress (r5049) */ public function add_metaboxes() { // Topics add_meta_box( 'bbp_akismet_topic_history', __( 'Akismet History', 'buddyboss' ), array( $this, 'history_metabox' ), bbp_get_topic_post_type(), 'normal', 'core' ); // Replies add_meta_box( 'bbp_akismet_reply_history', __( 'Akismet History', 'buddyboss' ), array( $this, 'history_metabox' ), bbp_get_reply_post_type(), 'normal', 'core' ); } /** * Output for Akismet History metabox * * @since bbPress (r5049) * * @uses get_post_history() To get the Akismet history for the post * @uses get_the_ID() To get the post ID * @uses bbp_time_since() To get the human readable time */ public function history_metabox() { // Post ID $history = $this->get_post_history( get_the_ID() ); ?> <div class="akismet-history" style="margin: 13px 0;"> <?php if ( !empty( $history ) ) : ?> <table> <tbody> <?php foreach ( $history as $row ) : ?> <tr> <td style="color: #999; text-align: right; white-space: nowrap;"> <span title="<?php echo esc_attr( date( 'D d M Y @ h:i:m a', $row['time'] ) . ' GMT' ); ?>"> <?php bbp_time_since( $row['time'], false, true ); ?> </span> </td> <td style="padding-left: 5px;"> <?php echo esc_html( $row['message'] ); ?> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php else : ?> <p><?php esc_html_e( 'No recorded history. Akismet has not checked this post.', 'buddyboss' ); ?></p> <?php endif; ?> </div> <?php } }
Changelog
Version | Description |
---|---|
bbPress (r3277) | Introduced. |
Methods
- __construct — The main bbPress Akismet loader
- add_metaboxes — Add Aksimet History metaboxes to topics and replies
- check_post — Converts topic/reply data into Akismet comment checking format
- filter_post_terms — Handle any terms submitted with a post flagged as spam
- get_post_history — Get a post's Akismet history
- get_user_roles — Return a user's roles on this site (including super_admin)
- history_metabox — Output for Akismet History metabox
- http_post — Submit data to Akismet service with unique bbPress User Agent
- maybe_spam — Ping Akismet service and check for spam/ham response
- setup_actions — Setup the admin hooks
- submit_post — Submit a post for spamming or hamming
- update_post_history — Update a post's Akismet history
- update_post_meta — Update post meta after a spam check
Questions?
We're always happy to help with code or other questions you might have! Search our developer docs, contact support, or connect with our sales team.