| [ Index ] |
PHP Cross Reference of Wordpress 2.7.1 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Manages WordPress comments 4 * 5 * @package WordPress 6 * @subpackage Comment 7 */ 8 9 /** 10 * Checks whether a comment passes internal checks to be allowed to add. 11 * 12 * If comment moderation is set in the administration, then all comments, 13 * regardless of their type and whitelist will be set to false. If the number of 14 * links exceeds the amount in the administration, then the check fails. If any 15 * of the parameter contents match the blacklist of words, then the check fails. 16 * 17 * If the number of links exceeds the amount in the administration, then the 18 * check fails. If any of the parameter contents match the blacklist of words, 19 * then the check fails. 20 * 21 * If the comment is a trackback and part of the blogroll, then the trackback is 22 * automatically whitelisted. If the comment author was approved before, then 23 * the comment is automatically whitelisted. 24 * 25 * If none of the checks fail, then the failback is to set the check to pass 26 * (return true). 27 * 28 * @since 1.2.0 29 * @uses $wpdb 30 * 31 * @param string $author Comment Author's name 32 * @param string $email Comment Author's email 33 * @param string $url Comment Author's URL 34 * @param string $comment Comment contents 35 * @param string $user_ip Comment Author's IP address 36 * @param string $user_agent Comment Author's User Agent 37 * @param string $comment_type Comment type, either user submitted comment, 38 * trackback, or pingback 39 * @return bool Whether the checks passed (true) and the comments should be 40 * displayed or set to moderated 41 */ 42 function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) { 43 global $wpdb; 44 45 if ( 1 == get_option('comment_moderation') ) 46 return false; // If moderation is set to manual 47 48 if ( get_option('comment_max_links') && preg_match_all("/<[Aa][^>]*[Hh][Rr][Ee][Ff]=['\"]([^\"'>]+)[^>]*>/", apply_filters('comment_text',$comment), $out) >= get_option('comment_max_links') ) 49 return false; // Check # of external links 50 51 $mod_keys = trim(get_option('moderation_keys')); 52 if ( !empty($mod_keys) ) { 53 $words = explode("\n", $mod_keys ); 54 55 foreach ( (array) $words as $word) { 56 $word = trim($word); 57 58 // Skip empty lines 59 if ( empty($word) ) 60 continue; 61 62 // Do some escaping magic so that '#' chars in the 63 // spam words don't break things: 64 $word = preg_quote($word, '#'); 65 66 $pattern = "#$word#i"; 67 if ( preg_match($pattern, $author) ) return false; 68 if ( preg_match($pattern, $email) ) return false; 69 if ( preg_match($pattern, $url) ) return false; 70 if ( preg_match($pattern, $comment) ) return false; 71 if ( preg_match($pattern, $user_ip) ) return false; 72 if ( preg_match($pattern, $user_agent) ) return false; 73 } 74 } 75 76 // Comment whitelisting: 77 if ( 1 == get_option('comment_whitelist')) { 78 if ( 'trackback' == $comment_type || 'pingback' == $comment_type ) { // check if domain is in blogroll 79 $uri = parse_url($url); 80 $domain = $uri['host']; 81 $uri = parse_url( get_option('home') ); 82 $home_domain = $uri['host']; 83 if ( $wpdb->get_var($wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_url LIKE (%s) LIMIT 1", '%'.$domain.'%')) || $domain == $home_domain ) 84 return true; 85 else 86 return false; 87 } elseif ( $author != '' && $email != '' ) { 88 // expected_slashed ($author, $email) 89 $ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1"); 90 if ( ( 1 == $ok_to_comment ) && 91 ( empty($mod_keys) || false === strpos( $email, $mod_keys) ) ) 92 return true; 93 else 94 return false; 95 } else { 96 return false; 97 } 98 } 99 return true; 100 } 101 102 /** 103 * Retrieve the approved comments for post $post_id. 104 * 105 * @since 2.0.0 106 * @uses $wpdb 107 * 108 * @param int $post_id The ID of the post 109 * @return array $comments The approved comments 110 */ 111 function get_approved_comments($post_id) { 112 global $wpdb; 113 return $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1' ORDER BY comment_date", $post_id)); 114 } 115 116 /** 117 * Retrieves comment data given a comment ID or comment object. 118 * 119 * If an object is passed then the comment data will be cached and then returned 120 * after being passed through a filter. If the comment is empty, then the global 121 * comment variable will be used, if it is set. 122 * 123 * If the comment is empty, then the global comment variable will be used, if it 124 * is set. 125 * 126 * @since 2.0.0 127 * @uses $wpdb 128 * 129 * @param object|string|int $comment Comment to retrieve. 130 * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants. 131 * @return object|array|null Depends on $output value. 132 */ 133 function &get_comment(&$comment, $output = OBJECT) { 134 global $wpdb; 135 136 if ( empty($comment) ) { 137 if ( isset($GLOBALS['comment']) ) 138 $_comment = & $GLOBALS['comment']; 139 else 140 $_comment = null; 141 } elseif ( is_object($comment) ) { 142 wp_cache_add($comment->comment_ID, $comment, 'comment'); 143 $_comment = $comment; 144 } else { 145 if ( isset($GLOBALS['comment']) && ($GLOBALS['comment']->comment_ID == $comment) ) { 146 $_comment = & $GLOBALS['comment']; 147 } elseif ( ! $_comment = wp_cache_get($comment, 'comment') ) { 148 $_comment = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment)); 149 wp_cache_add($_comment->comment_ID, $_comment, 'comment'); 150 } 151 } 152 153 $_comment = apply_filters('get_comment', $_comment); 154 155 if ( $output == OBJECT ) { 156 return $_comment; 157 } elseif ( $output == ARRAY_A ) { 158 $__comment = get_object_vars($_comment); 159 return $__comment; 160 } elseif ( $output == ARRAY_N ) { 161 $__comment = array_values(get_object_vars($_comment)); 162 return $__comment; 163 } else { 164 return $_comment; 165 } 166 } 167 168 /** 169 * Retrieve a list of comments. 170 * 171 * The list of comment arguments are 'status', 'orderby', 'comment_date_gmt', 172 * 'order', 'number', 'offset', and 'post_id'. 173 * 174 * @since 2.7.0 175 * @uses $wpdb 176 * 177 * @param mixed $args Optional. Array or string of options to override defaults. 178 * @return array List of comments. 179 */ 180 function get_comments( $args = '' ) { 181 global $wpdb; 182 183 $defaults = array('status' => '', 'orderby' => 'comment_date_gmt', 'order' => 'DESC', 'number' => '', 'offset' => '', 'post_id' => 0); 184 185 $args = wp_parse_args( $args, $defaults ); 186 extract( $args, EXTR_SKIP ); 187 188 // $args can be whatever, only use the args defined in defaults to compute the key 189 $key = md5( serialize( compact(array_keys($defaults)) ) ); 190 $last_changed = wp_cache_get('last_changed', 'comment'); 191 if ( !$last_changed ) { 192 $last_changed = time(); 193 wp_cache_set('last_changed', $last_changed, 'comment'); 194 } 195 $cache_key = "get_comments:$key:$last_changed"; 196 197 if ( $cache = wp_cache_get( $cache_key, 'comment' ) ) { 198 return $cache; 199 } 200 201 $post_id = absint($post_id); 202 203 if ( 'hold' == $status ) 204 $approved = "comment_approved = '0'"; 205 elseif ( 'approve' == $status ) 206 $approved = "comment_approved = '1'"; 207 elseif ( 'spam' == $status ) 208 $approved = "comment_approved = 'spam'"; 209 else 210 $approved = "( comment_approved = '0' OR comment_approved = '1' )"; 211 212 $order = ( 'ASC' == $order ) ? 'ASC' : 'DESC'; 213 214 $orderby = 'comment_date_gmt'; // Hard code for now 215 216 $number = absint($number); 217 $offset = absint($offset); 218 219 if ( !empty($number) ) { 220 if ( $offset ) 221 $number = 'LIMIT ' . $offset . ',' . $number; 222 else 223 $number = 'LIMIT ' . $number; 224 225 } else { 226 $number = ''; 227 } 228 229 if ( ! empty($post_id) ) 230 $post_where = $wpdb->prepare( 'comment_post_ID = %d AND', $post_id ); 231 else 232 $post_where = ''; 233 234 $comments = $wpdb->get_results( "SELECT * FROM $wpdb->comments WHERE $post_where $approved ORDER BY $orderby $order $number" ); 235 wp_cache_add( $cache_key, $comments, 'comment' ); 236 237 return $comments; 238 } 239 240 /** 241 * Retrieve all of the WordPress supported comment statuses. 242 * 243 * Comments have a limited set of valid status values, this provides the comment 244 * status values and descriptions. 245 * 246 * @package WordPress 247 * @subpackage Post 248 * @since 2.7.0 249 * 250 * @return array List of comment statuses. 251 */ 252 function get_comment_statuses( ) { 253 $status = array( 254 'hold' => __('Unapproved'), 255 'approve' => __('Approved'), 256 'spam' => _c('Spam|adjective'), 257 ); 258 259 return $status; 260 } 261 262 263 /** 264 * The date the last comment was modified. 265 * 266 * @since 1.5.0 267 * @uses $wpdb 268 * @global array $cache_lastcommentmodified 269 * 270 * @param string $timezone Which timezone to use in reference to 'gmt', 'blog', 271 * or 'server' locations. 272 * @return string Last comment modified date. 273 */ 274 function get_lastcommentmodified($timezone = 'server') { 275 global $cache_lastcommentmodified, $wpdb; 276 277 if ( isset($cache_lastcommentmodified[$timezone]) ) 278 return $cache_lastcommentmodified[$timezone]; 279 280 $add_seconds_server = date('Z'); 281 282 switch ( strtolower($timezone)) { 283 case 'gmt': 284 $lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1"); 285 break; 286 case 'blog': 287 $lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1"); 288 break; 289 case 'server': 290 $lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server)); 291 break; 292 } 293 294 $cache_lastcommentmodified[$timezone] = $lastcommentmodified; 295 296 return $lastcommentmodified; 297 } 298 299 /** 300 * The amount of comments in a post or total comments. 301 * 302 * A lot like {@link wp_count_comments()}, in that they both return comment 303 * stats (albeit with different types). The {@link wp_count_comments()} actual 304 * caches, but this function does not. 305 * 306 * @since 2.0.0 307 * @uses $wpdb 308 * 309 * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide. 310 * @return array The amount of spam, approved, awaiting moderation, and total comments. 311 */ 312 function get_comment_count( $post_id = 0 ) { 313 global $wpdb; 314 315 $post_id = (int) $post_id; 316 317 $where = ''; 318 if ( $post_id > 0 ) { 319 $where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id); 320 } 321 322 $totals = (array) $wpdb->get_results(" 323 SELECT comment_approved, COUNT( * ) AS total 324 FROM {$wpdb->comments} 325 {$where} 326 GROUP BY comment_approved 327 ", ARRAY_A); 328 329 $comment_count = array( 330 "approved" => 0, 331 "awaiting_moderation" => 0, 332 "spam" => 0, 333 "total_comments" => 0 334 ); 335 336 foreach ( $totals as $row ) { 337 switch ( $row['comment_approved'] ) { 338 case 'spam': 339 $comment_count['spam'] = $row['total']; 340 $comment_count["total_comments"] += $row['total']; 341 break; 342 case 1: 343 $comment_count['approved'] = $row['total']; 344 $comment_count['total_comments'] += $row['total']; 345 break; 346 case 0: 347 $comment_count['awaiting_moderation'] = $row['total']; 348 $comment_count['total_comments'] += $row['total']; 349 break; 350 default: 351 break; 352 } 353 } 354 355 return $comment_count; 356 } 357 358 /** 359 * Sanitizes the cookies sent to the user already. 360 * 361 * Will only do anything if the cookies have already been created for the user. 362 * Mostly used after cookies had been sent to use elsewhere. 363 * 364 * @since 2.0.4 365 */ 366 function sanitize_comment_cookies() { 367 if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) ) { 368 $comment_author = apply_filters('pre_comment_author_name', $_COOKIE['comment_author_'.COOKIEHASH]); 369 $comment_author = stripslashes($comment_author); 370 $comment_author = attribute_escape($comment_author); 371 $_COOKIE['comment_author_'.COOKIEHASH] = $comment_author; 372 } 373 374 if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ) { 375 $comment_author_email = apply_filters('pre_comment_author_email', $_COOKIE['comment_author_email_'.COOKIEHASH]); 376 $comment_author_email = stripslashes($comment_author_email); 377 $comment_author_email = attribute_escape($comment_author_email); 378 $_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email; 379 } 380 381 if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ) { 382 $comment_author_url = apply_filters('pre_comment_author_url', $_COOKIE['comment_author_url_'.COOKIEHASH]); 383 $comment_author_url = stripslashes($comment_author_url); 384 $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url; 385 } 386 } 387 388 /** 389 * Validates whether this comment is allowed to be made or not. 390 * 391 * @since 2.0.0 392 * @uses $wpdb 393 * @uses apply_filters() Calls 'pre_comment_approved' hook on the type of comment 394 * @uses do_action() Calls 'check_comment_flood' hook on $comment_author_IP, $comment_author_email, and $comment_date_gmt 395 * 396 * @param array $commentdata Contains information on the comment 397 * @return mixed Signifies the approval status (0|1|'spam') 398 */ 399 function wp_allow_comment($commentdata) { 400 global $wpdb; 401 extract($commentdata, EXTR_SKIP); 402 403 // Simple duplicate check 404 // expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content) 405 $dupe = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND ( comment_author = '$comment_author' "; 406 if ( $comment_author_email ) 407 $dupe .= "OR comment_author_email = '$comment_author_email' "; 408 $dupe .= ") AND comment_content = '$comment_content' LIMIT 1"; 409 if ( $wpdb->get_var($dupe) ) { 410 if ( defined('DOING_AJAX') ) 411 die( __('Duplicate comment detected; it looks as though you\'ve already said that!') ); 412 413 wp_die( __('Duplicate comment detected; it looks as though you\'ve already said that!') ); 414 } 415 416 do_action( 'check_comment_flood', $comment_author_IP, $comment_author_email, $comment_date_gmt ); 417 418 if ( $user_id ) { 419 $userdata = get_userdata($user_id); 420 $user = new WP_User($user_id); 421 $post_author = $wpdb->get_var($wpdb->prepare("SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1", $comment_post_ID)); 422 } 423 424 if ( isset($userdata) && ( $user_id == $post_author || $user->has_cap('moderate_comments') ) ) { 425 // The author and the admins get respect. 426 $approved = 1; 427 } else { 428 // Everyone else's comments will be checked. 429 if ( check_comment($comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_IP, $comment_agent, $comment_type) ) 430 $approved = 1; 431 else 432 $approved = 0; 433 if ( wp_blacklist_check($comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_IP, $comment_agent) ) 434 $approved = 'spam'; 435 } 436 437 $approved = apply_filters('pre_comment_approved', $approved); 438 return $approved; 439 } 440 441 /** 442 * Check whether comment flooding is occurring. 443 * 444 * Won't run, if current user can manage options, so to not block 445 * administrators. 446 * 447 * @since 2.3.0 448 * @uses $wpdb 449 * @uses apply_filters() Calls 'comment_flood_filter' filter with first 450 * parameter false, last comment timestamp, new comment timestamp. 451 * @uses do_action() Calls 'comment_flood_trigger' action with parameters with 452 * last comment timestamp and new comment timestamp. 453 * 454 * @param string $ip Comment IP. 455 * @param string $email Comment author email address. 456 * @param string $date MySQL time string. 457 */ 458 function check_comment_flood_db( $ip, $email, $date ) { 459 global $wpdb; 460 if ( current_user_can( 'manage_options' ) ) 461 return; // don't throttle admins 462 if ( $lasttime = $wpdb->get_var( $wpdb->prepare("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_author_IP = %s OR comment_author_email = %s ORDER BY comment_date DESC LIMIT 1", $ip, $email) ) ) { 463 $time_lastcomment = mysql2date('U', $lasttime); 464 $time_newcomment = mysql2date('U', $date); 465 $flood_die = apply_filters('comment_flood_filter', false, $time_lastcomment, $time_newcomment); 466 if ( $flood_die ) { 467 do_action('comment_flood_trigger', $time_lastcomment, $time_newcomment); 468 469 if ( defined('DOING_AJAX') ) 470 die( __('You are posting comments too quickly. Slow down.') ); 471 472 wp_die( __('You are posting comments too quickly. Slow down.'), '', array('response' => 403) ); 473 } 474 } 475 } 476 477 /** 478 * Separates an array of comments into an array keyed by comment_type. 479 * 480 * @since 2.7.0 481 * 482 * @param array $comments Array of comments 483 * @return array Array of comments keyed by comment_type. 484 */ 485 function &separate_comments(&$comments) { 486 $comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array()); 487 $count = count($comments); 488 for ( $i = 0; $i < $count; $i++ ) { 489 $type = $comments[$i]->comment_type; 490 if ( empty($type) ) 491 $type = 'comment'; 492 $comments_by_type[$type][] = &$comments[$i]; 493 if ( 'trackback' == $type || 'pingback' == $type ) 494 $comments_by_type['pings'][] = &$comments[$i]; 495 } 496 497 return $comments_by_type; 498 } 499 500 /** 501 * Calculate the total number of comment pages. 502 * 503 * @since 2.7.0 504 * @uses get_query_var() Used to fill in the default for $per_page parameter. 505 * @uses get_option() Used to fill in defaults for parameters. 506 * @uses Walker_Comment 507 * 508 * @param array $comments Optional array of comment objects. Defaults to $wp_query->comments 509 * @param int $per_page Optional comments per page. 510 * @param boolean $threaded Optional control over flat or threaded comments. 511 * @return int Number of comment pages. 512 */ 513 function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) { 514 global $wp_query; 515 516 if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) ) 517 return $wp_query->max_num_comment_pages; 518 519 if ( !$comments || !is_array($comments) ) 520 $comments = $wp_query->comments; 521 522 if ( empty($comments) ) 523 return 0; 524 525 if ( !isset($per_page) ) 526 $per_page = (int) get_query_var('comments_per_page'); 527 if ( 0 === $per_page ) 528 $per_page = (int) get_option('comments_per_page'); 529 if ( 0 === $per_page ) 530 return 1; 531 532 if ( !isset($threaded) ) 533 $threaded = get_option('thread_comments'); 534 535 if ( $threaded ) { 536 $walker = new Walker_Comment; 537 $count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page ); 538 } else { 539 $count = ceil( count( $comments ) / $per_page ); 540 } 541 542 return $count; 543 } 544 545 /** 546 * Calculate what page number a comment will appear on for comment paging. 547 * 548 * @since 2.7.0 549 * @uses get_comment() Gets the full comment of the $comment_ID parameter. 550 * @uses get_option() Get various settings to control function and defaults. 551 * @uses get_page_of_comment() Used to loop up to top level comment. 552 * 553 * @param int $comment_ID Comment ID. 554 * @param array $args Optional args. 555 * @return int|null Comment page number or null on error. 556 */ 557 function get_page_of_comment( $comment_ID, $args = array() ) { 558 global $wpdb; 559 560 if ( !$comment = get_comment( $comment_ID ) ) 561 return; 562 563 $defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' ); 564 $args = wp_parse_args( $args, $defaults ); 565 566 if ( '' === $args['per_page'] && get_option('page_comments') ) 567 $args['per_page'] = get_query_var('comments_per_page'); 568 if ( empty($args['per_page']) ) { 569 $args['per_page'] = 0; 570 $args['page'] = 0; 571 } 572 if ( $args['per_page'] < 1 ) 573 return 1; 574 575 if ( '' === $args['max_depth'] ) { 576 if ( get_option('thread_comments') ) 577 $args['max_depth'] = get_option('thread_comments_depth'); 578 else 579 $args['max_depth'] = -1; 580 } 581 582 // Find this comment's top level parent if threading is enabled 583 if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent ) 584 return get_page_of_comment( $comment->comment_parent, $args ); 585 586 $allowedtypes = array( 587 'comment' => '', 588 'pingback' => 'pingback', 589 'trackback' => 'trackback', 590 ); 591 592 $comtypewhere = ( 'all' != $args['type'] && isset($allowedtypes[$args['type']]) ) ? " AND comment_type = '" . $allowedtypes[$args['type']] . "'" : ''; 593 594 // Count comments older than this one 595 $oldercoms = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = 0 AND comment_approved = '1' AND comment_date_gmt < '%s'" . $comtypewhere, $comment->comment_post_ID, $comment->comment_date_gmt ) ); 596 597 // No older comments? Then it's page #1. 598 if ( 0 == $oldercoms ) 599 return 1; 600 601 // Divide comments older than this one by comments per page to get this comment's page number 602 return ceil( ( $oldercoms + 1 ) / $args['per_page'] ); 603 } 604 605 /** 606 * Does comment contain blacklisted characters or words. 607 * 608 * @since 1.5.0 609 * @uses do_action() Calls 'wp_blacklist_check' hook for all parameters. 610 * 611 * @param string $author The author of the comment 612 * @param string $email The email of the comment 613 * @param string $url The url used in the comment 614 * @param string $comment The comment content 615 * @param string $user_ip The comment author IP address 616 * @param string $user_agent The author's browser user agent 617 * @return bool True if comment contains blacklisted content, false if comment does not 618 */ 619 function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) { 620 do_action('wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent); 621 622 if ( preg_match_all('/&#(\d+);/', $comment . $author . $url, $chars) ) { 623 foreach ( (array) $chars[1] as $char ) { 624 // If it's an encoded char in the normal ASCII set, reject 625 if ( 38 == $char ) 626 continue; // Unless it's & 627 if ( $char < 128 ) 628 return true; 629 } 630 } 631 632 $mod_keys = trim( get_option('blacklist_keys') ); 633 if ( '' == $mod_keys ) 634 return false; // If moderation keys are empty 635 $words = explode("\n", $mod_keys ); 636 637 foreach ( (array) $words as $word ) { 638 $word = trim($word); 639 640 // Skip empty lines 641 if ( empty($word) ) { continue; } 642 643 // Do some escaping magic so that '#' chars in the 644 // spam words don't break things: 645 $word = preg_quote($word, '#'); 646 647 $pattern = "#$word#i"; 648 if ( 649 preg_match($pattern, $author) 650 || preg_match($pattern, $email) 651 || preg_match($pattern, $url) 652 || preg_match($pattern, $comment) 653 || preg_match($pattern, $user_ip) 654 || preg_match($pattern, $user_agent) 655 ) 656 return true; 657 } 658 return false; 659 } 660 661 /** 662 * Retrieve total comments for blog or single post. 663 * 664 * The properties of the returned object contain the 'moderated', 'approved', 665 * and spam comments for either the entire blog or single post. Those properties 666 * contain the amount of comments that match the status. The 'total_comments' 667 * property contains the integer of total comments. 668 * 669 * The comment stats are cached and then retrieved, if they already exist in the 670 * cache. 671 * 672 * @since 2.5.0 673 * 674 * @param int $post_id Optional. Post ID. 675 * @return object Comment stats. 676 */ 677 function wp_count_comments( $post_id = 0 ) { 678 global $wpdb; 679 680 $post_id = (int) $post_id; 681 682 $stats = apply_filters('wp_count_comments', array(), $post_id); 683 if ( !empty($stats) ) 684 return $stats; 685 686 $count = wp_cache_get("comments-{$post_id}", 'counts'); 687 688 if ( false !== $count ) 689 return $count; 690 691 $where = ''; 692 if( $post_id > 0 ) 693 $where = $wpdb->prepare( "WHERE comment_post_ID = %d", $post_id ); 694 695 $count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} {$where} GROUP BY comment_approved", ARRAY_A ); 696 697 $total = 0; 698 $approved = array('0' => 'moderated', '1' => 'approved', 'spam' => 'spam'); 699 $known_types = array_keys( $approved ); 700 foreach( (array) $count as $row_num => $row ) { 701 $total += $row['num_comments']; 702 if ( in_array( $row['comment_approved'], $known_types ) ) 703 $stats[$approved[$row['comment_approved']]] = $row['num_comments']; 704 } 705 706 $stats['total_comments'] = $total; 707 foreach ( $approved as $key ) { 708 if ( empty($stats[$key]) ) 709 $stats[$key] = 0; 710 } 711 712 $stats = (object) $stats; 713 wp_cache_set("comments-{$post_id}", $stats, 'counts'); 714 715 return $stats; 716 } 717 718 /** 719 * Removes comment ID and maybe updates post comment count. 720 * 721 * The post comment count will be updated if the comment was approved and has a 722 * post ID available. 723 * 724 * @since 2.0.0 725 * @uses $wpdb 726 * @uses do_action() Calls 'delete_comment' hook on comment ID 727 * @uses do_action() Calls 'wp_set_comment_status' hook on comment ID with 'delete' set for the second parameter 728 * @uses wp_transition_comment_status() Passes new and old comment status along with $comment object 729 * 730 * @param int $comment_id Comment ID 731 * @return bool False if delete comment query failure, true on success. 732 */ 733 function wp_delete_comment($comment_id) { 734 global $wpdb; 735 do_action('delete_comment', $comment_id); 736 737 $comment = get_comment($comment_id); 738 739 if ( ! $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment_id) ) ) 740 return false; 741 742 $post_id = $comment->comment_post_ID; 743 if ( $post_id && $comment->comment_approved == 1 ) 744 wp_update_comment_count($post_id); 745 746 clean_comment_cache($comment_id); 747 748 do_action('wp_set_comment_status', $comment_id, 'delete'); 749 wp_transition_comment_status('delete', $comment->comment_approved, $comment); 750 return true; 751 } 752 753 /** 754 * The status of a comment by ID. 755 * 756 * @since 1.0.0 757 * 758 * @param int $comment_id Comment ID 759 * @return string|bool Status might be 'deleted', 'approved', 'unapproved', 'spam'. False on failure. 760 */ 761 function wp_get_comment_status($comment_id) { 762 $comment = get_comment($comment_id); 763 if ( !$comment ) 764 return false; 765 766 $approved = $comment->comment_approved; 767 768 if ( $approved == NULL ) 769 return 'deleted'; 770 elseif ( $approved == '1' ) 771 return 'approved'; 772 elseif ( $approved == '0' ) 773 return 'unapproved'; 774 elseif ( $approved == 'spam' ) 775 return 'spam'; 776 else 777 return false; 778 } 779 780 /** 781 * Call hooks for when a comment status transition occurs. 782 * 783 * Calls hooks for comment status transitions. If the new comment status is not the same 784 * as the previous comment status, then two hooks will be ran, the first is 785 * 'transition_comment_status' with new status, old status, and comment data. The 786 * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the 787 * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the 788 * comment data. 789 * 790 * The final action will run whether or not the comment statuses are the same. The 791 * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status 792 * parameter and COMMENTTYPE is comment_type comment data. 793 * 794 * @since 2.7.0 795 * 796 * @param string $new_status New comment status. 797 * @param string $old_status Previous comment status. 798 * @param object $comment Comment data. 799 */ 800 function wp_transition_comment_status($new_status, $old_status, $comment) { 801 // Translate raw statuses to human readable formats for the hooks 802 // This is not a complete list of comment status, it's only the ones that need to be renamed 803 $comment_statuses = array( 804 0 => 'unapproved', 805 'hold' => 'unapproved', // wp_set_comment_status() uses "hold" 806 1 => 'approved', 807 'approve' => 'approved', // wp_set_comment_status() uses "approve" 808 ); 809 if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status]; 810 if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status]; 811 812 // Call the hooks 813 if ( $new_status != $old_status ) { 814 do_action('transition_comment_status', $new_status, $old_status, $comment); 815 do_action("comment_$old_status}_to_$new_status", $comment); 816 } 817 do_action("comment_$new_status}_$comment->comment_type", $comment->comment_ID, $comment); 818 } 819 820 /** 821 * Get current commenter's name, email, and URL. 822 * 823 * Expects cookies content to already be sanitized. User of this function might 824 * wish to recheck the returned array for validity. 825 * 826 * @see sanitize_comment_cookies() Use to sanitize cookies 827 * 828 * @since 2.0.4 829 * 830 * @return array Comment author, email, url respectively. 831 */ 832 function wp_get_current_commenter() { 833 // Cookies should already be sanitized. 834 835 $comment_author = ''; 836 if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) ) 837 $comment_author = $_COOKIE['comment_author_'.COOKIEHASH]; 838 839 $comment_author_email = ''; 840 if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ) 841 $comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH]; 842 843 $comment_author_url = ''; 844 if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ) 845 $comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH]; 846 847 return compact('comment_author', 'comment_author_email', 'comment_author_url'); 848 } 849 850 /** 851 * Inserts a comment to the database. 852 * 853 * The available comment data key names are 'comment_author_IP', 'comment_date', 854 * 'comment_date_gmt', 'comment_parent', 'comment_approved', and 'user_id'. 855 * 856 * @since 2.0.0 857 * @uses $wpdb 858 * 859 * @param array $commentdata Contains information on the comment. 860 * @return int The new comment's ID. 861 */ 862 function wp_insert_comment($commentdata) { 863 global $wpdb; 864 extract(stripslashes_deep($commentdata), EXTR_SKIP); 865 866 if ( ! isset($comment_author_IP) ) 867 $comment_author_IP = ''; 868 if ( ! isset($comment_date) ) 869 $comment_date = current_time('mysql'); 870 if ( ! isset($comment_date_gmt) ) 871 $comment_date_gmt = get_gmt_from_date($comment_date); 872 if ( ! isset($comment_parent) ) 873 $comment_parent = 0; 874 if ( ! isset($comment_approved) ) 875 $comment_approved = 1; 876 if ( ! isset($user_id) ) 877 $user_id = 0; 878 if ( ! isset($comment_type) ) 879 $comment_type = ''; 880 881 $result = $wpdb->query( $wpdb->prepare("INSERT INTO $wpdb->comments 882 (comment_post_ID, comment_author, comment_author_email, comment_author_url, comment_author_IP, comment_date, comment_date_gmt, comment_content, comment_approved, comment_agent, comment_type, comment_parent, user_id) 883 VALUES (%d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %d)", 884 $comment_post_ID, $comment_author, $comment_author_email, $comment_author_url, $comment_author_IP, $comment_date, $comment_date_gmt, $comment_content, $comment_approved, $comment_agent, $comment_type, $comment_parent, $user_id) ); 885 886 $id = (int) $wpdb->insert_id; 887 888 if ( $comment_approved == 1) 889 wp_update_comment_count($comment_post_ID); 890 891 return $id; 892 } 893 894 /** 895 * Filters and sanitizes comment data. 896 * 897 * Sets the comment data 'filtered' field to true when finished. This can be 898 * checked as to whether the comment should be filtered and to keep from 899 * filtering the same comment more than once. 900 * 901 * @since 2.0.0 902 * @uses apply_filters() Calls 'pre_user_id' hook on comment author's user ID 903 * @uses apply_filters() Calls 'pre_comment_user_agent' hook on comment author's user agent 904 * @uses apply_filters() Calls 'pre_comment_author_name' hook on comment author's name 905 * @uses apply_filters() Calls 'pre_comment_content' hook on the comment's content 906 * @uses apply_filters() Calls 'pre_comment_user_ip' hook on comment author's IP 907 * @uses apply_filters() Calls 'pre_comment_author_url' hook on comment author's URL 908 * @uses apply_filters() Calls 'pre_comment_author_email' hook on comment author's email address 909 * 910 * @param array $commentdata Contains information on the comment. 911 * @return array Parsed comment information. 912 */ 913 function wp_filter_comment($commentdata) { 914 $commentdata['user_id'] = apply_filters('pre_user_id', $commentdata['user_ID']); 915 $commentdata['comment_agent'] = apply_filters('pre_comment_user_agent', $commentdata['comment_agent']); 916 $commentdata['comment_author'] = apply_filters('pre_comment_author_name', $commentdata['comment_author']); 917 $commentdata['comment_content'] = apply_filters('pre_comment_content', $commentdata['comment_content']); 918 $commentdata['comment_author_IP'] = apply_filters('pre_comment_user_ip', $commentdata['comment_author_IP']); 919 $commentdata['comment_author_url'] = apply_filters('pre_comment_author_url', $commentdata['comment_author_url']); 920 $commentdata['comment_author_email'] = apply_filters('pre_comment_author_email', $commentdata['comment_author_email']); 921 $commentdata['filtered'] = true; 922 return $commentdata; 923 } 924 925 /** 926 * Whether comment should be blocked because of comment flood. 927 * 928 * @since 2.1.0 929 * 930 * @param bool $block Whether plugin has already blocked comment. 931 * @param int $time_lastcomment Timestamp for last comment. 932 * @param int $time_newcomment Timestamp for new comment. 933 * @return bool Whether comment should be blocked. 934 */ 935 function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) { 936 if ( $block ) // a plugin has already blocked... we'll let that decision stand 937 return $block; 938 if ( ($time_newcomment - $time_lastcomment) < 15 ) 939 return true; 940 return false; 941 } 942 943 /** 944 * Adds a new comment to the database. 945 * 946 * Filters new comment to ensure that the fields are sanitized and valid before 947 * inserting comment into database. Calls 'comment_post' action with comment ID 948 * and whether comment is approved by WordPress. Also has 'preprocess_comment' 949 * filter for processing the comment data before the function handles it. 950 * 951 * @since 1.5.0 952 * @uses apply_filters() Calls 'preprocess_comment' hook on $commentdata parameter array before processing 953 * @uses do_action() Calls 'comment_post' hook on $comment_ID returned from adding the comment and if the comment was approved. 954 * @uses wp_filter_comment() Used to filter comment before adding comment. 955 * @uses wp_allow_comment() checks to see if comment is approved. 956 * @uses wp_insert_comment() Does the actual comment insertion to the database. 957 * 958 * @param array $commentdata Contains information on the comment. 959 * @return int The ID of the comment after adding. 960 */ 961 function wp_new_comment( $commentdata ) { 962 $commentdata = apply_filters('preprocess_comment', $commentdata); 963 964 $commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID']; 965 $commentdata['user_ID'] = (int) $commentdata['user_ID']; 966 967 $commentdata['comment_parent'] = absint($commentdata['comment_parent']); 968 $parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : ''; 969 $commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0; 970 971 $commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '',$_SERVER['REMOTE_ADDR'] ); 972 $commentdata['comment_agent'] = $_SERVER['HTTP_USER_AGENT']; 973 974 $commentdata['comment_date'] = current_time('mysql'); 975 $commentdata['comment_date_gmt'] = current_time('mysql', 1); 976 977 $commentdata = wp_filter_comment($commentdata); 978 979 $commentdata['comment_approved'] = wp_allow_comment($commentdata); 980 981 $comment_ID = wp_insert_comment($commentdata); 982 983 do_action('comment_post', $comment_ID, $commentdata['comment_approved']); 984 985 if ( 'spam' !== $commentdata['comment_approved'] ) { // If it's spam save it silently for later crunching 986 if ( '0' == $commentdata['comment_approved'] ) 987 wp_notify_moderator($comment_ID); 988 989 $post = &get_post($commentdata['comment_post_ID']); // Don't notify if it's your own comment 990 991 if ( get_option('comments_notify') && $commentdata['comment_approved'] && $post->post_author != $commentdata['user_ID'] ) 992 wp_notify_postauthor($comment_ID, $commentdata['comment_type']); 993 } 994 995 return $comment_ID; 996 } 997 998 /** 999 * Sets the status of a comment. 1000 * 1001 * The 'wp_set_comment_status' action is called after the comment is handled and 1002 * will only be called, if the comment status is either 'hold', 'approve', or 1003 * 'spam'. If the comment status is not in the list, then false is returned and 1004 * if the status is 'delete', then the comment is deleted without calling the 1005 * action. 1006 * 1007 * @since 1.0.0 1008 * @uses wp_transition_comment_status() Passes new and old comment status along with $comment object 1009 * 1010 * @param int $comment_id Comment ID. 1011 * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'delete'. 1012 * @return bool False on failure or deletion and true on success. 1013 */ 1014 function wp_set_comment_status($comment_id, $comment_status) { 1015 global $wpdb; 1016 1017 switch ( $comment_status ) { 1018 case 'hold': 1019 $query = $wpdb->prepare("UPDATE $wpdb->comments SET comment_approved='0' WHERE comment_ID = %d LIMIT 1", $comment_id); 1020 break; 1021 case 'approve': 1022 $query = $wpdb->prepare("UPDATE $wpdb->comments SET comment_approved='1' WHERE comment_ID = %d LIMIT 1", $comment_id); 1023 if ( get_option('comments_notify') ) { 1024 $comment = get_comment($comment_id); 1025 wp_notify_postauthor($comment_id, $comment->comment_type); 1026 } 1027 break; 1028 case 'spam': 1029 $query = $wpdb->prepare("UPDATE $wpdb->comments SET comment_approved='spam' WHERE comment_ID = %d LIMIT 1", $comment_id); 1030 break; 1031 case 'delete': 1032 return wp_delete_comment($comment_id); 1033 break; 1034 default: 1035 return false; 1036 } 1037 1038 if ( !$wpdb->query($query) ) 1039 return false; 1040 1041 clean_comment_cache($comment_id); 1042 1043 $comment = get_comment($comment_id); 1044 1045 do_action('wp_set_comment_status', $comment_id, $comment_status); 1046 wp_transition_comment_status($comment_status, $comment->comment_approved, $comment); 1047 1048 wp_update_comment_count($comment->comment_post_ID); 1049 1050 return true; 1051 } 1052 1053 /** 1054 * Updates an existing comment in the database. 1055 * 1056 * Filters the comment and makes sure certain fields are valid before updating. 1057 * 1058 * @since 2.0.0 1059 * @uses $wpdb 1060 * @uses wp_transition_comment_status() Passes new and old comment status along with $comment object 1061 * 1062 * @param array $commentarr Contains information on the comment. 1063 * @return int Comment was updated if value is 1, or was not updated if value is 0. 1064 */ 1065 function wp_update_comment($commentarr) { 1066 global $wpdb; 1067 1068 // First, get all of the original fields 1069 $comment = get_comment($commentarr['comment_ID'], ARRAY_A); 1070 1071 // Escape data pulled from DB. 1072 foreach ( (array) $comment as $key => $value ) 1073 $comment[$key] = $wpdb->escape($value); 1074 1075 // Merge old and new fields with new fields overwriting old ones. 1076 $commentarr = array_merge($comment, $commentarr); 1077 1078 $commentarr = wp_filter_comment( $commentarr ); 1079 1080 // Now extract the merged array. 1081 extract(stripslashes_deep($commentarr), EXTR_SKIP); 1082 1083 $comment_content = apply_filters('comment_save_pre', $comment_content); 1084 1085 $comment_date_gmt = get_gmt_from_date($comment_date); 1086 1087 if ( !isset($comment_approved) ) 1088 $comment_approved = 1; 1089 else if ( 'hold' == $comment_approved ) 1090 $comment_approved = 0; 1091 else if ( 'approve' == $comment_approved ) 1092 $comment_approved = 1; 1093 1094 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->comments SET 1095 comment_content = %s, 1096 comment_author = %s, 1097 comment_author_email = %s, 1098 comment_approved = %s, 1099 comment_author_url = %s, 1100 comment_date = %s, 1101 comment_date_gmt = %s 1102 WHERE comment_ID = %d", 1103 $comment_content, 1104 $comment_author, 1105 $comment_author_email, 1106 $comment_approved, 1107 $comment_author_url, 1108 $comment_date, 1109 $comment_date_gmt, 1110 $comment_ID) ); 1111 1112 $rval = $wpdb->rows_affected; 1113 1114 clean_comment_cache($comment_ID); 1115 wp_update_comment_count($comment_post_ID); 1116 do_action('edit_comment', $comment_ID); 1117 $comment = get_comment($comment_ID); 1118 wp_transition_comment_status($comment_approved, $comment->comment_approved, $comment); 1119 return $rval; 1120 } 1121 1122 /** 1123 * Whether to defer comment counting. 1124 * 1125 * When setting $defer to true, all post comment counts will not be updated 1126 * until $defer is set to false. When $defer is set to false, then all 1127 * previously deferred updated post comment counts will then be automatically 1128 * updated without having to call wp_update_comment_count() after. 1129 * 1130 * @since 2.5.0 1131 * @staticvar bool $_defer 1132 * 1133 * @param bool $defer 1134 * @return unknown 1135 */ 1136 function wp_defer_comment_counting($defer=null) { 1137 static $_defer = false; 1138 1139 if ( is_bool($defer) ) { 1140 $_defer = $defer; 1141 // flush any deferred counts 1142 if ( !$defer ) 1143 wp_update_comment_count( null, true ); 1144 } 1145 1146 return $_defer; 1147 } 1148 1149 /** 1150 * Updates the comment count for post(s). 1151 * 1152 * When $do_deferred is false (is by default) and the comments have been set to 1153 * be deferred, the post_id will be added to a queue, which will be updated at a 1154 * later date and only updated once per post ID. 1155 * 1156 * If the comments have not be set up to be deferred, then the post will be 1157 * updated. When $do_deferred is set to true, then all previous deferred post 1158 * IDs will be updated along with the current $post_id. 1159 * 1160 * @since 2.1.0 1161 * @see wp_update_comment_count_now() For what could cause a false return value 1162 * 1163 * @param int $post_id Post ID 1164 * @param bool $do_deferred Whether to process previously deferred post comment counts 1165 * @return bool True on success, false on failure 1166 */ 1167 function wp_update_comment_count($post_id, $do_deferred=false) { 1168 static $_deferred = array(); 1169 1170 if ( $do_deferred ) { 1171 $_deferred = array_unique($_deferred); 1172 foreach ( $_deferred as $i => $_post_id ) { 1173 wp_update_comment_count_now($_post_id); 1174 unset( $_deferred[$i] ); /** @todo Move this outside of the foreach and reset $_deferred to an array instead */ 1175 } 1176 } 1177 1178 if ( wp_defer_comment_counting() ) { 1179 $_deferred[] = $post_id; 1180 return true; 1181 } 1182 elseif ( $post_id ) { 1183 return wp_update_comment_count_now($post_id); 1184 } 1185 1186 } 1187 1188 /** 1189 * Updates the comment count for the post. 1190 * 1191 * @since 2.5.0 1192 * @uses $wpdb 1193 * @uses do_action() Calls 'wp_update_comment_count' hook on $post_id, $new, and $old 1194 * @uses do_action() Calls 'edit_posts' hook on $post_id and $post 1195 * 1196 * @param int $post_id Post ID 1197 * @return bool False on '0' $post_id or if post with ID does not exist. True on success. 1198 */ 1199 function wp_update_comment_count_now($post_id) { 1200 global $wpdb; 1201 $post_id = (int) $post_id; 1202 if ( !$post_id ) 1203 return false; 1204 if ( !$post = get_post($post_id) ) 1205 return false; 1206 1207 $old = (int) $post->comment_count; 1208 $new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id) ); 1209 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET comment_count = %d WHERE ID = %d", $new, $post_id) ); 1210 1211 if ( 'page' == $post->post_type ) 1212 clean_page_cache( $post_id ); 1213 else 1214 clean_post_cache( $post_id ); 1215 1216 do_action('wp_update_comment_count', $post_id, $new, $old); 1217 do_action('edit_post', $post_id, $post); 1218 1219 return true; 1220 } 1221 1222 // 1223 // Ping and trackback functions. 1224 // 1225 1226 /** 1227 * Finds a pingback server URI based on the given URL. 1228 * 1229 * Checks the HTML for the rel="pingback" link and x-pingback headers. It does 1230 * a check for the x-pingback headers first and returns that, if available. The 1231 * check for the rel="pingback" has more overhead than just the header. 1232 * 1233 * @since 1.5.0 1234 * 1235 * @param string $url URL to ping. 1236 * @param int $deprecated Not Used. 1237 * @return bool|string False on failure, string containing URI on success. 1238 */ 1239 function discover_pingback_server_uri($url, $deprecated = 2048) { 1240 1241 $pingback_str_dquote = 'rel="pingback"'; 1242 $pingback_str_squote = 'rel=\'pingback\''; 1243 1244 /** @todo Should use Filter Extension or custom preg_match instead. */ 1245 $parsed_url = parse_url($url); 1246 1247 if ( ! isset( $parsed_url['host'] ) ) // Not an URL. This should never happen. 1248 return false; 1249 1250 $response = wp_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.1' ) ); 1251 1252 if ( is_wp_error( $response ) ) 1253 return false; 1254 1255 if ( isset( $response['headers']['x-pingback'] ) ) 1256 return $response['headers']['x-pingback']; 1257 1258 // Not an (x)html, sgml, or xml page, no use going further. 1259 if ( isset( $response['headers']['content-type'] ) && preg_match('#(image|audio|video|model)/#is', $response['headers']['content-type']) ) 1260 return false; 1261 1262 $contents = $response['body']; 1263 1264 $pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote); 1265 $pingback_link_offset_squote = strpos($contents, $pingback_str_squote); 1266 if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) { 1267 $quote = ($pingback_link_offset_dquote) ? '"' : '\''; 1268 $pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote; 1269 $pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset); 1270 $pingback_href_start = $pingback_href_pos+6; 1271 $pingback_href_end = @strpos($contents, $quote, $pingback_href_start); 1272 $pingback_server_url_len = $pingback_href_end - $pingback_href_start; 1273 $pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len); 1274 1275 // We may find rel="pingback" but an incomplete pingback URL 1276 if ( $pingback_server_url_len > 0 ) { // We got it! 1277 return $pingback_server_url; 1278 } 1279 } 1280 1281 return false; 1282 } 1283 1284 /** 1285 * Perform all pingbacks, enclosures, trackbacks, and send to pingback services. 1286 * 1287 * @since 2.1.0 1288 * @uses $wpdb 1289 */ 1290 function do_all_pings() { 1291 global $wpdb; 1292 1293 // Do pingbacks 1294 while ($ping = $wpdb->get_row("SELECT * FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pingme' LIMIT 1")) { 1295 $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE post_id = {$ping->ID} AND meta_key = '_pingme';"); 1296 pingback($ping->post_content, $ping->ID); 1297 } 1298 1299 // Do Enclosures 1300 while ($enclosure = $wpdb->get_row("SELECT * FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_encloseme' LIMIT 1")) { 1301 $wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_encloseme';", $enclosure->ID) ); 1302 do_enclose($enclosure->post_content, $enclosure->ID); 1303 } 1304 1305 // Do Trackbacks 1306 $trackbacks = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE to_ping <> '' AND post_status = 'publish'"); 1307 if ( is_array($trackbacks) ) 1308 foreach ( $trackbacks as $trackback ) 1309 do_trackbacks($trackback); 1310 1311 //Do Update Services/Generic Pings 1312 generic_ping(); 1313 } 1314 1315 /** 1316 * Perform trackbacks. 1317 * 1318 * @since 1.5.0 1319 * @uses $wpdb 1320 * 1321 * @param int $post_id Post ID to do trackbacks on. 1322 */ 1323 function do_trackbacks($post_id) { 1324 global $wpdb; 1325 1326 $post = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id) ); 1327 $to_ping = get_to_ping($post_id); 1328 $pinged = get_pung($post_id); 1329 if ( empty($to_ping) ) { 1330 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = '' WHERE ID = %d", $post_id) ); 1331 return; 1332 } 1333 1334 if ( empty($post->post_excerpt) ) 1335 $excerpt = apply_filters('the_content', $post->post_content); 1336 else 1337 $excerpt = apply_filters('the_excerpt', $post->post_excerpt); 1338 $excerpt = str_replace(']]>', ']]>', $excerpt); 1339 $excerpt = wp_html_excerpt($excerpt, 252) . '...'; 1340 1341 $post_title = apply_filters('the_title', $post->post_title); 1342 $post_title = strip_tags($post_title); 1343 1344 if ( $to_ping ) { 1345 foreach ( (array) $to_ping as $tb_ping ) { 1346 $tb_ping = trim($tb_ping); 1347 if ( !in_array($tb_ping, $pinged) ) { 1348 trackback($tb_ping, $post_title, $excerpt, $post_id); 1349 $pinged[] = $tb_ping; 1350 } else { 1351 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_ping', '')) WHERE ID = %d", $post_id) ); 1352 } 1353 } 1354 } 1355 } 1356 1357 /** 1358 * Sends pings to all of the ping site services. 1359 * 1360 * @since 1.2.0 1361 * 1362 * @param int $post_id Post ID. Not actually used. 1363 * @return int Same as Post ID from parameter 1364 */ 1365 function generic_ping($post_id = 0) { 1366 $services = get_option('ping_sites'); 1367 1368 $services = explode("\n", $services); 1369 foreach ( (array) $services as $service ) { 1370 $service = trim($service); 1371 if ( '' != $service ) 1372 weblog_ping($service); 1373 } 1374 1375 return $post_id; 1376 } 1377 1378 /** 1379 * Pings back the links found in a post. 1380 * 1381 * @since 0.71 1382 * @uses $wp_version 1383 * @uses IXR_Client 1384 * 1385 * @param string $content Post content to check for links. 1386 * @param int $post_ID Post ID. 1387 */ 1388 function pingback($content, $post_ID) { 1389 global $wp_version; 1390 include_once (ABSPATH . WPINC . '/class-IXR.php'); 1391 1392 // original code by Mort (http://mort.mine.nu:8080) 1393 $post_links = array(); 1394 1395 $pung = get_pung($post_ID); 1396 1397 // Variables 1398 $ltrs = '\w'; 1399 $gunk = '/#~:.?+=&%@!\-'; 1400 $punc = '.:?\-'; 1401 $any = $ltrs . $gunk . $punc; 1402 1403 // Step 1 1404 // Parsing the post, external links (if any) are stored in the $post_links array 1405 // This regexp comes straight from phpfreaks.com 1406 // http://www.phpfreaks.com/quickcode/Extract_All_URLs_on_a_Page/15.php 1407 preg_match_all("{\b http : [$any] +? (?= [$punc] * [^$any] | $)}x", $content, $post_links_temp); 1408 1409 // Step 2. 1410 // Walking thru the links array 1411 // first we get rid of links pointing to sites, not to specific files 1412 // Example: 1413 // http://dummy-weblog.org 1414 // http://dummy-weblog.org/ 1415 // http://dummy-weblog.org/post.php 1416 // We don't wanna ping first and second types, even if they have a valid <link/> 1417 1418 foreach ( (array) $post_links_temp[0] as $link_test ) : 1419 if ( !in_array($link_test, $pung) && (url_to_postid($link_test) != $post_ID) // If we haven't pung it already and it isn't a link to itself 1420 && !is_local_attachment($link_test) ) : // Also, let's never ping local attachments. 1421 if ( $test = @parse_url($link_test) ) { 1422 if ( isset($test['query']) ) 1423 $post_links[] = $link_test; 1424 elseif ( ($test['path'] != '/') && ($test['path'] != '') ) 1425 $post_links[] = $link_test; 1426 } 1427 endif; 1428 endforeach; 1429 1430 do_action_ref_array('pre_ping', array(&$post_links, &$pung)); 1431 1432 foreach ( (array) $post_links as $pagelinkedto ) { 1433 $pingback_server_url = discover_pingback_server_uri($pagelinkedto, 2048); 1434 1435 if ( $pingback_server_url ) { 1436 @ set_time_limit( 60 ); 1437 // Now, the RPC call 1438 $pagelinkedfrom = get_permalink($post_ID); 1439 1440 // using a timeout of 3 seconds should be enough to cover slow servers 1441 $client = new IXR_Client($pingback_server_url); 1442 $client->timeout = 3; 1443 $client->useragent .= ' -- WordPress/' . $wp_version; 1444 1445 // when set to true, this outputs debug messages by itself 1446 $client->debug = false; 1447 1448 if ( $client->query('pingback.ping', $pagelinkedfrom, $pagelinkedto) || ( isset($client->error->code) && 48 == $client->error->code ) ) // Already registered 1449 add_ping( $post_ID, $pagelinkedto ); 1450 } 1451 } 1452 } 1453 1454 /** 1455 * Check whether blog is public before returning sites. 1456 * 1457 * @since 2.1.0 1458 * 1459 * @param mixed $sites Will return if blog is public, will not return if not public. 1460 * @return mixed Empty string if blog is not public, returns $sites, if site is public. 1461 */ 1462 function privacy_ping_filter($sites) { 1463 if ( '0' != get_option('blog_public') ) 1464 return $sites; 1465 else 1466 return ''; 1467 } 1468 1469 /** 1470 * Send a Trackback. 1471 * 1472 * Updates database when sending trackback to prevent duplicates. 1473 * 1474 * @since 0.71 1475 * @uses $wpdb 1476 * 1477 * @param string $trackback_url URL to send trackbacks. 1478 * @param string $title Title of post. 1479 * @param string $excerpt Excerpt of post. 1480 * @param int $ID Post ID. 1481 * @return mixed Database query from update. 1482 */ 1483 function trackback($trackback_url, $title, $excerpt, $ID) { 1484 global $wpdb; 1485 1486 if ( empty($trackback_url) ) 1487 return; 1488 1489 $options = array(); 1490 $options['timeout'] = 4; 1491 $options['body'] = array( 1492 'title' => $title, 1493 'url' => get_permalink($ID), 1494 'blog_name' => get_option('blogname'), 1495 'excerpt' => $excerpt 1496 ); 1497 1498 $response = wp_remote_post($trackback_url, $options); 1499 1500 if ( is_wp_error( $response ) ) 1501 return; 1502 1503 $tb_url = addslashes( $trackback_url ); 1504 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', '$tb_url') WHERE ID = %d", $ID) ); 1505 return $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_url', '')) WHERE ID = %d", $ID) ); 1506 } 1507 1508 /** 1509 * Send a pingback. 1510 * 1511 * @since 1.2.0 1512 * @uses $wp_version 1513 * @uses IXR_Client 1514 * 1515 * @param string $server Host of blog to connect to. 1516 * @param string $path Path to send the ping. 1517 */ 1518 function weblog_ping($server = '', $path = '') { 1519 global $wp_version; 1520 include_once (ABSPATH . WPINC . '/class-IXR.php'); 1521 1522 // using a timeout of 3 seconds should be enough to cover slow servers 1523 $client = new IXR_Client($server, ((!strlen(trim($path)) || ('/' == $path)) ? false : $path)); 1524 $client->timeout = 3; 1525 $client->useragent .= ' -- WordPress/'.$wp_version; 1526 1527 // when set to true, this outputs debug messages by itself 1528 $client->debug = false; 1529 $home = trailingslashit( get_option('home') ); 1530 if ( !$client->query('weblogUpdates.extendedPing', get_option('blogname'), $home, get_bloginfo('rss2_url') ) ) // then try a normal ping 1531 $client->query('weblogUpdates.ping', get_option('blogname'), $home); 1532 } 1533 1534 // 1535 // Cache 1536 // 1537 1538 /** 1539 * Removes comment ID from the comment cache. 1540 * 1541 * @since 2.3.0 1542 * @package WordPress 1543 * @subpackage Cache 1544 * 1545 * @param int $id Comment ID to remove from cache 1546 */ 1547 function clean_comment_cache($id) { 1548 wp_cache_delete($id, 'comment'); 1549 } 1550 1551 /** 1552 * Updates the comment cache of given comments. 1553 * 1554 * Will add the comments in $comments to the cache. If comment ID already exists 1555 * in the comment cache then it will not be updated. The comment is added to the 1556 * cache using the comment group with the key using the ID of the comments. 1557 * 1558 * @since 2.3.0 1559 * @package WordPress 1560 * @subpackage Cache 1561 * 1562 * @param array $comments Array of comment row objects 1563 */ 1564 function update_comment_cache($comments) { 1565 foreach ( (array) $comments as $comment ) 1566 wp_cache_add($comment->comment_ID, $comment, 'comment'); 1567 } 1568 1569 // 1570 // Internal 1571 // 1572 1573 /** 1574 * Close comments on old posts on the fly, without any extra DB queries. Hooked to the_posts. 1575 * 1576 * @access private 1577 * @since 2.7.0 1578 * 1579 * @param object $posts Post data object. 1580 * @return object 1581 */ 1582 function _close_comments_for_old_posts( $posts ) { 1583 if ( empty($posts) || !is_singular() || !get_option('close_comments_for_old_posts') ) 1584 return $posts; 1585 1586 $days_old = (int) get_option('close_comments_days_old'); 1587 if ( !$days_old ) 1588 return $posts; 1589 1590 if ( time() - strtotime( $posts[0]->post_date_gmt ) > ( $days_old * 24 * 60 * 60 ) ) { 1591 $posts[0]->comment_status = 'closed'; 1592 $posts[0]->ping_status = 'closed'; 1593 } 1594 1595 return $posts; 1596 } 1597 1598 /** 1599 * Close comments on an old post. Hooked to comments_open and pings_open. 1600 * 1601 * @access private 1602 * @since 2.7.0 1603 * 1604 * @param bool $open Comments open or closed 1605 * @param int $post_id Post ID 1606 * @return bool $open 1607 */ 1608 function _close_comments_for_old_post( $open, $post_id ) { 1609 if ( ! $open ) 1610 return $open; 1611 1612 if ( !get_option('close_comments_for_old_posts') ) 1613 return $open; 1614 1615 $days_old = (int) get_option('close_comments_days_old'); 1616 if ( !$days_old ) 1617 return $open; 1618 1619 $post = get_post($post_id); 1620 1621 if ( time() - strtotime( $post->post_date_gmt ) > ( $days_old * 24 * 60 * 60 ) ) 1622 return false; 1623 1624 return $open; 1625 } 1626 1627 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Mar 23 16:23:02 2009 | Cross-referenced by PHPXref 0.7 |