wip
This commit is contained in:
@@ -535,7 +535,7 @@ class WP_REST_Request implements ArrayAccess {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @return array Parameter map of key to value
|
||||
* @return array Parameter map of key to value.
|
||||
*/
|
||||
public function get_query_params() {
|
||||
return $this->params['GET'];
|
||||
@@ -587,7 +587,7 @@ class WP_REST_Request implements ArrayAccess {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @return array Parameter map of key to value
|
||||
* @return array Parameter map of key to value.
|
||||
*/
|
||||
public function get_file_params() {
|
||||
return $this->params['FILES'];
|
||||
@@ -613,7 +613,7 @@ class WP_REST_Request implements ArrayAccess {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @return array Parameter map of key to value
|
||||
* @return array Parameter map of key to value.
|
||||
*/
|
||||
public function get_default_params() {
|
||||
return $this->params['defaults'];
|
||||
|
||||
@@ -43,7 +43,7 @@ class WP_REST_Response extends WP_HTTP_Response {
|
||||
/**
|
||||
* Adds a link to the response.
|
||||
*
|
||||
* @internal The $rel parameter is first, as this looks nicer when sending multiple.
|
||||
* {@internal The $rel parameter is first, as this looks nicer when sending multiple.}
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
@@ -76,9 +76,9 @@ class WP_REST_Response extends WP_HTTP_Response {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $rel Link relation. Either an IANA registered type, or an absolute URL.
|
||||
* @param string $href Optional. Only remove links for the relation matching the given href.
|
||||
* Default null.
|
||||
* @param string $rel Link relation. Either an IANA registered type, or an absolute URL.
|
||||
* @param string|null $href Optional. Only remove links for the relation matching the given href.
|
||||
* Default null.
|
||||
*/
|
||||
public function remove_link( $rel, $href = null ) {
|
||||
if ( ! isset( $this->links[ $rel ] ) ) {
|
||||
@@ -135,7 +135,7 @@ class WP_REST_Response extends WP_HTTP_Response {
|
||||
/**
|
||||
* Sets a single link header.
|
||||
*
|
||||
* @internal The $rel parameter is first, as this looks nicer when sending multiple.
|
||||
* {@internal The $rel parameter is first, as this looks nicer when sending multiple.}
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
|
||||
@@ -166,8 +166,8 @@ class WP_REST_Server {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @return WP_Error|null|true WP_Error indicates unsuccessful login, null indicates successful
|
||||
* or no authentication provided
|
||||
* @return WP_Error|null|true WP_Error if authentication error occurred, null if authentication
|
||||
* method wasn't used, true if authentication succeeded.
|
||||
*/
|
||||
public function check_authentication() {
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ class WP_REST_Server {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param WP_Error|null|true $errors WP_Error if authentication error, null if authentication
|
||||
* @param WP_Error|null|true $errors WP_Error if authentication error occurred, null if authentication
|
||||
* method wasn't used, true if authentication succeeded.
|
||||
*/
|
||||
return apply_filters( 'rest_authentication_errors', null );
|
||||
@@ -224,10 +224,10 @@ class WP_REST_Server {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $code WP_Error-style code.
|
||||
* @param string $message Human-readable message.
|
||||
* @param int $status Optional. HTTP status code to send. Default null.
|
||||
* @return string JSON representation of the error
|
||||
* @param string $code WP_Error-style code.
|
||||
* @param string $message Human-readable message.
|
||||
* @param int|null $status Optional. HTTP status code to send. Default null.
|
||||
* @return string JSON representation of the error.
|
||||
*/
|
||||
protected function json_error( $code, $message, $status = null ) {
|
||||
if ( $status ) {
|
||||
@@ -278,8 +278,8 @@ class WP_REST_Server {
|
||||
*
|
||||
* @global WP_User $current_user The currently authenticated user.
|
||||
*
|
||||
* @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
|
||||
* Default null.
|
||||
* @param string|null $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
|
||||
* Default null.
|
||||
* @return null|false Null if not served and a HEAD request, false otherwise.
|
||||
*/
|
||||
public function serve_request( $path = null ) {
|
||||
@@ -655,12 +655,11 @@ class WP_REST_Server {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target links for a REST API Link.
|
||||
* Gets the target hints for a REST API Link.
|
||||
*
|
||||
* @since 6.7.0
|
||||
*
|
||||
* @param array $link
|
||||
*
|
||||
* @param array $link The link to get target hints for.
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function get_target_hints_for_link( $link ) {
|
||||
@@ -764,6 +763,7 @@ class WP_REST_Server {
|
||||
*
|
||||
* @param array $data Data from the request.
|
||||
* @param bool|string[] $embed Whether to embed all links or a filtered list of link relations.
|
||||
* Default true.
|
||||
* @return array {
|
||||
* Data with sub-requests embedded.
|
||||
*
|
||||
@@ -1339,9 +1339,7 @@ class WP_REST_Server {
|
||||
* @return false|string Boolean false or string error message.
|
||||
*/
|
||||
protected function get_json_last_error() {
|
||||
$last_error_code = json_last_error();
|
||||
|
||||
if ( JSON_ERROR_NONE === $last_error_code || empty( $last_error_code ) ) {
|
||||
if ( JSON_ERROR_NONE === json_last_error() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1355,11 +1353,7 @@ class WP_REST_Server {
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param array $request {
|
||||
* Request.
|
||||
*
|
||||
* @type string $context Context.
|
||||
* }
|
||||
* @param WP_REST_Request $request Request data.
|
||||
* @return WP_REST_Response The API root index data.
|
||||
*/
|
||||
public function get_index( $request ) {
|
||||
@@ -1753,6 +1747,12 @@ class WP_REST_Server {
|
||||
$has_error = false;
|
||||
|
||||
foreach ( $requests as $single_request ) {
|
||||
if ( is_wp_error( $single_request ) ) {
|
||||
$has_error = true;
|
||||
$validation[] = $single_request;
|
||||
continue;
|
||||
}
|
||||
|
||||
$match = $this->match_request_to_handler( $single_request );
|
||||
$matches[] = $match;
|
||||
$error = null;
|
||||
@@ -1823,6 +1823,12 @@ class WP_REST_Server {
|
||||
}
|
||||
|
||||
foreach ( $requests as $i => $single_request ) {
|
||||
if ( is_wp_error( $single_request ) ) {
|
||||
$result = $this->error_to_response( $single_request );
|
||||
$responses[] = $this->envelope_response( $result, false )->get_data();
|
||||
continue;
|
||||
}
|
||||
|
||||
$clean_request = clone $single_request;
|
||||
$clean_request->set_url_params( array() );
|
||||
$clean_request->set_attributes( array() );
|
||||
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API ability categories controller for Abilities API.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Abilities_API
|
||||
* @since 6.9.0
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Core controller used to access ability categories via the REST API.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Abilities_V1_Categories_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* REST API namespace.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wp-abilities/v1';
|
||||
|
||||
/**
|
||||
* REST API base route.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'categories';
|
||||
|
||||
/**
|
||||
* Registers the routes for ability categories.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes(): void {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<slug>[a-z0-9]+(?:-[a-z0-9]+)*)',
|
||||
array(
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'description' => __( 'Unique identifier for the ability category.' ),
|
||||
'type' => 'string',
|
||||
'pattern' => '^[a-z0-9]+(?:-[a-z0-9]+)*$',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all ability categories.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object on success.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$categories = wp_get_ability_categories();
|
||||
|
||||
$page = $request['page'];
|
||||
$per_page = $request['per_page'];
|
||||
$offset = ( $page - 1 ) * $per_page;
|
||||
|
||||
$total_categories = count( $categories );
|
||||
$max_pages = (int) ceil( $total_categories / $per_page );
|
||||
|
||||
if ( $request->get_method() === 'HEAD' ) {
|
||||
$response = new WP_REST_Response( array() );
|
||||
} else {
|
||||
$categories = array_slice( $categories, $offset, $per_page );
|
||||
|
||||
$data = array();
|
||||
foreach ( $categories as $category ) {
|
||||
$item = $this->prepare_item_for_response( $category, $request );
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
$response->header( 'X-WP-Total', (string) $total_categories );
|
||||
$response->header( 'X-WP-TotalPages', (string) $max_pages );
|
||||
|
||||
$query_params = $request->get_query_params();
|
||||
$base = add_query_arg(
|
||||
urlencode_deep( $query_params ),
|
||||
rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) )
|
||||
);
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
|
||||
if ( $page < $max_pages ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific ability category.
|
||||
*
|
||||
* @since 6.9.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_item( $request ) {
|
||||
$category = wp_get_ability_category( $request['slug'] );
|
||||
if ( ! $category ) {
|
||||
return new WP_Error(
|
||||
'rest_ability_category_not_found',
|
||||
__( 'Ability category not found.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $category, $request );
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read ability categories.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool True if the request has read access.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
return current_user_can( 'read' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read an ability category.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool True if the request has read access.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return current_user_can( 'read' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an ability category for response.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_Ability_Category $category The ability category object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $category, $request ) {
|
||||
$data = array(
|
||||
'slug' => $category->get_slug(),
|
||||
'label' => $category->get_label(),
|
||||
'description' => $category->get_description(),
|
||||
'meta' => $category->get_meta(),
|
||||
);
|
||||
|
||||
$context = $request['context'] ?? 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $category->get_slug() ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'abilities' => array(
|
||||
'href' => rest_url( sprintf( '%s/abilities?category=%s', $this->namespace, $category->get_slug() ) ),
|
||||
),
|
||||
);
|
||||
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ability category's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @return array<string, mixed> Item schema data.
|
||||
*/
|
||||
public function get_item_schema(): array {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'ability-category',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'slug' => array(
|
||||
'description' => __( 'Unique identifier for the ability category.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'label' => array(
|
||||
'description' => __( 'Display label for the category.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'Description of the category.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'meta' => array(
|
||||
'description' => __( 'Meta information about the category.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @return array<string, mixed> Collection parameters.
|
||||
*/
|
||||
public function get_collection_params(): array {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
'page' => array(
|
||||
'description' => __( 'Current page of the collection.' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'minimum' => 1,
|
||||
),
|
||||
'per_page' => array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.' ),
|
||||
'type' => 'integer',
|
||||
'default' => 50,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API list controller for Abilities API.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Abilities_API
|
||||
* @since 6.9.0
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Core controller used to access abilities via the REST API.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Abilities_V1_List_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* REST API namespace.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wp-abilities/v1';
|
||||
|
||||
/**
|
||||
* REST API base route.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'abilities';
|
||||
|
||||
/**
|
||||
* Registers the routes for abilities.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes(): void {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<name>[a-zA-Z0-9\-\/]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'Unique identifier for the ability.' ),
|
||||
'type' => 'string',
|
||||
'pattern' => '^[a-zA-Z0-9\-\/]+$',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all abilities.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object on success.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$abilities = array_filter(
|
||||
wp_get_abilities(),
|
||||
static function ( $ability ) {
|
||||
return $ability->get_meta_item( 'show_in_rest' );
|
||||
}
|
||||
);
|
||||
|
||||
// Filter by ability category if specified.
|
||||
$category = $request['category'];
|
||||
if ( ! empty( $category ) ) {
|
||||
$abilities = array_filter(
|
||||
$abilities,
|
||||
static function ( $ability ) use ( $category ) {
|
||||
return $ability->get_category() === $category;
|
||||
}
|
||||
);
|
||||
// Reset array keys after filtering.
|
||||
$abilities = array_values( $abilities );
|
||||
}
|
||||
|
||||
$page = $request['page'];
|
||||
$per_page = $request['per_page'];
|
||||
$offset = ( $page - 1 ) * $per_page;
|
||||
|
||||
$total_abilities = count( $abilities );
|
||||
$max_pages = (int) ceil( $total_abilities / $per_page );
|
||||
|
||||
if ( $request->get_method() === 'HEAD' ) {
|
||||
$response = new WP_REST_Response( array() );
|
||||
} else {
|
||||
$abilities = array_slice( $abilities, $offset, $per_page );
|
||||
|
||||
$data = array();
|
||||
foreach ( $abilities as $ability ) {
|
||||
$item = $this->prepare_item_for_response( $ability, $request );
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
$response->header( 'X-WP-Total', (string) $total_abilities );
|
||||
$response->header( 'X-WP-TotalPages', (string) $max_pages );
|
||||
|
||||
$query_params = $request->get_query_params();
|
||||
$base = add_query_arg( urlencode_deep( $query_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
|
||||
if ( $page < $max_pages ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific ability.
|
||||
*
|
||||
* @since 6.9.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_item( $request ) {
|
||||
$ability = wp_get_ability( $request['name'] );
|
||||
if ( ! $ability || ! $ability->get_meta_item( 'show_in_rest' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_ability_not_found',
|
||||
__( 'Ability not found.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $ability, $request );
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read ability items.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool True if the request has read access.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
return current_user_can( 'read' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read an ability item.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool True if the request has read access.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return current_user_can( 'read' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes schema empty object defaults.
|
||||
*
|
||||
* Converts empty array defaults to objects when the schema type is 'object'
|
||||
* to ensure proper JSON serialization as {} instead of [].
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param array<string, mixed> $schema The schema array.
|
||||
* @return array<string, mixed> The normalized schema.
|
||||
*/
|
||||
private function normalize_schema_empty_object_defaults( array $schema ): array {
|
||||
if ( isset( $schema['type'] ) && 'object' === $schema['type'] && isset( $schema['default'] ) ) {
|
||||
$default = $schema['default'];
|
||||
if ( is_array( $default ) && empty( $default ) ) {
|
||||
$schema['default'] = (object) $default;
|
||||
}
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an ability for response.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_Ability $ability The ability object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $ability, $request ) {
|
||||
$data = array(
|
||||
'name' => $ability->get_name(),
|
||||
'label' => $ability->get_label(),
|
||||
'description' => $ability->get_description(),
|
||||
'category' => $ability->get_category(),
|
||||
'input_schema' => $this->normalize_schema_empty_object_defaults( $ability->get_input_schema() ),
|
||||
'output_schema' => $this->normalize_schema_empty_object_defaults( $ability->get_output_schema() ),
|
||||
'meta' => $ability->get_meta(),
|
||||
);
|
||||
|
||||
$context = $request['context'] ?? 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $ability->get_name() ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
|
||||
$links['wp:action-run'] = array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s/run', $this->namespace, $this->rest_base, $ability->get_name() ) ),
|
||||
);
|
||||
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ability's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @return array<string, mixed> Item schema data.
|
||||
*/
|
||||
public function get_item_schema(): array {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'ability',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'Unique identifier for the ability.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'label' => array(
|
||||
'description' => __( 'Display label for the ability.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'Description of the ability.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'category' => array(
|
||||
'description' => __( 'Ability category this ability belongs to.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'input_schema' => array(
|
||||
'description' => __( 'JSON Schema for the ability input.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'output_schema' => array(
|
||||
'description' => __( 'JSON Schema for the ability output.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'meta' => array(
|
||||
'description' => __( 'Meta information about the ability.' ),
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'annotations' => array(
|
||||
'description' => __( 'Annotations for the ability.' ),
|
||||
'type' => array( 'boolean', 'null' ),
|
||||
'default' => null,
|
||||
),
|
||||
),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @return array<string, mixed> Collection parameters.
|
||||
*/
|
||||
public function get_collection_params(): array {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
'page' => array(
|
||||
'description' => __( 'Current page of the collection.' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'minimum' => 1,
|
||||
),
|
||||
'per_page' => array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.' ),
|
||||
'type' => 'integer',
|
||||
'default' => 50,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
),
|
||||
'category' => array(
|
||||
'description' => __( 'Limit results to abilities in specific ability category.' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API run controller for Abilities API.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Abilities_API
|
||||
* @since 6.9.0
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Core controller used to execute abilities via the REST API.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Abilities_V1_Run_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* REST API namespace.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wp-abilities/v1';
|
||||
|
||||
/**
|
||||
* REST API base route.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'abilities';
|
||||
|
||||
/**
|
||||
* Registers the routes for ability execution.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes(): void {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<name>[a-zA-Z0-9\-\/]+?)/run',
|
||||
array(
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'Unique identifier for the ability.' ),
|
||||
'type' => 'string',
|
||||
'pattern' => '^[a-zA-Z0-9\-\/]+$',
|
||||
),
|
||||
),
|
||||
|
||||
// TODO: We register ALLMETHODS because at route registration time, we don't know which abilities
|
||||
// exist or their annotations (`destructive`, `idempotent`, `readonly`). This is due to WordPress
|
||||
// load order - routes are registered early, before plugins have registered their abilities.
|
||||
// This approach works but could be improved with lazy route registration or a different
|
||||
// architecture that allows type-specific routes after abilities are registered.
|
||||
// This was the same issue that we ended up seeing with the Feature API.
|
||||
array(
|
||||
'methods' => WP_REST_Server::ALLMETHODS,
|
||||
'callback' => array( $this, 'execute_ability' ),
|
||||
'permission_callback' => array( $this, 'check_ability_permissions' ),
|
||||
'args' => $this->get_run_args(),
|
||||
),
|
||||
'schema' => array( $this, 'get_run_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an ability.
|
||||
*
|
||||
* @since 6.9.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 execute_ability( $request ) {
|
||||
$ability = wp_get_ability( $request['name'] );
|
||||
if ( ! $ability ) {
|
||||
return new WP_Error(
|
||||
'rest_ability_not_found',
|
||||
__( 'Ability not found.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$input = $this->get_input_from_request( $request );
|
||||
$result = $ability->execute( $input );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the HTTP method matches the expected method for the ability based on its annotations.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param string $request_method The HTTP method of the request.
|
||||
* @param array<string, (null|bool)> $annotations The ability annotations.
|
||||
* @return true|WP_Error True on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function validate_request_method( string $request_method, array $annotations ) {
|
||||
$expected_method = 'POST';
|
||||
if ( ! empty( $annotations['readonly'] ) ) {
|
||||
$expected_method = 'GET';
|
||||
} elseif ( ! empty( $annotations['destructive'] ) && ! empty( $annotations['idempotent'] ) ) {
|
||||
$expected_method = 'DELETE';
|
||||
}
|
||||
|
||||
if ( $expected_method === $request_method ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$error_message = __( 'Abilities that perform updates require POST method.' );
|
||||
if ( 'GET' === $expected_method ) {
|
||||
$error_message = __( 'Read-only abilities require GET method.' );
|
||||
} elseif ( 'DELETE' === $expected_method ) {
|
||||
$error_message = __( 'Abilities that perform destructive actions require DELETE method.' );
|
||||
}
|
||||
return new WP_Error(
|
||||
'rest_ability_invalid_method',
|
||||
$error_message,
|
||||
array( 'status' => 405 )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has permission to execute a specific ability.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has execution permission, WP_Error object otherwise.
|
||||
*/
|
||||
public function check_ability_permissions( $request ) {
|
||||
$ability = wp_get_ability( $request['name'] );
|
||||
if ( ! $ability || ! $ability->get_meta_item( 'show_in_rest' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_ability_not_found',
|
||||
__( 'Ability not found.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$is_valid = $this->validate_request_method(
|
||||
$request->get_method(),
|
||||
$ability->get_meta_item( 'annotations' )
|
||||
);
|
||||
if ( is_wp_error( $is_valid ) ) {
|
||||
return $is_valid;
|
||||
}
|
||||
|
||||
$input = $this->get_input_from_request( $request );
|
||||
$input = $ability->normalize_input( $input );
|
||||
$is_valid = $ability->validate_input( $input );
|
||||
if ( is_wp_error( $is_valid ) ) {
|
||||
$is_valid->add_data( array( 'status' => 400 ) );
|
||||
return $is_valid;
|
||||
}
|
||||
|
||||
$result = $ability->check_permissions( $input );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$result->add_data( array( 'status' => rest_authorization_required_code() ) );
|
||||
return $result;
|
||||
}
|
||||
if ( ! $result ) {
|
||||
return new WP_Error(
|
||||
'rest_ability_cannot_execute',
|
||||
__( 'Sorry, you are not allowed to execute this ability.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts input parameters from the request.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return mixed|null The input parameters.
|
||||
*/
|
||||
private function get_input_from_request( $request ) {
|
||||
if ( in_array( $request->get_method(), array( 'GET', 'DELETE' ), true ) ) {
|
||||
// For GET and DELETE requests, look for 'input' query parameter.
|
||||
$query_params = $request->get_query_params();
|
||||
return $query_params['input'] ?? null;
|
||||
}
|
||||
|
||||
// For POST requests, look for 'input' in JSON body.
|
||||
$json_params = $request->get_json_params();
|
||||
return $json_params['input'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the arguments for ability execution endpoint.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @return array<string, mixed> Arguments for the run endpoint.
|
||||
*/
|
||||
public function get_run_args(): array {
|
||||
return array(
|
||||
'input' => array(
|
||||
'description' => __( 'Input parameters for the ability execution.' ),
|
||||
'type' => array( 'integer', 'number', 'boolean', 'string', 'array', 'object', 'null' ),
|
||||
'default' => null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the schema for ability execution endpoint.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @return array<string, mixed> Schema for the run endpoint.
|
||||
*/
|
||||
public function get_run_schema(): array {
|
||||
return array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'ability-execution',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'result' => array(
|
||||
'description' => __( 'The result of the ability execution.' ),
|
||||
'type' => array( 'integer', 'number', 'boolean', 'string', 'array', 'object', 'null' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -802,7 +802,16 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller {
|
||||
'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',
|
||||
'oneOf' => array(
|
||||
array(
|
||||
'type' => 'string',
|
||||
'format' => 'uuid',
|
||||
),
|
||||
array(
|
||||
'type' => 'string',
|
||||
'enum' => array( '' ),
|
||||
),
|
||||
),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'name' => array(
|
||||
|
||||
@@ -70,6 +70,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
* prepares for WP_Query.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 6.9.0 Extends the `media_type` and `mime_type` request arguments to support array values.
|
||||
*
|
||||
* @param array $prepared_args Optional. Array of prepared arguments. Default empty array.
|
||||
* @param WP_REST_Request $request Optional. Request to prepare items for.
|
||||
@@ -82,19 +83,30 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
$query_args['post_status'] = 'inherit';
|
||||
}
|
||||
|
||||
$media_types = $this->get_media_types();
|
||||
$all_mime_types = array();
|
||||
$media_types = $this->get_media_types();
|
||||
|
||||
if ( ! empty( $request['media_type'] ) && isset( $media_types[ $request['media_type'] ] ) ) {
|
||||
$query_args['post_mime_type'] = $media_types[ $request['media_type'] ];
|
||||
if ( ! empty( $request['media_type'] ) && is_array( $request['media_type'] ) ) {
|
||||
foreach ( $request['media_type'] as $type ) {
|
||||
if ( isset( $media_types[ $type ] ) ) {
|
||||
$all_mime_types = array_merge( $all_mime_types, $media_types[ $type ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $request['mime_type'] ) ) {
|
||||
$parts = explode( '/', $request['mime_type'] );
|
||||
if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ], true ) ) {
|
||||
$query_args['post_mime_type'] = $request['mime_type'];
|
||||
if ( ! empty( $request['mime_type'] ) && is_array( $request['mime_type'] ) ) {
|
||||
foreach ( $request['mime_type'] as $mime_type ) {
|
||||
$parts = explode( '/', $mime_type );
|
||||
if ( isset( $media_types[ $parts[0] ] ) && in_array( $mime_type, $media_types[ $parts[0] ], true ) ) {
|
||||
$all_mime_types[] = $mime_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $all_mime_types ) ) {
|
||||
$query_args['post_mime_type'] = array_values( array_unique( $all_mime_types ) );
|
||||
}
|
||||
|
||||
// Filter query clauses to include filenames.
|
||||
if ( isset( $query_args['s'] ) ) {
|
||||
add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' );
|
||||
@@ -543,6 +555,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
* Applies edits to a media item and creates a new attachment record.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @since 6.9.0 Adds flips capability and editable fields for the newly-created attachment post.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
|
||||
@@ -584,6 +597,20 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
} else {
|
||||
$modifiers = array();
|
||||
|
||||
if ( isset( $request['flip']['horizontal'] ) || isset( $request['flip']['vertical'] ) ) {
|
||||
$flip_args = array(
|
||||
'vertical' => isset( $request['flip']['vertical'] ) ? (bool) $request['flip']['vertical'] : false,
|
||||
'horizontal' => isset( $request['flip']['horizontal'] ) ? (bool) $request['flip']['horizontal'] : false,
|
||||
);
|
||||
|
||||
$modifiers[] = array(
|
||||
'type' => 'flip',
|
||||
'args' => array(
|
||||
'flip' => $flip_args,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $request['rotation'] ) ) {
|
||||
$modifiers[] = array(
|
||||
'type' => 'rotate',
|
||||
@@ -637,6 +664,21 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
foreach ( $modifiers as $modifier ) {
|
||||
$args = $modifier['args'];
|
||||
switch ( $modifier['type'] ) {
|
||||
case 'flip':
|
||||
/*
|
||||
* Flips the current image.
|
||||
* The vertical flip is the first argument (flip along horizontal axis), the horizontal flip is the second argument (flip along vertical axis).
|
||||
* See: WP_Image_Editor::flip()
|
||||
*/
|
||||
$result = $image_editor->flip( $args['flip']['vertical'], $args['flip']['horizontal'] );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return new WP_Error(
|
||||
'rest_image_flip_failed',
|
||||
__( 'Unable to flip this image.' ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'rotate':
|
||||
// Rotation direction: clockwise vs. counterclockwise.
|
||||
$rotate = 0 - $args['angle'];
|
||||
@@ -711,24 +753,31 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
return $saved;
|
||||
}
|
||||
|
||||
// Create new attachment post.
|
||||
$new_attachment_post = array(
|
||||
'post_mime_type' => $saved['mime-type'],
|
||||
'guid' => $uploads['url'] . "/$filename",
|
||||
'post_title' => $image_name,
|
||||
'post_content' => '',
|
||||
);
|
||||
// Grab original attachment post so we can use it to set defaults.
|
||||
$original_attachment_post = get_post( $attachment_id );
|
||||
|
||||
// Copy post_content, post_excerpt, and post_title from the edited image's attachment post.
|
||||
$attachment_post = get_post( $attachment_id );
|
||||
// Check request fields and assign default values.
|
||||
$new_attachment_post = $this->prepare_item_for_database( $request );
|
||||
$new_attachment_post->post_mime_type = $saved['mime-type'];
|
||||
$new_attachment_post->guid = $uploads['url'] . "/$filename";
|
||||
|
||||
if ( $attachment_post ) {
|
||||
$new_attachment_post['post_content'] = $attachment_post->post_content;
|
||||
$new_attachment_post['post_excerpt'] = $attachment_post->post_excerpt;
|
||||
$new_attachment_post['post_title'] = $attachment_post->post_title;
|
||||
}
|
||||
// Unset ID so wp_insert_attachment generates a new ID.
|
||||
unset( $new_attachment_post->ID );
|
||||
|
||||
$new_attachment_id = wp_insert_attachment( wp_slash( $new_attachment_post ), $saved['path'], 0, true );
|
||||
// Set new attachment post title with fallbacks.
|
||||
$new_attachment_post->post_title = $new_attachment_post->post_title ?? $original_attachment_post->post_title ?? $image_name;
|
||||
|
||||
// Set new attachment post caption (post_excerpt).
|
||||
$new_attachment_post->post_excerpt = $new_attachment_post->post_excerpt ?? $original_attachment_post->post_excerpt ?? '';
|
||||
|
||||
// Set new attachment post description (post_content) with fallbacks.
|
||||
$new_attachment_post->post_content = $new_attachment_post->post_content ?? $original_attachment_post->post_content ?? '';
|
||||
|
||||
// Set post parent if set in request, else the default of `0` (no parent).
|
||||
$new_attachment_post->post_parent = $new_attachment_post->post_parent ?? 0;
|
||||
|
||||
// Insert the new attachment post.
|
||||
$new_attachment_id = wp_insert_attachment( wp_slash( (array) $new_attachment_post ), $saved['path'], 0, true );
|
||||
|
||||
if ( is_wp_error( $new_attachment_id ) ) {
|
||||
if ( 'db_update_error' === $new_attachment_id->get_error_code() ) {
|
||||
@@ -740,8 +789,8 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
return $new_attachment_id;
|
||||
}
|
||||
|
||||
// Copy the image alt text from the edited image.
|
||||
$image_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
|
||||
// First, try to use the alt text from the request. If not set, copy the image alt text from the original attachment.
|
||||
$image_alt = isset( $request['alt_text'] ) ? sanitize_text_field( $request['alt_text'] ) : get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
|
||||
|
||||
if ( ! empty( $image_alt ) ) {
|
||||
// update_post_meta() expects slashed.
|
||||
@@ -970,6 +1019,33 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
return apply_filters( 'rest_prepare_attachment', $response, $post, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares attachment links for the request.
|
||||
*
|
||||
* @since 6.9.0
|
||||
*
|
||||
* @param WP_Post $post Post object.
|
||||
* @return array Links for the given attachment.
|
||||
*/
|
||||
protected function prepare_links( $post ) {
|
||||
$links = parent::prepare_links( $post );
|
||||
|
||||
if ( ! empty( $post->post_parent ) ) {
|
||||
$post = get_post( $post->post_parent );
|
||||
|
||||
if ( ! empty( $post ) ) {
|
||||
$links['https://api.w.org/attached-to'] = array(
|
||||
'href' => rest_url( rest_get_route_for_post( $post ) ),
|
||||
'embeddable' => true,
|
||||
'post_type' => $post->post_type,
|
||||
'id' => $post->ID,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the attachment's schema, conforming to JSON Schema.
|
||||
*
|
||||
@@ -1278,6 +1354,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
* Retrieves the query params for collections of attachments.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 6.9.0 Extends the `media_type` and `mime_type` request arguments to support array values.
|
||||
*
|
||||
* @return array Query parameters for the attachment collection as an array.
|
||||
*/
|
||||
@@ -1285,19 +1362,25 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
$params = parent::get_collection_params();
|
||||
$params['status']['default'] = 'inherit';
|
||||
$params['status']['items']['enum'] = array( 'inherit', 'private', 'trash' );
|
||||
$media_types = $this->get_media_types();
|
||||
$media_types = array_keys( $this->get_media_types() );
|
||||
|
||||
$params['media_type'] = array(
|
||||
'default' => null,
|
||||
'description' => __( 'Limit result set to attachments of a particular media type.' ),
|
||||
'type' => 'string',
|
||||
'enum' => array_keys( $media_types ),
|
||||
'description' => __( 'Limit result set to attachments of a particular media type or media types.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
'enum' => $media_types,
|
||||
),
|
||||
);
|
||||
|
||||
$params['mime_type'] = array(
|
||||
'default' => null,
|
||||
'description' => __( 'Limit result set to attachments of a particular MIME type.' ),
|
||||
'type' => 'string',
|
||||
'description' => __( 'Limit result set to attachments of a particular MIME type or MIME types.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
|
||||
return $params;
|
||||
@@ -1453,17 +1536,19 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
* Gets the request args for the edit item route.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @since 6.9.0 Adds flips capability and editable fields for the newly-created attachment post.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_edit_media_item_args() {
|
||||
return array(
|
||||
$args = array(
|
||||
'src' => array(
|
||||
'description' => __( 'URL to the edited image file.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'required' => true,
|
||||
),
|
||||
// The `modifiers` param takes precedence over the older format.
|
||||
'modifiers' => array(
|
||||
'description' => __( 'Array of image edits.' ),
|
||||
'type' => 'array',
|
||||
@@ -1476,6 +1561,43 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
'args',
|
||||
),
|
||||
'oneOf' => array(
|
||||
array(
|
||||
'title' => __( 'Flip' ),
|
||||
'properties' => array(
|
||||
'type' => array(
|
||||
'description' => __( 'Flip type.' ),
|
||||
'type' => 'string',
|
||||
'enum' => array( 'flip' ),
|
||||
),
|
||||
'args' => array(
|
||||
'description' => __( 'Flip arguments.' ),
|
||||
'type' => 'object',
|
||||
'required' => array(
|
||||
'flip',
|
||||
),
|
||||
'properties' => array(
|
||||
'flip' => array(
|
||||
'description' => __( 'Flip direction.' ),
|
||||
'type' => 'object',
|
||||
'required' => array(
|
||||
'horizontal',
|
||||
'vertical',
|
||||
),
|
||||
'properties' => array(
|
||||
'horizontal' => array(
|
||||
'description' => __( 'Whether to flip in the horizontal direction.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'vertical' => array(
|
||||
'description' => __( 'Whether to flip in the vertical direction.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Rotation' ),
|
||||
'properties' => array(
|
||||
@@ -1573,5 +1695,33 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
'maximum' => 100,
|
||||
),
|
||||
);
|
||||
|
||||
/*
|
||||
* Get the args based on the post schema. This calls `rest_get_endpoint_args_for_schema()`,
|
||||
* which also takes care of sanitization and validation.
|
||||
*/
|
||||
$update_item_args = $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE );
|
||||
|
||||
if ( isset( $update_item_args['caption'] ) ) {
|
||||
$args['caption'] = $update_item_args['caption'];
|
||||
}
|
||||
|
||||
if ( isset( $update_item_args['description'] ) ) {
|
||||
$args['description'] = $update_item_args['description'];
|
||||
}
|
||||
|
||||
if ( isset( $update_item_args['title'] ) ) {
|
||||
$args['title'] = $update_item_args['title'];
|
||||
}
|
||||
|
||||
if ( isset( $update_item_args['post'] ) ) {
|
||||
$args['post'] = $update_item_args['post'];
|
||||
}
|
||||
|
||||
if ( isset( $update_item_args['alt_text'] ) ) {
|
||||
$args['alt_text'] = $update_item_args['alt_text'];
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
* @return true|WP_Error True if the request has read access, error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
$is_note = 'note' === $request['type'];
|
||||
$is_edit_context = 'edit' === $request['context'];
|
||||
$protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' );
|
||||
$forbidden_params = array();
|
||||
|
||||
if ( ! empty( $request['post'] ) ) {
|
||||
foreach ( (array) $request['post'] as $post_id ) {
|
||||
@@ -141,10 +145,51 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $post && $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
|
||||
if ( current_user_can( 'edit_post', $post->ID ) ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_not_supported_post_type',
|
||||
__( 'Sorry, this post type does not support notes.' ),
|
||||
array( 'status' => 403 )
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $protected_params as $param ) {
|
||||
if ( 'status' === $param ) {
|
||||
if ( 'approve' !== $request[ $param ] ) {
|
||||
$forbidden_params[] = $param;
|
||||
}
|
||||
} elseif ( 'type' === $param ) {
|
||||
if ( 'comment' !== $request[ $param ] ) {
|
||||
$forbidden_params[] = $param;
|
||||
}
|
||||
} elseif ( ! empty( $request[ $param ] ) ) {
|
||||
$forbidden_params[] = $param;
|
||||
}
|
||||
}
|
||||
return new WP_Error(
|
||||
'rest_forbidden_param',
|
||||
/* translators: %s: List of forbidden parameters. */
|
||||
sprintf( __( 'Query parameter not permitted: %s' ), implode( ', ', $forbidden_params ) ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
|
||||
// Re-map edit context capabilities when requesting `note` for a post.
|
||||
if ( $is_edit_context && $is_note && ! empty( $request['post'] ) ) {
|
||||
foreach ( (array) $request['post'] as $post_id ) {
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit comments.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
}
|
||||
} elseif ( $is_edit_context && ! current_user_can( 'moderate_comments' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit comments.' ),
|
||||
@@ -153,9 +198,6 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
$protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' );
|
||||
$forbidden_params = array();
|
||||
|
||||
foreach ( $protected_params as $param ) {
|
||||
if ( 'status' === $param ) {
|
||||
if ( 'approve' !== $request[ $param ] ) {
|
||||
@@ -302,12 +344,13 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
$max_pages = (int) $query->max_num_pages;
|
||||
|
||||
if ( $total_comments < 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
// Out-of-bounds, run the query without pagination/offset to get the total count.
|
||||
unset( $prepared_args['number'], $prepared_args['offset'] );
|
||||
|
||||
$query = new WP_Comment_Query();
|
||||
$prepared_args['count'] = true;
|
||||
$prepared_args['orderby'] = 'none';
|
||||
$query = new WP_Comment_Query();
|
||||
$prepared_args['count'] = true;
|
||||
$prepared_args['orderby'] = 'none';
|
||||
$prepared_args['update_comment_meta_cache'] = false;
|
||||
|
||||
$total_comments = $query->query( $prepared_args );
|
||||
$max_pages = (int) ceil( $total_comments / $request['per_page'] );
|
||||
@@ -394,7 +437,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
return $comment;
|
||||
}
|
||||
|
||||
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
|
||||
// Re-map edit context capabilities when requesting `note` type.
|
||||
$edit_cap = 'note' === $comment->comment_type ? array( 'edit_comment', $comment->comment_ID ) : array( 'moderate_comments' );
|
||||
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( ...$edit_cap ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit comments.' ),
|
||||
@@ -452,6 +497,16 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
* @return true|WP_Error True if the request has access to create items, error object otherwise.
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
$is_note = ! empty( $request['type'] ) && 'note' === $request['type'];
|
||||
|
||||
if ( ! is_user_logged_in() && $is_note ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_login_required',
|
||||
__( 'Sorry, you must be logged in to comment.' ),
|
||||
array( 'status' => 401 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! is_user_logged_in() ) {
|
||||
if ( get_option( 'comment_registration' ) ) {
|
||||
return new WP_Error(
|
||||
@@ -505,7 +560,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
|
||||
$edit_cap = $is_note ? array( 'edit_post', (int) $request['post'] ) : array( 'moderate_comments' );
|
||||
if ( isset( $request['status'] ) && ! current_user_can( ...$edit_cap ) ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_invalid_status',
|
||||
/* translators: %s: Request parameter. */
|
||||
@@ -532,7 +588,15 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'draft' === $post->post_status ) {
|
||||
if ( $is_note && ! $this->check_post_type_supports_notes( $post->post_type ) ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_not_supported_post_type',
|
||||
__( 'Sorry, this post type does not support notes.' ),
|
||||
array( 'status' => 403 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'draft' === $post->post_status && ! $is_note ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_draft_post',
|
||||
__( 'Sorry, you are not allowed to create a comment on this post.' ),
|
||||
@@ -556,7 +620,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! comments_open( $post->ID ) ) {
|
||||
if ( ! comments_open( $post->ID ) && ! $is_note ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_closed',
|
||||
__( 'Sorry, comments are closed for this item.' ),
|
||||
@@ -584,8 +648,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
// Do not allow comments to be created with a non-default type.
|
||||
if ( ! empty( $request['type'] ) && 'comment' !== $request['type'] ) {
|
||||
// Do not allow comments to be created with a non-core type.
|
||||
if ( ! empty( $request['type'] ) && ! in_array( $request['type'], array( 'comment', 'note' ), true ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_comment_type',
|
||||
__( 'Cannot create a comment with that type.' ),
|
||||
@@ -598,12 +662,17 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
return $prepared_comment;
|
||||
}
|
||||
|
||||
$prepared_comment['comment_type'] = 'comment';
|
||||
$prepared_comment['comment_type'] = $request['type'];
|
||||
|
||||
if ( ! isset( $prepared_comment['comment_content'] ) ) {
|
||||
$prepared_comment['comment_content'] = '';
|
||||
}
|
||||
|
||||
// Include note metadata into check_is_comment_content_allowed.
|
||||
if ( isset( $request['meta']['_wp_note_status'] ) ) {
|
||||
$prepared_comment['meta']['_wp_note_status'] = $request['meta']['_wp_note_status'];
|
||||
}
|
||||
|
||||
if ( ! $this->check_is_comment_content_allowed( $prepared_comment ) ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_content_invalid',
|
||||
@@ -666,7 +735,11 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
$prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment, true );
|
||||
// Don't check for duplicates or flooding for notes.
|
||||
$prepared_comment['comment_approved'] =
|
||||
'note' === $prepared_comment['comment_type'] ?
|
||||
'1' :
|
||||
wp_allow_comment( $prepared_comment, true );
|
||||
|
||||
if ( is_wp_error( $prepared_comment['comment_approved'] ) ) {
|
||||
$error_code = $prepared_comment['comment_approved']->get_error_code();
|
||||
@@ -859,8 +932,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
if ( is_wp_error( $prepared_args ) ) {
|
||||
return $prepared_args;
|
||||
}
|
||||
|
||||
if ( isset( $prepared_args['comment_content'] ) && empty( $prepared_args['comment_content'] ) ) {
|
||||
if ( ! $this->check_is_comment_content_allowed( $prepared_args ) ) {
|
||||
return new WP_Error(
|
||||
'rest_comment_content_invalid',
|
||||
__( 'Invalid comment content.' ),
|
||||
@@ -1207,6 +1279,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
array(
|
||||
'count' => true,
|
||||
'orderby' => 'none',
|
||||
'type' => 'all',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1223,6 +1296,22 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
// Embedding children for notes requires `type` and `status` inheritance.
|
||||
if ( isset( $links['children'] ) && 'note' === $comment->comment_type ) {
|
||||
$args = array(
|
||||
'parent' => $comment->comment_ID,
|
||||
'type' => $comment->comment_type,
|
||||
'status' => 'all',
|
||||
);
|
||||
|
||||
$rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) );
|
||||
|
||||
$links['children'] = array(
|
||||
'href' => $rest_url,
|
||||
'embeddable' => true,
|
||||
);
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
@@ -1520,6 +1609,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
'default' => 'comment',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1821,7 +1911,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
* @return bool Whether the comment can be read.
|
||||
*/
|
||||
protected function check_read_permission( $comment, $request ) {
|
||||
if ( ! empty( $comment->comment_post_ID ) ) {
|
||||
if ( 'note' !== $comment->comment_type && ! empty( $comment->comment_post_ID ) ) {
|
||||
$post = get_post( $comment->comment_post_ID );
|
||||
if ( $post ) {
|
||||
if ( $this->check_read_post_permission( $post, $request ) && 1 === (int) $comment->comment_approved ) {
|
||||
@@ -1903,6 +1993,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
* @return bool True if the content is allowed, false otherwise.
|
||||
*/
|
||||
protected function check_is_comment_content_allowed( $prepared_comment ) {
|
||||
if ( ! isset( $prepared_comment['comment_content'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$check = wp_parse_args(
|
||||
$prepared_comment,
|
||||
array(
|
||||
@@ -1922,10 +2016,42 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow empty notes only when resolution metadata is valid.
|
||||
if (
|
||||
isset( $check['comment_type'] ) &&
|
||||
'note' === $check['comment_type'] &&
|
||||
isset( $check['meta']['_wp_note_status'] ) &&
|
||||
in_array( $check['meta']['_wp_note_status'], array( 'resolved', 'reopen' ), true )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allow a comment to be created with missing or empty
|
||||
* comment_content. See wp_handle_comment_submission().
|
||||
*/
|
||||
return '' !== $check['comment_content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if post type supports notes.
|
||||
*
|
||||
* @param string $post_type Post type name.
|
||||
* @return bool True if post type supports notes, false otherwise.
|
||||
*/
|
||||
private function check_post_type_supports_notes( $post_type ) {
|
||||
$supports = get_all_post_type_supports( $post_type );
|
||||
if ( ! isset( $supports['editor'] ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! is_array( $supports['editor'] ) ) {
|
||||
return false;
|
||||
}
|
||||
foreach ( $supports['editor'] as $item ) {
|
||||
if ( ! empty( $item['notes'] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,11 +662,11 @@ abstract class WP_REST_Controller {
|
||||
/**
|
||||
* Sanitizes the slug value.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @internal We can't use sanitize_title() directly, as the second
|
||||
* {@internal We can't use sanitize_title() directly, as the second
|
||||
* parameter is the fallback title, which would end up being set to the
|
||||
* request object.
|
||||
* request object.}
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see https://github.com/WP-API/WP-API/issues/1585
|
||||
*
|
||||
|
||||
@@ -69,6 +69,7 @@ class WP_REST_Font_Collections_Controller extends WP_REST_Controller {
|
||||
*
|
||||
* @since 6.5.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_items( $request ) {
|
||||
|
||||
@@ -88,7 +88,7 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller {
|
||||
// Lists/updates a single global style variation based on the given id.
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
|
||||
'/' . $this->rest_base . '/(?P<id>[\/\d+]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
@@ -96,9 +96,8 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller {
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
|
||||
'description' => __( 'ID of global styles config.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -115,17 +114,17 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the global styles ID or stylesheet to decode endpoint.
|
||||
* Sanitize the global styles stylesheet to decode endpoint.
|
||||
* For example, `wp/v2/global-styles/twentytwentytwo%200.4.0`
|
||||
* would be decoded to `twentytwentytwo 0.4.0`.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param string $id_or_stylesheet Global styles ID or stylesheet.
|
||||
* @return string Sanitized global styles ID or stylesheet.
|
||||
* @param string $stylesheet Global styles stylesheet.
|
||||
* @return string Sanitized global styles stylesheet.
|
||||
*/
|
||||
public function _sanitize_global_styles_callback( $id_or_stylesheet ) {
|
||||
return urldecode( $id_or_stylesheet );
|
||||
public function _sanitize_global_styles_callback( $stylesheet ) {
|
||||
return urldecode( $stylesheet );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +138,7 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller {
|
||||
protected function get_post( $id ) {
|
||||
$error = new WP_Error(
|
||||
'rest_global_styles_not_found',
|
||||
__( 'No global styles config exist with that id.' ),
|
||||
__( 'No global styles config exists with that ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
|
||||
@@ -464,7 +463,7 @@ class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller {
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'ID of global styles config.' ),
|
||||
'type' => 'string',
|
||||
'type' => 'integer',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
|
||||
@@ -203,9 +203,15 @@ class WP_REST_Global_Styles_Revisions_Controller extends WP_REST_Revisions_Contr
|
||||
$total_revisions = $revisions_query->found_posts;
|
||||
|
||||
if ( $total_revisions < 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
// Out-of-bounds, run the query without pagination/offset to get the total count.
|
||||
unset( $query_args['paged'], $query_args['offset'] );
|
||||
$count_query = new WP_Query();
|
||||
|
||||
$count_query = new WP_Query();
|
||||
$query_args['fields'] = 'ids';
|
||||
$query_args['posts_per_page'] = 1;
|
||||
$query_args['update_post_meta_cache'] = false;
|
||||
$query_args['update_post_term_cache'] = false;
|
||||
|
||||
$count_query->query( $query_args );
|
||||
|
||||
$total_revisions = $count_query->found_posts;
|
||||
|
||||
@@ -88,7 +88,7 @@ class WP_REST_Menu_Items_Controller extends WP_REST_Posts_Controller {
|
||||
* @param bool $read_only_access Whether the current user has read access to menu items
|
||||
* via the REST API.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @param WP_REST_Controller $this The current instance of the controller.
|
||||
* @param WP_REST_Controller $controller The current instance of the controller.
|
||||
*/
|
||||
$read_only_access = apply_filters( 'rest_menu_read_access', false, $request, $this );
|
||||
if ( $read_only_access ) {
|
||||
|
||||
@@ -487,10 +487,15 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
||||
$total_posts = $posts_query->found_posts;
|
||||
|
||||
if ( $total_posts < 1 && $page > 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
// Out-of-bounds, run the query without pagination/offset to get the total count.
|
||||
unset( $query_args['paged'] );
|
||||
|
||||
$count_query = new WP_Query();
|
||||
$count_query = new WP_Query();
|
||||
$query_args['fields'] = 'ids';
|
||||
$query_args['posts_per_page'] = 1;
|
||||
$query_args['update_post_meta_cache'] = false;
|
||||
$query_args['update_post_term_cache'] = false;
|
||||
|
||||
$count_query->query( $query_args );
|
||||
$total_posts = $count_query->found_posts;
|
||||
}
|
||||
|
||||
@@ -308,12 +308,16 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
||||
$total_revisions = $revisions_query->found_posts;
|
||||
|
||||
if ( $total_revisions < 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
// Out-of-bounds, run the query without pagination/offset to get the total count.
|
||||
unset( $query_args['paged'], $query_args['offset'] );
|
||||
|
||||
$count_query = new WP_Query();
|
||||
$count_query->query( $query_args );
|
||||
$count_query = new WP_Query();
|
||||
$query_args['fields'] = 'ids';
|
||||
$query_args['posts_per_page'] = 1;
|
||||
$query_args['update_post_meta_cache'] = false;
|
||||
$query_args['update_post_term_cache'] = false;
|
||||
|
||||
$count_query->query( $query_args );
|
||||
$total_revisions = $count_query->found_posts;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,15 @@ class WP_REST_Themes_Controller extends WP_REST_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
if ( $theme->is_block_theme() && $this->is_same_theme( $theme, wp_get_theme() ) ) {
|
||||
$links['https://api.w.org/export-theme'] = array(
|
||||
'href' => rest_url( 'wp-block-editor/v1/export' ),
|
||||
'targetHints' => array(
|
||||
'allow' => current_user_can( 'export' ) ? array( 'GET' ) : array(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
|
||||
@@ -399,10 +399,13 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
||||
$total_users = $query->get_total();
|
||||
|
||||
if ( $total_users < 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
// Out-of-bounds, run the query without pagination/offset to get the total count.
|
||||
unset( $prepared_args['number'], $prepared_args['offset'] );
|
||||
$count_query = new WP_User_Query( $prepared_args );
|
||||
$total_users = $count_query->get_total();
|
||||
|
||||
$prepared_args['number'] = 1;
|
||||
$prepared_args['fields'] = 'ID';
|
||||
$count_query = new WP_User_Query( $prepared_args );
|
||||
$total_users = $count_query->get_total();
|
||||
}
|
||||
|
||||
$response->header( 'X-WP-Total', (int) $total_users );
|
||||
|
||||
@@ -100,7 +100,7 @@ class WP_REST_Post_Format_Search_Handler extends WP_REST_Search_Handler {
|
||||
* @type string $title Optional. Post format name.
|
||||
* @type string $url Optional. Post format permalink URL.
|
||||
* @type string $type Optional. String 'post-format'.
|
||||
*}
|
||||
* }
|
||||
*/
|
||||
public function prepare_item( $id, array $fields ) {
|
||||
$data = array();
|
||||
|
||||
Reference in New Issue
Block a user