( ! empty( $update['name'] ) && $item['name'] !== $update['name'] ) {
$item['name'] = $update['name'];
$save = true;
}
if ( $save ) {
$saved = static::set_user_application_passwords( $user_id, $passwords );
if ( ! $saved ) {
return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
}
}
/**
* Fires when an application password is updated.
*
* @since 5.6.0
*
* @param int $user_id The user ID.
* @param array $item The updated app password details.
* @param array $update The information to update.
*/
do_action( 'wp_update_application_password', $user_id, $item, $update );
return true;
}
return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
}
/**
* Records that an application password has been used.
*
* @since 5.6.0
*
* @param int $user_id User ID.
* @param string $uuid The password's UUID.
* @return true|WP_Error True if the usage was recorded, a WP_Error if an error occurs.
*/
public static function record_application_password_usage( $user_id, $uuid ) {
$passwords = static::get_user_application_passwords( $user_id );
foreach ( $passwords as &$password ) {
if ( $password['uuid'] !== $uuid ) {
continue;
}
// Only record activity once a day.
if ( $password['last_used'] + DAY_IN_SECONDS > time() ) {
return true;
}
$password['last_used'] = time();
$password['last_ip'] = $_SERVER['REMOTE_ADDR'];
$saved = static::set_user_application_passwords( $user_id, $passwords );
if ( ! $saved ) {
return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
}
return true;
}
// Specified application password not found!
return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
}
/**
* Deletes an application password.
*
* @since 5.6.0
*
* @param int $user_id User ID.
* @param string $uuid The password's UUID.
* @return true|WP_Error Whether the password was successfully found and deleted, a WP_Error otherwise.
*/
public static function delete_application_password( $user_id, $uuid ) {
$passwords = static::get_user_application_passwords( $user_id );
foreach ( $passwords as $key => $item ) {
if ( $item['uuid'] === $uuid ) {
unset( $passwords[ $key ] );
$saved = static::set_user_application_passwords( $user_id, $passwords );
if ( ! $saved ) {
return new WP_Error( 'db_error', __( 'Could not delete application password.' ) );
}
/**
* Fires when an application password is deleted.
*
* @since 5.6.0
*
* @param int $user_id The user ID.
* @param array $item The data about the application password.
*/
do_action( 'wp_delete_application_password', $user_id, $item );
return true;
}
}
return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
}
/**
* Deletes all application passwords for the given user.
*
* @since 5.6.0
*
* @param int $user_id User ID.
* @return int|WP_Error The number of passwords that were deleted or a WP_Error on failure.
*/
public static function delete_all_application_passwords( $user_id ) {
$passwords = static::get_user_application_passwords( $user_id );
if ( $passwords ) {
$saved = static::set_user_application_passwords( $user_id, array() );
if ( ! $saved ) {
return new WP_Error( 'db_error', __( 'Could not delete application passwords.' ) );
}
foreach ( $passwords as $item ) {
/** This action is documented in wp-includes/class-wp-application-passwords.php */
do_action( 'wp_delete_application_password', $user_id, $item );
}
return count( $passwords );
}
return 0;
}
/**
* Sets a user's application passwords.
*
* @since 5.6.0
*
* @param int $user_id User ID.
* @param array $passwords Application passwords.
*
* @return bool
*/
protected static function set_user_application_passwords( $user_id, $passwords ) {
return update_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, $passwords );
}
/**
* Sanitizes and then splits a password into smaller chunks.
*
* @since 5.6.0
*
* @param string $raw_password The raw application password.
* @return string The chunked password.
*/
public static function chunk_password( $raw_password ) {
$raw_password = preg_replace( '/[^a-z\d]/i', '', $raw_password );
return trim( chunk_split( $raw_password, 4, ' ' ) );
}
}
e = $this->prepare_item_for_response( $item, $request );
$response->set_status( 201 );
$response->header( 'Location', $response->get_links()['self'][0]['href'] );
return $response;
}
/**
* Checks if a given request has access to update application passwords.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
*/
public function update_item_permissions_check( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
if ( ! current_user_can( 'edit_app_password', $user->ID, $request['uuid'] ) ) {
return new WP_Error(
'rest_cannot_edit_application_password',
__( 'Sorry, you are not allowed to edit this application password.' ),
array( 'status' => rest_authorization_required_code() )
);
}
return true;
}
/**
* Updates an application password.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function update_item( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
$item = $this->get_application_password( $request );
if ( is_wp_error( $item ) ) {
return $item;
}
$prepared = $this->prepare_item_for_database( $request );
if ( is_wp_error( $prepared ) ) {
return $prepared;
}
$saved = WP_Application_Passwords::update_application_password( $user->ID, $item['uuid'], wp_slash( (array) $prepared ) );
if ( is_wp_error( $saved ) ) {
return $saved;
}
$fields_update = $this->update_additional_fields_for_object( $item, $request );
if ( is_wp_error( $fields_update ) ) {
return $fields_update;
}
$item = WP_Application_Passwords::get_user_application_password( $user->ID, $item['uuid'] );
/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php */
do_action( 'rest_after_insert_application_password', $item, $request, false );
$request->set_param( 'context', 'edit' );
return $this->prepare_item_for_response( $item, $request );
}
/**
* Checks if a given request has access to delete all application passwords for a user.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
*/
public function delete_items_permissions_check( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
if ( ! current_user_can( 'delete_app_passwords', $user->ID ) ) {
return new WP_Error(
'rest_cannot_delete_application_passwords',
__( 'Sorry, you are not allowed to delete application passwords for this user.' ),
array( 'status' => rest_authorization_required_code() )
);
}
return true;
}
/**
* Deletes all application passwords for a user.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function delete_items( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
$deleted = WP_Application_Passwords::delete_all_application_passwords( $user->ID );
if ( is_wp_error( $deleted ) ) {
return $deleted;
}
return new WP_REST_Response(
array(
'deleted' => true,
'count' => $deleted,
)
);
}
/**
* Checks if a given request has access to delete a specific application password for a user.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
*/
public function delete_item_permissions_check( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
if ( ! current_user_can( 'delete_app_password', $user->ID, $request['uuid'] ) ) {
return new WP_Error(
'rest_cannot_delete_application_password',
__( 'Sorry, you are not allowed to delete this application password.' ),
array( 'status' => rest_authorization_required_code() )
);
}
return true;
}
/**
* Deletes an application password for a user.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function delete_item( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
$password = $this->get_application_password( $request );
if ( is_wp_error( $password ) ) {
return $password;
}
$request->set_param( 'context', 'edit' );
$previous = $this->prepare_item_for_response( $password, $request );
$deleted = WP_Application_Passwords::delete_application_password( $user->ID, $password['uuid'] );
if ( is_wp_error( $deleted ) ) {
return $deleted;
}
return new WP_REST_Response(
array(
'deleted' => true,
'previous' => $previous->get_data(),
)
);
}
/**
* Checks if a given request has access to get the currently used application password for a user.
*
* @since 5.7.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
*/
public function get_current_item_permissions_check( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
if ( get_current_user_id() !== $user->ID ) {
return new WP_Error(
'rest_cannot_introspect_app_password_for_non_authenticated_user',
__( 'The authenticated application password can only be introspected for the current user.' ),
array( 'status' => rest_authorization_required_code() )
);
}
return true;
}
/**
* Retrieves the application password being currently used for authentication of a user.
*
* @since 5.7.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_current_item( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
$uuid = rest_get_authenticated_app_password();
if ( ! $uuid ) {
return new WP_Error(
'rest_no_authenticated_app_password',
__( 'Cannot introspect application password.' ),
array( 'status' => 404 )
);
}
$password = WP_Application_Passwords::get_user_application_password( $user->ID, $uuid );
if ( ! $password ) {
return new WP_Error(
'rest_application_password_not_found',
__( 'Application password not found.' ),
array( 'status' => 500 )
);
}
return $this->prepare_item_for_response( $password, $request );
}
/**
* Performs a permissions check for the request.
*
* @since 5.6.0
* @deprecated 5.7.0 Use `edit_user` directly or one of the specific meta capabilities introduced in 5.7.0.
*
* @param WP_REST_Request $request
* @return true|WP_Error
*/
protected function do_permissions_check( $request ) {
_deprecated_function( __METHOD__, '5.7.0' );
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
if ( ! current_user_can( 'edit_user', $user->ID ) ) {
return new WP_Error(
'rest_cannot_manage_application_passwords',
__( 'Sorry, you are not allowed to manage application passwords for this user.' ),
array( 'status' => rest_authorization_required_code() )
);
}
return true;
}
/**
* Prepares an application password for a create or update operation.
*
* @since 5.6.0
*
* @param WP_REST_Request $request Request object.
* @return object|WP_Error The prepared item, or WP_Error object on failure.
*/
protected function prepare_item_for_database( $request ) {
$prepared = (object) array(
'name' => $request['name'],
);
if ( $request['app_id'] && ! $request['uuid'] ) {
$prepared->app_id = $request['app_id'];
}
/**
* Filters an application password before it is inserted via the REST API.
*
* @since 5.6.0
*
* @param stdClass $prepared An object representing a single application password prepared for inserting or updating the database.
* @param WP_REST_Request $request Request object.
*/
return apply_filters( 'rest_pre_insert_application_password', $prepared, $request );
}
/**
* Prepares the application password for the REST response.
*
* @since 5.6.0
*
* @param array $item WordPress representation of the item.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function prepare_item_for_response( $item, $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
$fields = $this->get_fields_for_response( $request );
$prepared = array(
'uuid' => $item['uuid'],
'app_id' => empty( $item['app_id'] ) ? '' : $item['app_id'],
'name' => $item['name'],
'created' => gmdate( 'Y-m-d\TH:i:s', $item['created'] ),
'last_used' => $item['last_used'] ? gmdate( 'Y-m-d\TH:i:s', $item['last_used'] ) : null,
'last_ip' => $item['last_ip'] ? $item['last_ip'] : null,
);
if ( isset( $item['new_password'] ) ) {
$prepared['password'] = $item['new_password'];
}
$prepared = $this->add_additional_fields_to_object( $prepared, $request );
$prepared = $this->filter_response_by_context( $prepared, $request['context'] );
$response = new WP_REST_Response( $prepared );
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
$response->add_links( $this->prepare_links( $user, $item ) );
}
/**
* Filters the REST API response for an application password.
*
* @since 5.6.0
*
* @param WP_REST_Response $response The response object.
* @param array $item The application password array.
* @param WP_REST_Request $request The request object.
*/
return apply_filters( 'rest_prepare_application_password', $response, $item, $request );
}
/**
* Prepares links for the request.
*
* @since 5.6.0
*
* @param WP_User $user The requested user.
* @param array $item The application password.
* @return array The list of links.
*/
protected function prepare_links( WP_User $user, $item ) {
return array(
'self' => array(
'href' => rest_url(
sprintf(
'%s/users/%d/application-passwords/%s',
$this->namespace,
$user->ID,
$item['uuid']
)
),
),
);
}
/**
* Gets the requested user.
*
* @since 5.6.0
*
* @param WP_REST_Request $request The request object.
* @return WP_User|WP_Error The WordPress user associated with the request, or a WP_Error if none found.
*/
protected function get_user( $request ) {
if ( ! wp_is_application_passwords_available() ) {
return new WP_Error(
'application_passwords_disabled',
__( 'Application passwords are not available.' ),
array( 'status' => 501 )
);
}
$error = new WP_Error(
'rest_user_invalid_id',
__( 'Invalid user ID.' ),
array( 'status' => 404 )
);
$id = $request['user_id'];
if ( 'me' === $id ) {
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
__( 'You are not currently logged in.' ),
array( 'status' => 401 )
);
}
$user = wp_get_current_user();
} else {
$id = (int) $id;
if ( $id <= 0 ) {
return $error;
}
$user = get_userdata( $id );
}
if ( empty( $user ) || ! $user->exists() ) {
return $error;
}
if ( is_multisite() && ! user_can( $user->ID, 'manage_sites' ) && ! is_user_member_of_blog( $user->ID ) ) {
return $error;
}
if ( ! wp_is_application_passwords_available_for_user( $user ) ) {
return new WP_Error(
'application_passwords_disabled_for_user',
__( 'Application passwords are not available for your account. Please contact the site administrator for assistance.' ),
array( 'status' => 501 )
);
}
return $user;
}
/**
* Gets the requested application password for a user.
*
* @since 5.6.0
*
* @param WP_REST_Request $request The request object.
* @return array|WP_Error The application password details if found, a WP_Error otherwise.
*/
protected function get_application_password( $request ) {
$user = $this->get_user( $request );
if ( is_wp_error( $user ) ) {
return $user;
}
$password = WP_Application_Passwords::get_user_application_password( $user->ID, $request['uuid'] );
if ( ! $password ) {
return new WP_Error(
'rest_application_password_not_found',
__( 'Application password not found.' ),
array( 'status' => 404 )
);
}
return $password;
}
/**
* Retrieves the query params for the collections.
*
* @since 5.6.0
*
* @return array Query parameters for the collection.
*/
public function get_collection_params() {
return array(
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
);
}
/**
* Retrieves the application password's schema, conforming to JSON Schema.
*
* @since 5.6.0
*
* @return array Item schema data.
*/
public function get_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}
$this->schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'application-password',
'type' => 'object',
'properties' => array(
'uuid' => array(
'description' => __( 'The unique identifier for the application password.' ),
'type' => 'string',
'format' => 'uuid',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
'app_id' => array(
'description' => __( 'A UUID provided by the application to uniquely identify it. It is recommended to use an UUID v5 with the URL or DNS namespace.' ),
'type' => 'string',
'format' => 'uuid',
'context' => array( 'view', 'edit', 'embed' ),
),
'name' => array(
'description' => __( 'The name of the application password.' ),
'type' => 'string',
'required' => true,
'context' => array( 'view', 'edit', 'embed' ),
'minLength' => 1,
'pattern' => '.*\S.*',
),
'password' => array(
'description' => __( 'The generated password. Only available after adding an application.' ),
'type' => 'string',
'context' => array( 'edit' ),
'readonly' => true,
),
'created' => array(
'description' => __( 'The GMT date the application password was created.' ),
'type' => 'string',
'format' => 'date-time',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'last_used' => array(
'description' => __( 'The GMT date the application password was last used.' ),
'type' => array( 'string', 'null' ),
'format' => 'date-time',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'last_ip' => array(
'description' => __( 'The IP address the application password was last used by.' ),
'type' => array( 'string', 'null' ),
'format' => 'ip',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
),
);
return $this->add_additional_fields_schema( $this->schema );
}
}
ured_cat_id );
$response = rest_do_request( $request );
if ( $response->is_error() ) {
return;
}
$patterns = $response->get_data();
$registry = WP_Block_Patterns_Registry::get_instance();
foreach ( $patterns as $pattern ) {
$pattern['source'] = 'pattern-directory/featured';
$normalized_pattern = wp_normalize_remote_block_pattern( $pattern );
$pattern_name = sanitize_title( $normalized_pattern['title'] );
// Some patterns might be already registered as core patterns with the `core` prefix.
$is_registered = $registry->is_registered( $pattern_name ) || $registry->is_registered( "core/$pattern_name" );
if ( ! $is_registered ) {
register_block_pattern( $pattern_name, $normalized_pattern );
}
}
}
/**
* Registers patterns from Pattern Directory provided by a theme's
* `theme.json` file.
*
* @since 6.0.0
* @since 6.2.0 Normalized the pattern from the API (snake_case) to the
* format expected by `register_block_pattern()` (camelCase).
* @since 6.3.0 Add 'pattern-directory/theme' to the pattern's 'source'.
* @access private
*/
function _register_remote_theme_patterns() {
/** This filter is documented in wp-includes/block-patterns.php */
if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) {
return;
}
if ( ! wp_theme_has_theme_json() ) {
return;
}
$pattern_settings = wp_get_theme_directory_pattern_slugs();
if ( empty( $pattern_settings ) ) {
return;
}
$request = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
$request['slug'] = $pattern_settings;
$response = rest_do_request( $request );
if ( $response->is_error() ) {
return;
}
$patterns = $response->get_data();
$patterns_registry = WP_Block_Patterns_Registry::get_instance();
foreach ( $patterns as $pattern ) {
$pattern['source'] = 'pattern-directory/theme';
$normalized_pattern = wp_normalize_remote_block_pattern( $pattern );
$pattern_name = sanitize_title( $normalized_pattern['title'] );
// Some patterns might be already registered as core patterns with the `core` prefix.
$is_registered = $patterns_registry->is_registered( $pattern_name ) || $patterns_registry->is_registered( "core/$pattern_name" );
if ( ! $is_registered ) {
register_block_pattern( $pattern_name, $normalized_pattern );
}
}
}
/**
* Register any patterns that the active theme may provide under its
* `./patterns/` directory.
*
* @since 6.0.0
* @since 6.1.0 The `postTypes` property was added.
* @since 6.2.0 The `templateTypes` property was added.
* @since 6.4.0 Uses the `WP_Theme::get_block_patterns` method.
* @access private
*/
function _register_theme_block_patterns() {
/*
* During the bootstrap process, a check for active and valid themes is run.
* If no themes are returned, the theme's functions.php file will not be loaded,
* which can lead to errors if patterns expect some variables or constants to
* already be set at this point, so bail early if that is the case.
*/
if ( empty( wp_get_active_and_valid_themes() ) ) {
return;
}
/*
* Register patterns for the active theme. If the theme is a child theme,
* let it override any patterns from the parent theme that shares the same slug.
*/
$themes = array();
$theme = wp_get_theme();
$themes[] = $theme;
if ( $theme->parent() ) {
$themes[] = $theme->parent();
}
$registry = WP_Block_Patterns_Registry::get_instance();
foreach ( $themes as $theme ) {
$patterns = $theme->get_block_patterns();
$dirpath = $theme->get_stylesheet_directory() . '/patterns/';
$text_domain = $theme->get( 'TextDomain' );
foreach ( $patterns as $file => $pattern_data ) {
if ( $registry->is_registered( $pattern_data['slug'] ) ) {
continue;
}
$file_path = $dirpath . $file;
if ( ! file_exists( $file_path ) ) {
_doing_it_wrong(
__FUNCTION__,
sprintf(
/* translators: %s: file name. */
__( 'Could not register file "%s" as a block pattern as the file does not exist.' ),
$file
),
'6.4.0'
);
$theme->delete_pattern_cache();
continue;
}
$pattern_data['filePath'] = $file_path;
// Translate the pattern metadata.
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
$pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain );
if ( ! empty( $pattern_data['description'] ) ) {
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
$pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain );
}
register_block_pattern( $pattern_data['slug'], $pattern_data );
}
}
}
add_action( 'init', '_register_theme_block_patterns' );