PageRenderTime 182ms CodeModel.GetById 60ms app.highlight 83ms RepoModel.GetById 18ms app.codeStats 1ms

/wp-includes/comment.php

https://github.com/dipakdotyadav/WordPress
PHP | 2135 lines | 1083 code | 293 blank | 759 comment | 300 complexity | f3af8be2a3a934dd0b4888a71d817025 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 author was approved before, then the comment is
  22 * automatically whitelisted.
  23 *
  24 * If none of the checks fail, then the failback is to set the check to pass
  25 * (return true).
  26 *
  27 * @since 1.2.0
  28 * @uses $wpdb
  29 *
  30 * @param string $author Comment Author's name
  31 * @param string $email Comment Author's email
  32 * @param string $url Comment Author's URL
  33 * @param string $comment Comment contents
  34 * @param string $user_ip Comment Author's IP address
  35 * @param string $user_agent Comment Author's User Agent
  36 * @param string $comment_type Comment type, either user submitted comment,
  37 *		trackback, or pingback
  38 * @return bool Whether the checks passed (true) and the comments should be
  39 *		displayed or set to moderated
  40 */
  41function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) {
  42	global $wpdb;
  43
  44	if ( 1 == get_option('comment_moderation') )
  45		return false; // If moderation is set to manual
  46
  47	$comment = apply_filters( 'comment_text', $comment );
  48
  49	// Check # of external links
  50	if ( $max_links = get_option( 'comment_max_links' ) ) {
  51		$num_links = preg_match_all( '/<a [^>]*href/i', $comment, $out );
  52		$num_links = apply_filters( 'comment_max_links_url', $num_links, $url ); // provide for counting of $url as a link
  53		if ( $num_links >= $max_links )
  54			return false;
  55	}
  56
  57	$mod_keys = trim(get_option('moderation_keys'));
  58	if ( !empty($mod_keys) ) {
  59		$words = explode("\n", $mod_keys );
  60
  61		foreach ( (array) $words as $word) {
  62			$word = trim($word);
  63
  64			// Skip empty lines
  65			if ( empty($word) )
  66				continue;
  67
  68			// Do some escaping magic so that '#' chars in the
  69			// spam words don't break things:
  70			$word = preg_quote($word, '#');
  71
  72			$pattern = "#$word#i";
  73			if ( preg_match($pattern, $author) ) return false;
  74			if ( preg_match($pattern, $email) ) return false;
  75			if ( preg_match($pattern, $url) ) return false;
  76			if ( preg_match($pattern, $comment) ) return false;
  77			if ( preg_match($pattern, $user_ip) ) return false;
  78			if ( preg_match($pattern, $user_agent) ) return false;
  79		}
  80	}
  81
  82	// Comment whitelisting:
  83	if ( 1 == get_option('comment_whitelist')) {
  84		if ( 'trackback' != $comment_type && 'pingback' != $comment_type && $author != '' && $email != '' ) {
  85			// expected_slashed ($author, $email)
  86			$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");
  87			if ( ( 1 == $ok_to_comment ) &&
  88				( empty($mod_keys) || false === strpos( $email, $mod_keys) ) )
  89					return true;
  90			else
  91				return false;
  92		} else {
  93			return false;
  94		}
  95	}
  96	return true;
  97}
  98
  99/**
 100 * Retrieve the approved comments for post $post_id.
 101 *
 102 * @since 2.0.0
 103 * @uses $wpdb
 104 *
 105 * @param int $post_id The ID of the post
 106 * @return array $comments The approved comments
 107 */
 108function get_approved_comments($post_id) {
 109	global $wpdb;
 110	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));
 111}
 112
 113/**
 114 * Retrieves comment data given a comment ID or comment object.
 115 *
 116 * If an object is passed then the comment data will be cached and then returned
 117 * after being passed through a filter. If the comment is empty, then the global
 118 * comment variable will be used, if it is set.
 119 *
 120 * If the comment is empty, then the global comment variable will be used, if it
 121 * is set.
 122 *
 123 * @since 2.0.0
 124 * @uses $wpdb
 125 *
 126 * @param object|string|int $comment Comment to retrieve.
 127 * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants.
 128 * @return object|array|null Depends on $output value.
 129 */
 130function get_comment(&$comment, $output = OBJECT) {
 131	global $wpdb;
 132	$null = null;
 133
 134	if ( empty($comment) ) {
 135		if ( isset($GLOBALS['comment']) )
 136			$_comment = & $GLOBALS['comment'];
 137		else
 138			$_comment = null;
 139	} elseif ( is_object($comment) ) {
 140		wp_cache_add($comment->comment_ID, $comment, 'comment');
 141		$_comment = $comment;
 142	} else {
 143		if ( isset($GLOBALS['comment']) && ($GLOBALS['comment']->comment_ID == $comment) ) {
 144			$_comment = & $GLOBALS['comment'];
 145		} elseif ( ! $_comment = wp_cache_get($comment, 'comment') ) {
 146			$_comment = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment));
 147			if ( ! $_comment )
 148				return $null;
 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 comment list can be for the blog as a whole or for an individual post.
 172 *
 173 * The list of comment arguments are 'status', 'orderby', 'comment_date_gmt',
 174 * 'order', 'number', 'offset', and 'post_id'.
 175 *
 176 * @since 2.7.0
 177 * @uses $wpdb
 178 *
 179 * @param mixed $args Optional. Array or string of options to override defaults.
 180 * @return array List of comments.
 181 */
 182function get_comments( $args = '' ) {
 183	$query = new WP_Comment_Query;
 184	return $query->query( $args );
 185}
 186
 187/**
 188 * WordPress Comment Query class.
 189 *
 190 * @since 3.1.0
 191 */
 192class WP_Comment_Query {
 193	/**
 194	 * Metadata query container
 195	 *
 196	 * @since 3.5.0
 197	 * @access public
 198	 * @var object WP_Meta_Query
 199	 */
 200	var $meta_query = false;
 201
 202	/**
 203	 * Execute the query
 204	 *
 205	 * @since 3.1.0
 206	 *
 207	 * @param string|array $query_vars
 208	 * @return int|array
 209	 */
 210	function query( $query_vars ) {
 211		global $wpdb;
 212
 213		$defaults = array(
 214			'author_email' => '',
 215			'ID' => '',
 216			'karma' => '',
 217			'number' => '',
 218			'offset' => '',
 219			'orderby' => '',
 220			'order' => 'DESC',
 221			'parent' => '',
 222			'post_ID' => '',
 223			'post_id' => 0,
 224			'post_author' => '',
 225			'post_name' => '',
 226			'post_parent' => '',
 227			'post_status' => '',
 228			'post_type' => '',
 229			'status' => '',
 230			'type' => '',
 231			'user_id' => '',
 232			'search' => '',
 233			'count' => false,
 234			'meta_key' => '',
 235			'meta_value' => '',
 236			'meta_query' => '',
 237		);
 238
 239		$groupby = '';
 240
 241		$this->query_vars = wp_parse_args( $query_vars, $defaults );
 242
 243		// Parse meta query
 244		$this->meta_query = new WP_Meta_Query();
 245		$this->meta_query->parse_query_vars( $this->query_vars );
 246
 247		do_action_ref_array( 'pre_get_comments', array( &$this ) );
 248		extract( $this->query_vars, EXTR_SKIP );
 249
 250		// $args can be whatever, only use the args defined in defaults to compute the key
 251		$key = md5( serialize( compact(array_keys($defaults)) )  );
 252		$last_changed = wp_cache_get( 'last_changed', 'comment' );
 253		if ( ! $last_changed ) {
 254			$last_changed = microtime();
 255			wp_cache_set( 'last_changed', $last_changed, 'comment' );
 256		}
 257		$cache_key = "get_comments:$key:$last_changed";
 258
 259		if ( $cache = wp_cache_get( $cache_key, 'comment' ) )
 260			return $cache;
 261
 262		$post_id = absint($post_id);
 263
 264		if ( 'hold' == $status )
 265			$approved = "comment_approved = '0'";
 266		elseif ( 'approve' == $status )
 267			$approved = "comment_approved = '1'";
 268		elseif ( ! empty( $status ) && 'all' != $status )
 269			$approved = $wpdb->prepare( "comment_approved = %s", $status );
 270		else
 271			$approved = "( comment_approved = '0' OR comment_approved = '1' )";
 272
 273		$order = ( 'ASC' == strtoupper($order) ) ? 'ASC' : 'DESC';
 274
 275		if ( ! empty( $orderby ) ) {
 276			$ordersby = is_array($orderby) ? $orderby : preg_split('/[,\s]/', $orderby);
 277			$allowed_keys = array(
 278				'comment_agent',
 279				'comment_approved',
 280				'comment_author',
 281				'comment_author_email',
 282				'comment_author_IP',
 283				'comment_author_url',
 284				'comment_content',
 285				'comment_date',
 286				'comment_date_gmt',
 287				'comment_ID',
 288				'comment_karma',
 289				'comment_parent',
 290				'comment_post_ID',
 291				'comment_type',
 292				'user_id',
 293			);
 294			if ( ! empty( $this->query_vars['meta_key'] ) ) {
 295				$allowed_keys[] = $this->query_vars['meta_key'];
 296				$allowed_keys[] = 'meta_value';
 297				$allowed_keys[] = 'meta_value_num';
 298			}
 299			$ordersby = array_intersect( $ordersby, $allowed_keys );
 300			foreach ( $ordersby as $key => $value ) {
 301				if ( $value == $this->query_vars['meta_key'] || $value == 'meta_value' ) {
 302					$ordersby[ $key ] = "$wpdb->commentmeta.meta_value";
 303				} elseif ( $value == 'meta_value_num' ) {
 304					$ordersby[ $key ] = "$wpdb->commentmeta.meta_value+0";
 305				}
 306			}
 307			$orderby = empty( $ordersby ) ? 'comment_date_gmt' : implode(', ', $ordersby);
 308		} else {
 309			$orderby = 'comment_date_gmt';
 310		}
 311
 312		$number = absint($number);
 313		$offset = absint($offset);
 314
 315		if ( !empty($number) ) {
 316			if ( $offset )
 317				$limits = 'LIMIT ' . $offset . ',' . $number;
 318			else
 319				$limits = 'LIMIT ' . $number;
 320		} else {
 321			$limits = '';
 322		}
 323
 324		if ( $count )
 325			$fields = 'COUNT(*)';
 326		else
 327			$fields = '*';
 328
 329		$join = '';
 330		$where = $approved;
 331
 332		if ( ! empty($post_id) )
 333			$where .= $wpdb->prepare( ' AND comment_post_ID = %d', $post_id );
 334		if ( '' !== $author_email )
 335			$where .= $wpdb->prepare( ' AND comment_author_email = %s', $author_email );
 336		if ( '' !== $karma )
 337			$where .= $wpdb->prepare( ' AND comment_karma = %d', $karma );
 338		if ( 'comment' == $type ) {
 339			$where .= " AND comment_type = ''";
 340		} elseif( 'pings' == $type ) {
 341			$where .= ' AND comment_type IN ("pingback", "trackback")';
 342		} elseif ( ! empty( $type ) ) {
 343			$where .= $wpdb->prepare( ' AND comment_type = %s', $type );
 344		}
 345		if ( '' !== $parent )
 346			$where .= $wpdb->prepare( ' AND comment_parent = %d', $parent );
 347		if ( '' !== $user_id )
 348			$where .= $wpdb->prepare( ' AND user_id = %d', $user_id );
 349		if ( '' !== $search )
 350			$where .= $this->get_search_sql( $search, array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' ) );
 351
 352		$post_fields = array_filter( compact( array( 'post_author', 'post_name', 'post_parent', 'post_status', 'post_type', ) ) );
 353		if ( ! empty( $post_fields ) ) {
 354			$join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
 355			foreach( $post_fields as $field_name => $field_value )
 356				$where .= $wpdb->prepare( " AND {$wpdb->posts}.{$field_name} = %s", $field_value );
 357		}
 358
 359		if ( ! empty( $this->meta_query->queries ) ) {
 360			$clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this );
 361			$join .= $clauses['join'];
 362			$where .= $clauses['where'];
 363			$groupby = "{$wpdb->comments}.comment_ID";
 364		}
 365
 366		$pieces = array( 'fields', 'join', 'where', 'orderby', 'order', 'limits', 'groupby' );
 367		$clauses = apply_filters_ref_array( 'comments_clauses', array( compact( $pieces ), &$this ) );
 368		foreach ( $pieces as $piece )
 369			$$piece = isset( $clauses[ $piece ] ) ? $clauses[ $piece ] : '';
 370
 371		if ( $groupby )
 372			$groupby = 'GROUP BY ' . $groupby;
 373
 374		$query = "SELECT $fields FROM $wpdb->comments $join WHERE $where $groupby ORDER BY $orderby $order $limits";
 375
 376		if ( $count )
 377			return $wpdb->get_var( $query );
 378
 379		$comments = $wpdb->get_results( $query );
 380		$comments = apply_filters_ref_array( 'the_comments', array( $comments, &$this ) );
 381
 382		wp_cache_add( $cache_key, $comments, 'comment' );
 383
 384		return $comments;
 385	}
 386
 387	/*
 388	 * Used internally to generate an SQL string for searching across multiple columns
 389	 *
 390	 * @access protected
 391	 * @since 3.1.0
 392	 *
 393	 * @param string $string
 394	 * @param array $cols
 395	 * @return string
 396	 */
 397	function get_search_sql( $string, $cols ) {
 398		$string = esc_sql( like_escape( $string ) );
 399
 400		$searches = array();
 401		foreach ( $cols as $col )
 402			$searches[] = "$col LIKE '%$string%'";
 403
 404		return ' AND (' . implode(' OR ', $searches) . ')';
 405	}
 406}
 407
 408/**
 409 * Retrieve all of the WordPress supported comment statuses.
 410 *
 411 * Comments have a limited set of valid status values, this provides the comment
 412 * status values and descriptions.
 413 *
 414 * @package WordPress
 415 * @subpackage Post
 416 * @since 2.7.0
 417 *
 418 * @return array List of comment statuses.
 419 */
 420function get_comment_statuses() {
 421	$status = array(
 422		'hold'		=> __('Unapproved'),
 423		/* translators: comment status  */
 424		'approve'	=> _x('Approved', 'adjective'),
 425		/* translators: comment status */
 426		'spam'		=> _x('Spam', 'adjective'),
 427	);
 428
 429	return $status;
 430}
 431
 432/**
 433 * The date the last comment was modified.
 434 *
 435 * @since 1.5.0
 436 * @uses $wpdb
 437 *
 438 * @param string $timezone Which timezone to use in reference to 'gmt', 'blog',
 439 *		or 'server' locations.
 440 * @return string Last comment modified date.
 441 */
 442function get_lastcommentmodified($timezone = 'server') {
 443	global $wpdb;
 444	static $cache_lastcommentmodified = array();
 445
 446	if ( isset($cache_lastcommentmodified[$timezone]) )
 447		return $cache_lastcommentmodified[$timezone];
 448
 449	$add_seconds_server = date('Z');
 450
 451	switch ( strtolower($timezone)) {
 452		case 'gmt':
 453			$lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
 454			break;
 455		case 'blog':
 456			$lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
 457			break;
 458		case 'server':
 459			$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));
 460			break;
 461	}
 462
 463	$cache_lastcommentmodified[$timezone] = $lastcommentmodified;
 464
 465	return $lastcommentmodified;
 466}
 467
 468/**
 469 * The amount of comments in a post or total comments.
 470 *
 471 * A lot like {@link wp_count_comments()}, in that they both return comment
 472 * stats (albeit with different types). The {@link wp_count_comments()} actual
 473 * caches, but this function does not.
 474 *
 475 * @since 2.0.0
 476 * @uses $wpdb
 477 *
 478 * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide.
 479 * @return array The amount of spam, approved, awaiting moderation, and total comments.
 480 */
 481function get_comment_count( $post_id = 0 ) {
 482	global $wpdb;
 483
 484	$post_id = (int) $post_id;
 485
 486	$where = '';
 487	if ( $post_id > 0 ) {
 488		$where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id);
 489	}
 490
 491	$totals = (array) $wpdb->get_results("
 492		SELECT comment_approved, COUNT( * ) AS total
 493		FROM {$wpdb->comments}
 494		{$where}
 495		GROUP BY comment_approved
 496	", ARRAY_A);
 497
 498	$comment_count = array(
 499		"approved"              => 0,
 500		"awaiting_moderation"   => 0,
 501		"spam"                  => 0,
 502		"total_comments"        => 0
 503	);
 504
 505	foreach ( $totals as $row ) {
 506		switch ( $row['comment_approved'] ) {
 507			case 'spam':
 508				$comment_count['spam'] = $row['total'];
 509				$comment_count["total_comments"] += $row['total'];
 510				break;
 511			case 1:
 512				$comment_count['approved'] = $row['total'];
 513				$comment_count['total_comments'] += $row['total'];
 514				break;
 515			case 0:
 516				$comment_count['awaiting_moderation'] = $row['total'];
 517				$comment_count['total_comments'] += $row['total'];
 518				break;
 519			default:
 520				break;
 521		}
 522	}
 523
 524	return $comment_count;
 525}
 526
 527//
 528// Comment meta functions
 529//
 530
 531/**
 532 * Add meta data field to a comment.
 533 *
 534 * @since 2.9.0
 535 * @uses add_metadata
 536 * @link http://codex.wordpress.org/Function_Reference/add_comment_meta
 537 *
 538 * @param int $comment_id Comment ID.
 539 * @param string $meta_key Metadata name.
 540 * @param mixed $meta_value Metadata value.
 541 * @param bool $unique Optional, default is false. Whether the same key should not be added.
 542 * @return bool False for failure. True for success.
 543 */
 544function add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false) {
 545	return add_metadata('comment', $comment_id, $meta_key, $meta_value, $unique);
 546}
 547
 548/**
 549 * Remove metadata matching criteria from a comment.
 550 *
 551 * You can match based on the key, or key and value. Removing based on key and
 552 * value, will keep from removing duplicate metadata with the same key. It also
 553 * allows removing all metadata matching key, if needed.
 554 *
 555 * @since 2.9.0
 556 * @uses delete_metadata
 557 * @link http://codex.wordpress.org/Function_Reference/delete_comment_meta
 558 *
 559 * @param int $comment_id comment ID
 560 * @param string $meta_key Metadata name.
 561 * @param mixed $meta_value Optional. Metadata value.
 562 * @return bool False for failure. True for success.
 563 */
 564function delete_comment_meta($comment_id, $meta_key, $meta_value = '') {
 565	return delete_metadata('comment', $comment_id, $meta_key, $meta_value);
 566}
 567
 568/**
 569 * Retrieve comment meta field for a comment.
 570 *
 571 * @since 2.9.0
 572 * @uses get_metadata
 573 * @link http://codex.wordpress.org/Function_Reference/get_comment_meta
 574 *
 575 * @param int $comment_id Comment ID.
 576 * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
 577 * @param bool $single Whether to return a single value.
 578 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
 579 *  is true.
 580 */
 581function get_comment_meta($comment_id, $key = '', $single = false) {
 582	return get_metadata('comment', $comment_id, $key, $single);
 583}
 584
 585/**
 586 * Update comment meta field based on comment ID.
 587 *
 588 * Use the $prev_value parameter to differentiate between meta fields with the
 589 * same key and comment ID.
 590 *
 591 * If the meta field for the comment does not exist, it will be added.
 592 *
 593 * @since 2.9.0
 594 * @uses update_metadata
 595 * @link http://codex.wordpress.org/Function_Reference/update_comment_meta
 596 *
 597 * @param int $comment_id Comment ID.
 598 * @param string $meta_key Metadata key.
 599 * @param mixed $meta_value Metadata value.
 600 * @param mixed $prev_value Optional. Previous value to check before removing.
 601 * @return bool False on failure, true if success.
 602 */
 603function update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '') {
 604	return update_metadata('comment', $comment_id, $meta_key, $meta_value, $prev_value);
 605}
 606
 607/**
 608 * Sets the cookies used to store an unauthenticated commentator's identity. Typically used
 609 * to recall previous comments by this commentator that are still held in moderation.
 610 *
 611 * @param object $comment Comment object.
 612 * @param object $user Comment author's object.
 613 *
 614 * @since 3.4.0
 615 */
 616function wp_set_comment_cookies($comment, $user) {
 617	if ( $user->exists() )
 618		return;
 619
 620	$comment_cookie_lifetime = apply_filters('comment_cookie_lifetime', 30000000);
 621	setcookie('comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
 622	setcookie('comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
 623	setcookie('comment_author_url_' . COOKIEHASH, esc_url($comment->comment_author_url), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
 624}
 625
 626/**
 627 * Sanitizes the cookies sent to the user already.
 628 *
 629 * Will only do anything if the cookies have already been created for the user.
 630 * Mostly used after cookies had been sent to use elsewhere.
 631 *
 632 * @since 2.0.4
 633 */
 634function sanitize_comment_cookies() {
 635	if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) ) {
 636		$comment_author = apply_filters('pre_comment_author_name', $_COOKIE['comment_author_'.COOKIEHASH]);
 637		$comment_author = wp_unslash($comment_author);
 638		$comment_author = esc_attr($comment_author);
 639		$_COOKIE['comment_author_'.COOKIEHASH] = $comment_author;
 640	}
 641
 642	if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ) {
 643		$comment_author_email = apply_filters('pre_comment_author_email', $_COOKIE['comment_author_email_'.COOKIEHASH]);
 644		$comment_author_email = wp_unslash($comment_author_email);
 645		$comment_author_email = esc_attr($comment_author_email);
 646		$_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email;
 647	}
 648
 649	if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ) {
 650		$comment_author_url = apply_filters('pre_comment_author_url', $_COOKIE['comment_author_url_'.COOKIEHASH]);
 651		$comment_author_url = wp_unslash($comment_author_url);
 652		$_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
 653	}
 654}
 655
 656/**
 657 * Validates whether this comment is allowed to be made.
 658 *
 659 * @since 2.0.0
 660 * @uses $wpdb
 661 * @uses apply_filters() Calls 'pre_comment_approved' hook on the type of comment
 662 * @uses apply_filters() Calls 'comment_duplicate_trigger' hook on commentdata.
 663 * @uses do_action() Calls 'check_comment_flood' hook on $comment_author_IP, $comment_author_email, and $comment_date_gmt
 664 *
 665 * @param array $commentdata Contains information on the comment
 666 * @return mixed Signifies the approval status (0|1|'spam')
 667 */
 668function wp_allow_comment($commentdata) {
 669	global $wpdb;
 670	extract($commentdata, EXTR_SKIP);
 671
 672	// Simple duplicate check
 673	// expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
 674	$dupe = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND comment_parent = '$comment_parent' AND comment_approved != 'trash' AND ( comment_author = '$comment_author' ";
 675	if ( $comment_author_email )
 676		$dupe .= "OR comment_author_email = '$comment_author_email' ";
 677	$dupe .= ") AND comment_content = '$comment_content' LIMIT 1";
 678	if ( $wpdb->get_var($dupe) ) {
 679		do_action( 'comment_duplicate_trigger', $commentdata );
 680		if ( defined('DOING_AJAX') )
 681			die( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
 682
 683		wp_die( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
 684	}
 685
 686	do_action( 'check_comment_flood', $comment_author_IP, $comment_author_email, $comment_date_gmt );
 687
 688	if ( ! empty( $user_id ) ) {
 689		$user = get_userdata( $user_id );
 690		$post_author = $wpdb->get_var($wpdb->prepare("SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1", $comment_post_ID));
 691	}
 692
 693	if ( isset( $user ) && ( $user_id == $post_author || $user->has_cap( 'moderate_comments' ) ) ) {
 694		// The author and the admins get respect.
 695		$approved = 1;
 696	 } else {
 697		// Everyone else's comments will be checked.
 698		if ( check_comment($comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_IP, $comment_agent, $comment_type) )
 699			$approved = 1;
 700		else
 701			$approved = 0;
 702		if ( wp_blacklist_check($comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_IP, $comment_agent) )
 703			$approved = 'spam';
 704	}
 705
 706	$approved = apply_filters( 'pre_comment_approved', $approved, $commentdata );
 707	return $approved;
 708}
 709
 710/**
 711 * Check whether comment flooding is occurring.
 712 *
 713 * Won't run, if current user can manage options, so to not block
 714 * administrators.
 715 *
 716 * @since 2.3.0
 717 * @uses $wpdb
 718 * @uses apply_filters() Calls 'comment_flood_filter' filter with first
 719 *		parameter false, last comment timestamp, new comment timestamp.
 720 * @uses do_action() Calls 'comment_flood_trigger' action with parameters with
 721 *		last comment timestamp and new comment timestamp.
 722 *
 723 * @param string $ip Comment IP.
 724 * @param string $email Comment author email address.
 725 * @param string $date MySQL time string.
 726 */
 727function check_comment_flood_db( $ip, $email, $date ) {
 728	global $wpdb;
 729	if ( current_user_can( 'manage_options' ) )
 730		return; // don't throttle admins
 731	$hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
 732	if ( $lasttime = $wpdb->get_var( $wpdb->prepare( "SELECT `comment_date_gmt` FROM `$wpdb->comments` WHERE `comment_date_gmt` >= %s AND ( `comment_author_IP` = %s OR `comment_author_email` = %s ) ORDER BY `comment_date_gmt` DESC LIMIT 1", $hour_ago, $ip, $email ) ) ) {
 733		$time_lastcomment = mysql2date('U', $lasttime, false);
 734		$time_newcomment  = mysql2date('U', $date, false);
 735		$flood_die = apply_filters('comment_flood_filter', false, $time_lastcomment, $time_newcomment);
 736		if ( $flood_die ) {
 737			do_action('comment_flood_trigger', $time_lastcomment, $time_newcomment);
 738
 739			if ( defined('DOING_AJAX') )
 740				die( __('You are posting comments too quickly. Slow down.') );
 741
 742			wp_die( __('You are posting comments too quickly. Slow down.'), '', array('response' => 403) );
 743		}
 744	}
 745}
 746
 747/**
 748 * Separates an array of comments into an array keyed by comment_type.
 749 *
 750 * @since 2.7.0
 751 *
 752 * @param array $comments Array of comments
 753 * @return array Array of comments keyed by comment_type.
 754 */
 755function separate_comments(&$comments) {
 756	$comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array());
 757	$count = count($comments);
 758	for ( $i = 0; $i < $count; $i++ ) {
 759		$type = $comments[$i]->comment_type;
 760		if ( empty($type) )
 761			$type = 'comment';
 762		$comments_by_type[$type][] = &$comments[$i];
 763		if ( 'trackback' == $type || 'pingback' == $type )
 764			$comments_by_type['pings'][] = &$comments[$i];
 765	}
 766
 767	return $comments_by_type;
 768}
 769
 770/**
 771 * Calculate the total number of comment pages.
 772 *
 773 * @since 2.7.0
 774 * @uses get_query_var() Used to fill in the default for $per_page parameter.
 775 * @uses get_option() Used to fill in defaults for parameters.
 776 * @uses Walker_Comment
 777 *
 778 * @param array $comments Optional array of comment objects. Defaults to $wp_query->comments
 779 * @param int $per_page Optional comments per page.
 780 * @param boolean $threaded Optional control over flat or threaded comments.
 781 * @return int Number of comment pages.
 782 */
 783function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) {
 784	global $wp_query;
 785
 786	if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) )
 787		return $wp_query->max_num_comment_pages;
 788
 789	if ( !$comments || !is_array($comments) )
 790		$comments = $wp_query->comments;
 791
 792	if ( empty($comments) )
 793		return 0;
 794
 795	if ( !isset($per_page) )
 796		$per_page = (int) get_query_var('comments_per_page');
 797	if ( 0 === $per_page )
 798		$per_page = (int) get_option('comments_per_page');
 799	if ( 0 === $per_page )
 800		return 1;
 801
 802	if ( !isset($threaded) )
 803		$threaded = get_option('thread_comments');
 804
 805	if ( $threaded ) {
 806		$walker = new Walker_Comment;
 807		$count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page );
 808	} else {
 809		$count = ceil( count( $comments ) / $per_page );
 810	}
 811
 812	return $count;
 813}
 814
 815/**
 816 * Calculate what page number a comment will appear on for comment paging.
 817 *
 818 * @since 2.7.0
 819 * @uses get_comment() Gets the full comment of the $comment_ID parameter.
 820 * @uses get_option() Get various settings to control function and defaults.
 821 * @uses get_page_of_comment() Used to loop up to top level comment.
 822 *
 823 * @param int $comment_ID Comment ID.
 824 * @param array $args Optional args.
 825 * @return int|null Comment page number or null on error.
 826 */
 827function get_page_of_comment( $comment_ID, $args = array() ) {
 828	global $wpdb;
 829
 830	if ( !$comment = get_comment( $comment_ID ) )
 831		return;
 832
 833	$defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' );
 834	$args = wp_parse_args( $args, $defaults );
 835
 836	if ( '' === $args['per_page'] && get_option('page_comments') )
 837		$args['per_page'] = get_query_var('comments_per_page');
 838	if ( empty($args['per_page']) ) {
 839		$args['per_page'] = 0;
 840		$args['page'] = 0;
 841	}
 842	if ( $args['per_page'] < 1 )
 843		return 1;
 844
 845	if ( '' === $args['max_depth'] ) {
 846		if ( get_option('thread_comments') )
 847			$args['max_depth'] = get_option('thread_comments_depth');
 848		else
 849			$args['max_depth'] = -1;
 850	}
 851
 852	// Find this comment's top level parent if threading is enabled
 853	if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent )
 854		return get_page_of_comment( $comment->comment_parent, $args );
 855
 856	$allowedtypes = array(
 857		'comment' => '',
 858		'pingback' => 'pingback',
 859		'trackback' => 'trackback',
 860	);
 861
 862	$comtypewhere = ( 'all' != $args['type'] && isset($allowedtypes[$args['type']]) ) ? " AND comment_type = '" . $allowedtypes[$args['type']] . "'" : '';
 863
 864	// Count comments older than this one
 865	$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 ) );
 866
 867	// No older comments? Then it's page #1.
 868	if ( 0 == $oldercoms )
 869		return 1;
 870
 871	// Divide comments older than this one by comments per page to get this comment's page number
 872	return ceil( ( $oldercoms + 1 ) / $args['per_page'] );
 873}
 874
 875/**
 876 * Does comment contain blacklisted characters or words.
 877 *
 878 * @since 1.5.0
 879 * @uses do_action() Calls 'wp_blacklist_check' hook for all parameters.
 880 *
 881 * @param string $author The author of the comment
 882 * @param string $email The email of the comment
 883 * @param string $url The url used in the comment
 884 * @param string $comment The comment content
 885 * @param string $user_ip The comment author IP address
 886 * @param string $user_agent The author's browser user agent
 887 * @return bool True if comment contains blacklisted content, false if comment does not
 888 */
 889function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) {
 890	do_action('wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent);
 891
 892	$mod_keys = trim( get_option('blacklist_keys') );
 893	if ( '' == $mod_keys )
 894		return false; // If moderation keys are empty
 895	$words = explode("\n", $mod_keys );
 896
 897	foreach ( (array) $words as $word ) {
 898		$word = trim($word);
 899
 900		// Skip empty lines
 901		if ( empty($word) ) { continue; }
 902
 903		// Do some escaping magic so that '#' chars in the
 904		// spam words don't break things:
 905		$word = preg_quote($word, '#');
 906
 907		$pattern = "#$word#i";
 908		if (
 909			   preg_match($pattern, $author)
 910			|| preg_match($pattern, $email)
 911			|| preg_match($pattern, $url)
 912			|| preg_match($pattern, $comment)
 913			|| preg_match($pattern, $user_ip)
 914			|| preg_match($pattern, $user_agent)
 915		 )
 916			return true;
 917	}
 918	return false;
 919}
 920
 921/**
 922 * Retrieve total comments for blog or single post.
 923 *
 924 * The properties of the returned object contain the 'moderated', 'approved',
 925 * and spam comments for either the entire blog or single post. Those properties
 926 * contain the amount of comments that match the status. The 'total_comments'
 927 * property contains the integer of total comments.
 928 *
 929 * The comment stats are cached and then retrieved, if they already exist in the
 930 * cache.
 931 *
 932 * @since 2.5.0
 933 *
 934 * @param int $post_id Optional. Post ID.
 935 * @return object Comment stats.
 936 */
 937function wp_count_comments( $post_id = 0 ) {
 938	global $wpdb;
 939
 940	$post_id = (int) $post_id;
 941
 942	$stats = apply_filters('wp_count_comments', array(), $post_id);
 943	if ( !empty($stats) )
 944		return $stats;
 945
 946	$count = wp_cache_get("comments-{$post_id}", 'counts');
 947
 948	if ( false !== $count )
 949		return $count;
 950
 951	$where = '';
 952	if ( $post_id > 0 )
 953		$where = $wpdb->prepare( "WHERE comment_post_ID = %d", $post_id );
 954
 955	$count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} {$where} GROUP BY comment_approved", ARRAY_A );
 956
 957	$total = 0;
 958	$approved = array('0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed');
 959	foreach ( (array) $count as $row ) {
 960		// Don't count post-trashed toward totals
 961		if ( 'post-trashed' != $row['comment_approved'] && 'trash' != $row['comment_approved'] )
 962			$total += $row['num_comments'];
 963		if ( isset( $approved[$row['comment_approved']] ) )
 964			$stats[$approved[$row['comment_approved']]] = $row['num_comments'];
 965	}
 966
 967	$stats['total_comments'] = $total;
 968	foreach ( $approved as $key ) {
 969		if ( empty($stats[$key]) )
 970			$stats[$key] = 0;
 971	}
 972
 973	$stats = (object) $stats;
 974	wp_cache_set("comments-{$post_id}", $stats, 'counts');
 975
 976	return $stats;
 977}
 978
 979/**
 980 * Trashes or deletes a comment.
 981 *
 982 * The comment is moved to trash instead of permanently deleted unless trash is
 983 * disabled, item is already in the trash, or $force_delete is true.
 984 *
 985 * The post comment count will be updated if the comment was approved and has a
 986 * post ID available.
 987 *
 988 * @since 2.0.0
 989 * @uses $wpdb
 990 * @uses do_action() Calls 'delete_comment' hook on comment ID
 991 * @uses do_action() Calls 'deleted_comment' hook on comment ID after deletion, on success
 992 * @uses do_action() Calls 'wp_set_comment_status' hook on comment ID with 'delete' set for the second parameter
 993 * @uses wp_transition_comment_status() Passes new and old comment status along with $comment object
 994 *
 995 * @param int $comment_id Comment ID
 996 * @param bool $force_delete Whether to bypass trash and force deletion. Default is false.
 997 * @return bool False if delete comment query failure, true on success.
 998 */
 999function wp_delete_comment($comment_id, $force_delete = false) {
1000	global $wpdb;
1001	if (!$comment = get_comment($comment_id))
1002		return false;
1003
1004	if ( !$force_delete && EMPTY_TRASH_DAYS && !in_array( wp_get_comment_status($comment_id), array( 'trash', 'spam' ) ) )
1005		return wp_trash_comment($comment_id);
1006
1007	do_action('delete_comment', $comment_id);
1008
1009	// Move children up a level.
1010	$children = $wpdb->get_col( $wpdb->prepare("SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d", $comment_id) );
1011	if ( !empty($children) ) {
1012		$wpdb->update($wpdb->comments, array('comment_parent' => $comment->comment_parent), array('comment_parent' => $comment_id));
1013		clean_comment_cache($children);
1014	}
1015
1016	// Delete metadata
1017	$meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->commentmeta WHERE comment_id = %d", $comment_id ) );
1018	foreach ( $meta_ids as $mid )
1019		delete_metadata_by_mid( 'comment', $mid );
1020
1021	if ( ! $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment_id ) ) )
1022		return false;
1023	do_action('deleted_comment', $comment_id);
1024
1025	$post_id = $comment->comment_post_ID;
1026	if ( $post_id && $comment->comment_approved == 1 )
1027		wp_update_comment_count($post_id);
1028
1029	clean_comment_cache($comment_id);
1030
1031	do_action('wp_set_comment_status', $comment_id, 'delete');
1032	wp_transition_comment_status('delete', $comment->comment_approved, $comment);
1033	return true;
1034}
1035
1036/**
1037 * Moves a comment to the Trash
1038 *
1039 * If trash is disabled, comment is permanently deleted.
1040 *
1041 * @since 2.9.0
1042 * @uses do_action() on 'trash_comment' before trashing
1043 * @uses do_action() on 'trashed_comment' after trashing
1044 * @uses wp_delete_comment() if trash is disabled
1045 *
1046 * @param int $comment_id Comment ID.
1047 * @return mixed False on failure
1048 */
1049function wp_trash_comment($comment_id) {
1050	if ( !EMPTY_TRASH_DAYS )
1051		return wp_delete_comment($comment_id, true);
1052
1053	if ( !$comment = get_comment($comment_id) )
1054		return false;
1055
1056	do_action('trash_comment', $comment_id);
1057
1058	if ( wp_set_comment_status($comment_id, 'trash') ) {
1059		add_comment_meta($comment_id, '_wp_trash_meta_status', $comment->comment_approved);
1060		add_comment_meta($comment_id, '_wp_trash_meta_time', time() );
1061		do_action('trashed_comment', $comment_id);
1062		return true;
1063	}
1064
1065	return false;
1066}
1067
1068/**
1069 * Removes a comment from the Trash
1070 *
1071 * @since 2.9.0
1072 * @uses do_action() on 'untrash_comment' before untrashing
1073 * @uses do_action() on 'untrashed_comment' after untrashing
1074 *
1075 * @param int $comment_id Comment ID.
1076 * @return mixed False on failure
1077 */
1078function wp_untrash_comment($comment_id) {
1079	if ( ! (int)$comment_id )
1080		return false;
1081
1082	do_action('untrash_comment', $comment_id);
1083
1084	$status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true);
1085	if ( empty($status) )
1086		$status = '0';
1087
1088	if ( wp_set_comment_status($comment_id, $status) ) {
1089		delete_comment_meta($comment_id, '_wp_trash_meta_time');
1090		delete_comment_meta($comment_id, '_wp_trash_meta_status');
1091		do_action('untrashed_comment', $comment_id);
1092		return true;
1093	}
1094
1095	return false;
1096}
1097
1098/**
1099 * Marks a comment as Spam
1100 *
1101 * @since 2.9.0
1102 * @uses do_action() on 'spam_comment' before spamming
1103 * @uses do_action() on 'spammed_comment' after spamming
1104 *
1105 * @param int $comment_id Comment ID.
1106 * @return mixed False on failure
1107 */
1108function wp_spam_comment($comment_id) {
1109	if ( !$comment = get_comment($comment_id) )
1110		return false;
1111
1112	do_action('spam_comment', $comment_id);
1113
1114	if ( wp_set_comment_status($comment_id, 'spam') ) {
1115		add_comment_meta($comment_id, '_wp_trash_meta_status', $comment->comment_approved);
1116		do_action('spammed_comment', $comment_id);
1117		return true;
1118	}
1119
1120	return false;
1121}
1122
1123/**
1124 * Removes a comment from the Spam
1125 *
1126 * @since 2.9.0
1127 * @uses do_action() on 'unspam_comment' before unspamming
1128 * @uses do_action() on 'unspammed_comment' after unspamming
1129 *
1130 * @param int $comment_id Comment ID.
1131 * @return mixed False on failure
1132 */
1133function wp_unspam_comment($comment_id) {
1134	if ( ! (int)$comment_id )
1135		return false;
1136
1137	do_action('unspam_comment', $comment_id);
1138
1139	$status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true);
1140	if ( empty($status) )
1141		$status = '0';
1142
1143	if ( wp_set_comment_status($comment_id, $status) ) {
1144		delete_comment_meta($comment_id, '_wp_trash_meta_status');
1145		do_action('unspammed_comment', $comment_id);
1146		return true;
1147	}
1148
1149	return false;
1150}
1151
1152/**
1153 * The status of a comment by ID.
1154 *
1155 * @since 1.0.0
1156 *
1157 * @param int $comment_id Comment ID
1158 * @return string|bool Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure.
1159 */
1160function wp_get_comment_status($comment_id) {
1161	$comment = get_comment($comment_id);
1162	if ( !$comment )
1163		return false;
1164
1165	$approved = $comment->comment_approved;
1166
1167	if ( $approved == null )
1168		return false;
1169	elseif ( $approved == '1' )
1170		return 'approved';
1171	elseif ( $approved == '0' )
1172		return 'unapproved';
1173	elseif ( $approved == 'spam' )
1174		return 'spam';
1175	elseif ( $approved == 'trash' )
1176		return 'trash';
1177	else
1178		return false;
1179}
1180
1181/**
1182 * Call hooks for when a comment status transition occurs.
1183 *
1184 * Calls hooks for comment status transitions. If the new comment status is not the same
1185 * as the previous comment status, then two hooks will be ran, the first is
1186 * 'transition_comment_status' with new status, old status, and comment data. The
1187 * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the
1188 * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the
1189 * comment data.
1190 *
1191 * The final action will run whether or not the comment statuses are the same. The
1192 * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status
1193 * parameter and COMMENTTYPE is comment_type comment data.
1194 *
1195 * @since 2.7.0
1196 *
1197 * @param string $new_status New comment status.
1198 * @param string $old_status Previous comment status.
1199 * @param object $comment Comment data.
1200 */
1201function wp_transition_comment_status($new_status, $old_status, $comment) {
1202	// Translate raw statuses to human readable formats for the hooks
1203	// This is not a complete list of comment status, it's only the ones that need to be renamed
1204	$comment_statuses = array(
1205		0         => 'unapproved',
1206		'hold'    => 'unapproved', // wp_set_comment_status() uses "hold"
1207		1         => 'approved',
1208		'approve' => 'approved', // wp_set_comment_status() uses "approve"
1209	);
1210	if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status];
1211	if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status];
1212
1213	// Call the hooks
1214	if ( $new_status != $old_status ) {
1215		do_action('transition_comment_status', $new_status, $old_status, $comment);
1216		do_action("comment_{$old_status}_to_{$new_status}", $comment);
1217	}
1218	do_action("comment_{$new_status}_{$comment->comment_type}", $comment->comment_ID, $comment);
1219}
1220
1221/**
1222 * Get current commenter's name, email, and URL.
1223 *
1224 * Expects cookies content to already be sanitized. User of this function might
1225 * wish to recheck the returned array for validity.
1226 *
1227 * @see sanitize_comment_cookies() Use to sanitize cookies
1228 *
1229 * @since 2.0.4
1230 *
1231 * @return array Comment author, email, url respectively.
1232 */
1233function wp_get_current_commenter() {
1234	// Cookies should already be sanitized.
1235
1236	$comment_author = '';
1237	if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) )
1238		$comment_author = $_COOKIE['comment_author_'.COOKIEHASH];
1239
1240	$comment_author_email = '';
1241	if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) )
1242		$comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH];
1243
1244	$comment_author_url = '';
1245	if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) )
1246		$comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH];
1247
1248	return apply_filters('wp_get_current_commenter', compact('comment_author', 'comment_author_email', 'comment_author_url'));
1249}
1250
1251/**
1252 * Inserts a comment to the database.
1253 *
1254 * The available comment data key names are 'comment_author_IP', 'comment_date',
1255 * 'comment_date_gmt', 'comment_parent', 'comment_approved', and 'user_id'.
1256 *
1257 * @since 2.0.0
1258 * @uses $wpdb
1259 *
1260 * @param array $commentdata Contains information on the comment.
1261 * @return int The new comment's ID.
1262 */
1263function wp_insert_comment($commentdata) {
1264	global $wpdb;
1265	extract(wp_unslash($commentdata), EXTR_SKIP);
1266
1267	if ( ! isset($comment_author_IP) )
1268		$comment_author_IP = '';
1269	if ( ! isset($comment_date) )
1270		$comment_date = current_time('mysql');
1271	if ( ! isset($comment_date_gmt) )
1272		$comment_date_gmt = get_gmt_from_date($comment_date);
1273	if ( ! isset($comment_parent) )
1274		$comment_parent = 0;
1275	if ( ! isset($comment_approved) )
1276		$comment_approved = 1;
1277	if ( ! isset($comment_karma) )
1278		$comment_karma = 0;
1279	if ( ! isset($user_id) )
1280		$user_id = 0;
1281	if ( ! isset($comment_type) )
1282		$comment_type = '';
1283
1284	$data = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_date', 'comment_date_gmt', 'comment_content', 'comment_karma', 'comment_approved', 'comment_agent', 'comment_type', 'comment_parent', 'user_id');
1285	$wpdb->insert($wpdb->comments, $data);
1286
1287	$id = (int) $wpdb->insert_id;
1288
1289	if ( $comment_approved == 1 )
1290		wp_update_comment_count($comment_post_ID);
1291
1292	$comment = get_comment($id);
1293	do_action('wp_insert_comment', $id, $comment);
1294
1295	wp_cache_set( 'last_changed', microtime(), 'comment' );
1296
1297	return $id;
1298}
1299
1300/**
1301 * Filters and sanitizes comment data.
1302 *
1303 * Sets the comment data 'filtered' field to true when finished. This can be
1304 * checked as to whether the comment should be filtered and to keep from
1305 * filtering the same comment more than once.
1306 *
1307 * @since 2.0.0
1308 * @uses apply_filters() Calls 'pre_user_id' hook on comment author's user ID
1309 * @uses apply_filters() Calls 'pre_comment_user_agent' hook on comment author's user agent
1310 * @uses apply_filters() Calls 'pre_comment_author_name' hook on comment author's name
1311 * @uses apply_filters() Calls 'pre_comment_content' hook on the comment's content
1312 * @uses apply_filters() Calls 'pre_comment_user_ip' hook on comment author's IP
1313 * @uses apply_filters() Calls 'pre_comment_author_url' hook on comment author's URL
1314 * @uses apply_filters() Calls 'pre_comment_author_email' hook on comment author's email address
1315 *
1316 * @param array $commentdata Contains information on the comment.
1317 * @return array Parsed comment information.
1318 */
1319function wp_filter_comment($commentdata) {
1320	if ( isset($commentdata['user_ID']) )
1321		$commentdata['user_id'] = apply_filters('pre_user_id', $commentdata['user_ID']);
1322	elseif ( isset($commentdata['user_id']) )
1323		$commentdata['user_id'] = apply_filters('pre_user_id', $commentdata['user_id']);
1324	$commentdata['comment_agent']        = apply_filters('pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) );
1325	$commentdata['comment_author']       = apply_filters('pre_comment_author_name', $commentdata['comment_author']);
1326	$commentdata['comment_content']      = apply_filters('pre_comment_content', $commentdata['comment_content']);
1327	$commentdata['comment_author_IP']    = apply_filters('pre_comment_user_ip', $commentdata['comment_author_IP']);
1328	$commentdata['comment_author_url']   = apply_filters('pre_comment_author_url', $commentdata['comment_author_url']);
1329	$commentdata['comment_author_email'] = apply_filters('pre_comment_author_email', $commentdata['comment_author_email']);
1330	$commentdata['filtered'] = true;
1331	return $commentdata;
1332}
1333
1334/**
1335 * Whether comment should be blocked because of comment flood.
1336 *
1337 * @since 2.1.0
1338 *
1339 * @param bool $block Whether plugin has already blocked comment.
1340 * @param int $time_lastcomment Timestamp for last comment.
1341 * @param int $time_newcomment Timestamp for new comment.
1342 * @return bool Whether comment should be blocked.
1343 */
1344function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) {
1345	if ( $block ) // a plugin has already blocked... we'll let that decision stand
1346		return $block;
1347	if ( ($time_newcomment - $time_lastcomment) < 15 )
1348		return true;
1349	return false;
1350}
1351
1352/**
1353 * Adds a new comment to the database.
1354 *
1355 * Filters new comment to ensure that the fields are sanitized and valid before
1356 * inserting comment into database. Calls 'comment_post' action with comment ID
1357 * and whether comment is approved by WordPress. Also has 'preprocess_comment'
1358 * filter for processing the comment data before the function handles it.
1359 *
1360 * We use REMOTE_ADDR here directly. If you are behind a proxy, you should ensure
1361 * that it is properly set, such as in wp-config.php, for your environment.
1362 * See {@link http://core.trac.wordpress.org/ticket/9235}
1363 *
1364 * @since 1.5.0
1365 * @uses apply_filters() Calls 'preprocess_comment' hook on $commentdata parameter array before processing
1366 * @uses do_action() Calls 'comment_post' hook on $comment_ID returned from adding the comment and if the comment was approved.
1367 * @uses wp_filter_comment() Used to filter comment before adding comment.
1368 * @uses wp_allow_comment() checks to see if comment is approved.
1369 * @uses wp_insert_comment() Does the actual comment insertion to the database.
1370 *
1371 * @param array $commentdata Contains information on the comment.
1372 * @return int The ID of the comment after adding.
1373 */
1374function wp_new_comment( $commentdata ) {
1375	$commentdata = apply_filters('preprocess_comment', $commentdata);
1376
1377	$commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
1378	if ( isset($commentdata['user_ID']) )
1379		$commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
1380	elseif ( isset($commentdata['user_id']) )
1381		$commentdata['user_id'] = (int) $commentdata['user_id'];
1382
1383	$commentdata['comment_parent'] = isset($commentdata['comment_parent']) ? absint($commentdata['comment_parent']) : 0;
1384	$parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : '';
1385	$commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0;
1386
1387	$commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '',$_SERVER['REMOTE_ADDR'] );
1388	$commentdata['comment_agent']     = substr($_SERVER['HTTP_USER_AGENT'], 0, 254);
1389
1390	$commentdata['comment_date']     = current_time('mysql');
1391	$commentdata['comment_date_gmt'] = current_time('mysql', 1);
1392
1393	$commentdata = wp_filter_comment($commentdata);
1394
1395	$commentdata['comment_approved'] = wp_allow_comment($commentdata);
1396
1397	$comment_ID = wp_insert_comment($commentdata);
1398
1399	do_action('comment_post', $comment_ID, $commentdata['comment_approved']);
1400
1401	if ( 'spam' !== $commentdata['comment_approved'] ) { // If it's spam save it silently for later crunching
1402		if ( '0' == $commentdata['comment_approved'] )
1403			wp_notify_moderator($comment_ID);
1404
1405		$post = get_post($commentdata['comment_post_ID']); // Don't notify if it's your own comment
1406
1407		if ( get_option('comments_notify') && $commentdata['comment_approved'] && ( ! isset( $commentdata['user_id'] ) || $post->post_author != $commentdata['user_id'] ) )
1408			wp_notify_postauthor($comment_ID, isset( $commentdata['comment_type'] ) ? $commentdata['comment_type'] : '' );
1409	}
1410
1411	return $comment_ID;
1412}
1413
1414/**
1415 * Sets the status of a comment.
1416 *
1417 * The 'wp_set_comment_status' action is called after the comment is handled.
1418 * If the comment status is not in the list, then false is returned.
1419 *
1420 * @since 1.0.0
1421 * @uses wp_transition_comment_status() Passes new and old comment status along with $comment object
1422 *
1423 * @param int $comment_id Comment ID.
1424 * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'.
1425 * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false.
1426 * @return bool False on failure or deletion and true on success.
1427 */
1428function wp_set_comment_status($comment_id, $comment_status, $wp_error = false) {
1429	global $wpdb;
1430
1431	$status = '0';
1432	switch ( $comment_status ) {
1433		case 'hold':
1434		case '0':
1435			$status = '0';
1436			break;
1437		case 'approve':
1438		case '1':
1439			$status = '1';
1440			if ( get_option('comments_notify') ) {
1441				$comment = get_comment($comment_id);
1442				wp_notify_postauthor($comment_id, $comment->comment_type);
1443			}
1444			break;
1445		case 'spam':
1446			$status = 'spam';
1447			break;
1448		case 'trash':
1449			$status = 'trash';
1450			break;
1451		default:
1452			return false;
1453	}
1454
1455	$comment_old = clone get_comment($comment_id);
1456
1457	if ( !$wpdb->update( $wpdb->comments, array('comment_approved' => $status), array('comment_ID' => $comment_id) ) ) {
1458		if ( $wp_error )
1459			return new WP_Error('db_update_error', __('Could not update comment status'), $wpdb->last_error);
1460		else
1461			return false;
1462	}
1463
1464	clean_comment_cache($comment_id);
1465
1466	$comment = get_comment($comment_id);
1467
1468	do_action('wp_set_comment_status', $comment_id, $comment_status);
1469	wp_transition_comment_status($comment_status, $comment_old->comment_approved, $comment);
1470
1471	wp_update_comment_count($comment->comment_post_ID);
1472
1473	return true;
1474}
1475
1476/**
1477 * Updates an existing comment in the database.
1478 *
1479 * Filters the comment and makes sure certain fields are valid before updating.
1480 *
1481 * @since 2.0.0
1482 * @uses $wpdb
1483 * @uses wp_transition_comment_status() Passes new and old comment status a…

Large files files are truncated, but you can click here to view the full file