--- a/wp/wp-includes/user.php Tue Dec 15 15:52:01 2020 +0100
+++ b/wp/wp-includes/user.php Wed Sep 21 18:19:35 2022 +0200
@@ -149,7 +149,11 @@
if ( ! $user ) {
return new WP_Error(
'invalid_username',
- __( 'Unknown username. Check again or try your email address.' )
+ sprintf(
+ /* translators: %s: User name. */
+ __( '<strong>Error</strong>: The username <strong>%s</strong> is not registered on this site. If you are unsure of your username, try your email address instead.' ),
+ $username
+ )
);
}
@@ -298,6 +302,186 @@
}
/**
+ * Authenticates the user using an application password.
+ *
+ * @since 5.6.0
+ *
+ * @param WP_User|WP_Error|null $input_user WP_User or WP_Error object if a previous
+ * callback failed authentication.
+ * @param string $username Username for authentication.
+ * @param string $password Password for authentication.
+ * @return WP_User|WP_Error|null WP_User on success, WP_Error on failure, null if
+ * null is passed in and this isn't an API request.
+ */
+function wp_authenticate_application_password( $input_user, $username, $password ) {
+ if ( $input_user instanceof WP_User ) {
+ return $input_user;
+ }
+
+ if ( ! WP_Application_Passwords::is_in_use() ) {
+ return $input_user;
+ }
+
+ $is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) );
+
+ /**
+ * Filters whether this is an API request that Application Passwords can be used on.
+ *
+ * By default, Application Passwords is available for the REST API and XML-RPC.
+ *
+ * @since 5.6.0
+ *
+ * @param bool $is_api_request If this is an acceptable API request.
+ */
+ $is_api_request = apply_filters( 'application_password_is_api_request', $is_api_request );
+
+ if ( ! $is_api_request ) {
+ return $input_user;
+ }
+
+ $error = null;
+ $user = get_user_by( 'login', $username );
+
+ if ( ! $user && is_email( $username ) ) {
+ $user = get_user_by( 'email', $username );
+ }
+
+ // If the login name is invalid, short circuit.
+ if ( ! $user ) {
+ if ( is_email( $username ) ) {
+ $error = new WP_Error(
+ 'invalid_email',
+ __( '<strong>Error</strong>: Unknown email address. Check again or try your username.' )
+ );
+ } else {
+ $error = new WP_Error(
+ 'invalid_username',
+ __( '<strong>Error</strong>: Unknown username. Check again or try your email address.' )
+ );
+ }
+ } elseif ( ! wp_is_application_passwords_available() ) {
+ $error = new WP_Error(
+ 'application_passwords_disabled',
+ __( 'Application passwords are not available.' )
+ );
+ } elseif ( ! wp_is_application_passwords_available_for_user( $user ) ) {
+ $error = new WP_Error(
+ 'application_passwords_disabled_for_user',
+ __( 'Application passwords are not available for your account. Please contact the site administrator for assistance.' )
+ );
+ }
+
+ if ( $error ) {
+ /**
+ * Fires when an application password failed to authenticate the user.
+ *
+ * @since 5.6.0
+ *
+ * @param WP_Error $error The authentication error.
+ */
+ do_action( 'application_password_failed_authentication', $error );
+
+ return $error;
+ }
+
+ /*
+ * Strip out anything non-alphanumeric. This is so passwords can be used with
+ * or without spaces to indicate the groupings for readability.
+ *
+ * Generated application passwords are exclusively alphanumeric.
+ */
+ $password = preg_replace( '/[^a-z\d]/i', '', $password );
+
+ $hashed_passwords = WP_Application_Passwords::get_user_application_passwords( $user->ID );
+
+ foreach ( $hashed_passwords as $key => $item ) {
+ if ( ! wp_check_password( $password, $item['password'], $user->ID ) ) {
+ continue;
+ }
+
+ $error = new WP_Error();
+
+ /**
+ * Fires when an application password has been successfully checked as valid.
+ *
+ * This allows for plugins to add additional constraints to prevent an application password from being used.
+ *
+ * @since 5.6.0
+ *
+ * @param WP_Error $error The error object.
+ * @param WP_User $user The user authenticating.
+ * @param array $item The details about the application password.
+ * @param string $password The raw supplied password.
+ */
+ do_action( 'wp_authenticate_application_password_errors', $error, $user, $item, $password );
+
+ if ( is_wp_error( $error ) && $error->has_errors() ) {
+ /** This action is documented in wp-includes/user.php */
+ do_action( 'application_password_failed_authentication', $error );
+
+ return $error;
+ }
+
+ WP_Application_Passwords::record_application_password_usage( $user->ID, $item['uuid'] );
+
+ /**
+ * Fires after an application password was used for authentication.
+ *
+ * @since 5.6.0
+ *
+ * @param WP_User $user The user who was authenticated.
+ * @param array $item The application password used.
+ */
+ do_action( 'application_password_did_authenticate', $user, $item );
+
+ return $user;
+ }
+
+ $error = new WP_Error(
+ 'incorrect_password',
+ __( 'The provided password is an invalid application password.' )
+ );
+
+ /** This action is documented in wp-includes/user.php */
+ do_action( 'application_password_failed_authentication', $error );
+
+ return $error;
+}
+
+/**
+ * Validates the application password credentials passed via Basic Authentication.
+ *
+ * @since 5.6.0
+ *
+ * @param int|false $input_user User ID if one has been determined, false otherwise.
+ * @return int|false The authenticated user ID if successful, false otherwise.
+ */
+function wp_validate_application_password( $input_user ) {
+ // Don't authenticate twice.
+ if ( ! empty( $input_user ) ) {
+ return $input_user;
+ }
+
+ if ( ! wp_is_application_passwords_available() ) {
+ return $input_user;
+ }
+
+ // Both $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] must be set in order to attempt authentication.
+ if ( ! isset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
+ return $input_user;
+ }
+
+ $authenticated = wp_authenticate_application_password( null, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] );
+
+ if ( $authenticated instanceof WP_User ) {
+ return $authenticated->ID;
+ }
+
+ // If it wasn't a user what got returned, just pass on what we had received originally.
+ return $input_user;
+}
+
+/**
* For Multisite blogs, check if the authenticated user has been marked as a
* spammer, or if the user's primary blog has been marked as spam.
*
@@ -335,8 +519,8 @@
*
* @since 3.9.0
*
- * @param int|bool $user_id The user ID (or false) as received from
- * the `determine_current_user` filter.
+ * @param int|false $user_id The user ID (or false) as received from
+ * the `determine_current_user` filter.
* @return int|false User ID if validated, false otherwise. If a user ID from
* an earlier filter callback is received, that value is returned.
*/
@@ -567,7 +751,7 @@
*
* @see WP_User_Query
*
- * @param array $args Optional. Arguments to retrieve users. See WP_User_Query::prepare_query().
+ * @param array $args Optional. Arguments to retrieve users. See WP_User_Query::prepare_query()
* for more information on accepted arguments.
* @return array List of users.
*/
@@ -592,8 +776,8 @@
* @param int $user_id User ID
* @param bool $all Whether to retrieve all sites, or only sites that are not
* marked as deleted, archived, or spam.
- * @return array A list of the user's sites. An empty array if the user doesn't exist
- * or belongs to no sites.
+ * @return object[] A list of the user's sites. An empty array if the user doesn't exist
+ * or belongs to no sites.
*/
function get_blogs_of_user( $user_id, $all = false ) {
global $wpdb;
@@ -613,10 +797,10 @@
*
* @since 4.6.0
*
- * @param null|array $sites An array of site objects of which the user is a member.
- * @param int $user_id User ID.
- * @param bool $all Whether the returned array should contain all sites, including
- * those marked 'deleted', 'archived', or 'spam'. Default false.
+ * @param null|object[] $sites An array of site objects of which the user is a member.
+ * @param int $user_id User ID.
+ * @param bool $all Whether the returned array should contain all sites, including
+ * those marked 'deleted', 'archived', or 'spam'. Default false.
*/
$sites = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all );
@@ -705,10 +889,10 @@
*
* @since MU (3.0.0)
*
- * @param array $sites An array of site objects belonging to the user.
- * @param int $user_id User ID.
- * @param bool $all Whether the returned sites array should contain all sites, including
- * those marked 'deleted', 'archived', or 'spam'. Default false.
+ * @param object[] $sites An array of site objects belonging to the user.
+ * @param int $user_id User ID.
+ * @param bool $all Whether the returned sites array should contain all sites, including
+ * those marked 'deleted', 'archived', or 'spam'. Default false.
*/
return apply_filters( 'get_blogs_of_user', $sites, $user_id, $all );
}
@@ -828,10 +1012,12 @@
* @param string $key Optional. The meta key to retrieve. By default,
* returns data for all keys.
* @param bool $single Optional. Whether to return a single value.
- * This parameter has no effect if $key is not specified.
+ * This parameter has no effect if `$key` is not specified.
* Default false.
- * @return mixed An array if $single is false. The value of meta data field
- * if $single is true. False for an invalid $user_id.
+ * @return mixed An array of values if `$single` is false.
+ * The value of meta data field if `$single` is true.
+ * False for an invalid `$user_id` (non-numeric, zero, or negative value).
+ * An empty string if a valid but non-existing user ID is passed.
*/
function get_user_meta( $user_id, $key = '', $single = false ) {
return get_metadata( 'user', $user_id, $key, $single );
@@ -896,8 +1082,9 @@
}
/**
- * Filter the user count before queries are run. Return a non-null value to cause count_users()
- * to return early.
+ * Filters the user count before queries are run.
+ *
+ * Return a non-null value to cause count_users() to return early.
*
* @since 5.1.0
*
@@ -1078,9 +1265,9 @@
* @type string $order Whether to order users in ascending or descending
* order. Accepts 'ASC' (ascending) or 'DESC' (descending).
* Default 'ASC'.
- * @type array|string $include Array or comma-separated list of user IDs to include.
+ * @type int[]|string $include Array or comma-separated list of user IDs to include.
* Default empty.
- * @type array|string $exclude Array or comma-separated list of user IDs to exclude.
+ * @type int[]|string $exclude Array or comma-separated list of user IDs to exclude.
* Default empty.
* @type bool|int $multi Whether to skip the ID attribute on the 'select' element.
* Accepts 1|true or 0|false. Default 0|false.
@@ -1103,9 +1290,9 @@
* @type string|array $role An array or a comma-separated list of role names that users must
* match to be included in results. Note that this is an inclusive
* list: users must match *each* role. Default empty.
- * @type array $role__in An array of role names. Matched users must have at least one of
+ * @type string[] $role__in An array of role names. Matched users must have at least one of
* these roles. Default empty array.
- * @type array $role__not_in An array of role names to exclude. Users matching one or more of
+ * @type string[] $role__not_in An array of role names to exclude. Users matching one or more of
* these roles will not be included in results. Default empty array.
* }
* @return string HTML dropdown list of users.
@@ -1190,6 +1377,7 @@
if ( $parsed_args['include_selected'] && ( $parsed_args['selected'] > 0 ) ) {
$found_selected = false;
$parsed_args['selected'] = (int) $parsed_args['selected'];
+
foreach ( (array) $users as $user ) {
$user->ID = (int) $user->ID;
if ( $user->ID === $parsed_args['selected'] ) {
@@ -1198,7 +1386,10 @@
}
if ( ! $found_selected ) {
- $users[] = get_userdata( $parsed_args['selected'] );
+ $selected_user = get_userdata( $parsed_args['selected'] );
+ if ( $selected_user ) {
+ $users[] = $selected_user;
+ }
}
}
@@ -1343,6 +1534,12 @@
} elseif ( 'js' === $context ) {
$value = esc_js( $value );
}
+
+ // Restore the type for integer fields after esc_attr().
+ if ( in_array( $field, $int_fields, true ) ) {
+ $value = (int) $value;
+ }
+
return $value;
}
@@ -1351,8 +1548,8 @@
*
* @since 3.0.0
*
- * @param WP_User $user User object to be cached
- * @return bool|null Returns false on failure.
+ * @param object|WP_User $user User object or database row to be cached
+ * @return void|false Void on success, false on failure.
*/
function update_user_caches( $user ) {
if ( $user instanceof WP_User ) {
@@ -1374,10 +1571,15 @@
*
* @since 3.0.0
* @since 4.4.0 'clean_user_cache' action was added.
+ * @since 5.8.0 Refreshes the global user instance if cleaning the user cache for the current user.
+ *
+ * @global WP_User $current_user The current user object which holds the user data.
*
* @param WP_User|int $user User object or ID to be cleaned from the cache
*/
function clean_user_cache( $user ) {
+ global $current_user;
+
if ( is_numeric( $user ) ) {
$user = new WP_User( $user );
}
@@ -1400,6 +1602,13 @@
* @param WP_User $user User object.
*/
do_action( 'clean_user_cache', $user->ID, $user );
+
+ // Refresh the global user instance if the cleaning current user.
+ if ( get_current_user_id() === (int) $user->ID ) {
+ $user_id = (int) $user->ID;
+ $current_user = null;
+ wp_set_current_user( $user_id, '' );
+ }
}
/**
@@ -1411,8 +1620,8 @@
*
* @since 2.0.0
*
- * @param string $username Username.
- * @return int|false The user's ID on success, and false on failure.
+ * @param string $username The username to check for existence.
+ * @return int|false The user ID on success, false on failure.
*/
function username_exists( $username ) {
$user = get_user_by( 'login', $username );
@@ -1423,12 +1632,13 @@
}
/**
- * Filters whether the given username exists or not.
+ * Filters whether the given username exists.
*
* @since 4.9.0
*
- * @param int|false $user_id The user's ID on success, and false on failure.
- * @param string $username Username to check.
+ * @param int|false $user_id The user ID associated with the username,
+ * or false if the username does not exist.
+ * @param string $username The username to check for existence.
*/
return apply_filters( 'username_exists', $user_id, $username );
}
@@ -1442,32 +1652,44 @@
*
* @since 2.1.0
*
- * @param string $email Email.
- * @return int|false The user's ID on success, and false on failure.
+ * @param string $email The email to check for existence.
+ * @return int|false The user ID on success, false on failure.
*/
function email_exists( $email ) {
$user = get_user_by( 'email', $email );
if ( $user ) {
- return $user->ID;
+ $user_id = $user->ID;
+ } else {
+ $user_id = false;
}
- return false;
+
+ /**
+ * Filters whether the given email exists.
+ *
+ * @since 5.6.0
+ *
+ * @param int|false $user_id The user ID associated with the email,
+ * or false if the email does not exist.
+ * @param string $email The email to check for existence.
+ */
+ return apply_filters( 'email_exists', $user_id, $email );
}
/**
* Checks whether a username is valid.
*
* @since 2.0.1
- * @since 4.4.0 Empty sanitized usernames are now considered invalid
+ * @since 4.4.0 Empty sanitized usernames are now considered invalid.
*
* @param string $username Username.
- * @return bool Whether username given is valid
+ * @return bool Whether username given is valid.
*/
function validate_username( $username ) {
$sanitized = sanitize_user( $username, true );
$valid = ( $sanitized == $username && ! empty( $sanitized ) );
/**
- * Filters whether the provided username is valid or not.
+ * Filters whether the provided username is valid.
*
* @since 2.0.1
*
@@ -1658,7 +1880,7 @@
/*
* If there is no update, just check for `email_exists`. If there is an update,
- * check if current email and new email are the same, or not, and check `email_exists`
+ * check if current email and new email are the same, and check `email_exists`
* accordingly.
*/
if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
@@ -1791,9 +2013,10 @@
/**
* Filters user data before the record is created or updated.
*
- * It only includes data in the wp_users table wp_user, not any user metadata.
+ * It only includes data in the users table, not any user metadata.
*
* @since 4.9.0
+ * @since 5.8.0 The $userdata parameter was added.
*
* @param array $data {
* Values and keys for the user.
@@ -1807,10 +2030,11 @@
* @type string $user_registered MySQL timestamp describing the moment when the user registered. Defaults to
* the current UTC timestamp.
* }
- * @param bool $update Whether the user is being updated rather than created.
- * @param int|null $id ID of the user to be updated, or NULL if the user is being created.
+ * @param bool $update Whether the user is being updated rather than created.
+ * @param int|null $id ID of the user to be updated, or NULL if the user is being created.
+ * @param array $userdata The raw array of data passed to wp_insert_user().
*/
- $data = apply_filters( 'wp_pre_insert_user_data', $data, $update, $update ? (int) $ID : null );
+ $data = apply_filters( 'wp_pre_insert_user_data', $data, $update, ( $update ? (int) $ID : null ), $userdata );
if ( empty( $data ) || ! is_array( $data ) ) {
return new WP_Error( 'empty_data', __( 'Not enough data to create this user.' ) );
@@ -1836,6 +2060,7 @@
* Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
*
* @since 4.4.0
+ * @since 5.8.0 The $userdata parameter was added.
*
* @param array $meta {
* Default meta values and keys for the user.
@@ -1854,10 +2079,11 @@
* Default 'true'.
* @type string $locale User's locale. Default empty.
* }
- * @param WP_User $user User object.
- * @param bool $update Whether the user is being updated rather than created.
+ * @param WP_User $user User object.
+ * @param bool $update Whether the user is being updated rather than created.
+ * @param array $userdata The raw array of data passed to wp_insert_user().
*/
- $meta = apply_filters( 'insert_user_meta', $meta, $user, $update );
+ $meta = apply_filters( 'insert_user_meta', $meta, $user, $update, $userdata );
// Update user meta.
foreach ( $meta as $key => $value ) {
@@ -1883,11 +2109,13 @@
* Fires immediately after an existing user is updated.
*
* @since 2.0.0
+ * @since 5.8.0 The $userdata parameter was added.
*
* @param int $user_id User ID.
* @param WP_User $old_user_data Object containing user's data prior to update.
+ * @param array $userdata The raw array of data passed to wp_insert_user().
*/
- do_action( 'profile_update', $user_id, $old_user_data );
+ do_action( 'profile_update', $user_id, $old_user_data, $userdata );
if ( isset( $userdata['spam'] ) && $userdata['spam'] != $old_user_data->spam ) {
if ( 1 == $userdata['spam'] ) {
@@ -1915,10 +2143,12 @@
* Fires immediately after a new user is registered.
*
* @since 1.5.0
+ * @since 5.8.0 The $userdata parameter was added.
*
- * @param int $user_id User ID.
+ * @param int $user_id User ID.
+ * @param array $userdata The raw array of data passed to wp_insert_user().
*/
- do_action( 'user_register', $user_id );
+ do_action( 'user_register', $user_id, $userdata );
}
return $user_id;
@@ -2048,19 +2278,19 @@
* @since 4.3.0
*
* @param array $pass_change_email {
- * Used to build wp_mail().
+ * Used to build wp_mail().
*
- * @type string $to The intended recipients. Add emails in a comma separated string.
- * @type string $subject The subject of the email.
- * @type string $message The content of the email.
- * The following strings have a special meaning and will get replaced dynamically:
- * - ###USERNAME### The current user's username.
- * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
- * - ###EMAIL### The user's email address.
- * - ###SITENAME### The name of the site.
- * - ###SITEURL### The URL to the site.
- * @type string $headers Headers. Add headers in a newline (\r\n) separated string.
- * }
+ * @type string $to The intended recipients. Add emails in a comma separated string.
+ * @type string $subject The subject of the email.
+ * @type string $message The content of the email.
+ * The following strings have a special meaning and will get replaced dynamically:
+ * - ###USERNAME### The current user's username.
+ * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
+ * - ###EMAIL### The user's email address.
+ * - ###SITENAME### The name of the site.
+ * - ###SITEURL### The URL to the site.
+ * @type string $headers Headers. Add headers in a newline (\r\n) separated string.
+ * }
* @param array $user The original user array.
* @param array $userdata The updated user array.
*/
@@ -2106,20 +2336,20 @@
* @since 4.3.0
*
* @param array $email_change_email {
- * Used to build wp_mail().
+ * Used to build wp_mail().
*
- * @type string $to The intended recipients.
- * @type string $subject The subject of the email.
- * @type string $message The content of the email.
- * The following strings have a special meaning and will get replaced dynamically:
- * - ###USERNAME### The current user's username.
- * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
- * - ###NEW_EMAIL### The new email address.
- * - ###EMAIL### The old email address.
- * - ###SITENAME### The name of the site.
- * - ###SITEURL### The URL to the site.
- * @type string $headers Headers.
- * }
+ * @type string $to The intended recipients.
+ * @type string $subject The subject of the email.
+ * @type string $message The content of the email.
+ * The following strings have a special meaning and will get replaced dynamically:
+ * - ###USERNAME### The current user's username.
+ * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
+ * - ###NEW_EMAIL### The new email address.
+ * - ###EMAIL### The old email address.
+ * - ###SITENAME### The name of the site.
+ * - ###SITEURL### The URL to the site.
+ * @type string $headers Headers.
+ * }
* @param array $user The original user array.
* @param array $userdata The updated user array.
*/
@@ -2458,6 +2688,184 @@
}
/**
+ * Handles sending a password retrieval email to a user.
+ *
+ * @since 2.5.0
+ * @since 5.7.0 Added `$user_login` parameter.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @global PasswordHash $wp_hasher Portable PHP password hashing framework.
+ *
+ * @param string $user_login Optional. Username to send a password retrieval email for.
+ * Defaults to `$_POST['user_login']` if not set.
+ * @return true|WP_Error True when finished, WP_Error object on error.
+ */
+function retrieve_password( $user_login = null ) {
+ $errors = new WP_Error();
+ $user_data = false;
+
+ // Use the passed $user_login if available, otherwise use $_POST['user_login'].
+ if ( ! $user_login && ! empty( $_POST['user_login'] ) ) {
+ $user_login = $_POST['user_login'];
+ }
+
+ if ( empty( $user_login ) ) {
+ $errors->add( 'empty_username', __( '<strong>Error</strong>: Please enter a username or email address.' ) );
+ } elseif ( strpos( $user_login, '@' ) ) {
+ $user_data = get_user_by( 'email', trim( wp_unslash( $user_login ) ) );
+ if ( empty( $user_data ) ) {
+ $errors->add( 'invalid_email', __( '<strong>Error</strong>: There is no account with that username or email address.' ) );
+ }
+ } else {
+ $user_data = get_user_by( 'login', trim( wp_unslash( $user_login ) ) );
+ }
+
+ /**
+ * Filters the user data during a password reset request.
+ *
+ * Allows, for example, custom validation using data other than username or email address.
+ *
+ * @since 5.7.0
+ *
+ * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
+ * @param WP_Error $errors A WP_Error object containing any errors generated
+ * by using invalid credentials.
+ */
+ $user_data = apply_filters( 'lostpassword_user_data', $user_data, $errors );
+
+ /**
+ * Fires before errors are returned from a password reset request.
+ *
+ * @since 2.1.0
+ * @since 4.4.0 Added the `$errors` parameter.
+ * @since 5.4.0 Added the `$user_data` parameter.
+ *
+ * @param WP_Error $errors A WP_Error object containing any errors generated
+ * by using invalid credentials.
+ * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
+ */
+ do_action( 'lostpassword_post', $errors, $user_data );
+
+ /**
+ * Filters the errors encountered on a password reset request.
+ *
+ * The filtered WP_Error object may, for example, contain errors for an invalid
+ * username or email address. A WP_Error object should always be returned,
+ * but may or may not contain errors.
+ *
+ * If any errors are present in $errors, this will abort the password reset request.
+ *
+ * @since 5.5.0
+ *
+ * @param WP_Error $errors A WP_Error object containing any errors generated
+ * by using invalid credentials.
+ * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
+ */
+ $errors = apply_filters( 'lostpassword_errors', $errors, $user_data );
+
+ if ( $errors->has_errors() ) {
+ return $errors;
+ }
+
+ if ( ! $user_data ) {
+ $errors->add( 'invalidcombo', __( '<strong>Error</strong>: There is no account with that username or email address.' ) );
+ return $errors;
+ }
+
+ // Redefining user_login ensures we return the right case in the email.
+ $user_login = $user_data->user_login;
+ $user_email = $user_data->user_email;
+ $key = get_password_reset_key( $user_data );
+
+ if ( is_wp_error( $key ) ) {
+ return $key;
+ }
+
+ // Localize password reset message content for user.
+ $locale = get_user_locale( $user_data );
+
+ $switched_locale = switch_to_locale( $locale );
+
+ if ( is_multisite() ) {
+ $site_name = get_network()->site_name;
+ } else {
+ /*
+ * The blogname option is escaped with esc_html on the way into the database
+ * in sanitize_option. We want to reverse this for the plain text arena of emails.
+ */
+ $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
+ }
+
+ $message = __( 'Someone has requested a password reset for the following account:' ) . "\r\n\r\n";
+ /* translators: %s: Site name. */
+ $message .= sprintf( __( 'Site Name: %s' ), $site_name ) . "\r\n\r\n";
+ /* translators: %s: User login. */
+ $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n";
+ $message .= __( 'If this was a mistake, ignore this email and nothing will happen.' ) . "\r\n\r\n";
+ $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n";
+ $message .= network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . '&wp_lang=' . $locale . "\r\n\r\n";
+
+ if ( ! is_user_logged_in() ) {
+ $requester_ip = $_SERVER['REMOTE_ADDR'];
+ if ( $requester_ip ) {
+ $message .= sprintf(
+ /* translators: %s: IP address of password reset requester. */
+ __( 'This password reset request originated from the IP address %s.' ),
+ $requester_ip
+ ) . "\r\n";
+ }
+ }
+
+ /* translators: Password reset notification email subject. %s: Site title. */
+ $title = sprintf( __( '[%s] Password Reset' ), $site_name );
+
+ /**
+ * Filters the subject of the password reset email.
+ *
+ * @since 2.8.0
+ * @since 4.4.0 Added the `$user_login` and `$user_data` parameters.
+ *
+ * @param string $title Email subject.
+ * @param string $user_login The username for the user.
+ * @param WP_User $user_data WP_User object.
+ */
+ $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data );
+
+ /**
+ * Filters the message body of the password reset mail.
+ *
+ * If the filtered message is empty, the password reset email will not be sent.
+ *
+ * @since 2.8.0
+ * @since 4.1.0 Added `$user_login` and `$user_data` parameters.
+ *
+ * @param string $message Email message.
+ * @param string $key The activation key.
+ * @param string $user_login The username for the user.
+ * @param WP_User $user_data WP_User object.
+ */
+ $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data );
+
+ if ( $switched_locale ) {
+ restore_previous_locale();
+ }
+
+ if ( $message && ! wp_mail( $user_email, wp_specialchars_decode( $title ), $message ) ) {
+ $errors->add(
+ 'retrieve_password_email_failure',
+ sprintf(
+ /* translators: %s: Documentation URL. */
+ __( '<strong>Error</strong>: The email could not be sent. Your site may not be correctly configured to send emails. <a href="%s">Get support for resetting your password</a>.' ),
+ esc_url( __( 'https://wordpress.org/support/article/resetting-your-password/' ) )
+ )
+ );
+ return $errors;
+ }
+
+ return true;
+}
+
+/**
* Handles resetting the user's password.
*
* @since 2.5.0
@@ -2477,7 +2885,7 @@
do_action( 'password_reset', $user, $new_pass );
wp_set_password( $new_pass, $user->ID );
- update_user_option( $user->ID, 'default_password_nag', false, true );
+ update_user_meta( $user->ID, 'default_password_nag', false );
/**
* Fires after the user's password is reset.
@@ -2588,7 +2996,7 @@
return $errors;
}
- update_user_option( $user_id, 'default_password_nag', true, true ); // Set up the password change nag.
+ update_user_meta( $user_id, 'default_password_nag', true ); // Set up the password change nag.
/**
* Fires after a new user registration has been recorded.
@@ -2780,7 +3188,7 @@
*
* @since 3.9.0
*
- * @param int|bool $user_id User ID if one has been determined, false otherwise.
+ * @param int|false $user_id User ID if one has been determined, false otherwise.
*/
$user_id = apply_filters( 'determine_current_user', false );
if ( ! $user_id ) {
@@ -2925,7 +3333,7 @@
}
/**
- * Get all user privacy request types.
+ * Get all personal data request types.
*
* @since 4.9.6
* @access private
@@ -3238,59 +3646,6 @@
'admin_email' => $admin_email,
);
- /* translators: Do not translate SITENAME, USER_EMAIL, DESCRIPTION, MANAGE_URL, SITEURL; those are placeholders. */
- $email_text = __(
- 'Howdy,
-
-A user data privacy request has been confirmed on ###SITENAME###:
-
-User: ###USER_EMAIL###
-Request: ###DESCRIPTION###
-
-You can view and manage these data privacy requests here:
-
-###MANAGE_URL###
-
-Regards,
-All at ###SITENAME###
-###SITEURL###'
- );
-
- /**
- * Filters the body of the user request confirmation email.
- *
- * The email is sent to an administrator when an user request is confirmed.
- * The following strings have a special meaning and will get replaced dynamically:
- *
- * ###SITENAME### The name of the site.
- * ###USER_EMAIL### The user email for the request.
- * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for.
- * ###MANAGE_URL### The URL to manage requests.
- * ###SITEURL### The URL to the site.
- *
- * @since 4.9.6
- *
- * @param string $email_text Text in the email.
- * @param array $email_data {
- * Data relating to the account action email.
- *
- * @type WP_User_Request $request User request object.
- * @type string $user_email The email address confirming a request
- * @type string $description Description of the action being performed so the user knows what the email is for.
- * @type string $manage_url The link to click manage privacy requests of this type.
- * @type string $sitename The site name sending the mail.
- * @type string $siteurl The site URL sending the mail.
- * @type string $admin_email The administrator email receiving the mail.
- * }
- */
- $content = apply_filters( 'user_confirmed_action_email_content', $email_text, $email_data );
-
- $content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
- $content = str_replace( '###USER_EMAIL###', $email_data['user_email'], $content );
- $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
- $content = str_replace( '###MANAGE_URL###', esc_url_raw( $email_data['manage_url'] ), $content );
- $content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
-
$subject = sprintf(
/* translators: Privacy data request confirmed notification email subject. 1: Site title, 2: Name of the confirmed action. */
__( '[%1$s] Action Confirmed: %2$s' ),
@@ -3319,6 +3674,103 @@
*/
$subject = apply_filters( 'user_request_confirmed_email_subject', $subject, $email_data['sitename'], $email_data );
+ /* translators: Do not translate SITENAME, USER_EMAIL, DESCRIPTION, MANAGE_URL, SITEURL; those are placeholders. */
+ $content = __(
+ 'Howdy,
+
+A user data privacy request has been confirmed on ###SITENAME###:
+
+User: ###USER_EMAIL###
+Request: ###DESCRIPTION###
+
+You can view and manage these data privacy requests here:
+
+###MANAGE_URL###
+
+Regards,
+All at ###SITENAME###
+###SITEURL###'
+ );
+
+ /**
+ * Filters the body of the user request confirmation email.
+ *
+ * The email is sent to an administrator when a user request is confirmed.
+ *
+ * The following strings have a special meaning and will get replaced dynamically:
+ *
+ * ###SITENAME### The name of the site.
+ * ###USER_EMAIL### The user email for the request.
+ * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for.
+ * ###MANAGE_URL### The URL to manage requests.
+ * ###SITEURL### The URL to the site.
+ *
+ * @since 4.9.6
+ * @deprecated 5.8.0 Use {@see 'user_request_confirmed_email_content'} instead.
+ * For user erasure fulfillment email content
+ * use {@see 'user_erasure_fulfillment_email_content'} instead.
+ *
+ * @param string $content The email content.
+ * @param array $email_data {
+ * Data relating to the account action email.
+ *
+ * @type WP_User_Request $request User request object.
+ * @type string $user_email The email address confirming a request
+ * @type string $description Description of the action being performed
+ * so the user knows what the email is for.
+ * @type string $manage_url The link to click manage privacy requests of this type.
+ * @type string $sitename The site name sending the mail.
+ * @type string $siteurl The site URL sending the mail.
+ * @type string $admin_email The administrator email receiving the mail.
+ * }
+ */
+ $content = apply_filters_deprecated(
+ 'user_confirmed_action_email_content',
+ array( $content, $email_data ),
+ '5.8.0',
+ sprintf(
+ /* translators: 1 & 2: Deprecation replacement options. */
+ __( '%1$s or %2$s' ),
+ 'user_request_confirmed_email_content',
+ 'user_erasure_fulfillment_email_content'
+ )
+ );
+
+ /**
+ * Filters the body of the user request confirmation email.
+ *
+ * The email is sent to an administrator when a user request is confirmed.
+ * The following strings have a special meaning and will get replaced dynamically:
+ *
+ * ###SITENAME### The name of the site.
+ * ###USER_EMAIL### The user email for the request.
+ * ###DESCRIPTION### Description of the action being performed so the user knows what the email is for.
+ * ###MANAGE_URL### The URL to manage requests.
+ * ###SITEURL### The URL to the site.
+ *
+ * @since 5.8.0
+ *
+ * @param string $content The email content.
+ * @param array $email_data {
+ * Data relating to the account action email.
+ *
+ * @type WP_User_Request $request User request object.
+ * @type string $user_email The email address confirming a request
+ * @type string $description Description of the action being performed so the user knows what the email is for.
+ * @type string $manage_url The link to click manage privacy requests of this type.
+ * @type string $sitename The site name sending the mail.
+ * @type string $siteurl The site URL sending the mail.
+ * @type string $admin_email The administrator email receiving the mail.
+ * }
+ */
+ $content = apply_filters( 'user_request_confirmed_email_content', $content, $email_data );
+
+ $content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
+ $content = str_replace( '###USER_EMAIL###', $email_data['user_email'], $content );
+ $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
+ $content = str_replace( '###MANAGE_URL###', esc_url_raw( $email_data['manage_url'] ), $content );
+ $content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
+
$headers = '';
/**
@@ -3410,6 +3862,7 @@
* Filters the subject of the email sent when an erasure request is completed.
*
* @since 4.9.8
+ * @deprecated 5.8.0 Use {@see 'user_erasure_fulfillment_email_subject'} instead.
*
* @param string $subject The email subject.
* @param string $sitename The name of the site.
@@ -3425,12 +3878,37 @@
* @type string $siteurl The site URL sending the mail.
* }
*/
- $subject = apply_filters( 'user_erasure_complete_email_subject', $subject, $email_data['sitename'], $email_data );
-
- if ( empty( $email_data['privacy_policy_url'] ) ) {
- /* translators: Do not translate SITENAME, SITEURL; those are placeholders. */
- $email_text = __(
- 'Howdy,
+ $subject = apply_filters_deprecated(
+ 'user_erasure_complete_email_subject',
+ array( $subject, $email_data['sitename'], $email_data ),
+ '5.8.0',
+ 'user_erasure_fulfillment_email_subject'
+ );
+
+ /**
+ * Filters the subject of the email sent when an erasure request is completed.
+ *
+ * @since 5.8.0
+ *
+ * @param string $subject The email subject.
+ * @param string $sitename The name of the site.
+ * @param array $email_data {
+ * Data relating to the account action email.
+ *
+ * @type WP_User_Request $request User request object.
+ * @type string $message_recipient The address that the email will be sent to. Defaults
+ * to the value of `$request->email`, but can be changed
+ * by the `user_erasure_fulfillment_email_to` filter.
+ * @type string $privacy_policy_url Privacy policy URL.
+ * @type string $sitename The site name sending the mail.
+ * @type string $siteurl The site URL sending the mail.
+ * }
+ */
+ $subject = apply_filters( 'user_erasure_fulfillment_email_subject', $subject, $email_data['sitename'], $email_data );
+
+ /* translators: Do not translate SITENAME, SITEURL; those are placeholders. */
+ $content = __(
+ 'Howdy,
Your request to erase your personal data on ###SITENAME### has been completed.
@@ -3439,10 +3917,11 @@
Regards,
All at ###SITENAME###
###SITEURL###'
- );
- } else {
+ );
+
+ if ( ! empty( $email_data['privacy_policy_url'] ) ) {
/* translators: Do not translate SITENAME, SITEURL, PRIVACY_POLICY_URL; those are placeholders. */
- $email_text = __(
+ $content = __(
'Howdy,
Your request to erase your personal data on ###SITENAME### has been completed.
@@ -3460,7 +3939,7 @@
/**
* Filters the body of the data erasure fulfillment notification.
*
- * The email is sent to a user when a their data erasure request is fulfilled
+ * The email is sent to a user when their data erasure request is fulfilled
* by an administrator.
*
* The following strings have a special meaning and will get replaced dynamically:
@@ -3470,8 +3949,11 @@
* ###SITEURL### The URL to the site.
*
* @since 4.9.6
+ * @deprecated 5.8.0 Use {@see 'user_erasure_fulfillment_email_content'} instead.
+ * For user request confirmation email content
+ * use {@see 'user_request_confirmed_email_content'} instead.
*
- * @param string $email_text Text in the email.
+ * @param string $content The email content.
* @param array $email_data {
* Data relating to the account action email.
*
@@ -3484,7 +3966,46 @@
* @type string $siteurl The site URL sending the mail.
* }
*/
- $content = apply_filters( 'user_confirmed_action_email_content', $email_text, $email_data );
+ $content = apply_filters_deprecated(
+ 'user_confirmed_action_email_content',
+ array( $content, $email_data ),
+ '5.8.0',
+ sprintf(
+ /* translators: 1 & 2: Deprecation replacement options. */
+ __( '%1$s or %2$s' ),
+ 'user_erasure_fulfillment_email_content',
+ 'user_request_confirmed_email_content'
+ )
+ );
+
+ /**
+ * Filters the body of the data erasure fulfillment notification.
+ *
+ * The email is sent to a user when their data erasure request is fulfilled
+ * by an administrator.
+ *
+ * The following strings have a special meaning and will get replaced dynamically:
+ *
+ * ###SITENAME### The name of the site.
+ * ###PRIVACY_POLICY_URL### Privacy policy page URL.
+ * ###SITEURL### The URL to the site.
+ *
+ * @since 5.8.0
+ *
+ * @param string $content The email content.
+ * @param array $email_data {
+ * Data relating to the account action email.
+ *
+ * @type WP_User_Request $request User request object.
+ * @type string $message_recipient The address that the email will be sent to. Defaults
+ * to the value of `$request->email`, but can be changed
+ * by the `user_erasure_fulfillment_email_to` filter.
+ * @type string $privacy_policy_url Privacy policy URL.
+ * @type string $sitename The site name sending the mail.
+ * @type string $siteurl The site URL sending the mail.
+ * }
+ */
+ $content = apply_filters( 'user_erasure_fulfillment_email_content', $content, $email_data );
$content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
$content = str_replace( '###PRIVACY_POLICY_URL###', $email_data['privacy_policy_url'], $content );
@@ -3496,6 +4017,7 @@
* Filters the headers of the data erasure fulfillment notification.
*
* @since 5.4.0
+ * @deprecated 5.8.0 Use {@see 'user_erasure_fulfillment_email_headers'} instead.
*
* @param string|array $headers The email headers.
* @param string $subject The email subject.
@@ -3513,7 +4035,35 @@
* @type string $siteurl The site URL sending the mail.
* }
*/
- $headers = apply_filters( 'user_erasure_complete_email_headers', $headers, $subject, $content, $request_id, $email_data );
+ $headers = apply_filters_deprecated(
+ 'user_erasure_complete_email_headers',
+ array( $headers, $subject, $content, $request_id, $email_data ),
+ '5.8.0',
+ 'user_erasure_fulfillment_email_headers'
+ );
+
+ /**
+ * Filters the headers of the data erasure fulfillment notification.
+ *
+ * @since 5.8.0
+ *
+ * @param string|array $headers The email headers.
+ * @param string $subject The email subject.
+ * @param string $content The email content.
+ * @param int $request_id The request ID.
+ * @param array $email_data {
+ * Data relating to the account action email.
+ *
+ * @type WP_User_Request $request User request object.
+ * @type string $message_recipient The address that the email will be sent to. Defaults
+ * to the value of `$request->email`, but can be changed
+ * by the `user_erasure_fulfillment_email_to` filter.
+ * @type string $privacy_policy_url Privacy policy URL.
+ * @type string $sitename The site name sending the mail.
+ * @type string $siteurl The site URL sending the mail.
+ * }
+ */
+ $headers = apply_filters( 'user_erasure_fulfillment_email_headers', $headers, $subject, $content, $request_id, $email_data );
$email_sent = wp_mail( $user_email, $subject, $content, $headers );
@@ -3571,13 +4121,17 @@
* users on the site, or guests without a user account.
*
* @since 4.9.6
+ * @since 5.7.0 Added the `$status` parameter.
*
- * @param string $email_address User email address. This can be the address of a registered or non-registered user.
- * @param string $action_name Name of the action that is being confirmed. Required.
- * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed.
- * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure.
+ * @param string $email_address User email address. This can be the address of a registered
+ * or non-registered user.
+ * @param string $action_name Name of the action that is being confirmed. Required.
+ * @param array $request_data Misc data you want to send with the verification request and pass
+ * to the actions once the request is confirmed.
+ * @param string $status Optional request status (pending or confirmed). Default 'pending'.
+ * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure.
*/
-function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array() ) {
+function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array(), $status = 'pending' ) {
$email_address = sanitize_email( $email_address );
$action_name = sanitize_key( $action_name );
@@ -3585,10 +4139,14 @@
return new WP_Error( 'invalid_email', __( 'Invalid email address.' ) );
}
- if ( ! $action_name ) {
+ if ( ! in_array( $action_name, _wp_privacy_action_request_types(), true ) ) {
return new WP_Error( 'invalid_action', __( 'Invalid action name.' ) );
}
+ if ( ! in_array( $status, array( 'pending', 'confirmed' ), true ) ) {
+ return new WP_Error( 'invalid_status', __( 'Invalid request status.' ) );
+ }
+
$user = get_user_by( 'email', $email_address );
$user_id = $user && ! is_wp_error( $user ) ? $user->ID : 0;
@@ -3607,7 +4165,7 @@
);
if ( $requests_query->found_posts ) {
- return new WP_Error( 'duplicate_request', __( 'An incomplete request for this email address already exists.' ) );
+ return new WP_Error( 'duplicate_request', __( 'An incomplete personal data request for this email address already exists.' ) );
}
$request_id = wp_insert_post(
@@ -3616,7 +4174,7 @@
'post_name' => $action_name,
'post_title' => $email_address,
'post_content' => wp_json_encode( $request_data ),
- 'post_status' => 'request-pending',
+ 'post_status' => 'request-' . $status,
'post_type' => 'user_request',
'post_date' => current_time( 'mysql', false ),
'post_date_gmt' => current_time( 'mysql', true ),
@@ -3668,14 +4226,14 @@
* @since 4.9.6
*
* @param string $request_id ID of the request created via wp_create_user_request().
- * @return bool|WP_Error True on success, `WP_Error` on failure.
+ * @return true|WP_Error True on success, `WP_Error` on failure.
*/
function wp_send_user_request( $request_id ) {
$request_id = absint( $request_id );
$request = wp_get_user_request( $request_id );
if ( ! $request ) {
- return new WP_Error( 'invalid_request', __( 'Invalid user request.' ) );
+ return new WP_Error( 'invalid_request', __( 'Invalid personal data request.' ) );
}
// Localize message content for user; fallback to site default for visitors.
@@ -3703,8 +4261,31 @@
'siteurl' => home_url(),
);
+ /* translators: Confirm privacy data request notification email subject. 1: Site title, 2: Name of the action. */
+ $subject = sprintf( __( '[%1$s] Confirm Action: %2$s' ), $email_data['sitename'], $email_data['description'] );
+
+ /**
+ * Filters the subject of the email sent when an account action is attempted.
+ *
+ * @since 4.9.6
+ *
+ * @param string $subject The email subject.
+ * @param string $sitename The name of the site.
+ * @param array $email_data {
+ * Data relating to the account action email.
+ *
+ * @type WP_User_Request $request User request object.
+ * @type string $email The email address this is being sent to.
+ * @type string $description Description of the action being performed so the user knows what the email is for.
+ * @type string $confirm_url The link to click on to confirm the account action.
+ * @type string $sitename The site name sending the mail.
+ * @type string $siteurl The site URL sending the mail.
+ * }
+ */
+ $subject = apply_filters( 'user_request_action_email_subject', $subject, $email_data['sitename'], $email_data );
+
/* translators: Do not translate DESCRIPTION, CONFIRM_URL, SITENAME, SITEURL: those are placeholders. */
- $email_text = __(
+ $content = __(
'Howdy,
A request has been made to perform the following action on your account:
@@ -3734,7 +4315,7 @@
*
* @since 4.9.6
*
- * @param string $email_text Text in the email.
+ * @param string $content Text in the email.
* @param array $email_data {
* Data relating to the account action email.
*
@@ -3746,7 +4327,7 @@
* @type string $siteurl The site URL sending the mail.
* }
*/
- $content = apply_filters( 'user_request_action_email_content', $email_text, $email_data );
+ $content = apply_filters( 'user_request_action_email_content', $content, $email_data );
$content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
$content = str_replace( '###CONFIRM_URL###', esc_url_raw( $email_data['confirm_url'] ), $content );
@@ -3754,29 +4335,6 @@
$content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
$content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content );
- /* translators: Confirm privacy data request notification email subject. 1: Site title, 2: Name of the action. */
- $subject = sprintf( __( '[%1$s] Confirm Action: %2$s' ), $email_data['sitename'], $email_data['description'] );
-
- /**
- * Filters the subject of the email sent when an account action is attempted.
- *
- * @since 4.9.6
- *
- * @param string $subject The email subject.
- * @param string $sitename The name of the site.
- * @param array $email_data {
- * Data relating to the account action email.
- *
- * @type WP_User_Request $request User request object.
- * @type string $email The email address this is being sent to.
- * @type string $description Description of the action being performed so the user knows what the email is for.
- * @type string $confirm_url The link to click on to confirm the account action.
- * @type string $sitename The site name sending the mail.
- * @type string $siteurl The site URL sending the mail.
- * }
- */
- $subject = apply_filters( 'user_request_action_email_subject', $subject, $email_data['sitename'], $email_data );
-
$headers = '';
/**
@@ -3852,24 +4410,26 @@
*
* @param string $request_id ID of the request being confirmed.
* @param string $key Provided key to validate.
- * @return bool|WP_Error True on success, WP_Error on failure.
+ * @return true|WP_Error True on success, WP_Error on failure.
*/
function wp_validate_user_request_key( $request_id, $key ) {
global $wp_hasher;
- $request_id = absint( $request_id );
- $request = wp_get_user_request( $request_id );
-
- if ( ! $request ) {
- return new WP_Error( 'invalid_request', __( 'Invalid request.' ) );
+ $request_id = absint( $request_id );
+ $request = wp_get_user_request( $request_id );
+ $saved_key = $request->confirm_key;
+ $key_request_time = $request->modified_timestamp;
+
+ if ( ! $request || ! $saved_key || ! $key_request_time ) {
+ return new WP_Error( 'invalid_request', __( 'Invalid personal data request.' ) );
}
if ( ! in_array( $request->status, array( 'request-pending', 'request-failed' ), true ) ) {
- return new WP_Error( 'expired_link', __( 'This link has expired.' ) );
+ return new WP_Error( 'expired_request', __( 'This personal data request has expired.' ) );
}
if ( empty( $key ) ) {
- return new WP_Error( 'missing_key', __( 'Missing confirm key.' ) );
+ return new WP_Error( 'missing_key', __( 'The confirmation key is missing from this personal data request.' ) );
}
if ( empty( $wp_hasher ) ) {
@@ -3877,17 +4437,6 @@
$wp_hasher = new PasswordHash( 8, true );
}
- $key_request_time = $request->modified_timestamp;
- $saved_key = $request->confirm_key;
-
- if ( ! $saved_key ) {
- return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
- }
-
- if ( ! $key_request_time ) {
- return new WP_Error( 'invalid_key', __( 'Invalid action.' ) );
- }
-
/**
* Filters the expiration time of confirm keys.
*
@@ -3899,11 +4448,11 @@
$expiration_time = $key_request_time + $expiration_duration;
if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) {
- return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
+ return new WP_Error( 'invalid_key', __( 'The confirmation key is invalid for this personal data request.' ) );
}
if ( ! $expiration_time || time() > $expiration_time ) {
- return new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) );
+ return new WP_Error( 'expired_key', __( 'The confirmation key has expired for this personal data request.' ) );
}
return true;
@@ -3927,3 +4476,61 @@
return new WP_User_Request( $post );
}
+
+/**
+ * Checks if Application Passwords is globally available.
+ *
+ * By default, Application Passwords is available to all sites using SSL or to local environments.
+ * Use {@see 'wp_is_application_passwords_available'} to adjust its availability.
+ *
+ * @since 5.6.0
+ *
+ * @return bool
+ */
+function wp_is_application_passwords_available() {
+ $available = is_ssl() || 'local' === wp_get_environment_type();
+
+ /**
+ * Filters whether Application Passwords is available.
+ *
+ * @since 5.6.0
+ *
+ * @param bool $available True if available, false otherwise.
+ */
+ return apply_filters( 'wp_is_application_passwords_available', $available );
+}
+
+/**
+ * Checks if Application Passwords is available for a specific user.
+ *
+ * By default all users can use Application Passwords. Use {@see 'wp_is_application_passwords_available_for_user'}
+ * to restrict availability to certain users.
+ *
+ * @since 5.6.0
+ *
+ * @param int|WP_User $user The user to check.
+ * @return bool
+ */
+function wp_is_application_passwords_available_for_user( $user ) {
+ if ( ! wp_is_application_passwords_available() ) {
+ return false;
+ }
+
+ if ( ! is_object( $user ) ) {
+ $user = get_userdata( $user );
+ }
+
+ if ( ! $user || ! $user->exists() ) {
+ return false;
+ }
+
+ /**
+ * Filters whether Application Passwords is available for a specific user.
+ *
+ * @since 5.6.0
+ *
+ * @param bool $available True if available, false otherwise.
+ * @param WP_User $user The user to check.
+ */
+ return apply_filters( 'wp_is_application_passwords_available_for_user', true, $user );
+}