[ Index ] |
PHP Cross Reference of BBPress |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Main bbPress Akismet Class 5 * 6 * @package bbPress 7 * @subpackage Akismet 8 */ 9 10 // Exit if accessed directly 11 defined( 'ABSPATH' ) || exit; 12 13 if ( ! class_exists( 'BBP_Akismet' ) ) : 14 /** 15 * Loads Akismet extension 16 * 17 * @since 2.0.0 bbPress (r3277) 18 * 19 * @package bbPress 20 * @subpackage Akismet 21 */ 22 class BBP_Akismet { 23 24 /** 25 * The main bbPress Akismet loader 26 * 27 * @since 2.0.0 bbPress (r3277) 28 */ 29 public function __construct() { 30 $this->setup_actions(); 31 } 32 33 /** 34 * Setup the admin hooks 35 * 36 * @since 2.0.0 bbPress (r3277) 37 * 38 * @access private 39 */ 40 private function setup_actions() { 41 42 // Prevent debug notices 43 $checks = array(); 44 45 // bbPress functions to check for spam 46 $checks['check'] = array( 47 'bbp_new_topic_pre_insert' => 1, // New topic check 48 'bbp_new_reply_pre_insert' => 1, // New reply check 49 'bbp_edit_topic_pre_insert' => 1, // Edit topic check 50 'bbp_edit_reply_pre_insert' => 1 // Edit reply check 51 ); 52 53 // bbPress functions for spam and ham submissions 54 $checks['submit'] = array( 55 'bbp_spammed_topic' => 10, // Spammed topic 56 'bbp_unspammed_topic' => 10, // Unspammed reply 57 'bbp_spammed_reply' => 10, // Spammed reply 58 'bbp_unspammed_reply' => 10, // Unspammed reply 59 ); 60 61 // Add the checks 62 foreach ( $checks as $type => $functions ) { 63 foreach ( $functions as $function => $priority ) { 64 add_filter( $function, array( $this, $type . '_post' ), $priority ); 65 } 66 } 67 68 // Update post meta 69 add_action( 'wp_insert_post', array( $this, 'update_post_meta' ), 10, 2 ); 70 71 // Cleanup 72 add_action( 'akismet_scheduled_delete', array( $this, 'delete_old_spam' ) ); 73 add_action( 'akismet_scheduled_delete', array( $this, 'delete_old_spam_meta' ) ); 74 add_action( 'akismet_scheduled_delete', array( $this, 'delete_orphaned_spam_meta' ) ); 75 76 // Admin 77 if ( is_admin() ) { 78 add_action( 'add_meta_boxes', array( $this, 'add_metaboxes' ) ); 79 } 80 } 81 82 /** 83 * Converts topic/reply data into Akismet comment checking format 84 * 85 * @since 2.0.0 bbPress (r3277) 86 * 87 * @param array $post_data 88 * 89 * @return array Array of post data 90 */ 91 public function check_post( $post_data = array() ) { 92 93 // Define local variables 94 $user_data = array(); 95 $post_permalink = ''; 96 97 // Cast the post_author to 0 if it's empty 98 if ( empty( $post_data['post_author'] ) ) { 99 $post_data['post_author'] = 0; 100 } 101 102 /** Author ************************************************************/ 103 104 $user_data['last_active'] = ''; 105 $user_data['registered'] = date( 'Y-m-d H:i:s'); 106 $user_data['total_posts'] = (int) bbp_get_user_post_count( $post_data['post_author'] ); 107 108 // Get user data 109 $userdata = get_userdata( $post_data['post_author'] ); 110 $anonymous_data = bbp_filter_anonymous_post_data(); 111 112 // Author is anonymous 113 if ( ! bbp_has_errors() ) { 114 $user_data['name'] = $anonymous_data['bbp_anonymous_name']; 115 $user_data['email'] = $anonymous_data['bbp_anonymous_email']; 116 $user_data['website'] = $anonymous_data['bbp_anonymous_website']; 117 118 // Author is logged in 119 } elseif ( ! empty( $userdata ) ) { 120 $user_data['name'] = $userdata->display_name; 121 $user_data['email'] = $userdata->user_email; 122 $user_data['website'] = $userdata->user_url; 123 $user_data['registered'] = $userdata->user_registered; 124 125 // Missing author data, so set some empty strings 126 } else { 127 $user_data['name'] = ''; 128 $user_data['email'] = ''; 129 $user_data['website'] = ''; 130 } 131 132 /** Post **************************************************************/ 133 134 if ( ! empty( $post_data['post_parent'] ) ) { 135 136 // Use post parent for permalink 137 $post_permalink = get_permalink( $post_data['post_parent'] ); 138 139 // Use post parent to get datetime of last reply on this topic 140 $reply_id = bbp_get_topic_last_reply_id( $post_data['post_parent'] ); 141 if ( ! empty( $reply_id ) ) { 142 $user_data['last_active'] = get_post_field( 'post_date', $reply_id ); 143 } 144 } 145 146 // Pass title & content together into comment content 147 $_post_content = trim( $post_data['post_title'] . "\n\n" . $post_data['post_content'] ); 148 149 // Check if the post data is spammy... 150 $_post = $this->maybe_spam( array( 151 'comment_author' => $user_data['name'], 152 'comment_author_email' => $user_data['email'], 153 'comment_author_url' => $user_data['website'], 154 'comment_content' => $_post_content, 155 'comment_post_ID' => $post_data['post_parent'], 156 'comment_type' => $post_data['post_type'], 157 'comment_total' => $user_data['total_posts'], 158 'comment_last_active_gmt' => $user_data['last_active'], 159 'comment_account_registered_gmt' => $user_data['registered'], 160 'permalink' => $post_permalink, 161 'referrer' => wp_get_raw_referer(), 162 'user_agent' => bbp_current_author_ua(), 163 'user_ID' => $post_data['post_author'], 164 'user_ip' => bbp_current_author_ip(), 165 'user_role' => $this->get_user_roles( $post_data['post_author'] ), 166 ) ); 167 168 // Set the results (from maybe_spam() above) 169 $post_data['bbp_akismet_result_headers'] = $_post['bbp_akismet_result_headers']; 170 $post_data['bbp_akismet_result'] = $_post['bbp_akismet_result']; 171 $post_data['bbp_post_as_submitted'] = $_post; 172 173 // Avoid recursion by unsetting results from post-as-submitted 174 unset( 175 $post_data['bbp_post_as_submitted']['bbp_akismet_result_headers'], 176 $post_data['bbp_post_as_submitted']['bbp_akismet_result'] 177 ); 178 179 // Allow post_data to be manipulated 180 $post_data = apply_filters( 'bbp_akismet_check_post', $post_data ); 181 182 // Parse and log the last response 183 $this->last_post = $this->parse_response( $post_data ); 184 185 // Return the last response back to the filter 186 return $this->last_post; 187 } 188 189 /** 190 * Parse the response from the Akismet service, and alter the post data as 191 * necessary. For example, switch the status to `spam` if spammy. 192 * 193 * Note: this method also is responsible for allowing users who can moderate to 194 * never have their posts marked as spam. This is because they are "trusted" 195 * users. However, their posts are still sent to Akismet to be checked. 196 * 197 * @since 2.6.0 bbPress (r6873) 198 * 199 * @param array $post_data 200 * 201 * @return array 202 */ 203 private function parse_response( $post_data = array() ) { 204 205 // Get the parent ID of the post as submitted 206 $parent_id = ! empty( $post_data['bbp_post_as_submitted']['comment_post_ID'] ) 207 ? absint( $post_data['bbp_post_as_submitted']['comment_post_ID'] ) 208 : 0; 209 210 // Allow moderators to skip spam (includes per-forum moderators via $parent_id) 211 $skip_spam = current_user_can( 'moderate', $parent_id ); 212 213 // Bail early if current user can skip spam enforcement 214 if ( apply_filters( 'bbp_bypass_spam_enforcement', $skip_spam, $post_data ) ) { 215 return $post_data; 216 } 217 218 // Discard obvious spam 219 if ( get_option( 'akismet_strictness' ) ) { 220 221 // Akismet is 100% confident this is spam 222 if ( 223 ! empty( $post_data['bbp_akismet_result_headers']['x-akismet-pro-tip'] ) 224 && 225 ( 'discard' === $post_data['bbp_akismet_result_headers']['x-akismet-pro-tip'] ) 226 ) { 227 228 // URL to redirect to (current, or forum root) 229 $redirect_to = ( ! empty( $_SERVER['HTTP_HOST'] ) && ! empty( $_SERVER['REQUEST_URI'] ) ) 230 ? bbp_get_url_scheme() . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] 231 : bbp_get_root_url(); 232 233 // Do the redirect (post data not saved!) 234 bbp_redirect( $redirect_to ); 235 } 236 } 237 238 // Result is spam, so set the status as such 239 if ( 'true' === $post_data['bbp_akismet_result'] ) { 240 241 // Let plugins do their thing 242 do_action( 'bbp_akismet_spam_caught' ); 243 244 // Set post_status to spam 245 $post_data['post_status'] = bbp_get_spam_status_id(); 246 247 // Filter spammy tags into meta data 248 add_filter( 'bbp_new_reply_pre_set_terms', array( $this, 'filter_post_terms' ), 1, 3 ); 249 } 250 251 // Return the (potentially modified) post data 252 return $post_data; 253 } 254 255 /** 256 * Submit a post for spamming or hamming 257 * 258 * @since 2.0.0 bbPress (r3277) 259 * 260 * @param int $post_id 261 * 262 * @global string $akismet_api_host 263 * @global string $akismet_api_port 264 * @global object $current_user 265 * @global object $current_site 266 * 267 * @return array Array of existing topic terms 268 */ 269 public function submit_post( $post_id = 0 ) { 270 global $current_user, $current_site; 271 272 // Innocent until proven guilty 273 $request_type = 'ham'; 274 $current_filter = current_filter(); 275 276 // Check this filter and adjust the $request_type accordingly 277 switch ( $current_filter ) { 278 279 // Mysterious, and straight from the can 280 case 'bbp_spammed_topic' : 281 case 'bbp_spammed_reply' : 282 $request_type = 'spam'; 283 break; 284 285 // Honey-glazed, a straight off the bone 286 case 'bbp_unspammed_topic' : 287 case 'bbp_unspammed_reply' : 288 $request_type = 'ham'; 289 break; 290 291 // Possibly poison... 292 default : 293 return; 294 } 295 296 // Setup some variables 297 $post_id = (int) $post_id; 298 299 // Make sure we have a post 300 $_post = get_post( $post_id ); 301 302 // Bail if get_post() fails 303 if ( empty( $_post ) ) { 304 return; 305 } 306 307 // Bail if we're spamming, but the post_status isn't spam 308 if ( ( 'spam' === $request_type ) && ( bbp_get_spam_status_id() !== $_post->post_status ) ) { 309 return; 310 } 311 312 // Pass title & content together into comment content 313 $_post_content = trim( $_post->post_title . "\n\n" . $_post->post_content ); 314 315 // Set some default post_data 316 $post_data = array( 317 'comment_approved' => $_post->post_status, 318 'comment_author' => $_post->post_author ? get_the_author_meta( 'display_name', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_name', true ), 319 'comment_author_email' => $_post->post_author ? get_the_author_meta( 'email', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_email', true ), 320 'comment_author_url' => $_post->post_author ? bbp_get_user_profile_url( $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_website', true ), 321 'comment_content' => $_post_content, 322 'comment_date_gmt' => $_post->post_date_gmt, 323 'comment_ID' => $post_id, 324 'comment_post_ID' => $_post->post_parent, 325 'comment_type' => $_post->post_type, 326 'permalink' => get_permalink( $post_id ), 327 'user_ID' => $_post->post_author, 328 'user_ip' => get_post_meta( $post_id, '_bbp_author_ip', true ), 329 'user_role' => $this->get_user_roles( $_post->post_author ), 330 ); 331 332 // Use the original version stored in post_meta if available 333 $as_submitted = get_post_meta( $post_id, '_bbp_akismet_as_submitted', true ); 334 if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) { 335 $post_data = array_merge( $post_data, $as_submitted ); 336 } 337 338 // Add the reporter IP address 339 $post_data['reporter_ip'] = bbp_current_author_ip(); 340 341 // Add some reporter info 342 if ( is_object( $current_user ) ) { 343 $post_data['reporter'] = $current_user->user_login; 344 } 345 346 // Add the current site domain 347 if ( is_object( $current_site ) ) { 348 $post_data['site_domain'] = $current_site->domain; 349 } 350 351 // Place your slide beneath the microscope 352 $post_data = $this->maybe_spam( $post_data, 'submit', $request_type ); 353 354 // Manual user action 355 if ( isset( $post_data['reporter'] ) ) { 356 357 // What kind of action 358 switch ( $request_type ) { 359 360 // Spammy 361 case 'spam' : 362 if ( 'topic' === $post_data['comment_type'] ) { 363 /* translators: %s: reporter name */ 364 $message = sprintf( esc_html__( '%s reported this topic as spam', 'bbpress' ), 365 $post_data['reporter'] 366 ); 367 } elseif ( 'reply' === $post_data['comment_type'] ) { 368 /* translators: %s: reporter name */ 369 $message = sprintf( esc_html__( '%s reported this reply as spam', 'bbpress' ), 370 $post_data['reporter'] 371 ); 372 } else { 373 /* translators: 1: reporter name, 2: comment type */ 374 $message = sprintf( esc_html__( '%1$s reported this %2$s as spam', 'bbpress' ), 375 $post_data['reporter'], 376 $post_data['comment_type'] 377 ); 378 } 379 380 $this->update_post_history( $post_id, $message, 'report-spam' ); 381 update_post_meta( $post_id, '_bbp_akismet_user_result', 'true' ); 382 update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] ); 383 break; 384 385 // Hammy 386 case 'ham' : 387 if ( 'topic' === $post_data['comment_type'] ) { 388 /* translators: %s: reporter name */ 389 $message = sprintf( esc_html__( '%s reported this topic as not spam', 'bbpress' ), 390 $post_data['reporter'] 391 ); 392 } elseif ( 'reply' === $post_data['comment_type'] ) { 393 /* translators: %s: reporter name */ 394 $message = sprintf( esc_html__( '%s reported this reply as not spam', 'bbpress' ), 395 $post_data['reporter'] 396 ); 397 } else { 398 /* translators: 1: reporter name, 2: comment type */ 399 $message = sprintf( esc_html__( '%1$s reported this %2$s as not spam', 'bbpress' ), 400 $post_data['reporter'], 401 $post_data['comment_type'] 402 ); 403 } 404 405 $this->update_post_history( $post_id, $message, 'report-ham' ); 406 update_post_meta( $post_id, '_bbp_akismet_user_result', 'false' ); 407 update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] ); 408 409 // @todo Topic term revision history 410 break; 411 412 // Possible other actions 413 default : 414 break; 415 } 416 } 417 418 do_action( 'bbp_akismet_submit_' . $request_type . '_post', $post_id, $post_data['bbp_akismet_result'] ); 419 } 420 421 /** 422 * Ping Akismet service and check for spam/ham response 423 * 424 * @since 2.0.0 bbPress (r3277) 425 * 426 * @param array $post_data 427 * @param string $check Accepts check|submit 428 * @param string $spam Accepts spam|ham 429 * 430 * @global string $akismet_api_host 431 * @global string $akismet_api_port 432 * 433 * @return array Array of post data 434 */ 435 private function maybe_spam( $post_data = array(), $check = 'check', $spam = 'spam' ) { 436 global $akismet_api_host, $akismet_api_port; 437 438 // Define variables 439 $query_string = $path = ''; 440 $response = array( '', '' ); 441 442 // Make sure post data is an array 443 if ( ! is_array( $post_data ) ) { 444 $post_data = array(); 445 } 446 447 // Populate post data 448 $post_data['blog'] = get_option( 'home' ); 449 $post_data['blog_charset'] = get_option( 'blog_charset' ); 450 $post_data['blog_lang'] = get_locale(); 451 $post_data['referrer'] = wp_get_raw_referer(); 452 $post_data['user_agent'] = bbp_current_author_ua(); 453 454 // Loop through _POST args and rekey strings 455 if ( ! empty( $_POST ) && is_countable( $_POST ) ) { 456 foreach ( $_POST as $key => $value ) { 457 if ( is_string( $value ) ) { 458 $post_data[ 'POST_' . $key ] = $value; 459 } 460 } 461 } 462 463 // Loop through _SERVER args and remove allowed keys 464 if ( ! empty( $_SERVER ) && is_countable( $_SERVER ) ) { 465 466 // Keys to ignore 467 $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ); 468 469 foreach ( $_SERVER as $key => $value ) { 470 471 // Key should not be ignored 472 if ( ! in_array( $key, $ignore, true ) && is_string( $value ) ) { 473 $post_data[ $key ] = $value; 474 475 // Key should be ignored 476 } else { 477 $post_data[ $key ] = ''; 478 } 479 } 480 } 481 482 // Encode post data 483 if ( ! empty( $post_data ) && is_countable( $post_data ) ) { 484 foreach ( $post_data as $key => $data ) { 485 $query_string .= $key . '=' . urlencode( wp_unslash( $data ) ) . '&'; 486 } 487 } 488 489 // Only accepts spam|ham 490 if ( ! in_array( $spam, array( 'spam', 'ham' ), true ) ) { 491 $spam = 'spam'; 492 } 493 494 // Setup the API route 495 if ( 'check' === $check ) { 496 $path = '/1.1/comment-check'; 497 } elseif ( 'submit' === $check ) { 498 $path = '/1.1/submit-' . $spam; 499 } 500 501 // Send data to Akismet 502 if ( ! apply_filters( 'bbp_bypass_check_for_spam', false, $post_data ) ) { 503 $response = $this->http_post( $query_string, $akismet_api_host, $path, $akismet_api_port ); 504 } 505 506 // Set the result headers 507 $post_data['bbp_akismet_result_headers'] = ! empty( $response[0] ) 508 ? $response[0] // raw 509 : esc_html__( 'No response', 'bbpress' ); 510 511 // Set the result 512 $post_data['bbp_akismet_result'] = ! empty( $response[1] ) 513 ? $response[1] // raw 514 : esc_html__( 'No response', 'bbpress' ); 515 516 // Return the post data, with the results of the external Akismet request 517 return $post_data; 518 } 519 520 /** 521 * Update post meta after a spam check 522 * 523 * @since 2.0.0 bbPress (r3308) 524 * 525 * @param int $post_id 526 * @param object $_post 527 * 528 * @global object $this->last_post 529 */ 530 public function update_post_meta( $post_id = 0, $_post = false ) { 531 532 // Define local variable(s) 533 $as_submitted = false; 534 535 // Setup some variables 536 $post_id = (int) $post_id; 537 538 // Ensure we have a post object 539 if ( empty( $_post ) ) { 540 $_post = get_post( $post_id ); 541 } 542 543 // Set up Akismet last post data 544 if ( ! empty( $this->last_post['bbp_post_as_submitted'] ) ) { 545 $as_submitted = $this->last_post['bbp_post_as_submitted']; 546 } 547 548 // wp_insert_post() might be called in other contexts. Ensure this is 549 // the same topic/reply as was checked by BBP_Akismet::check_post() 550 if ( is_object( $_post ) && ! empty( $this->last_post ) && is_array( $as_submitted ) ) { 551 552 // Get user data 553 $userdata = get_userdata( $_post->post_author ); 554 $anonymous_data = bbp_filter_anonymous_post_data(); 555 556 // Which name? 557 $name = ! empty( $anonymous_data['bbp_anonymous_name'] ) 558 ? $anonymous_data['bbp_anonymous_name'] 559 : $userdata->display_name; 560 561 // Which email? 562 $email = ! empty( $anonymous_data['bbp_anonymous_email'] ) 563 ? $anonymous_data['bbp_anonymous_email'] 564 : $userdata->user_email; 565 566 // More checks 567 if ( 568 569 // Post matches 570 ( intval( $as_submitted['comment_post_ID'] ) === intval( $_post->post_parent ) ) 571 572 && 573 574 // Name matches 575 ( $as_submitted['comment_author'] === $name ) 576 577 && 578 579 // Email matches 580 ( $as_submitted['comment_author_email'] === $email ) 581 ) { 582 583 // Delete old content daily 584 if ( ! wp_next_scheduled( 'akismet_scheduled_delete' ) ) { 585 wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' ); 586 } 587 588 // Normal result: true 589 if ( ! empty( $this->last_post['bbp_akismet_result'] ) && ( $this->last_post['bbp_akismet_result'] === 'true' ) ) { 590 591 // Leave a trail so other's know what we did 592 update_post_meta( $post_id, '_bbp_akismet_result', 'true' ); 593 $this->update_post_history( 594 $post_id, 595 esc_html__( 'Akismet caught this post as spam', 'bbpress' ), 596 'check-spam' 597 ); 598 599 // If post_status isn't the spam status, as expected, leave a note 600 if ( bbp_get_spam_status_id() !== $_post->post_status ) { 601 $this->update_post_history( 602 $post_id, 603 sprintf( 604 esc_html__( 'Post status was changed to %s', 'bbpress' ), 605 $_post->post_status 606 ), 607 'status-changed-' . $_post->post_status 608 ); 609 } 610 611 // Normal result: false 612 } elseif ( ! empty( $this->last_post['bbp_akismet_result'] ) && ( $this->last_post['bbp_akismet_result'] === 'false' ) ) { 613 614 // Leave a trail so other's know what we did 615 update_post_meta( $post_id, '_bbp_akismet_result', 'false' ); 616 $this->update_post_history( 617 $post_id, 618 esc_html__( 'Akismet cleared this post as not spam', 'bbpress' ), 619 'check-ham' 620 ); 621 622 // If post_status is the spam status, which isn't expected, leave a note 623 if ( bbp_get_spam_status_id() === $_post->post_status ) { 624 $this->update_post_history( 625 $post_id, 626 sprintf( 627 esc_html__( 'Post status was changed to %s', 'bbpress' ), 628 $_post->post_status 629 ), 630 'status-changed-' . $_post->post_status 631 ); 632 } 633 634 // Abnormal result: error 635 } else { 636 // Leave a trail so other's know what we did 637 update_post_meta( $post_id, '_bbp_akismet_error', time() ); 638 $this->update_post_history( 639 $post_id, 640 sprintf( 641 esc_html__( 'Akismet was unable to check this post (response: %s), will automatically retry again later.', 'bbpress' ), 642 $this->last_post['bbp_akismet_result'] 643 ), 644 'check-error' 645 ); 646 } 647 648 // Record the complete original data as submitted for checking 649 if ( isset( $this->last_post['bbp_post_as_submitted'] ) ) { 650 update_post_meta( 651 $post_id, 652 '_bbp_akismet_as_submitted', 653 $this->last_post['bbp_post_as_submitted'] 654 ); 655 } 656 } 657 } 658 } 659 660 /** 661 * Update Akismet history of a Post 662 * 663 * @since 2.0.0 bbPress (r3308) 664 * 665 * @param int $post_id 666 * @param string $message 667 * @param string $event 668 */ 669 private function update_post_history( $post_id = 0, $message = null, $event = null ) { 670 671 // Define local variable(s) 672 $user = ''; 673 674 // Get the current user 675 $current_user = wp_get_current_user(); 676 677 // Get the user's login name if possible 678 if ( is_object( $current_user ) && isset( $current_user->user_login ) ) { 679 $user = $current_user->user_login; 680 } 681 682 // This used to be akismet_microtime() but it was removed in 3.0 683 $mtime = explode( ' ', microtime() ); 684 $message_time = $mtime[1] + $mtime[0]; 685 686 // Setup the event to be saved 687 $event = array( 688 'time' => $message_time, 689 'message' => $message, 690 'event' => $event, 691 'user' => $user, 692 ); 693 694 // Save the event data 695 add_post_meta( $post_id, '_bbp_akismet_history', $event ); 696 } 697 698 /** 699 * Get the Akismet history of a Post 700 * 701 * @since 2.0.0 bbPress (r3308) 702 * 703 * @param int $post_id 704 * 705 * @return array Array of Akismet history 706 */ 707 public function get_post_history( $post_id = 0 ) { 708 709 // Retrieve any previous history 710 $history = get_post_meta( $post_id, '_bbp_akismet_history' ); 711 712 // Sort it by the time recorded 713 usort( $history, 'akismet_cmp_time' ); 714 715 return $history; 716 } 717 718 /** 719 * Handle any terms submitted with a post flagged as spam 720 * 721 * @since 2.0.0 bbPress (r3308) 722 * 723 * @param string $terms Comma-separated list of terms 724 * @param int $topic_id 725 * @param int $reply_id 726 * 727 * @return array Array of existing topic terms 728 */ 729 public function filter_post_terms( $terms = '', $topic_id = 0, $reply_id = 0 ) { 730 731 // Validate the reply_id and topic_id 732 $reply_id = bbp_get_reply_id( $reply_id ); 733 $topic_id = bbp_get_topic_id( $topic_id ); 734 735 // Get any pre-existing terms 736 $existing_terms = bbp_get_topic_tag_names( $topic_id ); 737 738 // Save the terms for later in case the reply gets hammed 739 if ( ! empty( $terms ) ) { 740 update_post_meta( $reply_id, '_bbp_akismet_spam_terms', $terms ); 741 } 742 743 // Keep the topic tags the same for now 744 return $existing_terms; 745 } 746 747 /** 748 * Submit data to Akismet service with unique bbPress User Agent 749 * 750 * This code is directly taken from the akismet_http_post() function and 751 * documented to bbPress 2.0 standard. 752 * 753 * @since 2.0.0 bbPress (r3466) 754 * 755 * @param string $request The request we are sending 756 * @param string $host The host to send our request to 757 * @param string $path The path from the host 758 * @param string $port The port to use 759 * @param string $ip Optional Override $host with an IP address 760 * @return mixed WP_Error on error, array on success, empty on failure 761 */ 762 private function http_post( $request, $host, $path, $port = 80, $ip = '' ) { 763 764 // Preload required variables 765 $bbp_version = bbp_get_version(); 766 $ak_version = constant( 'AKISMET_VERSION' ); 767 $http_host = $host; 768 $blog_charset = get_option( 'blog_charset' ); 769 770 // User Agent & Content Type 771 $akismet_ua = "bbPress/{$bbp_version} | Akismet/{$ak_version}"; 772 $content_type = 'application/x-www-form-urlencoded; charset=' . $blog_charset; 773 774 // Use specific IP (if provided) 775 if ( ! empty( $ip ) && long2ip( ip2long( $ip ) ) ) { 776 $http_host = $ip; 777 } 778 779 // Setup the arguments 780 $http_args = array( 781 'httpversion' => '1.0', 782 'timeout' => 15, 783 'body' => $request, 784 'headers' => array( 785 'Content-Type' => $content_type, 786 'Host' => $host, 787 'User-Agent' => $akismet_ua 788 ) 789 ); 790 791 // Return the response 792 return $this->get_response( $http_host . $path, $http_args ); 793 } 794 795 /** 796 * Handles the repeated calls to wp_remote_post(), including SSL support. 797 * 798 * @since 2.6.7 (bbPress r7194) 799 * 800 * @param string $host_and_path Scheme-less URL 801 * @param array $http_args Array of arguments for wp_remote_post() 802 * @return array 803 */ 804 private function get_response( $host_and_path = '', $http_args = array() ) { 805 806 // Default variables 807 $akismet_url = $http_akismet_url = 'http://' . $host_and_path; 808 $is_ssl = $ssl_failed = false; 809 $now = time(); 810 811 // Check if SSL requests were disabled fewer than 24 hours ago 812 $ssl_disabled_time = get_option( 'akismet_ssl_disabled' ); 813 814 // Clean-up if 24 hours have passed 815 if ( ! empty( $ssl_disabled_time ) && ( $ssl_disabled_time < ( $now - DAY_IN_SECONDS ) ) ) { 816 delete_option( 'akismet_ssl_disabled' ); 817 $ssl_disabled_time = false; 818 } 819 820 // Maybe HTTPS if not disabled 821 if ( empty( $ssl_disabled_time ) && ( $is_ssl = wp_http_supports( array( 'ssl' ) ) ) ) { 822 $akismet_url = set_url_scheme( $akismet_url, 'https' ); 823 } 824 825 // Initial remote request 826 $response = wp_remote_post( $akismet_url, $http_args ); 827 828 // Initial request produced an error, so retry... 829 if ( ! empty( $is_ssl ) && is_wp_error( $response ) ) { 830 831 // Intermittent connection problems may cause the first HTTPS 832 // request to fail and subsequent HTTP requests to succeed randomly. 833 // Retry the HTTPS request once before disabling SSL for a time. 834 $response = wp_remote_post( $akismet_url, $http_args ); 835 836 // SSL request failed twice, so try again without it 837 if ( is_wp_error( $response ) ) { 838 $response = wp_remote_post( $http_akismet_url, $http_args ); 839 $ssl_failed = true; 840 } 841 } 842 843 // Bail if errored 844 if ( is_wp_error( $response ) ) { 845 return array( '', '' ); 846 } 847 848 // Maybe disable SSL for future requests 849 if ( ! empty( $ssl_failed ) ) { 850 update_option( 'akismet_ssl_disabled', $now ); 851 } 852 853 // No errors so return response 854 return array( 855 $response['headers'], 856 $response['body'] 857 ); 858 } 859 860 /** 861 * Return a user's roles on this site (including super_admin) 862 * 863 * @since 2.3.0 bbPress (r4812) 864 * 865 * @param int $user_id 866 * 867 * @return boolean 868 */ 869 private function get_user_roles( $user_id = 0 ) { 870 871 // Default return value 872 $roles = array(); 873 874 // Bail if cannot query the user 875 if ( ! class_exists( 'WP_User' ) || empty( $user_id ) ) { 876 return false; 877 } 878 879 // User ID 880 $user = new WP_User( $user_id ); 881 if ( isset( $user->roles ) ) { 882 $roles = (array) $user->roles; 883 } 884 885 // Super admin 886 if ( is_multisite() && is_super_admin( $user_id ) ) { 887 $roles[] = 'super_admin'; 888 } 889 890 return implode( ',', $roles ); 891 } 892 893 /** Admin *****************************************************************/ 894 895 /** 896 * Add Aksimet History meta-boxes to topics and replies 897 * 898 * @since 2.4.0 bbPress (r5049) 899 */ 900 public function add_metaboxes() { 901 902 // Topics 903 add_meta_box( 904 'bbp_akismet_topic_history', 905 __( 'Akismet History', 'bbpress' ), 906 array( $this, 'history_metabox' ), 907 bbp_get_topic_post_type(), 908 'normal', 909 'core' 910 ); 911 912 // Replies 913 add_meta_box( 914 'bbp_akismet_reply_history', 915 __( 'Akismet History', 'bbpress' ), 916 array( $this, 'history_metabox' ), 917 bbp_get_reply_post_type(), 918 'normal', 919 'core' 920 ); 921 } 922 923 /** 924 * Output for Akismet History meta-box 925 * 926 * @since 2.4.0 bbPress (r5049) 927 */ 928 public function history_metabox() { 929 930 // Post ID 931 $history = $this->get_post_history( get_the_ID() ); ?> 932 933 <div class="akismet-history" style="margin: 13px 0;"> 934 935 <?php if ( ! empty( $history ) ) : ?> 936 937 <table> 938 <tbody> 939 940 <?php foreach ( $history as $row ) : ?> 941 942 <tr> 943 <td style="color: #999; text-align: right; white-space: nowrap;"> 944 <span title="<?php echo esc_attr( date( 'D d M Y @ h:i:m a', $row['time'] ) . ' GMT' ); ?>"> 945 <?php bbp_time_since( $row['time'], false, true ); ?> 946 </span> 947 </td> 948 <td style="padding-left: 5px;"> 949 <?php echo esc_html( $row['message'] ); ?> 950 </td> 951 </tr> 952 953 <?php endforeach; ?> 954 </tbody> 955 </table> 956 957 <?php else : ?> 958 959 <p><?php esc_html_e( 'No recorded history. Akismet has not checked this post.', 'bbpress' ); ?></p> 960 961 <?php endif; ?> 962 963 </div> 964 965 <?php 966 } 967 968 /** 969 * Get the number of rows to delete in a single clean-up query. 970 * 971 * @since 2.6.9 bbPress (r7225) 972 * 973 * @param string $filter The name of the filter to run. 974 * @return int 975 */ 976 public function get_delete_limit( $filter = '' ) { 977 978 // Default filter 979 if ( empty( $filter ) ) { 980 $filter = '_bbp_akismet_delete_spam_limit'; 981 } 982 983 /** 984 * Determines how many rows will be deleted in each batch. 985 * 986 * @param int The number of rows. Default 1000. 987 */ 988 $delete_limit = (int) apply_filters( $filter, 1000 ); 989 990 // Validate and return the deletion limit 991 return max( 1, $delete_limit ); 992 } 993 994 /** 995 * Get the interval (in days) for spam to remain in the queue. 996 * 997 * @since 2.6.9 bbPress (r7225) 998 * 999 * @param string $filter The name of the filter to run. 1000 * @return int 1001 */ 1002 public function get_delete_interval( $filter = '' ) { 1003 1004 // Default filter 1005 if ( empty( $filter ) ) { 1006 $filter = '_bbp_akismet_delete_spam_interval'; 1007 } 1008 1009 /** 1010 * Determines how many days a piece of spam will be left in the Spam 1011 * queue before being deleted. 1012 * 1013 * @param int The number of days. Default 15. 1014 */ 1015 $delete_interval = (int) apply_filters( $filter, 15 ); 1016 1017 // Validate and return the deletion interval 1018 return max( 1, $delete_interval ); 1019 } 1020 1021 /** 1022 * Deletes old spam topics & replies from the queue after 15 days 1023 * (determined by `_bbp_akismet_delete_spam_interval` filter) 1024 * since they are not useful in the long term. 1025 * 1026 * @since 2.6.7 bbPress (r7203) 1027 * 1028 * @global wpdb $wpdb 1029 */ 1030 public function delete_old_spam() { 1031 global $wpdb; 1032 1033 // Get the deletion limit & interval 1034 $delete_limit = $this->get_delete_limit( '_bbp_akismet_delete_spam_limit' ); 1035 $delete_interval = $this->get_delete_interval( '_bbp_akismet_delete_spam_interval' ); 1036 1037 // Setup the query 1038 $sql = "SELECT id FROM {$wpdb->posts} WHERE post_type IN ('topic', 'reply') AND post_status = 'spam' AND DATE_SUB(NOW(), INTERVAL %d DAY) > post_date_gmt LIMIT %d"; 1039 1040 // Query loop of topic & reply IDs 1041 while ( $spam_ids = $wpdb->get_col( $wpdb->prepare( $sql, $delete_interval, $delete_limit ) ) ) { 1042 1043 // Exit loop if no spam IDs 1044 if ( empty( $spam_ids ) ) { 1045 break; 1046 } 1047 1048 // Reset queries 1049 $wpdb->queries = array(); 1050 1051 // Loop through each of the topic/reply IDs 1052 foreach ( $spam_ids as $spam_id ) { 1053 1054 /** 1055 * Perform a single action on the single topic/reply ID for 1056 * simpler batch processing. 1057 * 1058 * Maybe we should run the bbp_delete_topic or bbp_delete_reply 1059 * actions here, too? 1060 * 1061 * @param string The current function. 1062 * @param int The current topic/reply ID. 1063 */ 1064 do_action( '_bbp_akismet_batch_delete', __FUNCTION__, $spam_id ); 1065 } 1066 1067 // Prepared as strings since id is an unsigned BIGINT, and using % 1068 // will constrain the value to the maximum signed BIGINT. 1069 $format_string = implode( ', ', array_fill( 0, count( $spam_ids ), '%s' ) ); 1070 1071 // Run the delete queries 1072 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->posts} WHERE ID IN ( {$format_string} )", $spam_ids ) ); 1073 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ( {$format_string} )", $spam_ids ) ); 1074 1075 // Clean the post cache for these topics & replies 1076 clean_post_cache( $spam_ids ); 1077 1078 /** 1079 * Single action that encompasses all topic/reply IDs after the 1080 * delete queries have been run. 1081 * 1082 * @param int Count of topic/reply IDs 1083 * @param array Array of topic/reply IDs 1084 */ 1085 do_action( '_bbp_akismet_delete_spam_count', count( $spam_ids ), $spam_ids ); 1086 } 1087 1088 /** 1089 * Determines whether tables should be optimized. 1090 * 1091 * @param int Random number between 1 and 5000. 1092 */ 1093 $optimize = (int) apply_filters( '_bbp_akismet_optimize_tables', mt_rand( 1, 5000 ), array( $wpdb->posts, $wpdb->postmeta ) ); 1094 1095 // Lucky number 11 1096 if ( 11 === $optimize ) { 1097 $wpdb->query( "OPTIMIZE TABLE {$wpdb->posts}" ); 1098 $wpdb->query( "OPTIMIZE TABLE {$wpdb->postmeta}" ); 1099 } 1100 } 1101 1102 /** 1103 * Deletes `_bbp_akismet_as_submitted` meta keys after 15 days 1104 * (determined by `_bbp_akismet_delete_spam_meta_interval` filter) 1105 * since they are large and not useful in the long term. 1106 * 1107 * @since 2.6.7 bbPress (r7203) 1108 * 1109 * @global wpdb $wpdb 1110 */ 1111 public function delete_old_spam_meta() { 1112 global $wpdb; 1113 1114 // Get the deletion limit & interval 1115 $delete_limit = $this->get_delete_limit( '_bbp_akismet_delete_spam_meta_limit' ); 1116 $delete_interval = $this->get_delete_interval( '_bbp_akismet_delete_spam_meta_interval' ); 1117 1118 // Setup the query 1119 $sql = "SELECT m.post_id FROM {$wpdb->postmeta} as m INNER JOIN {$wpdb->posts} as p ON m.post_id = p.ID WHERE m.meta_key = '_bbp_akismet_as_submitted' AND DATE_SUB(NOW(), INTERVAL %d DAY) > p.post_date_gmt LIMIT %d"; 1120 1121 // Query loop of topic & reply IDs 1122 while ( $spam_ids = $wpdb->get_col( $wpdb->prepare( $sql, $delete_interval, $delete_limit ) ) ) { 1123 1124 // Exit loop if no spam IDs 1125 if ( empty( $spam_ids ) ) { 1126 break; 1127 } 1128 1129 // Reset queries 1130 $wpdb->queries = array(); 1131 1132 // Loop through each of the topic/reply IDs 1133 foreach ( $spam_ids as $spam_id ) { 1134 1135 // Delete the as_submitted meta data 1136 delete_post_meta( $spam_id, '_bbp_akismet_as_submitted' ); 1137 1138 /** 1139 * Perform a single action on the single topic/reply ID for 1140 * simpler batch processing. 1141 * 1142 * @param string The current function. 1143 * @param int The current topic/reply ID. 1144 */ 1145 do_action( '_bbp_akismet_batch_delete', __FUNCTION__, $spam_id ); 1146 } 1147 1148 /** 1149 * Single action that encompasses all topic/reply IDs after the 1150 * delete queries have been run. 1151 * 1152 * @param int Count of topic/reply IDs 1153 * @param array Array of topic/reply IDs 1154 */ 1155 do_action( '_bbp_akismet_delete_spam_meta_count', count( $spam_ids ), $spam_ids ); 1156 } 1157 1158 // Maybe optimize 1159 $this->maybe_optimize_postmeta(); 1160 } 1161 1162 /** 1163 * Clears post meta that no longer has corresponding posts in the database 1164 * (determined by `_bbp_akismet_delete_spam_orphaned_limit` filter) 1165 * since it is not useful in the long term. 1166 * 1167 * @since 2.6.7 bbPress (r7203) 1168 * 1169 * @global wpdb $wpdb 1170 */ 1171 public function delete_orphaned_spam_meta() { 1172 global $wpdb; 1173 1174 // Get the deletion limit 1175 $delete_limit = $this->get_delete_limit( '_bbp_akismet_delete_spam_orphaned_limit' ); 1176 1177 // Default last meta ID 1178 $last_meta_id = 0; 1179 1180 // Start time (float) 1181 $start_time = isset( $_SERVER['REQUEST_TIME_FLOAT'] ) 1182 ? (float) $_SERVER['REQUEST_TIME_FLOAT'] 1183 : microtime( true ); 1184 1185 // Maximum time 1186 $max_exec_time = (float) max( ini_get( 'max_execution_time' ) - 5, 3 ); 1187 1188 // Setup the query 1189 $sql = "SELECT m.meta_id, m.post_id, m.meta_key FROM {$wpdb->postmeta} as m LEFT JOIN {$wpdb->posts} as p ON m.post_id = p.ID WHERE p.ID IS NULL AND m.meta_id > %d ORDER BY m.meta_id LIMIT %d"; 1190 1191 // Query loop of topic & reply IDs 1192 while ( $spam_meta_results = $wpdb->get_results( $wpdb->prepare( $sql, $last_meta_id, $delete_limit ) ) ) { 1193 1194 // Exit loop if no spam IDs 1195 if ( empty( $spam_meta_results ) ) { 1196 break; 1197 } 1198 1199 // Reset queries 1200 $wpdb->queries = array(); 1201 1202 // Reset deleted meta count 1203 $spam_meta_deleted = array(); 1204 1205 // Loop through each of the metas 1206 foreach ( $spam_meta_results as $spam_meta ) { 1207 1208 // Skip if not an Akismet key 1209 if ( 'akismet_' !== substr( $spam_meta->meta_key, 0, 8 ) ) { 1210 continue; 1211 } 1212 1213 // Delete the meta 1214 delete_post_meta( $spam_meta->post_id, $spam_meta->meta_key ); 1215 1216 /** 1217 * Perform a single action on the single topic/reply ID for 1218 * simpler batch processing. 1219 * 1220 * @param string The current function. 1221 * @param int The current topic/reply ID. 1222 */ 1223 do_action( '_bbp_akismet_batch_delete', __FUNCTION__, $spam_meta ); 1224 1225 // Stash the meta ID being deleted 1226 $spam_meta_deleted[] = $last_meta_id = $spam_meta->meta_id; 1227 } 1228 1229 /** 1230 * Single action that encompasses all topic/reply IDs after the 1231 * delete queries have been run. 1232 * 1233 * @param int Count of spam meta IDs 1234 * @param array Array of spam meta IDs 1235 */ 1236 do_action( '_bbp_akismet_delete_spam_meta_count', count( $spam_meta_deleted ), $spam_meta_deleted ); 1237 1238 // Break if getting close to max_execution_time. 1239 if ( ( microtime( true ) - $start_time ) > $max_exec_time ) { 1240 break; 1241 } 1242 } 1243 1244 // Maybe optimize 1245 $this->maybe_optimize_postmeta(); 1246 } 1247 1248 /** 1249 * Maybe OPTIMIZE the _postmeta database table. 1250 * 1251 * @since 2.7.0 bbPress (r7203) 1252 * 1253 * @global wpdb $wpdb 1254 */ 1255 private function maybe_optimize_postmeta() { 1256 global $wpdb; 1257 1258 /** 1259 * Determines whether tables should be optimized. 1260 * 1261 * @param int Random number between 1 and 5000. 1262 */ 1263 $optimize = (int) apply_filters( '_bbp_akismet_optimize_table', mt_rand( 1, 5000 ), $wpdb->postmeta ); 1264 1265 // Lucky number 11 1266 if ( 11 === $optimize ) { 1267 $wpdb->query( "OPTIMIZE TABLE {$wpdb->postmeta}" ); 1268 } 1269 } 1270 } 1271 endif;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Oct 4 01:00:52 2024 | Cross-referenced by PHPXref 0.7.1 |