PageRenderTime 352ms CodeModel.GetById 98ms app.highlight 156ms RepoModel.GetById 48ms app.codeStats 1ms

/wp-includes/comment.php

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

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