balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

pluggable.php (103920B)


      1 <?php
      2 /**
      3  * These functions can be replaced via plugins. If plugins do not redefine these
      4  * functions, then these will be used instead.
      5  *
      6  * @package WordPress
      7  */
      8 
      9 if ( ! function_exists( 'wp_set_current_user' ) ) :
     10 	/**
     11 	 * Changes the current user by ID or name.
     12 	 *
     13 	 * Set $id to null and specify a name if you do not know a user's ID.
     14 	 *
     15 	 * Some WordPress functionality is based on the current user and not based on
     16 	 * the signed in user. Therefore, it opens the ability to edit and perform
     17 	 * actions on users who aren't signed in.
     18 	 *
     19 	 * @since 2.0.3
     20 	 *
     21 	 * @global WP_User $current_user The current user object which holds the user data.
     22 	 *
     23 	 * @param int    $id   User ID
     24 	 * @param string $name User's username
     25 	 * @return WP_User Current user User object
     26 	 */
     27 	function wp_set_current_user( $id, $name = '' ) {
     28 		global $current_user;
     29 
     30 		// If `$id` matches the current user, there is nothing to do.
     31 		if ( isset( $current_user )
     32 		&& ( $current_user instanceof WP_User )
     33 		&& ( $id == $current_user->ID )
     34 		&& ( null !== $id )
     35 		) {
     36 			return $current_user;
     37 		}
     38 
     39 		$current_user = new WP_User( $id, $name );
     40 
     41 		setup_userdata( $current_user->ID );
     42 
     43 		/**
     44 		 * Fires after the current user is set.
     45 		 *
     46 		 * @since 2.0.1
     47 		 */
     48 		do_action( 'set_current_user' );
     49 
     50 		return $current_user;
     51 	}
     52 endif;
     53 
     54 if ( ! function_exists( 'wp_get_current_user' ) ) :
     55 	/**
     56 	 * Retrieve the current user object.
     57 	 *
     58 	 * Will set the current user, if the current user is not set. The current user
     59 	 * will be set to the logged-in person. If no user is logged-in, then it will
     60 	 * set the current user to 0, which is invalid and won't have any permissions.
     61 	 *
     62 	 * @since 2.0.3
     63 	 *
     64 	 * @see _wp_get_current_user()
     65 	 * @global WP_User $current_user Checks if the current user is set.
     66 	 *
     67 	 * @return WP_User Current WP_User instance.
     68 	 */
     69 	function wp_get_current_user() {
     70 		return _wp_get_current_user();
     71 	}
     72 endif;
     73 
     74 if ( ! function_exists( 'get_userdata' ) ) :
     75 	/**
     76 	 * Retrieve user info by user ID.
     77 	 *
     78 	 * @since 0.71
     79 	 *
     80 	 * @param int $user_id User ID
     81 	 * @return WP_User|false WP_User object on success, false on failure.
     82 	 */
     83 	function get_userdata( $user_id ) {
     84 		return get_user_by( 'id', $user_id );
     85 	}
     86 endif;
     87 
     88 if ( ! function_exists( 'get_user_by' ) ) :
     89 	/**
     90 	 * Retrieve user info by a given field
     91 	 *
     92 	 * @since 2.8.0
     93 	 * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter.
     94 	 * @since 5.8.0 Returns the global `$current_user` if it's the user being fetched.
     95 	 *
     96 	 * @global WP_User $current_user The current user object which holds the user data.
     97 	 *
     98 	 * @param string     $field The field to retrieve the user with. id | ID | slug | email | login.
     99 	 * @param int|string $value A value for $field. A user ID, slug, email address, or login name.
    100 	 * @return WP_User|false WP_User object on success, false on failure.
    101 	 */
    102 	function get_user_by( $field, $value ) {
    103 		global $current_user;
    104 
    105 		$userdata = WP_User::get_data_by( $field, $value );
    106 
    107 		if ( ! $userdata ) {
    108 			return false;
    109 		}
    110 
    111 		if ( $current_user instanceof WP_User && $current_user->ID === (int) $userdata->ID ) {
    112 			return $current_user;
    113 		}
    114 
    115 		$user = new WP_User;
    116 		$user->init( $userdata );
    117 
    118 		return $user;
    119 	}
    120 endif;
    121 
    122 if ( ! function_exists( 'cache_users' ) ) :
    123 	/**
    124 	 * Retrieve info for user lists to prevent multiple queries by get_userdata()
    125 	 *
    126 	 * @since 3.0.0
    127 	 *
    128 	 * @global wpdb $wpdb WordPress database abstraction object.
    129 	 *
    130 	 * @param array $user_ids User ID numbers list
    131 	 */
    132 	function cache_users( $user_ids ) {
    133 		global $wpdb;
    134 
    135 		$clean = _get_non_cached_ids( $user_ids, 'users' );
    136 
    137 		if ( empty( $clean ) ) {
    138 			return;
    139 		}
    140 
    141 		$list = implode( ',', $clean );
    142 
    143 		$users = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($list)" );
    144 
    145 		$ids = array();
    146 		foreach ( $users as $user ) {
    147 			update_user_caches( $user );
    148 			$ids[] = $user->ID;
    149 		}
    150 		update_meta_cache( 'user', $ids );
    151 	}
    152 endif;
    153 
    154 if ( ! function_exists( 'wp_mail' ) ) :
    155 	/**
    156 	 * Sends an email, similar to PHP's mail function.
    157 	 *
    158 	 * A true return value does not automatically mean that the user received the
    159 	 * email successfully. It just only means that the method used was able to
    160 	 * process the request without any errors.
    161 	 *
    162 	 * The default content type is `text/plain` which does not allow using HTML.
    163 	 * However, you can set the content type of the email by using the
    164 	 * {@see 'wp_mail_content_type'} filter.
    165 	 *
    166 	 * The default charset is based on the charset used on the blog. The charset can
    167 	 * be set using the {@see 'wp_mail_charset'} filter.
    168 	 *
    169 	 * @since 1.2.1
    170 	 * @since 5.5.0 is_email() is used for email validation,
    171 	 *              instead of PHPMailer's default validator.
    172 	 *
    173 	 * @global PHPMailer\PHPMailer\PHPMailer $phpmailer
    174 	 *
    175 	 * @param string|string[] $to          Array or comma-separated list of email addresses to send message.
    176 	 * @param string          $subject     Email subject.
    177 	 * @param string          $message     Message contents.
    178 	 * @param string|string[] $headers     Optional. Additional headers.
    179 	 * @param string|string[] $attachments Optional. Paths to files to attach.
    180 	 * @return bool Whether the email was sent successfully.
    181 	 */
    182 	function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
    183 		// Compact the input, apply the filters, and extract them back out.
    184 
    185 		/**
    186 		 * Filters the wp_mail() arguments.
    187 		 *
    188 		 * @since 2.2.0
    189 		 *
    190 		 * @param array $args {
    191 		 *     Array of the `wp_mail()` arguments.
    192 		 *
    193 		 *     @type string|string[] $to          Array or comma-separated list of email addresses to send message.
    194 		 *     @type string          $subject     Email subject.
    195 		 *     @type string          $message     Message contents.
    196 		 *     @type string|string[] $headers     Additional headers.
    197 		 *     @type string|string[] $attachments Paths to files to attach.
    198 		 * }
    199 		 */
    200 		$atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );
    201 
    202 		/**
    203 		 * Filters whether to preempt sending an email.
    204 		 *
    205 		 * Returning a non-null value will short-circuit {@see wp_mail()}, returning
    206 		 * that value instead. A boolean return value should be used to indicate whether
    207 		 * the email was successfully sent.
    208 		 *
    209 		 * @since 5.7.0
    210 		 *
    211 		 * @param null|bool $return Short-circuit return value.
    212 		 * @param array     $atts {
    213 		 *     Array of the `wp_mail()` arguments.
    214 		 *
    215 		 *     @type string|string[] $to          Array or comma-separated list of email addresses to send message.
    216 		 *     @type string          $subject     Email subject.
    217 		 *     @type string          $message     Message contents.
    218 		 *     @type string|string[] $headers     Additional headers.
    219 		 *     @type string|string[] $attachments Paths to files to attach.
    220 		 * }
    221 		 */
    222 		$pre_wp_mail = apply_filters( 'pre_wp_mail', null, $atts );
    223 
    224 		if ( null !== $pre_wp_mail ) {
    225 			return $pre_wp_mail;
    226 		}
    227 
    228 		if ( isset( $atts['to'] ) ) {
    229 			$to = $atts['to'];
    230 		}
    231 
    232 		if ( ! is_array( $to ) ) {
    233 			$to = explode( ',', $to );
    234 		}
    235 
    236 		if ( isset( $atts['subject'] ) ) {
    237 			$subject = $atts['subject'];
    238 		}
    239 
    240 		if ( isset( $atts['message'] ) ) {
    241 			$message = $atts['message'];
    242 		}
    243 
    244 		if ( isset( $atts['headers'] ) ) {
    245 			$headers = $atts['headers'];
    246 		}
    247 
    248 		if ( isset( $atts['attachments'] ) ) {
    249 			$attachments = $atts['attachments'];
    250 		}
    251 
    252 		if ( ! is_array( $attachments ) ) {
    253 			$attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
    254 		}
    255 		global $phpmailer;
    256 
    257 		// (Re)create it, if it's gone missing.
    258 		if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) {
    259 			require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
    260 			require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
    261 			require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
    262 			$phpmailer = new PHPMailer\PHPMailer\PHPMailer( true );
    263 
    264 			$phpmailer::$validator = static function ( $email ) {
    265 				return (bool) is_email( $email );
    266 			};
    267 		}
    268 
    269 		// Headers.
    270 		$cc       = array();
    271 		$bcc      = array();
    272 		$reply_to = array();
    273 
    274 		if ( empty( $headers ) ) {
    275 			$headers = array();
    276 		} else {
    277 			if ( ! is_array( $headers ) ) {
    278 				// Explode the headers out, so this function can take
    279 				// both string headers and an array of headers.
    280 				$tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
    281 			} else {
    282 				$tempheaders = $headers;
    283 			}
    284 			$headers = array();
    285 
    286 			// If it's actually got contents.
    287 			if ( ! empty( $tempheaders ) ) {
    288 				// Iterate through the raw headers.
    289 				foreach ( (array) $tempheaders as $header ) {
    290 					if ( strpos( $header, ':' ) === false ) {
    291 						if ( false !== stripos( $header, 'boundary=' ) ) {
    292 							$parts    = preg_split( '/boundary=/i', trim( $header ) );
    293 							$boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
    294 						}
    295 						continue;
    296 					}
    297 					// Explode them out.
    298 					list( $name, $content ) = explode( ':', trim( $header ), 2 );
    299 
    300 					// Cleanup crew.
    301 					$name    = trim( $name );
    302 					$content = trim( $content );
    303 
    304 					switch ( strtolower( $name ) ) {
    305 						// Mainly for legacy -- process a "From:" header if it's there.
    306 						case 'from':
    307 							$bracket_pos = strpos( $content, '<' );
    308 							if ( false !== $bracket_pos ) {
    309 								// Text before the bracketed email is the "From" name.
    310 								if ( $bracket_pos > 0 ) {
    311 									$from_name = substr( $content, 0, $bracket_pos - 1 );
    312 									$from_name = str_replace( '"', '', $from_name );
    313 									$from_name = trim( $from_name );
    314 								}
    315 
    316 								$from_email = substr( $content, $bracket_pos + 1 );
    317 								$from_email = str_replace( '>', '', $from_email );
    318 								$from_email = trim( $from_email );
    319 
    320 								// Avoid setting an empty $from_email.
    321 							} elseif ( '' !== trim( $content ) ) {
    322 								$from_email = trim( $content );
    323 							}
    324 							break;
    325 						case 'content-type':
    326 							if ( strpos( $content, ';' ) !== false ) {
    327 								list( $type, $charset_content ) = explode( ';', $content );
    328 								$content_type                   = trim( $type );
    329 								if ( false !== stripos( $charset_content, 'charset=' ) ) {
    330 									$charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
    331 								} elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
    332 									$boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
    333 									$charset  = '';
    334 								}
    335 
    336 								// Avoid setting an empty $content_type.
    337 							} elseif ( '' !== trim( $content ) ) {
    338 								$content_type = trim( $content );
    339 							}
    340 							break;
    341 						case 'cc':
    342 							$cc = array_merge( (array) $cc, explode( ',', $content ) );
    343 							break;
    344 						case 'bcc':
    345 							$bcc = array_merge( (array) $bcc, explode( ',', $content ) );
    346 							break;
    347 						case 'reply-to':
    348 							$reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
    349 							break;
    350 						default:
    351 							// Add it to our grand headers array.
    352 							$headers[ trim( $name ) ] = trim( $content );
    353 							break;
    354 					}
    355 				}
    356 			}
    357 		}
    358 
    359 		// Empty out the values that may be set.
    360 		$phpmailer->clearAllRecipients();
    361 		$phpmailer->clearAttachments();
    362 		$phpmailer->clearCustomHeaders();
    363 		$phpmailer->clearReplyTos();
    364 
    365 		// Set "From" name and email.
    366 
    367 		// If we don't have a name from the input headers.
    368 		if ( ! isset( $from_name ) ) {
    369 			$from_name = 'WordPress';
    370 		}
    371 
    372 		/*
    373 		 * If we don't have an email from the input headers, default to wordpress@$sitename
    374 		 * Some hosts will block outgoing mail from this address if it doesn't exist,
    375 		 * but there's no easy alternative. Defaulting to admin_email might appear to be
    376 		 * another option, but some hosts may refuse to relay mail from an unknown domain.
    377 		 * See https://core.trac.wordpress.org/ticket/5007.
    378 		 */
    379 		if ( ! isset( $from_email ) ) {
    380 			// Get the site domain and get rid of www.
    381 			$sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
    382 			if ( 'www.' === substr( $sitename, 0, 4 ) ) {
    383 				$sitename = substr( $sitename, 4 );
    384 			}
    385 
    386 			$from_email = 'wordpress@' . $sitename;
    387 		}
    388 
    389 		/**
    390 		 * Filters the email address to send from.
    391 		 *
    392 		 * @since 2.2.0
    393 		 *
    394 		 * @param string $from_email Email address to send from.
    395 		 */
    396 		$from_email = apply_filters( 'wp_mail_from', $from_email );
    397 
    398 		/**
    399 		 * Filters the name to associate with the "from" email address.
    400 		 *
    401 		 * @since 2.3.0
    402 		 *
    403 		 * @param string $from_name Name associated with the "from" email address.
    404 		 */
    405 		$from_name = apply_filters( 'wp_mail_from_name', $from_name );
    406 
    407 		try {
    408 			$phpmailer->setFrom( $from_email, $from_name, false );
    409 		} catch ( PHPMailer\PHPMailer\Exception $e ) {
    410 			$mail_error_data                             = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
    411 			$mail_error_data['phpmailer_exception_code'] = $e->getCode();
    412 
    413 			/** This filter is documented in wp-includes/pluggable.php */
    414 			do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
    415 
    416 			return false;
    417 		}
    418 
    419 		// Set mail's subject and body.
    420 		$phpmailer->Subject = $subject;
    421 		$phpmailer->Body    = $message;
    422 
    423 		// Set destination addresses, using appropriate methods for handling addresses.
    424 		$address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
    425 
    426 		foreach ( $address_headers as $address_header => $addresses ) {
    427 			if ( empty( $addresses ) ) {
    428 				continue;
    429 			}
    430 
    431 			foreach ( (array) $addresses as $address ) {
    432 				try {
    433 					// Break $recipient into name and address parts if in the format "Foo <bar@baz.com>".
    434 					$recipient_name = '';
    435 
    436 					if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
    437 						if ( count( $matches ) == 3 ) {
    438 							$recipient_name = $matches[1];
    439 							$address        = $matches[2];
    440 						}
    441 					}
    442 
    443 					switch ( $address_header ) {
    444 						case 'to':
    445 							$phpmailer->addAddress( $address, $recipient_name );
    446 							break;
    447 						case 'cc':
    448 							$phpmailer->addCc( $address, $recipient_name );
    449 							break;
    450 						case 'bcc':
    451 							$phpmailer->addBcc( $address, $recipient_name );
    452 							break;
    453 						case 'reply_to':
    454 							$phpmailer->addReplyTo( $address, $recipient_name );
    455 							break;
    456 					}
    457 				} catch ( PHPMailer\PHPMailer\Exception $e ) {
    458 					continue;
    459 				}
    460 			}
    461 		}
    462 
    463 		// Set to use PHP's mail().
    464 		$phpmailer->isMail();
    465 
    466 		// Set Content-Type and charset.
    467 
    468 		// If we don't have a content-type from the input headers.
    469 		if ( ! isset( $content_type ) ) {
    470 			$content_type = 'text/plain';
    471 		}
    472 
    473 		/**
    474 		 * Filters the wp_mail() content type.
    475 		 *
    476 		 * @since 2.3.0
    477 		 *
    478 		 * @param string $content_type Default wp_mail() content type.
    479 		 */
    480 		$content_type = apply_filters( 'wp_mail_content_type', $content_type );
    481 
    482 		$phpmailer->ContentType = $content_type;
    483 
    484 		// Set whether it's plaintext, depending on $content_type.
    485 		if ( 'text/html' === $content_type ) {
    486 			$phpmailer->isHTML( true );
    487 		}
    488 
    489 		// If we don't have a charset from the input headers.
    490 		if ( ! isset( $charset ) ) {
    491 			$charset = get_bloginfo( 'charset' );
    492 		}
    493 
    494 		/**
    495 		 * Filters the default wp_mail() charset.
    496 		 *
    497 		 * @since 2.3.0
    498 		 *
    499 		 * @param string $charset Default email charset.
    500 		 */
    501 		$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
    502 
    503 		// Set custom headers.
    504 		if ( ! empty( $headers ) ) {
    505 			foreach ( (array) $headers as $name => $content ) {
    506 				// Only add custom headers not added automatically by PHPMailer.
    507 				if ( ! in_array( $name, array( 'MIME-Version', 'X-Mailer' ), true ) ) {
    508 					try {
    509 						$phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
    510 					} catch ( PHPMailer\PHPMailer\Exception $e ) {
    511 						continue;
    512 					}
    513 				}
    514 			}
    515 
    516 			if ( false !== stripos( $content_type, 'multipart' ) && ! empty( $boundary ) ) {
    517 				$phpmailer->addCustomHeader( sprintf( 'Content-Type: %s; boundary="%s"', $content_type, $boundary ) );
    518 			}
    519 		}
    520 
    521 		if ( ! empty( $attachments ) ) {
    522 			foreach ( $attachments as $attachment ) {
    523 				try {
    524 					$phpmailer->addAttachment( $attachment );
    525 				} catch ( PHPMailer\PHPMailer\Exception $e ) {
    526 					continue;
    527 				}
    528 			}
    529 		}
    530 
    531 		/**
    532 		 * Fires after PHPMailer is initialized.
    533 		 *
    534 		 * @since 2.2.0
    535 		 *
    536 		 * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
    537 		 */
    538 		do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );
    539 
    540 		// Send!
    541 		try {
    542 			return $phpmailer->send();
    543 		} catch ( PHPMailer\PHPMailer\Exception $e ) {
    544 
    545 			$mail_error_data                             = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
    546 			$mail_error_data['phpmailer_exception_code'] = $e->getCode();
    547 
    548 			/**
    549 			 * Fires after a PHPMailer\PHPMailer\Exception is caught.
    550 			 *
    551 			 * @since 4.4.0
    552 			 *
    553 			 * @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array
    554 			 *                        containing the mail recipient, subject, message, headers, and attachments.
    555 			 */
    556 			do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
    557 
    558 			return false;
    559 		}
    560 	}
    561 endif;
    562 
    563 if ( ! function_exists( 'wp_authenticate' ) ) :
    564 	/**
    565 	 * Authenticate a user, confirming the login credentials are valid.
    566 	 *
    567 	 * @since 2.5.0
    568 	 * @since 4.5.0 `$username` now accepts an email address.
    569 	 *
    570 	 * @param string $username User's username or email address.
    571 	 * @param string $password User's password.
    572 	 * @return WP_User|WP_Error WP_User object if the credentials are valid,
    573 	 *                          otherwise WP_Error.
    574 	 */
    575 	function wp_authenticate( $username, $password ) {
    576 		$username = sanitize_user( $username );
    577 		$password = trim( $password );
    578 
    579 		/**
    580 		 * Filters whether a set of user login credentials are valid.
    581 		 *
    582 		 * A WP_User object is returned if the credentials authenticate a user.
    583 		 * WP_Error or null otherwise.
    584 		 *
    585 		 * @since 2.8.0
    586 		 * @since 4.5.0 `$username` now accepts an email address.
    587 		 *
    588 		 * @param null|WP_User|WP_Error $user     WP_User if the user is authenticated.
    589 		 *                                        WP_Error or null otherwise.
    590 		 * @param string                $username Username or email address.
    591 		 * @param string                $password User password
    592 		 */
    593 		$user = apply_filters( 'authenticate', null, $username, $password );
    594 
    595 		if ( null == $user ) {
    596 			// TODO: What should the error message be? (Or would these even happen?)
    597 			// Only needed if all authentication handlers fail to return anything.
    598 			$user = new WP_Error( 'authentication_failed', __( '<strong>Error</strong>: Invalid username, email address or incorrect password.' ) );
    599 		}
    600 
    601 		$ignore_codes = array( 'empty_username', 'empty_password' );
    602 
    603 		if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes, true ) ) {
    604 			$error = $user;
    605 
    606 			/**
    607 			 * Fires after a user login has failed.
    608 			 *
    609 			 * @since 2.5.0
    610 			 * @since 4.5.0 The value of `$username` can now be an email address.
    611 			 * @since 5.4.0 The `$error` parameter was added.
    612 			 *
    613 			 * @param string   $username Username or email address.
    614 			 * @param WP_Error $error    A WP_Error object with the authentication failure details.
    615 			 */
    616 			do_action( 'wp_login_failed', $username, $error );
    617 		}
    618 
    619 		return $user;
    620 	}
    621 endif;
    622 
    623 if ( ! function_exists( 'wp_logout' ) ) :
    624 	/**
    625 	 * Log the current user out.
    626 	 *
    627 	 * @since 2.5.0
    628 	 */
    629 	function wp_logout() {
    630 		$user_id = get_current_user_id();
    631 
    632 		wp_destroy_current_session();
    633 		wp_clear_auth_cookie();
    634 		wp_set_current_user( 0 );
    635 
    636 		/**
    637 		 * Fires after a user is logged out.
    638 		 *
    639 		 * @since 1.5.0
    640 		 * @since 5.5.0 Added the `$user_id` parameter.
    641 		 *
    642 		 * @param int $user_id ID of the user that was logged out.
    643 		 */
    644 		do_action( 'wp_logout', $user_id );
    645 	}
    646 endif;
    647 
    648 if ( ! function_exists( 'wp_validate_auth_cookie' ) ) :
    649 	/**
    650 	 * Validates authentication cookie.
    651 	 *
    652 	 * The checks include making sure that the authentication cookie is set and
    653 	 * pulling in the contents (if $cookie is not used).
    654 	 *
    655 	 * Makes sure the cookie is not expired. Verifies the hash in cookie is what is
    656 	 * should be and compares the two.
    657 	 *
    658 	 * @since 2.5.0
    659 	 *
    660 	 * @global int $login_grace_period
    661 	 *
    662 	 * @param string $cookie Optional. If used, will validate contents instead of cookie's.
    663 	 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
    664 	 * @return int|false User ID if valid cookie, false if invalid.
    665 	 */
    666 	function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
    667 		$cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );
    668 		if ( ! $cookie_elements ) {
    669 			/**
    670 			 * Fires if an authentication cookie is malformed.
    671 			 *
    672 			 * @since 2.7.0
    673 			 *
    674 			 * @param string $cookie Malformed auth cookie.
    675 			 * @param string $scheme Authentication scheme. Values include 'auth', 'secure_auth',
    676 			 *                       or 'logged_in'.
    677 			 */
    678 			do_action( 'auth_cookie_malformed', $cookie, $scheme );
    679 			return false;
    680 		}
    681 
    682 		$scheme     = $cookie_elements['scheme'];
    683 		$username   = $cookie_elements['username'];
    684 		$hmac       = $cookie_elements['hmac'];
    685 		$token      = $cookie_elements['token'];
    686 		$expired    = $cookie_elements['expiration'];
    687 		$expiration = $cookie_elements['expiration'];
    688 
    689 		// Allow a grace period for POST and Ajax requests.
    690 		if ( wp_doing_ajax() || 'POST' === $_SERVER['REQUEST_METHOD'] ) {
    691 			$expired += HOUR_IN_SECONDS;
    692 		}
    693 
    694 		// Quick check to see if an honest cookie has expired.
    695 		if ( $expired < time() ) {
    696 			/**
    697 			 * Fires once an authentication cookie has expired.
    698 			 *
    699 			 * @since 2.7.0
    700 			 *
    701 			 * @param string[] $cookie_elements An array of data for the authentication cookie.
    702 			 */
    703 			do_action( 'auth_cookie_expired', $cookie_elements );
    704 			return false;
    705 		}
    706 
    707 		$user = get_user_by( 'login', $username );
    708 		if ( ! $user ) {
    709 			/**
    710 			 * Fires if a bad username is entered in the user authentication process.
    711 			 *
    712 			 * @since 2.7.0
    713 			 *
    714 			 * @param string[] $cookie_elements An array of data for the authentication cookie.
    715 			 */
    716 			do_action( 'auth_cookie_bad_username', $cookie_elements );
    717 			return false;
    718 		}
    719 
    720 		$pass_frag = substr( $user->user_pass, 8, 4 );
    721 
    722 		$key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
    723 
    724 		// If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
    725 		$algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
    726 		$hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key );
    727 
    728 		if ( ! hash_equals( $hash, $hmac ) ) {
    729 			/**
    730 			 * Fires if a bad authentication cookie hash is encountered.
    731 			 *
    732 			 * @since 2.7.0
    733 			 *
    734 			 * @param string[] $cookie_elements An array of data for the authentication cookie.
    735 			 */
    736 			do_action( 'auth_cookie_bad_hash', $cookie_elements );
    737 			return false;
    738 		}
    739 
    740 		$manager = WP_Session_Tokens::get_instance( $user->ID );
    741 		if ( ! $manager->verify( $token ) ) {
    742 			/**
    743 			 * Fires if a bad session token is encountered.
    744 			 *
    745 			 * @since 4.0.0
    746 			 *
    747 			 * @param string[] $cookie_elements An array of data for the authentication cookie.
    748 			 */
    749 			do_action( 'auth_cookie_bad_session_token', $cookie_elements );
    750 			return false;
    751 		}
    752 
    753 		// Ajax/POST grace period set above.
    754 		if ( $expiration < time() ) {
    755 			$GLOBALS['login_grace_period'] = 1;
    756 		}
    757 
    758 		/**
    759 		 * Fires once an authentication cookie has been validated.
    760 		 *
    761 		 * @since 2.7.0
    762 		 *
    763 		 * @param string[] $cookie_elements An array of data for the authentication cookie.
    764 		 * @param WP_User  $user            User object.
    765 		 */
    766 		do_action( 'auth_cookie_valid', $cookie_elements, $user );
    767 
    768 		return $user->ID;
    769 	}
    770 endif;
    771 
    772 if ( ! function_exists( 'wp_generate_auth_cookie' ) ) :
    773 	/**
    774 	 * Generates authentication cookie contents.
    775 	 *
    776 	 * @since 2.5.0
    777 	 * @since 4.0.0 The `$token` parameter was added.
    778 	 *
    779 	 * @param int    $user_id    User ID.
    780 	 * @param int    $expiration The time the cookie expires as a UNIX timestamp.
    781 	 * @param string $scheme     Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
    782 	 *                           Default 'auth'.
    783 	 * @param string $token      User's session token to use for this cookie.
    784 	 * @return string Authentication cookie contents. Empty string if user does not exist.
    785 	 */
    786 	function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
    787 		$user = get_userdata( $user_id );
    788 		if ( ! $user ) {
    789 			return '';
    790 		}
    791 
    792 		if ( ! $token ) {
    793 			$manager = WP_Session_Tokens::get_instance( $user_id );
    794 			$token   = $manager->create( $expiration );
    795 		}
    796 
    797 		$pass_frag = substr( $user->user_pass, 8, 4 );
    798 
    799 		$key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
    800 
    801 		// If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
    802 		$algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
    803 		$hash = hash_hmac( $algo, $user->user_login . '|' . $expiration . '|' . $token, $key );
    804 
    805 		$cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
    806 
    807 		/**
    808 		 * Filters the authentication cookie.
    809 		 *
    810 		 * @since 2.5.0
    811 		 * @since 4.0.0 The `$token` parameter was added.
    812 		 *
    813 		 * @param string $cookie     Authentication cookie.
    814 		 * @param int    $user_id    User ID.
    815 		 * @param int    $expiration The time the cookie expires as a UNIX timestamp.
    816 		 * @param string $scheme     Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'.
    817 		 * @param string $token      User's session token used.
    818 		 */
    819 		return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token );
    820 	}
    821 endif;
    822 
    823 if ( ! function_exists( 'wp_parse_auth_cookie' ) ) :
    824 	/**
    825 	 * Parses a cookie into its components.
    826 	 *
    827 	 * @since 2.7.0
    828 	 *
    829 	 * @param string $cookie Authentication cookie.
    830 	 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
    831 	 * @return string[]|false Authentication cookie components.
    832 	 */
    833 	function wp_parse_auth_cookie( $cookie = '', $scheme = '' ) {
    834 		if ( empty( $cookie ) ) {
    835 			switch ( $scheme ) {
    836 				case 'auth':
    837 					$cookie_name = AUTH_COOKIE;
    838 					break;
    839 				case 'secure_auth':
    840 					$cookie_name = SECURE_AUTH_COOKIE;
    841 					break;
    842 				case 'logged_in':
    843 					$cookie_name = LOGGED_IN_COOKIE;
    844 					break;
    845 				default:
    846 					if ( is_ssl() ) {
    847 						$cookie_name = SECURE_AUTH_COOKIE;
    848 						$scheme      = 'secure_auth';
    849 					} else {
    850 						$cookie_name = AUTH_COOKIE;
    851 						$scheme      = 'auth';
    852 					}
    853 			}
    854 
    855 			if ( empty( $_COOKIE[ $cookie_name ] ) ) {
    856 				return false;
    857 			}
    858 			$cookie = $_COOKIE[ $cookie_name ];
    859 		}
    860 
    861 		$cookie_elements = explode( '|', $cookie );
    862 		if ( count( $cookie_elements ) !== 4 ) {
    863 			return false;
    864 		}
    865 
    866 		list( $username, $expiration, $token, $hmac ) = $cookie_elements;
    867 
    868 		return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
    869 	}
    870 endif;
    871 
    872 if ( ! function_exists( 'wp_set_auth_cookie' ) ) :
    873 	/**
    874 	 * Sets the authentication cookies based on user ID.
    875 	 *
    876 	 * The $remember parameter increases the time that the cookie will be kept. The
    877 	 * default the cookie is kept without remembering is two days. When $remember is
    878 	 * set, the cookies will be kept for 14 days or two weeks.
    879 	 *
    880 	 * @since 2.5.0
    881 	 * @since 4.3.0 Added the `$token` parameter.
    882 	 *
    883 	 * @param int         $user_id  User ID.
    884 	 * @param bool        $remember Whether to remember the user.
    885 	 * @param bool|string $secure   Whether the auth cookie should only be sent over HTTPS. Default is an empty
    886 	 *                              string which means the value of `is_ssl()` will be used.
    887 	 * @param string      $token    Optional. User's session token to use for this cookie.
    888 	 */
    889 	function wp_set_auth_cookie( $user_id, $remember = false, $secure = '', $token = '' ) {
    890 		if ( $remember ) {
    891 			/**
    892 			 * Filters the duration of the authentication cookie expiration period.
    893 			 *
    894 			 * @since 2.8.0
    895 			 *
    896 			 * @param int  $length   Duration of the expiration period in seconds.
    897 			 * @param int  $user_id  User ID.
    898 			 * @param bool $remember Whether to remember the user login. Default false.
    899 			 */
    900 			$expiration = time() + apply_filters( 'auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember );
    901 
    902 			/*
    903 			 * Ensure the browser will continue to send the cookie after the expiration time is reached.
    904 			 * Needed for the login grace period in wp_validate_auth_cookie().
    905 			 */
    906 			$expire = $expiration + ( 12 * HOUR_IN_SECONDS );
    907 		} else {
    908 			/** This filter is documented in wp-includes/pluggable.php */
    909 			$expiration = time() + apply_filters( 'auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember );
    910 			$expire     = 0;
    911 		}
    912 
    913 		if ( '' === $secure ) {
    914 			$secure = is_ssl();
    915 		}
    916 
    917 		// Front-end cookie is secure when the auth cookie is secure and the site's home URL uses HTTPS.
    918 		$secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );
    919 
    920 		/**
    921 		 * Filters whether the auth cookie should only be sent over HTTPS.
    922 		 *
    923 		 * @since 3.1.0
    924 		 *
    925 		 * @param bool $secure  Whether the cookie should only be sent over HTTPS.
    926 		 * @param int  $user_id User ID.
    927 		 */
    928 		$secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
    929 
    930 		/**
    931 		 * Filters whether the logged in cookie should only be sent over HTTPS.
    932 		 *
    933 		 * @since 3.1.0
    934 		 *
    935 		 * @param bool $secure_logged_in_cookie Whether the logged in cookie should only be sent over HTTPS.
    936 		 * @param int  $user_id                 User ID.
    937 		 * @param bool $secure                  Whether the auth cookie should only be sent over HTTPS.
    938 		 */
    939 		$secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
    940 
    941 		if ( $secure ) {
    942 			$auth_cookie_name = SECURE_AUTH_COOKIE;
    943 			$scheme           = 'secure_auth';
    944 		} else {
    945 			$auth_cookie_name = AUTH_COOKIE;
    946 			$scheme           = 'auth';
    947 		}
    948 
    949 		if ( '' === $token ) {
    950 			$manager = WP_Session_Tokens::get_instance( $user_id );
    951 			$token   = $manager->create( $expiration );
    952 		}
    953 
    954 		$auth_cookie      = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token );
    955 		$logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token );
    956 
    957 		/**
    958 		 * Fires immediately before the authentication cookie is set.
    959 		 *
    960 		 * @since 2.5.0
    961 		 * @since 4.9.0 The `$token` parameter was added.
    962 		 *
    963 		 * @param string $auth_cookie Authentication cookie value.
    964 		 * @param int    $expire      The time the login grace period expires as a UNIX timestamp.
    965 		 *                            Default is 12 hours past the cookie's expiration time.
    966 		 * @param int    $expiration  The time when the authentication cookie expires as a UNIX timestamp.
    967 		 *                            Default is 14 days from now.
    968 		 * @param int    $user_id     User ID.
    969 		 * @param string $scheme      Authentication scheme. Values include 'auth' or 'secure_auth'.
    970 		 * @param string $token       User's session token to use for this cookie.
    971 		 */
    972 		do_action( 'set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme, $token );
    973 
    974 		/**
    975 		 * Fires immediately before the logged-in authentication cookie is set.
    976 		 *
    977 		 * @since 2.6.0
    978 		 * @since 4.9.0 The `$token` parameter was added.
    979 		 *
    980 		 * @param string $logged_in_cookie The logged-in cookie value.
    981 		 * @param int    $expire           The time the login grace period expires as a UNIX timestamp.
    982 		 *                                 Default is 12 hours past the cookie's expiration time.
    983 		 * @param int    $expiration       The time when the logged-in authentication cookie expires as a UNIX timestamp.
    984 		 *                                 Default is 14 days from now.
    985 		 * @param int    $user_id          User ID.
    986 		 * @param string $scheme           Authentication scheme. Default 'logged_in'.
    987 		 * @param string $token            User's session token to use for this cookie.
    988 		 */
    989 		do_action( 'set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in', $token );
    990 
    991 		/**
    992 		 * Allows preventing auth cookies from actually being sent to the client.
    993 		 *
    994 		 * @since 4.7.4
    995 		 *
    996 		 * @param bool $send Whether to send auth cookies to the client.
    997 		 */
    998 		if ( ! apply_filters( 'send_auth_cookies', true ) ) {
    999 			return;
   1000 		}
   1001 
   1002 		setcookie( $auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
   1003 		setcookie( $auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
   1004 		setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
   1005 		if ( COOKIEPATH != SITECOOKIEPATH ) {
   1006 			setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
   1007 		}
   1008 	}
   1009 endif;
   1010 
   1011 if ( ! function_exists( 'wp_clear_auth_cookie' ) ) :
   1012 	/**
   1013 	 * Removes all of the cookies associated with authentication.
   1014 	 *
   1015 	 * @since 2.5.0
   1016 	 */
   1017 	function wp_clear_auth_cookie() {
   1018 		/**
   1019 		 * Fires just before the authentication cookies are cleared.
   1020 		 *
   1021 		 * @since 2.7.0
   1022 		 */
   1023 		do_action( 'clear_auth_cookie' );
   1024 
   1025 		/** This filter is documented in wp-includes/pluggable.php */
   1026 		if ( ! apply_filters( 'send_auth_cookies', true ) ) {
   1027 			return;
   1028 		}
   1029 
   1030 		// Auth cookies.
   1031 		setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
   1032 		setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
   1033 		setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
   1034 		setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
   1035 		setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
   1036 		setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
   1037 
   1038 		// Settings cookies.
   1039 		setcookie( 'wp-settings-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
   1040 		setcookie( 'wp-settings-time-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
   1041 
   1042 		// Old cookies.
   1043 		setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
   1044 		setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
   1045 		setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
   1046 		setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
   1047 
   1048 		// Even older cookies.
   1049 		setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
   1050 		setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
   1051 		setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
   1052 		setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
   1053 
   1054 		// Post password cookie.
   1055 		setcookie( 'wp-postpass_' . COOKIEHASH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
   1056 	}
   1057 endif;
   1058 
   1059 if ( ! function_exists( 'is_user_logged_in' ) ) :
   1060 	/**
   1061 	 * Determines whether the current visitor is a logged in user.
   1062 	 *
   1063 	 * For more information on this and similar theme functions, check out
   1064 	 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
   1065 	 * Conditional Tags} article in the Theme Developer Handbook.
   1066 	 *
   1067 	 * @since 2.0.0
   1068 	 *
   1069 	 * @return bool True if user is logged in, false if not logged in.
   1070 	 */
   1071 	function is_user_logged_in() {
   1072 		$user = wp_get_current_user();
   1073 
   1074 		return $user->exists();
   1075 	}
   1076 endif;
   1077 
   1078 if ( ! function_exists( 'auth_redirect' ) ) :
   1079 	/**
   1080 	 * Checks if a user is logged in, if not it redirects them to the login page.
   1081 	 *
   1082 	 * When this code is called from a page, it checks to see if the user viewing the page is logged in.
   1083 	 * If the user is not logged in, they are redirected to the login page. The user is redirected
   1084 	 * in such a way that, upon logging in, they will be sent directly to the page they were originally
   1085 	 * trying to access.
   1086 	 *
   1087 	 * @since 1.5.0
   1088 	 */
   1089 	function auth_redirect() {
   1090 		$secure = ( is_ssl() || force_ssl_admin() );
   1091 
   1092 		/**
   1093 		 * Filters whether to use a secure authentication redirect.
   1094 		 *
   1095 		 * @since 3.1.0
   1096 		 *
   1097 		 * @param bool $secure Whether to use a secure authentication redirect. Default false.
   1098 		 */
   1099 		$secure = apply_filters( 'secure_auth_redirect', $secure );
   1100 
   1101 		// If https is required and request is http, redirect.
   1102 		if ( $secure && ! is_ssl() && false !== strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
   1103 			if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) {
   1104 				wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
   1105 				exit;
   1106 			} else {
   1107 				wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   1108 				exit;
   1109 			}
   1110 		}
   1111 
   1112 		/**
   1113 		 * Filters the authentication redirect scheme.
   1114 		 *
   1115 		 * @since 2.9.0
   1116 		 *
   1117 		 * @param string $scheme Authentication redirect scheme. Default empty.
   1118 		 */
   1119 		$scheme = apply_filters( 'auth_redirect_scheme', '' );
   1120 
   1121 		$user_id = wp_validate_auth_cookie( '', $scheme );
   1122 		if ( $user_id ) {
   1123 			/**
   1124 			 * Fires before the authentication redirect.
   1125 			 *
   1126 			 * @since 2.8.0
   1127 			 *
   1128 			 * @param int $user_id User ID.
   1129 			 */
   1130 			do_action( 'auth_redirect', $user_id );
   1131 
   1132 			// If the user wants ssl but the session is not ssl, redirect.
   1133 			if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && false !== strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
   1134 				if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) {
   1135 					wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
   1136 					exit;
   1137 				} else {
   1138 					wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   1139 					exit;
   1140 				}
   1141 			}
   1142 
   1143 			return; // The cookie is good, so we're done.
   1144 		}
   1145 
   1146 		// The cookie is no good, so force login.
   1147 		nocache_headers();
   1148 
   1149 		$redirect = ( strpos( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) ? wp_get_referer() : set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   1150 
   1151 		$login_url = wp_login_url( $redirect, true );
   1152 
   1153 		wp_redirect( $login_url );
   1154 		exit;
   1155 	}
   1156 endif;
   1157 
   1158 if ( ! function_exists( 'check_admin_referer' ) ) :
   1159 	/**
   1160 	 * Ensures intent by verifying that a user was referred from another admin page with the correct security nonce.
   1161 	 *
   1162 	 * This function ensures the user intends to perform a given action, which helps protect against clickjacking style
   1163 	 * attacks. It verifies intent, not authorisation, therefore it does not verify the user's capabilities. This should
   1164 	 * be performed with `current_user_can()` or similar.
   1165 	 *
   1166 	 * If the nonce value is invalid, the function will exit with an "Are You Sure?" style message.
   1167 	 *
   1168 	 * @since 1.2.0
   1169 	 * @since 2.5.0 The `$query_arg` parameter was added.
   1170 	 *
   1171 	 * @param int|string $action    The nonce action.
   1172 	 * @param string     $query_arg Optional. Key to check for nonce in `$_REQUEST`. Default '_wpnonce'.
   1173 	 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
   1174 	 *                   2 if the nonce is valid and generated between 12-24 hours ago.
   1175 	 *                   False if the nonce is invalid.
   1176 	 */
   1177 	function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) {
   1178 		if ( -1 === $action ) {
   1179 			_doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '3.2.0' );
   1180 		}
   1181 
   1182 		$adminurl = strtolower( admin_url() );
   1183 		$referer  = strtolower( wp_get_referer() );
   1184 		$result   = isset( $_REQUEST[ $query_arg ] ) ? wp_verify_nonce( $_REQUEST[ $query_arg ], $action ) : false;
   1185 
   1186 		/**
   1187 		 * Fires once the admin request has been validated or not.
   1188 		 *
   1189 		 * @since 1.5.1
   1190 		 *
   1191 		 * @param string    $action The nonce action.
   1192 		 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
   1193 		 *                          0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
   1194 		 */
   1195 		do_action( 'check_admin_referer', $action, $result );
   1196 
   1197 		if ( ! $result && ! ( -1 === $action && strpos( $referer, $adminurl ) === 0 ) ) {
   1198 			wp_nonce_ays( $action );
   1199 			die();
   1200 		}
   1201 
   1202 		return $result;
   1203 	}
   1204 endif;
   1205 
   1206 if ( ! function_exists( 'check_ajax_referer' ) ) :
   1207 	/**
   1208 	 * Verifies the Ajax request to prevent processing requests external of the blog.
   1209 	 *
   1210 	 * @since 2.0.3
   1211 	 *
   1212 	 * @param int|string   $action    Action nonce.
   1213 	 * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false,
   1214 	 *                                `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce'
   1215 	 *                                (in that order). Default false.
   1216 	 * @param bool         $die       Optional. Whether to die early when the nonce cannot be verified.
   1217 	 *                                Default true.
   1218 	 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
   1219 	 *                   2 if the nonce is valid and generated between 12-24 hours ago.
   1220 	 *                   False if the nonce is invalid.
   1221 	 */
   1222 	function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
   1223 		if ( -1 == $action ) {
   1224 			_doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '4.7.0' );
   1225 		}
   1226 
   1227 		$nonce = '';
   1228 
   1229 		if ( $query_arg && isset( $_REQUEST[ $query_arg ] ) ) {
   1230 			$nonce = $_REQUEST[ $query_arg ];
   1231 		} elseif ( isset( $_REQUEST['_ajax_nonce'] ) ) {
   1232 			$nonce = $_REQUEST['_ajax_nonce'];
   1233 		} elseif ( isset( $_REQUEST['_wpnonce'] ) ) {
   1234 			$nonce = $_REQUEST['_wpnonce'];
   1235 		}
   1236 
   1237 		$result = wp_verify_nonce( $nonce, $action );
   1238 
   1239 		/**
   1240 		 * Fires once the Ajax request has been validated or not.
   1241 		 *
   1242 		 * @since 2.1.0
   1243 		 *
   1244 		 * @param string    $action The Ajax nonce action.
   1245 		 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
   1246 		 *                          0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
   1247 		 */
   1248 		do_action( 'check_ajax_referer', $action, $result );
   1249 
   1250 		if ( $die && false === $result ) {
   1251 			if ( wp_doing_ajax() ) {
   1252 				wp_die( -1, 403 );
   1253 			} else {
   1254 				die( '-1' );
   1255 			}
   1256 		}
   1257 
   1258 		return $result;
   1259 	}
   1260 endif;
   1261 
   1262 if ( ! function_exists( 'wp_redirect' ) ) :
   1263 	/**
   1264 	 * Redirects to another page.
   1265 	 *
   1266 	 * Note: wp_redirect() does not exit automatically, and should almost always be
   1267 	 * followed by a call to `exit;`:
   1268 	 *
   1269 	 *     wp_redirect( $url );
   1270 	 *     exit;
   1271 	 *
   1272 	 * Exiting can also be selectively manipulated by using wp_redirect() as a conditional
   1273 	 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_location'} filters:
   1274 	 *
   1275 	 *     if ( wp_redirect( $url ) ) {
   1276 	 *         exit;
   1277 	 *     }
   1278 	 *
   1279 	 * @since 1.5.1
   1280 	 * @since 5.1.0 The `$x_redirect_by` parameter was added.
   1281 	 * @since 5.4.0 On invalid status codes, wp_die() is called.
   1282 	 *
   1283 	 * @global bool $is_IIS
   1284 	 *
   1285 	 * @param string $location      The path or URL to redirect to.
   1286 	 * @param int    $status        Optional. HTTP response status code to use. Default '302' (Moved Temporarily).
   1287 	 * @param string $x_redirect_by Optional. The application doing the redirect. Default 'WordPress'.
   1288 	 * @return bool False if the redirect was cancelled, true otherwise.
   1289 	 */
   1290 	function wp_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
   1291 		global $is_IIS;
   1292 
   1293 		/**
   1294 		 * Filters the redirect location.
   1295 		 *
   1296 		 * @since 2.1.0
   1297 		 *
   1298 		 * @param string $location The path or URL to redirect to.
   1299 		 * @param int    $status   The HTTP response status code to use.
   1300 		 */
   1301 		$location = apply_filters( 'wp_redirect', $location, $status );
   1302 
   1303 		/**
   1304 		 * Filters the redirect HTTP response status code to use.
   1305 		 *
   1306 		 * @since 2.3.0
   1307 		 *
   1308 		 * @param int    $status   The HTTP response status code to use.
   1309 		 * @param string $location The path or URL to redirect to.
   1310 		 */
   1311 		$status = apply_filters( 'wp_redirect_status', $status, $location );
   1312 
   1313 		if ( ! $location ) {
   1314 			return false;
   1315 		}
   1316 
   1317 		if ( $status < 300 || 399 < $status ) {
   1318 			wp_die( __( 'HTTP redirect status code must be a redirection code, 3xx.' ) );
   1319 		}
   1320 
   1321 		$location = wp_sanitize_redirect( $location );
   1322 
   1323 		if ( ! $is_IIS && 'cgi-fcgi' !== PHP_SAPI ) {
   1324 			status_header( $status ); // This causes problems on IIS and some FastCGI setups.
   1325 		}
   1326 
   1327 		/**
   1328 		 * Filters the X-Redirect-By header.
   1329 		 *
   1330 		 * Allows applications to identify themselves when they're doing a redirect.
   1331 		 *
   1332 		 * @since 5.1.0
   1333 		 *
   1334 		 * @param string $x_redirect_by The application doing the redirect.
   1335 		 * @param int    $status        Status code to use.
   1336 		 * @param string $location      The path to redirect to.
   1337 		 */
   1338 		$x_redirect_by = apply_filters( 'x_redirect_by', $x_redirect_by, $status, $location );
   1339 		if ( is_string( $x_redirect_by ) ) {
   1340 			header( "X-Redirect-By: $x_redirect_by" );
   1341 		}
   1342 
   1343 		header( "Location: $location", true, $status );
   1344 
   1345 		return true;
   1346 	}
   1347 endif;
   1348 
   1349 if ( ! function_exists( 'wp_sanitize_redirect' ) ) :
   1350 	/**
   1351 	 * Sanitizes a URL for use in a redirect.
   1352 	 *
   1353 	 * @since 2.3.0
   1354 	 *
   1355 	 * @param string $location The path to redirect to.
   1356 	 * @return string Redirect-sanitized URL.
   1357 	 */
   1358 	function wp_sanitize_redirect( $location ) {
   1359 		// Encode spaces.
   1360 		$location = str_replace( ' ', '%20', $location );
   1361 
   1362 		$regex    = '/
   1363 		(
   1364 			(?: [\xC2-\xDF][\x80-\xBF]        # double-byte sequences   110xxxxx 10xxxxxx
   1365 			|   \xE0[\xA0-\xBF][\x80-\xBF]    # triple-byte sequences   1110xxxx 10xxxxxx * 2
   1366 			|   [\xE1-\xEC][\x80-\xBF]{2}
   1367 			|   \xED[\x80-\x9F][\x80-\xBF]
   1368 			|   [\xEE-\xEF][\x80-\xBF]{2}
   1369 			|   \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
   1370 			|   [\xF1-\xF3][\x80-\xBF]{3}
   1371 			|   \xF4[\x80-\x8F][\x80-\xBF]{2}
   1372 		){1,40}                              # ...one or more times
   1373 		)/x';
   1374 		$location = preg_replace_callback( $regex, '_wp_sanitize_utf8_in_redirect', $location );
   1375 		$location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!*\[\]()@]|i', '', $location );
   1376 		$location = wp_kses_no_null( $location );
   1377 
   1378 		// Remove %0D and %0A from location.
   1379 		$strip = array( '%0d', '%0a', '%0D', '%0A' );
   1380 		return _deep_replace( $strip, $location );
   1381 	}
   1382 
   1383 	/**
   1384 	 * URL encode UTF-8 characters in a URL.
   1385 	 *
   1386 	 * @ignore
   1387 	 * @since 4.2.0
   1388 	 * @access private
   1389 	 *
   1390 	 * @see wp_sanitize_redirect()
   1391 	 *
   1392 	 * @param array $matches RegEx matches against the redirect location.
   1393 	 * @return string URL-encoded version of the first RegEx match.
   1394 	 */
   1395 	function _wp_sanitize_utf8_in_redirect( $matches ) {
   1396 		return urlencode( $matches[0] );
   1397 	}
   1398 endif;
   1399 
   1400 if ( ! function_exists( 'wp_safe_redirect' ) ) :
   1401 	/**
   1402 	 * Performs a safe (local) redirect, using wp_redirect().
   1403 	 *
   1404 	 * Checks whether the $location is using an allowed host, if it has an absolute
   1405 	 * path. A plugin can therefore set or remove allowed host(s) to or from the
   1406 	 * list.
   1407 	 *
   1408 	 * If the host is not allowed, then the redirect defaults to wp-admin on the siteurl
   1409 	 * instead. This prevents malicious redirects which redirect to another host,
   1410 	 * but only used in a few places.
   1411 	 *
   1412 	 * Note: wp_safe_redirect() does not exit automatically, and should almost always be
   1413 	 * followed by a call to `exit;`:
   1414 	 *
   1415 	 *     wp_safe_redirect( $url );
   1416 	 *     exit;
   1417 	 *
   1418 	 * Exiting can also be selectively manipulated by using wp_safe_redirect() as a conditional
   1419 	 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_location'} filters:
   1420 	 *
   1421 	 *     if ( wp_safe_redirect( $url ) ) {
   1422 	 *         exit;
   1423 	 *     }
   1424 	 *
   1425 	 * @since 2.3.0
   1426 	 * @since 5.1.0 The return value from wp_redirect() is now passed on, and the `$x_redirect_by` parameter was added.
   1427 	 *
   1428 	 * @param string $location      The path or URL to redirect to.
   1429 	 * @param int    $status        Optional. HTTP response status code to use. Default '302' (Moved Temporarily).
   1430 	 * @param string $x_redirect_by Optional. The application doing the redirect. Default 'WordPress'.
   1431 	 * @return bool False if the redirect was cancelled, true otherwise.
   1432 	 */
   1433 	function wp_safe_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
   1434 
   1435 		// Need to look at the URL the way it will end up in wp_redirect().
   1436 		$location = wp_sanitize_redirect( $location );
   1437 
   1438 		/**
   1439 		 * Filters the redirect fallback URL for when the provided redirect is not safe (local).
   1440 		 *
   1441 		 * @since 4.3.0
   1442 		 *
   1443 		 * @param string $fallback_url The fallback URL to use by default.
   1444 		 * @param int    $status       The HTTP response status code to use.
   1445 		 */
   1446 		$location = wp_validate_redirect( $location, apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ) );
   1447 
   1448 		return wp_redirect( $location, $status, $x_redirect_by );
   1449 	}
   1450 endif;
   1451 
   1452 if ( ! function_exists( 'wp_validate_redirect' ) ) :
   1453 	/**
   1454 	 * Validates a URL for use in a redirect.
   1455 	 *
   1456 	 * Checks whether the $location is using an allowed host, if it has an absolute
   1457 	 * path. A plugin can therefore set or remove allowed host(s) to or from the
   1458 	 * list.
   1459 	 *
   1460 	 * If the host is not allowed, then the redirect is to $default supplied
   1461 	 *
   1462 	 * @since 2.8.1
   1463 	 *
   1464 	 * @param string $location The redirect to validate
   1465 	 * @param string $default  The value to return if $location is not allowed
   1466 	 * @return string redirect-sanitized URL
   1467 	 */
   1468 	function wp_validate_redirect( $location, $default = '' ) {
   1469 		$location = wp_sanitize_redirect( trim( $location, " \t\n\r\0\x08\x0B" ) );
   1470 		// Browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'.
   1471 		if ( '//' === substr( $location, 0, 2 ) ) {
   1472 			$location = 'http:' . $location;
   1473 		}
   1474 
   1475 		// In PHP 5 parse_url() may fail if the URL query part contains 'http://'.
   1476 		// See https://bugs.php.net/bug.php?id=38143
   1477 		$cut  = strpos( $location, '?' );
   1478 		$test = $cut ? substr( $location, 0, $cut ) : $location;
   1479 
   1480 		$lp = parse_url( $test );
   1481 
   1482 		// Give up if malformed URL.
   1483 		if ( false === $lp ) {
   1484 			return $default;
   1485 		}
   1486 
   1487 		// Allow only 'http' and 'https' schemes. No 'data:', etc.
   1488 		if ( isset( $lp['scheme'] ) && ! ( 'http' === $lp['scheme'] || 'https' === $lp['scheme'] ) ) {
   1489 			return $default;
   1490 		}
   1491 
   1492 		if ( ! isset( $lp['host'] ) && ! empty( $lp['path'] ) && '/' !== $lp['path'][0] ) {
   1493 			$path = '';
   1494 			if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
   1495 				$path = dirname( parse_url( 'http://placeholder' . $_SERVER['REQUEST_URI'], PHP_URL_PATH ) . '?' );
   1496 				$path = wp_normalize_path( $path );
   1497 			}
   1498 			$location = '/' . ltrim( $path . '/', '/' ) . $location;
   1499 		}
   1500 
   1501 		// Reject if certain components are set but host is not.
   1502 		// This catches URLs like https:host.com for which parse_url() does not set the host field.
   1503 		if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) {
   1504 			return $default;
   1505 		}
   1506 
   1507 		// Reject malformed components parse_url() can return on odd inputs.
   1508 		foreach ( array( 'user', 'pass', 'host' ) as $component ) {
   1509 			if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) {
   1510 				return $default;
   1511 			}
   1512 		}
   1513 
   1514 		$wpp = parse_url( home_url() );
   1515 
   1516 		/**
   1517 		 * Filters the list of allowed hosts to redirect to.
   1518 		 *
   1519 		 * @since 2.3.0
   1520 		 *
   1521 		 * @param string[] $hosts An array of allowed host names.
   1522 		 * @param string   $host  The host name of the redirect destination; empty string if not set.
   1523 		 */
   1524 		$allowed_hosts = (array) apply_filters( 'allowed_redirect_hosts', array( $wpp['host'] ), isset( $lp['host'] ) ? $lp['host'] : '' );
   1525 
   1526 		if ( isset( $lp['host'] ) && ( ! in_array( $lp['host'], $allowed_hosts, true ) && strtolower( $wpp['host'] ) !== $lp['host'] ) ) {
   1527 			$location = $default;
   1528 		}
   1529 
   1530 		return $location;
   1531 	}
   1532 endif;
   1533 
   1534 if ( ! function_exists( 'wp_notify_postauthor' ) ) :
   1535 	/**
   1536 	 * Notify an author (and/or others) of a comment/trackback/pingback on a post.
   1537 	 *
   1538 	 * @since 1.0.0
   1539 	 *
   1540 	 * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
   1541 	 * @param string         $deprecated Not used
   1542 	 * @return bool True on completion. False if no email addresses were specified.
   1543 	 */
   1544 	function wp_notify_postauthor( $comment_id, $deprecated = null ) {
   1545 		if ( null !== $deprecated ) {
   1546 			_deprecated_argument( __FUNCTION__, '3.8.0' );
   1547 		}
   1548 
   1549 		$comment = get_comment( $comment_id );
   1550 		if ( empty( $comment ) || empty( $comment->comment_post_ID ) ) {
   1551 			return false;
   1552 		}
   1553 
   1554 		$post   = get_post( $comment->comment_post_ID );
   1555 		$author = get_userdata( $post->post_author );
   1556 
   1557 		// Who to notify? By default, just the post author, but others can be added.
   1558 		$emails = array();
   1559 		if ( $author ) {
   1560 			$emails[] = $author->user_email;
   1561 		}
   1562 
   1563 		/**
   1564 		 * Filters the list of email addresses to receive a comment notification.
   1565 		 *
   1566 		 * By default, only post authors are notified of comments. This filter allows
   1567 		 * others to be added.
   1568 		 *
   1569 		 * @since 3.7.0
   1570 		 *
   1571 		 * @param string[] $emails     An array of email addresses to receive a comment notification.
   1572 		 * @param int      $comment_id The comment ID.
   1573 		 */
   1574 		$emails = apply_filters( 'comment_notification_recipients', $emails, $comment->comment_ID );
   1575 		$emails = array_filter( $emails );
   1576 
   1577 		// If there are no addresses to send the comment to, bail.
   1578 		if ( ! count( $emails ) ) {
   1579 			return false;
   1580 		}
   1581 
   1582 		// Facilitate unsetting below without knowing the keys.
   1583 		$emails = array_flip( $emails );
   1584 
   1585 		/**
   1586 		 * Filters whether to notify comment authors of their comments on their own posts.
   1587 		 *
   1588 		 * By default, comment authors aren't notified of their comments on their own
   1589 		 * posts. This filter allows you to override that.
   1590 		 *
   1591 		 * @since 3.8.0
   1592 		 *
   1593 		 * @param bool $notify     Whether to notify the post author of their own comment.
   1594 		 *                         Default false.
   1595 		 * @param int  $comment_id The comment ID.
   1596 		 */
   1597 		$notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID );
   1598 
   1599 		// The comment was left by the author.
   1600 		if ( $author && ! $notify_author && $comment->user_id == $post->post_author ) {
   1601 			unset( $emails[ $author->user_email ] );
   1602 		}
   1603 
   1604 		// The author moderated a comment on their own post.
   1605 		if ( $author && ! $notify_author && get_current_user_id() == $post->post_author ) {
   1606 			unset( $emails[ $author->user_email ] );
   1607 		}
   1608 
   1609 		// The post author is no longer a member of the blog.
   1610 		if ( $author && ! $notify_author && ! user_can( $post->post_author, 'read_post', $post->ID ) ) {
   1611 			unset( $emails[ $author->user_email ] );
   1612 		}
   1613 
   1614 		// If there's no email to send the comment to, bail, otherwise flip array back around for use below.
   1615 		if ( ! count( $emails ) ) {
   1616 			return false;
   1617 		} else {
   1618 			$emails = array_flip( $emails );
   1619 		}
   1620 
   1621 		$switched_locale = switch_to_locale( get_locale() );
   1622 
   1623 		$comment_author_domain = '';
   1624 		if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) {
   1625 			$comment_author_domain = gethostbyaddr( $comment->comment_author_IP );
   1626 		}
   1627 
   1628 		// The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
   1629 		// We want to reverse this for the plain text arena of emails.
   1630 		$blogname        = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
   1631 		$comment_content = wp_specialchars_decode( $comment->comment_content );
   1632 
   1633 		switch ( $comment->comment_type ) {
   1634 			case 'trackback':
   1635 				/* translators: %s: Post title. */
   1636 				$notify_message = sprintf( __( 'New trackback on your post "%s"' ), $post->post_title ) . "\r\n";
   1637 				/* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
   1638 				$notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
   1639 				/* translators: %s: Trackback/pingback/comment author URL. */
   1640 				$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
   1641 				/* translators: %s: Comment text. */
   1642 				$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
   1643 				$notify_message .= __( 'You can see all trackbacks on this post here:' ) . "\r\n";
   1644 				/* translators: Trackback notification email subject. 1: Site title, 2: Post title. */
   1645 				$subject = sprintf( __( '[%1$s] Trackback: "%2$s"' ), $blogname, $post->post_title );
   1646 				break;
   1647 
   1648 			case 'pingback':
   1649 				/* translators: %s: Post title. */
   1650 				$notify_message = sprintf( __( 'New pingback on your post "%s"' ), $post->post_title ) . "\r\n";
   1651 				/* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
   1652 				$notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
   1653 				/* translators: %s: Trackback/pingback/comment author URL. */
   1654 				$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
   1655 				/* translators: %s: Comment text. */
   1656 				$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
   1657 				$notify_message .= __( 'You can see all pingbacks on this post here:' ) . "\r\n";
   1658 				/* translators: Pingback notification email subject. 1: Site title, 2: Post title. */
   1659 				$subject = sprintf( __( '[%1$s] Pingback: "%2$s"' ), $blogname, $post->post_title );
   1660 				break;
   1661 
   1662 			default: // Comments.
   1663 				/* translators: %s: Post title. */
   1664 				$notify_message = sprintf( __( 'New comment on your post "%s"' ), $post->post_title ) . "\r\n";
   1665 				/* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */
   1666 				$notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
   1667 				/* translators: %s: Comment author email. */
   1668 				$notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
   1669 				/* translators: %s: Trackback/pingback/comment author URL. */
   1670 				$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
   1671 
   1672 				if ( $comment->comment_parent && user_can( $post->post_author, 'edit_comment', $comment->comment_parent ) ) {
   1673 					/* translators: Comment moderation. %s: Parent comment edit URL. */
   1674 					$notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n";
   1675 				}
   1676 
   1677 				/* translators: %s: Comment text. */
   1678 				$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
   1679 				$notify_message .= __( 'You can see all comments on this post here:' ) . "\r\n";
   1680 				/* translators: Comment notification email subject. 1: Site title, 2: Post title. */
   1681 				$subject = sprintf( __( '[%1$s] Comment: "%2$s"' ), $blogname, $post->post_title );
   1682 				break;
   1683 		}
   1684 
   1685 		$notify_message .= get_permalink( $comment->comment_post_ID ) . "#comments\r\n\r\n";
   1686 		/* translators: %s: Comment URL. */
   1687 		$notify_message .= sprintf( __( 'Permalink: %s' ), get_comment_link( $comment ) ) . "\r\n";
   1688 
   1689 		if ( user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) {
   1690 			if ( EMPTY_TRASH_DAYS ) {
   1691 				/* translators: Comment moderation. %s: Comment action URL. */
   1692 				$notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
   1693 			} else {
   1694 				/* translators: Comment moderation. %s: Comment action URL. */
   1695 				$notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
   1696 			}
   1697 			/* translators: Comment moderation. %s: Comment action URL. */
   1698 			$notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
   1699 		}
   1700 
   1701 		$wp_email = 'wordpress@' . preg_replace( '#^www\.#', '', wp_parse_url( network_home_url(), PHP_URL_HOST ) );
   1702 
   1703 		if ( '' === $comment->comment_author ) {
   1704 			$from = "From: \"$blogname\" <$wp_email>";
   1705 			if ( '' !== $comment->comment_author_email ) {
   1706 				$reply_to = "Reply-To: $comment->comment_author_email";
   1707 			}
   1708 		} else {
   1709 			$from = "From: \"$comment->comment_author\" <$wp_email>";
   1710 			if ( '' !== $comment->comment_author_email ) {
   1711 				$reply_to = "Reply-To: \"$comment->comment_author_email\" <$comment->comment_author_email>";
   1712 			}
   1713 		}
   1714 
   1715 		$message_headers = "$from\n"
   1716 		. 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
   1717 
   1718 		if ( isset( $reply_to ) ) {
   1719 			$message_headers .= $reply_to . "\n";
   1720 		}
   1721 
   1722 		/**
   1723 		 * Filters the comment notification email text.
   1724 		 *
   1725 		 * @since 1.5.2
   1726 		 *
   1727 		 * @param string $notify_message The comment notification email text.
   1728 		 * @param int    $comment_id     Comment ID.
   1729 		 */
   1730 		$notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment->comment_ID );
   1731 
   1732 		/**
   1733 		 * Filters the comment notification email subject.
   1734 		 *
   1735 		 * @since 1.5.2
   1736 		 *
   1737 		 * @param string $subject    The comment notification email subject.
   1738 		 * @param int    $comment_id Comment ID.
   1739 		 */
   1740 		$subject = apply_filters( 'comment_notification_subject', $subject, $comment->comment_ID );
   1741 
   1742 		/**
   1743 		 * Filters the comment notification email headers.
   1744 		 *
   1745 		 * @since 1.5.2
   1746 		 *
   1747 		 * @param string $message_headers Headers for the comment notification email.
   1748 		 * @param int    $comment_id      Comment ID.
   1749 		 */
   1750 		$message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment->comment_ID );
   1751 
   1752 		foreach ( $emails as $email ) {
   1753 			wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
   1754 		}
   1755 
   1756 		if ( $switched_locale ) {
   1757 			restore_previous_locale();
   1758 		}
   1759 
   1760 		return true;
   1761 	}
   1762 endif;
   1763 
   1764 if ( ! function_exists( 'wp_notify_moderator' ) ) :
   1765 	/**
   1766 	 * Notifies the moderator of the site about a new comment that is awaiting approval.
   1767 	 *
   1768 	 * @since 1.0.0
   1769 	 *
   1770 	 * @global wpdb $wpdb WordPress database abstraction object.
   1771 	 *
   1772 	 * Uses the {@see 'notify_moderator'} filter to determine whether the site moderator
   1773 	 * should be notified, overriding the site setting.
   1774 	 *
   1775 	 * @param int $comment_id Comment ID.
   1776 	 * @return true Always returns true.
   1777 	 */
   1778 	function wp_notify_moderator( $comment_id ) {
   1779 		global $wpdb;
   1780 
   1781 		$maybe_notify = get_option( 'moderation_notify' );
   1782 
   1783 		/**
   1784 		 * Filters whether to send the site moderator email notifications, overriding the site setting.
   1785 		 *
   1786 		 * @since 4.4.0
   1787 		 *
   1788 		 * @param bool $maybe_notify Whether to notify blog moderator.
   1789 		 * @param int  $comment_ID   The id of the comment for the notification.
   1790 		 */
   1791 		$maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id );
   1792 
   1793 		if ( ! $maybe_notify ) {
   1794 			return true;
   1795 		}
   1796 
   1797 		$comment = get_comment( $comment_id );
   1798 		$post    = get_post( $comment->comment_post_ID );
   1799 		$user    = get_userdata( $post->post_author );
   1800 		// Send to the administration and to the post author if the author can modify the comment.
   1801 		$emails = array( get_option( 'admin_email' ) );
   1802 		if ( $user && user_can( $user->ID, 'edit_comment', $comment_id ) && ! empty( $user->user_email ) ) {
   1803 			if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
   1804 				$emails[] = $user->user_email;
   1805 			}
   1806 		}
   1807 
   1808 		$switched_locale = switch_to_locale( get_locale() );
   1809 
   1810 		$comment_author_domain = '';
   1811 		if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) {
   1812 			$comment_author_domain = gethostbyaddr( $comment->comment_author_IP );
   1813 		}
   1814 
   1815 		$comments_waiting = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '0'" );
   1816 
   1817 		// The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
   1818 		// We want to reverse this for the plain text arena of emails.
   1819 		$blogname        = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
   1820 		$comment_content = wp_specialchars_decode( $comment->comment_content );
   1821 
   1822 		switch ( $comment->comment_type ) {
   1823 			case 'trackback':
   1824 				/* translators: %s: Post title. */
   1825 				$notify_message  = sprintf( __( 'A new trackback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
   1826 				$notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
   1827 				/* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
   1828 				$notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
   1829 				/* translators: %s: Trackback/pingback/comment author URL. */
   1830 				$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
   1831 				$notify_message .= __( 'Trackback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
   1832 				break;
   1833 
   1834 			case 'pingback':
   1835 				/* translators: %s: Post title. */
   1836 				$notify_message  = sprintf( __( 'A new pingback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
   1837 				$notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
   1838 				/* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
   1839 				$notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
   1840 				/* translators: %s: Trackback/pingback/comment author URL. */
   1841 				$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
   1842 				$notify_message .= __( 'Pingback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
   1843 				break;
   1844 
   1845 			default: // Comments.
   1846 				/* translators: %s: Post title. */
   1847 				$notify_message  = sprintf( __( 'A new comment on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
   1848 				$notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
   1849 				/* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */
   1850 				$notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
   1851 				/* translators: %s: Comment author email. */
   1852 				$notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
   1853 				/* translators: %s: Trackback/pingback/comment author URL. */
   1854 				$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
   1855 
   1856 				if ( $comment->comment_parent ) {
   1857 					/* translators: Comment moderation. %s: Parent comment edit URL. */
   1858 					$notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n";
   1859 				}
   1860 
   1861 				/* translators: %s: Comment text. */
   1862 				$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
   1863 				break;
   1864 		}
   1865 
   1866 		/* translators: Comment moderation. %s: Comment action URL. */
   1867 		$notify_message .= sprintf( __( 'Approve it: %s' ), admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" ) ) . "\r\n";
   1868 
   1869 		if ( EMPTY_TRASH_DAYS ) {
   1870 			/* translators: Comment moderation. %s: Comment action URL. */
   1871 			$notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment_id}#wpbody-content" ) ) . "\r\n";
   1872 		} else {
   1873 			/* translators: Comment moderation. %s: Comment action URL. */
   1874 			$notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment_id}#wpbody-content" ) ) . "\r\n";
   1875 		}
   1876 
   1877 		/* translators: Comment moderation. %s: Comment action URL. */
   1878 		$notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment_id}#wpbody-content" ) ) . "\r\n";
   1879 
   1880 		$notify_message .= sprintf(
   1881 			/* translators: Comment moderation. %s: Number of comments awaiting approval. */
   1882 			_n(
   1883 				'Currently %s comment is waiting for approval. Please visit the moderation panel:',
   1884 				'Currently %s comments are waiting for approval. Please visit the moderation panel:',
   1885 				$comments_waiting
   1886 			),
   1887 			number_format_i18n( $comments_waiting )
   1888 		) . "\r\n";
   1889 		$notify_message .= admin_url( 'edit-comments.php?comment_status=moderated#wpbody-content' ) . "\r\n";
   1890 
   1891 		/* translators: Comment moderation notification email subject. 1: Site title, 2: Post title. */
   1892 		$subject         = sprintf( __( '[%1$s] Please moderate: "%2$s"' ), $blogname, $post->post_title );
   1893 		$message_headers = '';
   1894 
   1895 		/**
   1896 		 * Filters the list of recipients for comment moderation emails.
   1897 		 *
   1898 		 * @since 3.7.0
   1899 		 *
   1900 		 * @param string[] $emails     List of email addresses to notify for comment moderation.
   1901 		 * @param int      $comment_id Comment ID.
   1902 		 */
   1903 		$emails = apply_filters( 'comment_moderation_recipients', $emails, $comment_id );
   1904 
   1905 		/**
   1906 		 * Filters the comment moderation email text.
   1907 		 *
   1908 		 * @since 1.5.2
   1909 		 *
   1910 		 * @param string $notify_message Text of the comment moderation email.
   1911 		 * @param int    $comment_id     Comment ID.
   1912 		 */
   1913 		$notify_message = apply_filters( 'comment_moderation_text', $notify_message, $comment_id );
   1914 
   1915 		/**
   1916 		 * Filters the comment moderation email subject.
   1917 		 *
   1918 		 * @since 1.5.2
   1919 		 *
   1920 		 * @param string $subject    Subject of the comment moderation email.
   1921 		 * @param int    $comment_id Comment ID.
   1922 		 */
   1923 		$subject = apply_filters( 'comment_moderation_subject', $subject, $comment_id );
   1924 
   1925 		/**
   1926 		 * Filters the comment moderation email headers.
   1927 		 *
   1928 		 * @since 2.8.0
   1929 		 *
   1930 		 * @param string $message_headers Headers for the comment moderation email.
   1931 		 * @param int    $comment_id      Comment ID.
   1932 		 */
   1933 		$message_headers = apply_filters( 'comment_moderation_headers', $message_headers, $comment_id );
   1934 
   1935 		foreach ( $emails as $email ) {
   1936 			wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
   1937 		}
   1938 
   1939 		if ( $switched_locale ) {
   1940 			restore_previous_locale();
   1941 		}
   1942 
   1943 		return true;
   1944 	}
   1945 endif;
   1946 
   1947 if ( ! function_exists( 'wp_password_change_notification' ) ) :
   1948 	/**
   1949 	 * Notify the blog admin of a user changing password, normally via email.
   1950 	 *
   1951 	 * @since 2.7.0
   1952 	 *
   1953 	 * @param WP_User $user User object.
   1954 	 */
   1955 	function wp_password_change_notification( $user ) {
   1956 		// Send a copy of password change notification to the admin,
   1957 		// but check to see if it's the admin whose password we're changing, and skip this.
   1958 		if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
   1959 			/* translators: %s: User name. */
   1960 			$message = sprintf( __( 'Password changed for user: %s' ), $user->user_login ) . "\r\n";
   1961 			// The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
   1962 			// We want to reverse this for the plain text arena of emails.
   1963 			$blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
   1964 
   1965 			$wp_password_change_notification_email = array(
   1966 				'to'      => get_option( 'admin_email' ),
   1967 				/* translators: Password change notification email subject. %s: Site title. */
   1968 				'subject' => __( '[%s] Password Changed' ),
   1969 				'message' => $message,
   1970 				'headers' => '',
   1971 			);
   1972 
   1973 			/**
   1974 			 * Filters the contents of the password change notification email sent to the site admin.
   1975 			 *
   1976 			 * @since 4.9.0
   1977 			 *
   1978 			 * @param array   $wp_password_change_notification_email {
   1979 			 *     Used to build wp_mail().
   1980 			 *
   1981 			 *     @type string $to      The intended recipient - site admin email address.
   1982 			 *     @type string $subject The subject of the email.
   1983 			 *     @type string $message The body of the email.
   1984 			 *     @type string $headers The headers of the email.
   1985 			 * }
   1986 			 * @param WP_User $user     User object for user whose password was changed.
   1987 			 * @param string  $blogname The site title.
   1988 			 */
   1989 			$wp_password_change_notification_email = apply_filters( 'wp_password_change_notification_email', $wp_password_change_notification_email, $user, $blogname );
   1990 
   1991 			wp_mail(
   1992 				$wp_password_change_notification_email['to'],
   1993 				wp_specialchars_decode( sprintf( $wp_password_change_notification_email['subject'], $blogname ) ),
   1994 				$wp_password_change_notification_email['message'],
   1995 				$wp_password_change_notification_email['headers']
   1996 			);
   1997 		}
   1998 	}
   1999 endif;
   2000 
   2001 if ( ! function_exists( 'wp_new_user_notification' ) ) :
   2002 	/**
   2003 	 * Email login credentials to a newly-registered user.
   2004 	 *
   2005 	 * A new user registration notification is also sent to admin email.
   2006 	 *
   2007 	 * @since 2.0.0
   2008 	 * @since 4.3.0 The `$plaintext_pass` parameter was changed to `$notify`.
   2009 	 * @since 4.3.1 The `$plaintext_pass` parameter was deprecated. `$notify` added as a third parameter.
   2010 	 * @since 4.6.0 The `$notify` parameter accepts 'user' for sending notification only to the user created.
   2011 	 *
   2012 	 * @param int    $user_id    User ID.
   2013 	 * @param null   $deprecated Not used (argument deprecated).
   2014 	 * @param string $notify     Optional. Type of notification that should happen. Accepts 'admin' or an empty
   2015 	 *                           string (admin only), 'user', or 'both' (admin and user). Default empty.
   2016 	 */
   2017 	function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) {
   2018 		if ( null !== $deprecated ) {
   2019 			_deprecated_argument( __FUNCTION__, '4.3.1' );
   2020 		}
   2021 
   2022 		// Accepts only 'user', 'admin' , 'both' or default '' as $notify.
   2023 		if ( ! in_array( $notify, array( 'user', 'admin', 'both', '' ), true ) ) {
   2024 			return;
   2025 		}
   2026 
   2027 		$user = get_userdata( $user_id );
   2028 
   2029 		// The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
   2030 		// We want to reverse this for the plain text arena of emails.
   2031 		$blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
   2032 
   2033 		if ( 'user' !== $notify ) {
   2034 			$switched_locale = switch_to_locale( get_locale() );
   2035 
   2036 			/* translators: %s: Site title. */
   2037 			$message = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n";
   2038 			/* translators: %s: User login. */
   2039 			$message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
   2040 			/* translators: %s: User email address. */
   2041 			$message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n";
   2042 
   2043 			$wp_new_user_notification_email_admin = array(
   2044 				'to'      => get_option( 'admin_email' ),
   2045 				/* translators: New user registration notification email subject. %s: Site title. */
   2046 				'subject' => __( '[%s] New User Registration' ),
   2047 				'message' => $message,
   2048 				'headers' => '',
   2049 			);
   2050 
   2051 			/**
   2052 			 * Filters the contents of the new user notification email sent to the site admin.
   2053 			 *
   2054 			 * @since 4.9.0
   2055 			 *
   2056 			 * @param array   $wp_new_user_notification_email_admin {
   2057 			 *     Used to build wp_mail().
   2058 			 *
   2059 			 *     @type string $to      The intended recipient - site admin email address.
   2060 			 *     @type string $subject The subject of the email.
   2061 			 *     @type string $message The body of the email.
   2062 			 *     @type string $headers The headers of the email.
   2063 			 * }
   2064 			 * @param WP_User $user     User object for new user.
   2065 			 * @param string  $blogname The site title.
   2066 			 */
   2067 			$wp_new_user_notification_email_admin = apply_filters( 'wp_new_user_notification_email_admin', $wp_new_user_notification_email_admin, $user, $blogname );
   2068 
   2069 			wp_mail(
   2070 				$wp_new_user_notification_email_admin['to'],
   2071 				wp_specialchars_decode( sprintf( $wp_new_user_notification_email_admin['subject'], $blogname ) ),
   2072 				$wp_new_user_notification_email_admin['message'],
   2073 				$wp_new_user_notification_email_admin['headers']
   2074 			);
   2075 
   2076 			if ( $switched_locale ) {
   2077 				restore_previous_locale();
   2078 			}
   2079 		}
   2080 
   2081 		// `$deprecated` was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notification.
   2082 		if ( 'admin' === $notify || ( empty( $deprecated ) && empty( $notify ) ) ) {
   2083 			return;
   2084 		}
   2085 
   2086 		$key = get_password_reset_key( $user );
   2087 		if ( is_wp_error( $key ) ) {
   2088 			return;
   2089 		}
   2090 
   2091 		$switched_locale = switch_to_locale( get_user_locale( $user ) );
   2092 
   2093 		/* translators: %s: User login. */
   2094 		$message  = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
   2095 		$message .= __( 'To set your password, visit the following address:' ) . "\r\n\r\n";
   2096 		$message .= network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user->user_login ), 'login' ) . "\r\n\r\n";
   2097 
   2098 		$message .= wp_login_url() . "\r\n";
   2099 
   2100 		$wp_new_user_notification_email = array(
   2101 			'to'      => $user->user_email,
   2102 			/* translators: Login details notification email subject. %s: Site title. */
   2103 			'subject' => __( '[%s] Login Details' ),
   2104 			'message' => $message,
   2105 			'headers' => '',
   2106 		);
   2107 
   2108 		/**
   2109 		 * Filters the contents of the new user notification email sent to the new user.
   2110 		 *
   2111 		 * @since 4.9.0
   2112 		 *
   2113 		 * @param array   $wp_new_user_notification_email {
   2114 		 *     Used to build wp_mail().
   2115 		 *
   2116 		 *     @type string $to      The intended recipient - New user email address.
   2117 		 *     @type string $subject The subject of the email.
   2118 		 *     @type string $message The body of the email.
   2119 		 *     @type string $headers The headers of the email.
   2120 		 * }
   2121 		 * @param WP_User $user     User object for new user.
   2122 		 * @param string  $blogname The site title.
   2123 		 */
   2124 		$wp_new_user_notification_email = apply_filters( 'wp_new_user_notification_email', $wp_new_user_notification_email, $user, $blogname );
   2125 
   2126 		wp_mail(
   2127 			$wp_new_user_notification_email['to'],
   2128 			wp_specialchars_decode( sprintf( $wp_new_user_notification_email['subject'], $blogname ) ),
   2129 			$wp_new_user_notification_email['message'],
   2130 			$wp_new_user_notification_email['headers']
   2131 		);
   2132 
   2133 		if ( $switched_locale ) {
   2134 			restore_previous_locale();
   2135 		}
   2136 	}
   2137 endif;
   2138 
   2139 if ( ! function_exists( 'wp_nonce_tick' ) ) :
   2140 	/**
   2141 	 * Returns the time-dependent variable for nonce creation.
   2142 	 *
   2143 	 * A nonce has a lifespan of two ticks. Nonces in their second tick may be
   2144 	 * updated, e.g. by autosave.
   2145 	 *
   2146 	 * @since 2.5.0
   2147 	 *
   2148 	 * @return float Float value rounded up to the next highest integer.
   2149 	 */
   2150 	function wp_nonce_tick() {
   2151 		/**
   2152 		 * Filters the lifespan of nonces in seconds.
   2153 		 *
   2154 		 * @since 2.5.0
   2155 		 *
   2156 		 * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day.
   2157 		 */
   2158 		$nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS );
   2159 
   2160 		return ceil( time() / ( $nonce_life / 2 ) );
   2161 	}
   2162 endif;
   2163 
   2164 if ( ! function_exists( 'wp_verify_nonce' ) ) :
   2165 	/**
   2166 	 * Verifies that a correct security nonce was used with time limit.
   2167 	 *
   2168 	 * A nonce is valid for 24 hours (by default).
   2169 	 *
   2170 	 * @since 2.0.3
   2171 	 *
   2172 	 * @param string     $nonce  Nonce value that was used for verification, usually via a form field.
   2173 	 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
   2174 	 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
   2175 	 *                   2 if the nonce is valid and generated between 12-24 hours ago.
   2176 	 *                   False if the nonce is invalid.
   2177 	 */
   2178 	function wp_verify_nonce( $nonce, $action = -1 ) {
   2179 		$nonce = (string) $nonce;
   2180 		$user  = wp_get_current_user();
   2181 		$uid   = (int) $user->ID;
   2182 		if ( ! $uid ) {
   2183 			/**
   2184 			 * Filters whether the user who generated the nonce is logged out.
   2185 			 *
   2186 			 * @since 3.5.0
   2187 			 *
   2188 			 * @param int    $uid    ID of the nonce-owning user.
   2189 			 * @param string $action The nonce action.
   2190 			 */
   2191 			$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
   2192 		}
   2193 
   2194 		if ( empty( $nonce ) ) {
   2195 			return false;
   2196 		}
   2197 
   2198 		$token = wp_get_session_token();
   2199 		$i     = wp_nonce_tick();
   2200 
   2201 		// Nonce generated 0-12 hours ago.
   2202 		$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
   2203 		if ( hash_equals( $expected, $nonce ) ) {
   2204 			return 1;
   2205 		}
   2206 
   2207 		// Nonce generated 12-24 hours ago.
   2208 		$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
   2209 		if ( hash_equals( $expected, $nonce ) ) {
   2210 			return 2;
   2211 		}
   2212 
   2213 		/**
   2214 		 * Fires when nonce verification fails.
   2215 		 *
   2216 		 * @since 4.4.0
   2217 		 *
   2218 		 * @param string     $nonce  The invalid nonce.
   2219 		 * @param string|int $action The nonce action.
   2220 		 * @param WP_User    $user   The current user object.
   2221 		 * @param string     $token  The user's session token.
   2222 		 */
   2223 		do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token );
   2224 
   2225 		// Invalid nonce.
   2226 		return false;
   2227 	}
   2228 endif;
   2229 
   2230 if ( ! function_exists( 'wp_create_nonce' ) ) :
   2231 	/**
   2232 	 * Creates a cryptographic token tied to a specific action, user, user session,
   2233 	 * and window of time.
   2234 	 *
   2235 	 * @since 2.0.3
   2236 	 * @since 4.0.0 Session tokens were integrated with nonce creation
   2237 	 *
   2238 	 * @param string|int $action Scalar value to add context to the nonce.
   2239 	 * @return string The token.
   2240 	 */
   2241 	function wp_create_nonce( $action = -1 ) {
   2242 		$user = wp_get_current_user();
   2243 		$uid  = (int) $user->ID;
   2244 		if ( ! $uid ) {
   2245 			/** This filter is documented in wp-includes/pluggable.php */
   2246 			$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
   2247 		}
   2248 
   2249 		$token = wp_get_session_token();
   2250 		$i     = wp_nonce_tick();
   2251 
   2252 		return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
   2253 	}
   2254 endif;
   2255 
   2256 if ( ! function_exists( 'wp_salt' ) ) :
   2257 	/**
   2258 	 * Returns a salt to add to hashes.
   2259 	 *
   2260 	 * Salts are created using secret keys. Secret keys are located in two places:
   2261 	 * in the database and in the wp-config.php file. The secret key in the database
   2262 	 * is randomly generated and will be appended to the secret keys in wp-config.php.
   2263 	 *
   2264 	 * The secret keys in wp-config.php should be updated to strong, random keys to maximize
   2265 	 * security. Below is an example of how the secret key constants are defined.
   2266 	 * Do not paste this example directly into wp-config.php. Instead, have a
   2267 	 * {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just
   2268 	 * for you.
   2269 	 *
   2270 	 *     define('AUTH_KEY',         ' Xakm<o xQy rw4EMsLKM-?!T+,PFF})H4lzcW57AF0U@N@< >M%G4Yt>f`z]MON');
   2271 	 *     define('SECURE_AUTH_KEY',  'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~');
   2272 	 *     define('LOGGED_IN_KEY',    '|i|Ux`9<p-h$aFf(qnT:sDO:D1P^wZ$$/Ra@miTJi9G;ddp_<q}6H1)o|a +&JCM');
   2273 	 *     define('NONCE_KEY',        '%:R{[P|,s.KuMltH5}cI;/k<Gx~j!f0I)m_sIyu+&NJZ)-iO>z7X>QYR0Z_XnZ@|');
   2274 	 *     define('AUTH_SALT',        'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW');
   2275 	 *     define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W<I`.JjPi)<Bmf1v,HpGe}T1:Xt7n');
   2276 	 *     define('LOGGED_IN_SALT',   '+XSqHc;@Q*K_b|Z?NC[3H!!EONbh.n<+=uKR:>*c(u`g~EJBf#8u#R{mUEZrozmm');
   2277 	 *     define('NONCE_SALT',       'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT');
   2278 	 *
   2279 	 * Salting passwords helps against tools which has stored hashed values of
   2280 	 * common dictionary strings. The added values makes it harder to crack.
   2281 	 *
   2282 	 * @since 2.5.0
   2283 	 *
   2284 	 * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php
   2285 	 *
   2286 	 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce)
   2287 	 * @return string Salt value
   2288 	 */
   2289 	function wp_salt( $scheme = 'auth' ) {
   2290 		static $cached_salts = array();
   2291 		if ( isset( $cached_salts[ $scheme ] ) ) {
   2292 			/**
   2293 			 * Filters the WordPress salt.
   2294 			 *
   2295 			 * @since 2.5.0
   2296 			 *
   2297 			 * @param string $cached_salt Cached salt for the given scheme.
   2298 			 * @param string $scheme      Authentication scheme. Values include 'auth',
   2299 			 *                            'secure_auth', 'logged_in', and 'nonce'.
   2300 			 */
   2301 			return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
   2302 		}
   2303 
   2304 		static $duplicated_keys;
   2305 		if ( null === $duplicated_keys ) {
   2306 			$duplicated_keys = array( 'put your unique phrase here' => true );
   2307 			foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) {
   2308 				foreach ( array( 'KEY', 'SALT' ) as $second ) {
   2309 					if ( ! defined( "{$first}_{$second}" ) ) {
   2310 						continue;
   2311 					}
   2312 					$value                     = constant( "{$first}_{$second}" );
   2313 					$duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] );
   2314 				}
   2315 			}
   2316 		}
   2317 
   2318 		$values = array(
   2319 			'key'  => '',
   2320 			'salt' => '',
   2321 		);
   2322 		if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) ) {
   2323 			$values['key'] = SECRET_KEY;
   2324 		}
   2325 		if ( 'auth' === $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) ) {
   2326 			$values['salt'] = SECRET_SALT;
   2327 		}
   2328 
   2329 		if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ), true ) ) {
   2330 			foreach ( array( 'key', 'salt' ) as $type ) {
   2331 				$const = strtoupper( "{$scheme}_{$type}" );
   2332 				if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) {
   2333 					$values[ $type ] = constant( $const );
   2334 				} elseif ( ! $values[ $type ] ) {
   2335 					$values[ $type ] = get_site_option( "{$scheme}_{$type}" );
   2336 					if ( ! $values[ $type ] ) {
   2337 						$values[ $type ] = wp_generate_password( 64, true, true );
   2338 						update_site_option( "{$scheme}_{$type}", $values[ $type ] );
   2339 					}
   2340 				}
   2341 			}
   2342 		} else {
   2343 			if ( ! $values['key'] ) {
   2344 				$values['key'] = get_site_option( 'secret_key' );
   2345 				if ( ! $values['key'] ) {
   2346 					$values['key'] = wp_generate_password( 64, true, true );
   2347 					update_site_option( 'secret_key', $values['key'] );
   2348 				}
   2349 			}
   2350 			$values['salt'] = hash_hmac( 'md5', $scheme, $values['key'] );
   2351 		}
   2352 
   2353 		$cached_salts[ $scheme ] = $values['key'] . $values['salt'];
   2354 
   2355 		/** This filter is documented in wp-includes/pluggable.php */
   2356 		return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
   2357 	}
   2358 endif;
   2359 
   2360 if ( ! function_exists( 'wp_hash' ) ) :
   2361 	/**
   2362 	 * Get hash of given string.
   2363 	 *
   2364 	 * @since 2.0.3
   2365 	 *
   2366 	 * @param string $data   Plain text to hash
   2367 	 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce)
   2368 	 * @return string Hash of $data
   2369 	 */
   2370 	function wp_hash( $data, $scheme = 'auth' ) {
   2371 		$salt = wp_salt( $scheme );
   2372 
   2373 		return hash_hmac( 'md5', $data, $salt );
   2374 	}
   2375 endif;
   2376 
   2377 if ( ! function_exists( 'wp_hash_password' ) ) :
   2378 	/**
   2379 	 * Create a hash (encrypt) of a plain text password.
   2380 	 *
   2381 	 * For integration with other applications, this function can be overwritten to
   2382 	 * instead use the other package password checking algorithm.
   2383 	 *
   2384 	 * @since 2.5.0
   2385 	 *
   2386 	 * @global PasswordHash $wp_hasher PHPass object
   2387 	 *
   2388 	 * @param string $password Plain text user password to hash
   2389 	 * @return string The hash string of the password
   2390 	 */
   2391 	function wp_hash_password( $password ) {
   2392 		global $wp_hasher;
   2393 
   2394 		if ( empty( $wp_hasher ) ) {
   2395 			require_once ABSPATH . WPINC . '/class-phpass.php';
   2396 			// By default, use the portable hash from phpass.
   2397 			$wp_hasher = new PasswordHash( 8, true );
   2398 		}
   2399 
   2400 		return $wp_hasher->HashPassword( trim( $password ) );
   2401 	}
   2402 endif;
   2403 
   2404 if ( ! function_exists( 'wp_check_password' ) ) :
   2405 	/**
   2406 	 * Checks the plaintext password against the encrypted Password.
   2407 	 *
   2408 	 * Maintains compatibility between old version and the new cookie authentication
   2409 	 * protocol using PHPass library. The $hash parameter is the encrypted password
   2410 	 * and the function compares the plain text password when encrypted similarly
   2411 	 * against the already encrypted password to see if they match.
   2412 	 *
   2413 	 * For integration with other applications, this function can be overwritten to
   2414 	 * instead use the other package password checking algorithm.
   2415 	 *
   2416 	 * @since 2.5.0
   2417 	 *
   2418 	 * @global PasswordHash $wp_hasher PHPass object used for checking the password
   2419 	 *                                 against the $hash + $password
   2420 	 * @uses PasswordHash::CheckPassword
   2421 	 *
   2422 	 * @param string     $password Plaintext user's password
   2423 	 * @param string     $hash     Hash of the user's password to check against.
   2424 	 * @param string|int $user_id  Optional. User ID.
   2425 	 * @return bool False, if the $password does not match the hashed password
   2426 	 */
   2427 	function wp_check_password( $password, $hash, $user_id = '' ) {
   2428 		global $wp_hasher;
   2429 
   2430 		// If the hash is still md5...
   2431 		if ( strlen( $hash ) <= 32 ) {
   2432 			$check = hash_equals( $hash, md5( $password ) );
   2433 			if ( $check && $user_id ) {
   2434 				// Rehash using new hash.
   2435 				wp_set_password( $password, $user_id );
   2436 				$hash = wp_hash_password( $password );
   2437 			}
   2438 
   2439 			/**
   2440 			 * Filters whether the plaintext password matches the encrypted password.
   2441 			 *
   2442 			 * @since 2.5.0
   2443 			 *
   2444 			 * @param bool       $check    Whether the passwords match.
   2445 			 * @param string     $password The plaintext password.
   2446 			 * @param string     $hash     The hashed password.
   2447 			 * @param string|int $user_id  User ID. Can be empty.
   2448 			 */
   2449 			return apply_filters( 'check_password', $check, $password, $hash, $user_id );
   2450 		}
   2451 
   2452 		// If the stored hash is longer than an MD5,
   2453 		// presume the new style phpass portable hash.
   2454 		if ( empty( $wp_hasher ) ) {
   2455 			require_once ABSPATH . WPINC . '/class-phpass.php';
   2456 			// By default, use the portable hash from phpass.
   2457 			$wp_hasher = new PasswordHash( 8, true );
   2458 		}
   2459 
   2460 		$check = $wp_hasher->CheckPassword( $password, $hash );
   2461 
   2462 		/** This filter is documented in wp-includes/pluggable.php */
   2463 		return apply_filters( 'check_password', $check, $password, $hash, $user_id );
   2464 	}
   2465 endif;
   2466 
   2467 if ( ! function_exists( 'wp_generate_password' ) ) :
   2468 	/**
   2469 	 * Generates a random password drawn from the defined set of characters.
   2470 	 *
   2471 	 * Uses wp_rand() is used to create passwords with far less predictability
   2472 	 * than similar native PHP functions like `rand()` or `mt_rand()`.
   2473 	 *
   2474 	 * @since 2.5.0
   2475 	 *
   2476 	 * @param int  $length              Optional. The length of password to generate. Default 12.
   2477 	 * @param bool $special_chars       Optional. Whether to include standard special characters.
   2478 	 *                                  Default true.
   2479 	 * @param bool $extra_special_chars Optional. Whether to include other special characters.
   2480 	 *                                  Used when generating secret keys and salts. Default false.
   2481 	 * @return string The random password.
   2482 	 */
   2483 	function wp_generate_password( $length = 12, $special_chars = true, $extra_special_chars = false ) {
   2484 		$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
   2485 		if ( $special_chars ) {
   2486 			$chars .= '!@#$%^&*()';
   2487 		}
   2488 		if ( $extra_special_chars ) {
   2489 			$chars .= '-_ []{}<>~`+=,.;:/?|';
   2490 		}
   2491 
   2492 		$password = '';
   2493 		for ( $i = 0; $i < $length; $i++ ) {
   2494 			$password .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 );
   2495 		}
   2496 
   2497 		/**
   2498 		 * Filters the randomly-generated password.
   2499 		 *
   2500 		 * @since 3.0.0
   2501 		 * @since 5.3.0 Added the `$length`, `$special_chars`, and `$extra_special_chars` parameters.
   2502 		 *
   2503 		 * @param string $password            The generated password.
   2504 		 * @param int    $length              The length of password to generate.
   2505 		 * @param bool   $special_chars       Whether to include standard special characters.
   2506 		 * @param bool   $extra_special_chars Whether to include other special characters.
   2507 		 */
   2508 		return apply_filters( 'random_password', $password, $length, $special_chars, $extra_special_chars );
   2509 	}
   2510 endif;
   2511 
   2512 if ( ! function_exists( 'wp_rand' ) ) :
   2513 	/**
   2514 	 * Generates a random number.
   2515 	 *
   2516 	 * @since 2.6.2
   2517 	 * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available.
   2518 	 *
   2519 	 * @global string $rnd_value
   2520 	 *
   2521 	 * @param int $min Lower limit for the generated number
   2522 	 * @param int $max Upper limit for the generated number
   2523 	 * @return int A random number between min and max
   2524 	 */
   2525 	function wp_rand( $min = 0, $max = 0 ) {
   2526 		global $rnd_value;
   2527 
   2528 		// Some misconfigured 32-bit environments (Entropy PHP, for example)
   2529 		// truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats.
   2530 		$max_random_number = 3000000000 === 2147483647 ? (float) '4294967295' : 4294967295; // 4294967295 = 0xffffffff
   2531 
   2532 		// We only handle ints, floats are truncated to their integer value.
   2533 		$min = (int) $min;
   2534 		$max = (int) $max;
   2535 
   2536 		// Use PHP's CSPRNG, or a compatible method.
   2537 		static $use_random_int_functionality = true;
   2538 		if ( $use_random_int_functionality ) {
   2539 			try {
   2540 				$_max = ( 0 != $max ) ? $max : $max_random_number;
   2541 				// wp_rand() can accept arguments in either order, PHP cannot.
   2542 				$_max = max( $min, $_max );
   2543 				$_min = min( $min, $_max );
   2544 				$val  = random_int( $_min, $_max );
   2545 				if ( false !== $val ) {
   2546 					return absint( $val );
   2547 				} else {
   2548 					$use_random_int_functionality = false;
   2549 				}
   2550 			} catch ( Error $e ) {
   2551 				$use_random_int_functionality = false;
   2552 			} catch ( Exception $e ) {
   2553 				$use_random_int_functionality = false;
   2554 			}
   2555 		}
   2556 
   2557 		// Reset $rnd_value after 14 uses.
   2558 		// 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value.
   2559 		if ( strlen( $rnd_value ) < 8 ) {
   2560 			if ( defined( 'WP_SETUP_CONFIG' ) ) {
   2561 				static $seed = '';
   2562 			} else {
   2563 				$seed = get_transient( 'random_seed' );
   2564 			}
   2565 			$rnd_value  = md5( uniqid( microtime() . mt_rand(), true ) . $seed );
   2566 			$rnd_value .= sha1( $rnd_value );
   2567 			$rnd_value .= sha1( $rnd_value . $seed );
   2568 			$seed       = md5( $seed . $rnd_value );
   2569 			if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) {
   2570 				set_transient( 'random_seed', $seed );
   2571 			}
   2572 		}
   2573 
   2574 		// Take the first 8 digits for our value.
   2575 		$value = substr( $rnd_value, 0, 8 );
   2576 
   2577 		// Strip the first eight, leaving the remainder for the next call to wp_rand().
   2578 		$rnd_value = substr( $rnd_value, 8 );
   2579 
   2580 		$value = abs( hexdec( $value ) );
   2581 
   2582 		// Reduce the value to be within the min - max range.
   2583 		if ( 0 != $max ) {
   2584 			$value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 );
   2585 		}
   2586 
   2587 		return abs( (int) $value );
   2588 	}
   2589 endif;
   2590 
   2591 if ( ! function_exists( 'wp_set_password' ) ) :
   2592 	/**
   2593 	 * Updates the user's password with a new encrypted one.
   2594 	 *
   2595 	 * For integration with other applications, this function can be overwritten to
   2596 	 * instead use the other package password checking algorithm.
   2597 	 *
   2598 	 * Please note: This function should be used sparingly and is really only meant for single-time
   2599 	 * application. Leveraging this improperly in a plugin or theme could result in an endless loop
   2600 	 * of password resets if precautions are not taken to ensure it does not execute on every page load.
   2601 	 *
   2602 	 * @since 2.5.0
   2603 	 *
   2604 	 * @global wpdb $wpdb WordPress database abstraction object.
   2605 	 *
   2606 	 * @param string $password The plaintext new user password
   2607 	 * @param int    $user_id  User ID
   2608 	 */
   2609 	function wp_set_password( $password, $user_id ) {
   2610 		global $wpdb;
   2611 
   2612 		$hash = wp_hash_password( $password );
   2613 		$wpdb->update(
   2614 			$wpdb->users,
   2615 			array(
   2616 				'user_pass'           => $hash,
   2617 				'user_activation_key' => '',
   2618 			),
   2619 			array( 'ID' => $user_id )
   2620 		);
   2621 
   2622 		clean_user_cache( $user_id );
   2623 	}
   2624 endif;
   2625 
   2626 if ( ! function_exists( 'get_avatar' ) ) :
   2627 	/**
   2628 	 * Retrieve the avatar `<img>` tag for a user, email address, MD5 hash, comment, or post.
   2629 	 *
   2630 	 * @since 2.5.0
   2631 	 * @since 4.2.0 Optional `$args` parameter added.
   2632 	 *
   2633 	 * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
   2634 	 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
   2635 	 * @param int    $size        Optional. Height and width of the avatar image file in pixels. Default 96.
   2636 	 * @param string $default     Optional. URL for the default image or a default type. Accepts '404'
   2637 	 *                            (return a 404 instead of a default image), 'retro' (8bit), 'monsterid'
   2638 	 *                            (monster), 'wavatar' (cartoon face), 'indenticon' (the "quilt"),
   2639 	 *                            'mystery', 'mm', or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF),
   2640 	 *                            or 'gravatar_default' (the Gravatar logo). Default is the value of the
   2641 	 *                            'avatar_default' option, with a fallback of 'mystery'.
   2642 	 * @param string $alt         Optional. Alternative text to use in img tag. Default empty.
   2643 	 * @param array  $args {
   2644 	 *     Optional. Extra arguments to retrieve the avatar.
   2645 	 *
   2646 	 *     @type int          $height        Display height of the avatar in pixels. Defaults to $size.
   2647 	 *     @type int          $width         Display width of the avatar in pixels. Defaults to $size.
   2648 	 *     @type bool         $force_default Whether to always show the default image, never the Gravatar. Default false.
   2649 	 *     @type string       $rating        What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are
   2650 	 *                                       judged in that order. Default is the value of the 'avatar_rating' option.
   2651 	 *     @type string       $scheme        URL scheme to use. See set_url_scheme() for accepted values.
   2652 	 *                                       Default null.
   2653 	 *     @type array|string $class         Array or string of additional classes to add to the img element.
   2654 	 *                                       Default null.
   2655 	 *     @type bool         $force_display Whether to always show the avatar - ignores the show_avatars option.
   2656 	 *                                       Default false.
   2657 	 *     @type string       $loading       Value for the `loading` attribute.
   2658 	 *                                       Default null.
   2659 	 *     @type string       $extra_attr    HTML attributes to insert in the IMG element. Is not sanitized. Default empty.
   2660 	 * }
   2661 	 * @return string|false `<img>` tag for the user's avatar. False on failure.
   2662 	 */
   2663 	function get_avatar( $id_or_email, $size = 96, $default = '', $alt = '', $args = null ) {
   2664 		$defaults = array(
   2665 			// get_avatar_data() args.
   2666 			'size'          => 96,
   2667 			'height'        => null,
   2668 			'width'         => null,
   2669 			'default'       => get_option( 'avatar_default', 'mystery' ),
   2670 			'force_default' => false,
   2671 			'rating'        => get_option( 'avatar_rating' ),
   2672 			'scheme'        => null,
   2673 			'alt'           => '',
   2674 			'class'         => null,
   2675 			'force_display' => false,
   2676 			'loading'       => null,
   2677 			'extra_attr'    => '',
   2678 		);
   2679 
   2680 		if ( wp_lazy_loading_enabled( 'img', 'get_avatar' ) ) {
   2681 			$defaults['loading'] = 'lazy';
   2682 		}
   2683 
   2684 		if ( empty( $args ) ) {
   2685 			$args = array();
   2686 		}
   2687 
   2688 		$args['size']    = (int) $size;
   2689 		$args['default'] = $default;
   2690 		$args['alt']     = $alt;
   2691 
   2692 		$args = wp_parse_args( $args, $defaults );
   2693 
   2694 		if ( empty( $args['height'] ) ) {
   2695 			$args['height'] = $args['size'];
   2696 		}
   2697 		if ( empty( $args['width'] ) ) {
   2698 			$args['width'] = $args['size'];
   2699 		}
   2700 
   2701 		if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
   2702 			$id_or_email = get_comment( $id_or_email );
   2703 		}
   2704 
   2705 		/**
   2706 		 * Allows the HTML for a user's avatar to be returned early.
   2707 		 *
   2708 		 * Passing a non-null value will effectively short-circuit get_avatar(), passing
   2709 		 * the value through the {@see 'get_avatar'} filter and returning early.
   2710 		 *
   2711 		 * @since 4.2.0
   2712 		 *
   2713 		 * @param string|null $avatar      HTML for the user's avatar. Default null.
   2714 		 * @param mixed       $id_or_email The avatar to retrieve. Accepts a user_id, Gravatar MD5 hash,
   2715 		 *                                 user email, WP_User object, WP_Post object, or WP_Comment object.
   2716 		 * @param array       $args        Arguments passed to get_avatar_url(), after processing.
   2717 		 */
   2718 		$avatar = apply_filters( 'pre_get_avatar', null, $id_or_email, $args );
   2719 
   2720 		if ( ! is_null( $avatar ) ) {
   2721 			/** This filter is documented in wp-includes/pluggable.php */
   2722 			return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
   2723 		}
   2724 
   2725 		if ( ! $args['force_display'] && ! get_option( 'show_avatars' ) ) {
   2726 			return false;
   2727 		}
   2728 
   2729 		$url2x = get_avatar_url( $id_or_email, array_merge( $args, array( 'size' => $args['size'] * 2 ) ) );
   2730 
   2731 		$args = get_avatar_data( $id_or_email, $args );
   2732 
   2733 		$url = $args['url'];
   2734 
   2735 		if ( ! $url || is_wp_error( $url ) ) {
   2736 			return false;
   2737 		}
   2738 
   2739 		$class = array( 'avatar', 'avatar-' . (int) $args['size'], 'photo' );
   2740 
   2741 		if ( ! $args['found_avatar'] || $args['force_default'] ) {
   2742 			$class[] = 'avatar-default';
   2743 		}
   2744 
   2745 		if ( $args['class'] ) {
   2746 			if ( is_array( $args['class'] ) ) {
   2747 				$class = array_merge( $class, $args['class'] );
   2748 			} else {
   2749 				$class[] = $args['class'];
   2750 			}
   2751 		}
   2752 
   2753 		// Add `loading` attribute.
   2754 		$extra_attr = $args['extra_attr'];
   2755 		$loading    = $args['loading'];
   2756 
   2757 		if ( in_array( $loading, array( 'lazy', 'eager' ), true ) && ! preg_match( '/\bloading\s*=/', $extra_attr ) ) {
   2758 			if ( ! empty( $extra_attr ) ) {
   2759 				$extra_attr .= ' ';
   2760 			}
   2761 
   2762 			$extra_attr .= "loading='{$loading}'";
   2763 		}
   2764 
   2765 		$avatar = sprintf(
   2766 			"<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>",
   2767 			esc_attr( $args['alt'] ),
   2768 			esc_url( $url ),
   2769 			esc_url( $url2x ) . ' 2x',
   2770 			esc_attr( implode( ' ', $class ) ),
   2771 			(int) $args['height'],
   2772 			(int) $args['width'],
   2773 			$extra_attr
   2774 		);
   2775 
   2776 		/**
   2777 		 * Filters the HTML for a user's avatar.
   2778 		 *
   2779 		 * @since 2.5.0
   2780 		 * @since 4.2.0 The `$args` parameter was added.
   2781 		 *
   2782 		 * @param string $avatar      HTML for the user's avatar.
   2783 		 * @param mixed  $id_or_email The avatar to retrieve. Accepts a user_id, Gravatar MD5 hash,
   2784 		 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
   2785 		 * @param int    $size        Square avatar width and height in pixels to retrieve.
   2786 		 * @param string $default     URL for the default image or a default type. Accepts '404', 'retro', 'monsterid',
   2787 		 *                            'wavatar', 'indenticon', 'mystery', 'mm', 'mysteryman', 'blank', or 'gravatar_default'.
   2788 		 *                            Default is the value of the 'avatar_default' option, with a fallback of 'mystery'.
   2789 		 * @param string $alt         Alternative text to use in the avatar image tag. Default empty.
   2790 		 * @param array  $args        Arguments passed to get_avatar_data(), after processing.
   2791 		 */
   2792 		return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
   2793 	}
   2794 endif;
   2795 
   2796 if ( ! function_exists( 'wp_text_diff' ) ) :
   2797 	/**
   2798 	 * Displays a human readable HTML representation of the difference between two strings.
   2799 	 *
   2800 	 * The Diff is available for getting the changes between versions. The output is
   2801 	 * HTML, so the primary use is for displaying the changes. If the two strings
   2802 	 * are equivalent, then an empty string will be returned.
   2803 	 *
   2804 	 * @since 2.6.0
   2805 	 *
   2806 	 * @see wp_parse_args() Used to change defaults to user defined settings.
   2807 	 * @uses Text_Diff
   2808 	 * @uses WP_Text_Diff_Renderer_Table
   2809 	 *
   2810 	 * @param string       $left_string  "old" (left) version of string
   2811 	 * @param string       $right_string "new" (right) version of string
   2812 	 * @param string|array $args {
   2813 	 *     Associative array of options to pass to WP_Text_Diff_Renderer_Table().
   2814 	 *
   2815 	 *     @type string $title           Titles the diff in a manner compatible
   2816 	 *                                   with the output. Default empty.
   2817 	 *     @type string $title_left      Change the HTML to the left of the title.
   2818 	 *                                   Default empty.
   2819 	 *     @type string $title_right     Change the HTML to the right of the title.
   2820 	 *                                   Default empty.
   2821 	 *     @type bool   $show_split_view True for split view (two columns), false for
   2822 	 *                                   un-split view (single column). Default true.
   2823 	 * }
   2824 	 * @return string Empty string if strings are equivalent or HTML with differences.
   2825 	 */
   2826 	function wp_text_diff( $left_string, $right_string, $args = null ) {
   2827 		$defaults = array(
   2828 			'title'           => '',
   2829 			'title_left'      => '',
   2830 			'title_right'     => '',
   2831 			'show_split_view' => true,
   2832 		);
   2833 		$args     = wp_parse_args( $args, $defaults );
   2834 
   2835 		if ( ! class_exists( 'WP_Text_Diff_Renderer_Table', false ) ) {
   2836 			require ABSPATH . WPINC . '/wp-diff.php';
   2837 		}
   2838 
   2839 		$left_string  = normalize_whitespace( $left_string );
   2840 		$right_string = normalize_whitespace( $right_string );
   2841 
   2842 		$left_lines  = explode( "\n", $left_string );
   2843 		$right_lines = explode( "\n", $right_string );
   2844 		$text_diff   = new Text_Diff( $left_lines, $right_lines );
   2845 		$renderer    = new WP_Text_Diff_Renderer_Table( $args );
   2846 		$diff        = $renderer->render( $text_diff );
   2847 
   2848 		if ( ! $diff ) {
   2849 			return '';
   2850 		}
   2851 
   2852 		$is_split_view       = ! empty( $args['show_split_view'] );
   2853 		$is_split_view_class = $is_split_view ? ' is-split-view' : '';
   2854 
   2855 		$r = "<table class='diff$is_split_view_class'>\n";
   2856 
   2857 		if ( $args['title'] ) {
   2858 			$r .= "<caption class='diff-title'>$args[title]</caption>\n";
   2859 		}
   2860 
   2861 		if ( $args['title_left'] || $args['title_right'] ) {
   2862 			$r .= '<thead>';
   2863 		}
   2864 
   2865 		if ( $args['title_left'] || $args['title_right'] ) {
   2866 			$th_or_td_left  = empty( $args['title_left'] ) ? 'td' : 'th';
   2867 			$th_or_td_right = empty( $args['title_right'] ) ? 'td' : 'th';
   2868 
   2869 			$r .= "<tr class='diff-sub-title'>\n";
   2870 			$r .= "\t<$th_or_td_left>$args[title_left]</$th_or_td_left>\n";
   2871 			if ( $is_split_view ) {
   2872 				$r .= "\t<$th_or_td_right>$args[title_right]</$th_or_td_right>\n";
   2873 			}
   2874 			$r .= "</tr>\n";
   2875 		}
   2876 
   2877 		if ( $args['title_left'] || $args['title_right'] ) {
   2878 			$r .= "</thead>\n";
   2879 		}
   2880 
   2881 		$r .= "<tbody>\n$diff\n</tbody>\n";
   2882 		$r .= '</table>';
   2883 
   2884 		return $r;
   2885 	}
   2886 endif;