080600
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,497 +1,497 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Autosaves_Controller class.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access autosaves via the REST API.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Revisions_Controller
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
|
||||
|
||||
/**
|
||||
* Parent post type.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_post_type;
|
||||
|
||||
/**
|
||||
* Parent post controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var WP_REST_Controller
|
||||
*/
|
||||
private $parent_controller;
|
||||
|
||||
/**
|
||||
* Revision controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var WP_REST_Revisions_Controller
|
||||
*/
|
||||
private $revisions_controller;
|
||||
|
||||
/**
|
||||
* The base of the parent controller's route.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_base;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param string $parent_post_type Post type of the parent.
|
||||
*/
|
||||
public function __construct( $parent_post_type ) {
|
||||
$this->parent_post_type = $parent_post_type;
|
||||
$post_type_object = get_post_type_object( $parent_post_type );
|
||||
$parent_controller = $post_type_object->get_rest_controller();
|
||||
|
||||
if ( ! $parent_controller ) {
|
||||
$parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
|
||||
}
|
||||
|
||||
$this->parent_controller = $parent_controller;
|
||||
|
||||
$revisions_controller = $post_type_object->get_revisions_rest_controller();
|
||||
if ( ! $revisions_controller ) {
|
||||
$revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
|
||||
}
|
||||
$this->revisions_controller = $revisions_controller;
|
||||
$this->rest_base = 'autosaves';
|
||||
$this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
|
||||
$this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for autosaves.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->parent_base . '/(?P<id>[\d]+)/' . $this->rest_base,
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The ID for the parent of the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->parent_controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The ID for the parent of the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'The ID for the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent post.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param int $parent_id Supplied ID.
|
||||
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function get_parent( $parent_id ) {
|
||||
return $this->revisions_controller->get_parent( $parent_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to get autosaves.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
$parent = $this->get_parent( $request['id'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'edit_post', $parent->ID ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_read',
|
||||
__( 'Sorry, you are not allowed to view autosaves of this post.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to create an autosave revision.
|
||||
*
|
||||
* Autosave revisions inherit permissions from the parent post,
|
||||
* check if the current user has permission to edit the post.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
$id = $request->get_param( 'id' );
|
||||
|
||||
if ( empty( $id ) ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_id',
|
||||
__( 'Invalid item ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->parent_controller->update_item_permissions_check( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates, updates or deletes an autosave revision.
|
||||
*
|
||||
* @since 5.0.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 create_item( $request ) {
|
||||
|
||||
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && ! defined( 'DOING_AUTOSAVE' ) ) {
|
||||
define( 'DOING_AUTOSAVE', true );
|
||||
}
|
||||
|
||||
$post = $this->get_parent( $request['id'] );
|
||||
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
$prepared_post = $this->parent_controller->prepare_item_for_database( $request );
|
||||
$prepared_post->ID = $post->ID;
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// We need to check post lock to ensure the original author didn't leave their browser tab open.
|
||||
if ( ! function_exists( 'wp_check_post_lock' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/post.php';
|
||||
}
|
||||
|
||||
$post_lock = wp_check_post_lock( $post->ID );
|
||||
$is_draft = 'draft' === $post->post_status || 'auto-draft' === $post->post_status;
|
||||
|
||||
if ( $is_draft && (int) $post->post_author === $user_id && ! $post_lock ) {
|
||||
/*
|
||||
* Draft posts for the same author: autosaving updates the post and does not create a revision.
|
||||
* Convert the post object to an array and add slashes, wp_update_post() expects escaped array.
|
||||
*/
|
||||
$autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
|
||||
} else {
|
||||
// Non-draft posts: create or update the post autosave. Pass the meta data.
|
||||
$autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $autosave_id ) ) {
|
||||
return $autosave_id;
|
||||
}
|
||||
|
||||
$autosave = get_post( $autosave_id );
|
||||
$request->set_param( 'context', 'edit' );
|
||||
|
||||
$response = $this->prepare_item_for_response( $autosave, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the autosave, if the ID is valid.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$parent_id = (int) $request->get_param( 'parent' );
|
||||
|
||||
if ( $parent_id <= 0 ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_id',
|
||||
__( 'Invalid post parent ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$autosave = wp_get_post_autosave( $parent_id );
|
||||
|
||||
if ( ! $autosave ) {
|
||||
return new WP_Error(
|
||||
'rest_post_no_autosave',
|
||||
__( 'There is no autosave revision for this post.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $autosave, $request );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a collection of autosaves using wp_get_post_autosave.
|
||||
*
|
||||
* Contains the user's autosave, for empty if it doesn't exist.
|
||||
*
|
||||
* @since 5.0.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 ) {
|
||||
$parent = $this->get_parent( $request['id'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
$response = array();
|
||||
$parent_id = $parent->ID;
|
||||
$revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
|
||||
|
||||
foreach ( $revisions as $revision ) {
|
||||
if ( str_contains( $revision->post_name, "{$parent_id}-autosave" ) ) {
|
||||
$data = $this->prepare_item_for_response( $revision, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
}
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the autosave's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = $this->revisions_controller->get_item_schema();
|
||||
|
||||
$schema['properties']['preview_link'] = array(
|
||||
'description' => __( 'Preview link for the post.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates autosave for the specified post.
|
||||
*
|
||||
* From wp-admin/post.php.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 6.4.0 The `$meta` parameter was added.
|
||||
*
|
||||
* @param array $post_data Associative array containing the post data.
|
||||
* @param array $meta Associative array containing the post meta data.
|
||||
* @return mixed The autosave revision ID or WP_Error.
|
||||
*/
|
||||
public function create_post_autosave( $post_data, array $meta = array() ) {
|
||||
|
||||
$post_id = (int) $post_data['ID'];
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
// Only create an autosave when it is different from the saved post.
|
||||
$autosave_is_different = false;
|
||||
$new_autosave = _wp_post_revision_data( $post_data, true );
|
||||
|
||||
foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
|
||||
if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) {
|
||||
$autosave_is_different = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if meta values have changed.
|
||||
if ( ! empty( $meta ) ) {
|
||||
$revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type );
|
||||
foreach ( $revisioned_meta_keys as $meta_key ) {
|
||||
// get_metadata_raw is used to avoid retrieving the default value.
|
||||
$old_meta = get_metadata_raw( 'post', $post_id, $meta_key, true );
|
||||
$new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : '';
|
||||
|
||||
if ( $new_meta !== $old_meta ) {
|
||||
$autosave_is_different = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// Store one autosave per author. If there is already an autosave, overwrite it.
|
||||
$old_autosave = wp_get_post_autosave( $post_id, $user_id );
|
||||
|
||||
if ( ! $autosave_is_different && $old_autosave ) {
|
||||
// Nothing to save, return the existing autosave.
|
||||
return $old_autosave->ID;
|
||||
}
|
||||
|
||||
if ( $old_autosave ) {
|
||||
$new_autosave['ID'] = $old_autosave->ID;
|
||||
$new_autosave['post_author'] = $user_id;
|
||||
|
||||
/** This filter is documented in wp-admin/post.php */
|
||||
do_action( 'wp_creating_autosave', $new_autosave );
|
||||
|
||||
// wp_update_post() expects escaped array.
|
||||
$revision_id = wp_update_post( wp_slash( $new_autosave ) );
|
||||
} else {
|
||||
// Create the new autosave as a special post revision.
|
||||
$revision_id = _wp_put_post_revision( $post_data, true );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $revision_id ) || 0 === $revision_id ) {
|
||||
return $revision_id;
|
||||
}
|
||||
|
||||
// Attached any passed meta values that have revisions enabled.
|
||||
if ( ! empty( $meta ) ) {
|
||||
foreach ( $revisioned_meta_keys as $meta_key ) {
|
||||
if ( isset( $meta[ $meta_key ] ) ) {
|
||||
update_metadata( 'post', $revision_id, $meta_key, wp_slash( $meta[ $meta_key ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $revision_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the revision for the REST response.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param WP_Post $item Post revision object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$post = $item;
|
||||
|
||||
$response = $this->revisions_controller->prepare_item_for_response( $post, $request );
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
if ( in_array( 'preview_link', $fields, true ) ) {
|
||||
$parent_id = wp_is_post_autosave( $post );
|
||||
$preview_post_id = false === $parent_id ? $post->ID : $parent_id;
|
||||
$preview_query_args = array();
|
||||
|
||||
if ( false !== $parent_id ) {
|
||||
$preview_query_args['preview_id'] = $parent_id;
|
||||
$preview_query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $parent_id );
|
||||
}
|
||||
|
||||
$response->data['preview_link'] = get_preview_post_link( $preview_post_id, $preview_query_args );
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$response->data = $this->add_additional_fields_to_object( $response->data, $request );
|
||||
$response->data = $this->filter_response_by_context( $response->data, $context );
|
||||
|
||||
/**
|
||||
* Filters a revision returned from the REST API.
|
||||
*
|
||||
* Allows modification of the revision right before it is returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post $post The original revision object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for the autosaves collection.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Autosaves_Controller class.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access autosaves via the REST API.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Revisions_Controller
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
|
||||
|
||||
/**
|
||||
* Parent post type.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_post_type;
|
||||
|
||||
/**
|
||||
* Parent post controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var WP_REST_Controller
|
||||
*/
|
||||
private $parent_controller;
|
||||
|
||||
/**
|
||||
* Revision controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var WP_REST_Revisions_Controller
|
||||
*/
|
||||
private $revisions_controller;
|
||||
|
||||
/**
|
||||
* The base of the parent controller's route.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_base;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param string $parent_post_type Post type of the parent.
|
||||
*/
|
||||
public function __construct( $parent_post_type ) {
|
||||
$this->parent_post_type = $parent_post_type;
|
||||
$post_type_object = get_post_type_object( $parent_post_type );
|
||||
$parent_controller = $post_type_object->get_rest_controller();
|
||||
|
||||
if ( ! $parent_controller ) {
|
||||
$parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
|
||||
}
|
||||
|
||||
$this->parent_controller = $parent_controller;
|
||||
|
||||
$revisions_controller = $post_type_object->get_revisions_rest_controller();
|
||||
if ( ! $revisions_controller ) {
|
||||
$revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
|
||||
}
|
||||
$this->revisions_controller = $revisions_controller;
|
||||
$this->rest_base = 'autosaves';
|
||||
$this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
|
||||
$this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for autosaves.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->parent_base . '/(?P<id>[\d]+)/' . $this->rest_base,
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The ID for the parent of the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->parent_controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The ID for the parent of the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'The ID for the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent post.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param int $parent_id Supplied ID.
|
||||
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function get_parent( $parent_id ) {
|
||||
return $this->revisions_controller->get_parent( $parent_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to get autosaves.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
$parent = $this->get_parent( $request['id'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'edit_post', $parent->ID ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_read',
|
||||
__( 'Sorry, you are not allowed to view autosaves of this post.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to create an autosave revision.
|
||||
*
|
||||
* Autosave revisions inherit permissions from the parent post,
|
||||
* check if the current user has permission to edit the post.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has access to create the item, WP_Error object otherwise.
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
$id = $request->get_param( 'id' );
|
||||
|
||||
if ( empty( $id ) ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_id',
|
||||
__( 'Invalid item ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->parent_controller->update_item_permissions_check( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates, updates or deletes an autosave revision.
|
||||
*
|
||||
* @since 5.0.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 create_item( $request ) {
|
||||
|
||||
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && ! defined( 'DOING_AUTOSAVE' ) ) {
|
||||
define( 'DOING_AUTOSAVE', true );
|
||||
}
|
||||
|
||||
$post = $this->get_parent( $request['id'] );
|
||||
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
$prepared_post = $this->parent_controller->prepare_item_for_database( $request );
|
||||
$prepared_post->ID = $post->ID;
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// We need to check post lock to ensure the original author didn't leave their browser tab open.
|
||||
if ( ! function_exists( 'wp_check_post_lock' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/post.php';
|
||||
}
|
||||
|
||||
$post_lock = wp_check_post_lock( $post->ID );
|
||||
$is_draft = 'draft' === $post->post_status || 'auto-draft' === $post->post_status;
|
||||
|
||||
if ( $is_draft && (int) $post->post_author === $user_id && ! $post_lock ) {
|
||||
/*
|
||||
* Draft posts for the same author: autosaving updates the post and does not create a revision.
|
||||
* Convert the post object to an array and add slashes, wp_update_post() expects escaped array.
|
||||
*/
|
||||
$autosave_id = wp_update_post( wp_slash( (array) $prepared_post ), true );
|
||||
} else {
|
||||
// Non-draft posts: create or update the post autosave. Pass the meta data.
|
||||
$autosave_id = $this->create_post_autosave( (array) $prepared_post, (array) $request->get_param( 'meta' ) );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $autosave_id ) ) {
|
||||
return $autosave_id;
|
||||
}
|
||||
|
||||
$autosave = get_post( $autosave_id );
|
||||
$request->set_param( 'context', 'edit' );
|
||||
|
||||
$response = $this->prepare_item_for_response( $autosave, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the autosave, if the ID is valid.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$parent_id = (int) $request->get_param( 'parent' );
|
||||
|
||||
if ( $parent_id <= 0 ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_id',
|
||||
__( 'Invalid post parent ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$autosave = wp_get_post_autosave( $parent_id );
|
||||
|
||||
if ( ! $autosave ) {
|
||||
return new WP_Error(
|
||||
'rest_post_no_autosave',
|
||||
__( 'There is no autosave revision for this post.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $autosave, $request );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a collection of autosaves using wp_get_post_autosave.
|
||||
*
|
||||
* Contains the user's autosave, for empty if it doesn't exist.
|
||||
*
|
||||
* @since 5.0.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 ) {
|
||||
$parent = $this->get_parent( $request['id'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
$response = array();
|
||||
$parent_id = $parent->ID;
|
||||
$revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
|
||||
|
||||
foreach ( $revisions as $revision ) {
|
||||
if ( str_contains( $revision->post_name, "{$parent_id}-autosave" ) ) {
|
||||
$data = $this->prepare_item_for_response( $revision, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
}
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the autosave's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = $this->revisions_controller->get_item_schema();
|
||||
|
||||
$schema['properties']['preview_link'] = array(
|
||||
'description' => __( 'Preview link for the post.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates autosave for the specified post.
|
||||
*
|
||||
* From wp-admin/post.php.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 6.4.0 The `$meta` parameter was added.
|
||||
*
|
||||
* @param array $post_data Associative array containing the post data.
|
||||
* @param array $meta Associative array containing the post meta data.
|
||||
* @return mixed The autosave revision ID or WP_Error.
|
||||
*/
|
||||
public function create_post_autosave( $post_data, array $meta = array() ) {
|
||||
|
||||
$post_id = (int) $post_data['ID'];
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
// Only create an autosave when it is different from the saved post.
|
||||
$autosave_is_different = false;
|
||||
$new_autosave = _wp_post_revision_data( $post_data, true );
|
||||
|
||||
foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
|
||||
if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) {
|
||||
$autosave_is_different = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if meta values have changed.
|
||||
if ( ! empty( $meta ) ) {
|
||||
$revisioned_meta_keys = wp_post_revision_meta_keys( $post->post_type );
|
||||
foreach ( $revisioned_meta_keys as $meta_key ) {
|
||||
// get_metadata_raw is used to avoid retrieving the default value.
|
||||
$old_meta = get_metadata_raw( 'post', $post_id, $meta_key, true );
|
||||
$new_meta = isset( $meta[ $meta_key ] ) ? $meta[ $meta_key ] : '';
|
||||
|
||||
if ( $new_meta !== $old_meta ) {
|
||||
$autosave_is_different = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// Store one autosave per author. If there is already an autosave, overwrite it.
|
||||
$old_autosave = wp_get_post_autosave( $post_id, $user_id );
|
||||
|
||||
if ( ! $autosave_is_different && $old_autosave ) {
|
||||
// Nothing to save, return the existing autosave.
|
||||
return $old_autosave->ID;
|
||||
}
|
||||
|
||||
if ( $old_autosave ) {
|
||||
$new_autosave['ID'] = $old_autosave->ID;
|
||||
$new_autosave['post_author'] = $user_id;
|
||||
|
||||
/** This filter is documented in wp-admin/post.php */
|
||||
do_action( 'wp_creating_autosave', $new_autosave );
|
||||
|
||||
// wp_update_post() expects escaped array.
|
||||
$revision_id = wp_update_post( wp_slash( $new_autosave ) );
|
||||
} else {
|
||||
// Create the new autosave as a special post revision.
|
||||
$revision_id = _wp_put_post_revision( $post_data, true );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $revision_id ) || 0 === $revision_id ) {
|
||||
return $revision_id;
|
||||
}
|
||||
|
||||
// Attached any passed meta values that have revisions enabled.
|
||||
if ( ! empty( $meta ) ) {
|
||||
foreach ( $revisioned_meta_keys as $meta_key ) {
|
||||
if ( isset( $meta[ $meta_key ] ) ) {
|
||||
update_metadata( 'post', $revision_id, $meta_key, wp_slash( $meta[ $meta_key ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $revision_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the revision for the REST response.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param WP_Post $item Post revision object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$post = $item;
|
||||
|
||||
$response = $this->revisions_controller->prepare_item_for_response( $post, $request );
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
if ( in_array( 'preview_link', $fields, true ) ) {
|
||||
$parent_id = wp_is_post_autosave( $post );
|
||||
$preview_post_id = false === $parent_id ? $post->ID : $parent_id;
|
||||
$preview_query_args = array();
|
||||
|
||||
if ( false !== $parent_id ) {
|
||||
$preview_query_args['preview_id'] = $parent_id;
|
||||
$preview_query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $parent_id );
|
||||
}
|
||||
|
||||
$response->data['preview_link'] = get_preview_post_link( $preview_post_id, $preview_query_args );
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$response->data = $this->add_additional_fields_to_object( $response->data, $request );
|
||||
$response->data = $this->filter_response_by_context( $response->data, $context );
|
||||
|
||||
/**
|
||||
* Filters a revision returned from the REST API.
|
||||
*
|
||||
* Allows modification of the revision right before it is returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post $post The original revision object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for the autosaves collection.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,328 +1,328 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Block_Directory_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.5.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for the blocks.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Directory_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-directory';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the necessary REST API routes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/search',
|
||||
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' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to install and activate plugins.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has permission, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_block_directory_cannot_view',
|
||||
__( 'Sorry, you are not allowed to browse the block directory.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and retrieve blocks metadata
|
||||
*
|
||||
* @since 5.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 ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
$response = plugins_api(
|
||||
'query_plugins',
|
||||
array(
|
||||
'block' => $request['term'],
|
||||
'per_page' => $request['per_page'],
|
||||
'page' => $request['page'],
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$response->add_data( array( 'status' => 500 ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ( $response->plugins as $plugin ) {
|
||||
// If the API returned a plugin with empty data for 'blocks', skip it.
|
||||
if ( empty( $plugin['blocks'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $plugin, $request );
|
||||
$result[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse block metadata for a block, and prepare it for an API response.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @since 5.9.0 Renamed `$plugin` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param array $item The plugin metadata.
|
||||
* @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 ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$plugin = $item;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
// There might be multiple blocks in a plugin. Only the first block is mapped.
|
||||
$block_data = reset( $plugin['blocks'] );
|
||||
|
||||
// A data array containing the properties we'll return.
|
||||
$block = array(
|
||||
'name' => $block_data['name'],
|
||||
'title' => ( $block_data['title'] ? $block_data['title'] : $plugin['name'] ),
|
||||
'description' => wp_trim_words( $plugin['short_description'], 30, '...' ),
|
||||
'id' => $plugin['slug'],
|
||||
'rating' => $plugin['rating'] / 20,
|
||||
'rating_count' => (int) $plugin['num_ratings'],
|
||||
'active_installs' => (int) $plugin['active_installs'],
|
||||
'author_block_rating' => $plugin['author_block_rating'] / 20,
|
||||
'author_block_count' => (int) $plugin['author_block_count'],
|
||||
'author' => wp_strip_all_tags( $plugin['author'] ),
|
||||
'icon' => ( isset( $plugin['icons']['1x'] ) ? $plugin['icons']['1x'] : 'block-default' ),
|
||||
'last_updated' => gmdate( 'Y-m-d\TH:i:s', strtotime( $plugin['last_updated'] ) ),
|
||||
'humanized_updated' => sprintf(
|
||||
/* translators: %s: Human-readable time difference. */
|
||||
__( '%s ago' ),
|
||||
human_time_diff( strtotime( $plugin['last_updated'] ) )
|
||||
),
|
||||
);
|
||||
|
||||
$this->add_additional_fields_to_object( $block, $request );
|
||||
|
||||
$response = new WP_REST_Response( $block );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $plugin ) );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of links to include in the response for the plugin.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param array $plugin The plugin data from WordPress.org.
|
||||
* @return array
|
||||
*/
|
||||
protected function prepare_links( $plugin ) {
|
||||
$links = array(
|
||||
'https://api.w.org/install-plugin' => array(
|
||||
'href' => add_query_arg( 'slug', urlencode( $plugin['slug'] ), rest_url( 'wp/v2/plugins' ) ),
|
||||
),
|
||||
);
|
||||
|
||||
$plugin_file = $this->find_plugin_for_slug( $plugin['slug'] );
|
||||
|
||||
if ( $plugin_file ) {
|
||||
$links['https://api.w.org/plugin'] = array(
|
||||
'href' => rest_url( 'wp/v2/plugins/' . substr( $plugin_file, 0, - 4 ) ),
|
||||
'embeddable' => true,
|
||||
);
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an installed plugin for the given slug.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param string $slug The WordPress.org directory slug for a plugin.
|
||||
* @return string The plugin file found matching it.
|
||||
*/
|
||||
protected function find_plugin_for_slug( $slug ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
$plugin_files = get_plugins( '/' . $slug );
|
||||
|
||||
if ( ! $plugin_files ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$plugin_files = array_keys( $plugin_files );
|
||||
|
||||
return $slug . '/' . reset( $plugin_files );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the theme's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.5.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' => 'block-directory-item',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The block name, in namespace/block-name format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'title' => array(
|
||||
'description' => __( 'The block title, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A short description of the block, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'The block slug.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'rating' => array(
|
||||
'description' => __( 'The star rating of the block.' ),
|
||||
'type' => 'number',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'rating_count' => array(
|
||||
'description' => __( 'The number of ratings.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'active_installs' => array(
|
||||
'description' => __( 'The number sites that have activated this block.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'author_block_rating' => array(
|
||||
'description' => __( 'The average rating of blocks published by the same author.' ),
|
||||
'type' => 'number',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'author_block_count' => array(
|
||||
'description' => __( 'The number of blocks published by the same author.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'author' => array(
|
||||
'description' => __( 'The WordPress.org username of the block author.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'icon' => array(
|
||||
'description' => __( 'The block icon.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'last_updated' => array(
|
||||
'description' => __( 'The date when the block was last updated.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'humanized_updated' => array(
|
||||
'description' => __( 'The date when the block was last updated, in fuzzy human readable format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the search params for the blocks collection.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['context']['default'] = 'view';
|
||||
|
||||
$query_params['term'] = array(
|
||||
'description' => __( 'Limit result set to blocks matching the search term.' ),
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'minLength' => 1,
|
||||
);
|
||||
|
||||
unset( $query_params['search'] );
|
||||
|
||||
/**
|
||||
* Filters REST API collection parameters for the block directory controller.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param array $query_params JSON Schema-formatted collection parameters.
|
||||
*/
|
||||
return apply_filters( 'rest_block_directory_collection_params', $query_params );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Block_Directory_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.5.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for the blocks.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Directory_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-directory';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the necessary REST API routes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/search',
|
||||
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' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to install and activate plugins.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has permission, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_block_directory_cannot_view',
|
||||
__( 'Sorry, you are not allowed to browse the block directory.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and retrieve blocks metadata
|
||||
*
|
||||
* @since 5.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 ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
$response = plugins_api(
|
||||
'query_plugins',
|
||||
array(
|
||||
'block' => $request['term'],
|
||||
'per_page' => $request['per_page'],
|
||||
'page' => $request['page'],
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$response->add_data( array( 'status' => 500 ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ( $response->plugins as $plugin ) {
|
||||
// If the API returned a plugin with empty data for 'blocks', skip it.
|
||||
if ( empty( $plugin['blocks'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $plugin, $request );
|
||||
$result[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse block metadata for a block, and prepare it for an API response.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @since 5.9.0 Renamed `$plugin` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param array $item The plugin metadata.
|
||||
* @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 ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$plugin = $item;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
// There might be multiple blocks in a plugin. Only the first block is mapped.
|
||||
$block_data = reset( $plugin['blocks'] );
|
||||
|
||||
// A data array containing the properties we'll return.
|
||||
$block = array(
|
||||
'name' => $block_data['name'],
|
||||
'title' => ( $block_data['title'] ? $block_data['title'] : $plugin['name'] ),
|
||||
'description' => wp_trim_words( $plugin['short_description'], 30, '...' ),
|
||||
'id' => $plugin['slug'],
|
||||
'rating' => $plugin['rating'] / 20,
|
||||
'rating_count' => (int) $plugin['num_ratings'],
|
||||
'active_installs' => (int) $plugin['active_installs'],
|
||||
'author_block_rating' => $plugin['author_block_rating'] / 20,
|
||||
'author_block_count' => (int) $plugin['author_block_count'],
|
||||
'author' => wp_strip_all_tags( $plugin['author'] ),
|
||||
'icon' => ( isset( $plugin['icons']['1x'] ) ? $plugin['icons']['1x'] : 'block-default' ),
|
||||
'last_updated' => gmdate( 'Y-m-d\TH:i:s', strtotime( $plugin['last_updated'] ) ),
|
||||
'humanized_updated' => sprintf(
|
||||
/* translators: %s: Human-readable time difference. */
|
||||
__( '%s ago' ),
|
||||
human_time_diff( strtotime( $plugin['last_updated'] ) )
|
||||
),
|
||||
);
|
||||
|
||||
$this->add_additional_fields_to_object( $block, $request );
|
||||
|
||||
$response = new WP_REST_Response( $block );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $plugin ) );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of links to include in the response for the plugin.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param array $plugin The plugin data from WordPress.org.
|
||||
* @return array
|
||||
*/
|
||||
protected function prepare_links( $plugin ) {
|
||||
$links = array(
|
||||
'https://api.w.org/install-plugin' => array(
|
||||
'href' => add_query_arg( 'slug', urlencode( $plugin['slug'] ), rest_url( 'wp/v2/plugins' ) ),
|
||||
),
|
||||
);
|
||||
|
||||
$plugin_file = $this->find_plugin_for_slug( $plugin['slug'] );
|
||||
|
||||
if ( $plugin_file ) {
|
||||
$links['https://api.w.org/plugin'] = array(
|
||||
'href' => rest_url( 'wp/v2/plugins/' . substr( $plugin_file, 0, - 4 ) ),
|
||||
'embeddable' => true,
|
||||
);
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an installed plugin for the given slug.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param string $slug The WordPress.org directory slug for a plugin.
|
||||
* @return string The plugin file found matching it.
|
||||
*/
|
||||
protected function find_plugin_for_slug( $slug ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
$plugin_files = get_plugins( '/' . $slug );
|
||||
|
||||
if ( ! $plugin_files ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$plugin_files = array_keys( $plugin_files );
|
||||
|
||||
return $slug . '/' . reset( $plugin_files );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the theme's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.5.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' => 'block-directory-item',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The block name, in namespace/block-name format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'title' => array(
|
||||
'description' => __( 'The block title, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A short description of the block, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'The block slug.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'rating' => array(
|
||||
'description' => __( 'The star rating of the block.' ),
|
||||
'type' => 'number',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'rating_count' => array(
|
||||
'description' => __( 'The number of ratings.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'active_installs' => array(
|
||||
'description' => __( 'The number sites that have activated this block.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'author_block_rating' => array(
|
||||
'description' => __( 'The average rating of blocks published by the same author.' ),
|
||||
'type' => 'number',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'author_block_count' => array(
|
||||
'description' => __( 'The number of blocks published by the same author.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'author' => array(
|
||||
'description' => __( 'The WordPress.org username of the block author.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'icon' => array(
|
||||
'description' => __( 'The block icon.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'last_updated' => array(
|
||||
'description' => __( 'The date when the block was last updated.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
'humanized_updated' => array(
|
||||
'description' => __( 'The date when the block was last updated, in fuzzy human readable format.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the search params for the blocks collection.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['context']['default'] = 'view';
|
||||
|
||||
$query_params['term'] = array(
|
||||
'description' => __( 'Limit result set to blocks matching the search term.' ),
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'minLength' => 1,
|
||||
);
|
||||
|
||||
unset( $query_params['search'] );
|
||||
|
||||
/**
|
||||
* Filters REST API collection parameters for the block directory controller.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param array $query_params JSON Schema-formatted collection parameters.
|
||||
*/
|
||||
return apply_filters( 'rest_block_directory_collection_params', $query_params );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,162 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Block_Pattern_Categories_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access block pattern categories via the REST API.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Pattern_Categories_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-patterns/categories';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
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' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read block patterns.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
|
||||
if ( current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view the registered block pattern categories.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all block pattern categories.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$response = array();
|
||||
$categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered();
|
||||
foreach ( $categories as $category ) {
|
||||
$prepared_category = $this->prepare_item_for_response( $category, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $prepared_category );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a raw block pattern category before it gets output in a REST API response.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param array $item Raw category as registered, before any changes.
|
||||
* @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 ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$keys = array( 'name', 'label', 'description' );
|
||||
$data = array();
|
||||
foreach ( $keys as $key ) {
|
||||
if ( isset( $item[ $key ] ) && rest_is_field_included( $key, $fields ) ) {
|
||||
$data[ $key ] = $item[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block pattern category schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'block-pattern-category',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The category name.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'label' => array(
|
||||
'description' => __( 'The category label, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The category description, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Block_Pattern_Categories_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access block pattern categories via the REST API.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Pattern_Categories_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-patterns/categories';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
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' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read block patterns.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
|
||||
if ( current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view the registered block pattern categories.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all block pattern categories.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$response = array();
|
||||
$categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered();
|
||||
foreach ( $categories as $category ) {
|
||||
$prepared_category = $this->prepare_item_for_response( $category, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $prepared_category );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a raw block pattern category before it gets output in a REST API response.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param array $item Raw category as registered, before any changes.
|
||||
* @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 ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$keys = array( 'name', 'label', 'description' );
|
||||
$data = array();
|
||||
foreach ( $keys as $key ) {
|
||||
if ( isset( $item[ $key ] ) && rest_is_field_included( $key, $fields ) ) {
|
||||
$data[ $key ] = $item[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block pattern category schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'block-pattern-category',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The category name.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'label' => array(
|
||||
'description' => __( 'The category label, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The category description, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,298 +1,298 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Block_Patterns_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access block patterns via the REST API.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Patterns_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Defines whether remote patterns should be loaded.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @var bool
|
||||
*/
|
||||
private $remote_patterns_loaded;
|
||||
|
||||
/**
|
||||
* An array that maps old categories names to new ones.
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @var array
|
||||
*/
|
||||
protected static $categories_migration = array(
|
||||
'buttons' => 'call-to-action',
|
||||
'columns' => 'text',
|
||||
'query' => 'posts',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-patterns/patterns';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
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' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read block patterns.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
|
||||
if ( current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view the registered block patterns.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all block patterns.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.2.0 Added migration for old core pattern categories to the new ones.
|
||||
*
|
||||
* @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 ) {
|
||||
if ( ! $this->remote_patterns_loaded ) {
|
||||
// Load block patterns from w.org.
|
||||
_load_remote_block_patterns(); // Patterns with the `core` keyword.
|
||||
_load_remote_featured_patterns(); // Patterns in the `featured` category.
|
||||
_register_remote_theme_patterns(); // Patterns requested by current theme.
|
||||
|
||||
$this->remote_patterns_loaded = true;
|
||||
}
|
||||
|
||||
$response = array();
|
||||
$patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered();
|
||||
foreach ( $patterns as $pattern ) {
|
||||
$migrated_pattern = $this->migrate_pattern_categories( $pattern );
|
||||
$prepared_pattern = $this->prepare_item_for_response( $migrated_pattern, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $prepared_pattern );
|
||||
}
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates old core pattern categories to the new categories.
|
||||
*
|
||||
* Core pattern categories are revamped. Migration is needed to ensure
|
||||
* backwards compatibility.
|
||||
*
|
||||
* @since 6.2.0
|
||||
*
|
||||
* @param array $pattern Raw pattern as registered, before applying any changes.
|
||||
* @return array Migrated pattern.
|
||||
*/
|
||||
protected function migrate_pattern_categories( $pattern ) {
|
||||
// No categories to migrate.
|
||||
if (
|
||||
! isset( $pattern['categories'] ) ||
|
||||
! is_array( $pattern['categories'] )
|
||||
) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
foreach ( $pattern['categories'] as $index => $category ) {
|
||||
// If the category exists as a key, then it needs migration.
|
||||
if ( isset( static::$categories_migration[ $category ] ) ) {
|
||||
$pattern['categories'][ $index ] = static::$categories_migration[ $category ];
|
||||
}
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a raw block pattern before it gets output in a REST API response.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.3.0 Added `source` property.
|
||||
*
|
||||
* @param array $item Raw pattern as registered, before any changes.
|
||||
* @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 ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$keys = array(
|
||||
'name' => 'name',
|
||||
'title' => 'title',
|
||||
'content' => 'content',
|
||||
'description' => 'description',
|
||||
'viewportWidth' => 'viewport_width',
|
||||
'inserter' => 'inserter',
|
||||
'categories' => 'categories',
|
||||
'keywords' => 'keywords',
|
||||
'blockTypes' => 'block_types',
|
||||
'postTypes' => 'post_types',
|
||||
'templateTypes' => 'template_types',
|
||||
'source' => 'source',
|
||||
);
|
||||
$data = array();
|
||||
foreach ( $keys as $item_key => $rest_key ) {
|
||||
if ( isset( $item[ $item_key ] ) && rest_is_field_included( $rest_key, $fields ) ) {
|
||||
$data[ $rest_key ] = $item[ $item_key ];
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block pattern schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.3.0 Added `source` property.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'block-pattern',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The pattern name.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'title' => array(
|
||||
'description' => __( 'The pattern title, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'content' => array(
|
||||
'description' => __( 'The pattern content.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The pattern detailed description.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'viewport_width' => array(
|
||||
'description' => __( 'The pattern viewport width for inserter preview.' ),
|
||||
'type' => 'number',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'inserter' => array(
|
||||
'description' => __( 'Determines whether the pattern is visible in inserter.' ),
|
||||
'type' => 'boolean',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'categories' => array(
|
||||
'description' => __( 'The pattern category slugs.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'keywords' => array(
|
||||
'description' => __( 'The pattern keywords.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'block_types' => array(
|
||||
'description' => __( 'Block types that the pattern is intended to be used with.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'post_types' => array(
|
||||
'description' => __( 'An array of post types that the pattern is restricted to be used with.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'template_types' => array(
|
||||
'description' => __( 'An array of template types where the pattern fits.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'source' => array(
|
||||
'description' => __( 'Where the pattern comes from e.g. core' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'enum' => array(
|
||||
'core',
|
||||
'plugin',
|
||||
'theme',
|
||||
'pattern-directory/core',
|
||||
'pattern-directory/theme',
|
||||
'pattern-directory/featured',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Block_Patterns_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access block patterns via the REST API.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Patterns_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Defines whether remote patterns should be loaded.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @var bool
|
||||
*/
|
||||
private $remote_patterns_loaded;
|
||||
|
||||
/**
|
||||
* An array that maps old categories names to new ones.
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @var array
|
||||
*/
|
||||
protected static $categories_migration = array(
|
||||
'buttons' => 'call-to-action',
|
||||
'columns' => 'text',
|
||||
'query' => 'posts',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-patterns/patterns';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
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' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read block patterns.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
|
||||
if ( current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view the registered block patterns.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all block patterns.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.2.0 Added migration for old core pattern categories to the new ones.
|
||||
*
|
||||
* @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 ) {
|
||||
if ( ! $this->remote_patterns_loaded ) {
|
||||
// Load block patterns from w.org.
|
||||
_load_remote_block_patterns(); // Patterns with the `core` keyword.
|
||||
_load_remote_featured_patterns(); // Patterns in the `featured` category.
|
||||
_register_remote_theme_patterns(); // Patterns requested by current theme.
|
||||
|
||||
$this->remote_patterns_loaded = true;
|
||||
}
|
||||
|
||||
$response = array();
|
||||
$patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered();
|
||||
foreach ( $patterns as $pattern ) {
|
||||
$migrated_pattern = $this->migrate_pattern_categories( $pattern );
|
||||
$prepared_pattern = $this->prepare_item_for_response( $migrated_pattern, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $prepared_pattern );
|
||||
}
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates old core pattern categories to the new categories.
|
||||
*
|
||||
* Core pattern categories are revamped. Migration is needed to ensure
|
||||
* backwards compatibility.
|
||||
*
|
||||
* @since 6.2.0
|
||||
*
|
||||
* @param array $pattern Raw pattern as registered, before applying any changes.
|
||||
* @return array Migrated pattern.
|
||||
*/
|
||||
protected function migrate_pattern_categories( $pattern ) {
|
||||
// No categories to migrate.
|
||||
if (
|
||||
! isset( $pattern['categories'] ) ||
|
||||
! is_array( $pattern['categories'] )
|
||||
) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
foreach ( $pattern['categories'] as $index => $category ) {
|
||||
// If the category exists as a key, then it needs migration.
|
||||
if ( isset( static::$categories_migration[ $category ] ) ) {
|
||||
$pattern['categories'][ $index ] = static::$categories_migration[ $category ];
|
||||
}
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a raw block pattern before it gets output in a REST API response.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.3.0 Added `source` property.
|
||||
*
|
||||
* @param array $item Raw pattern as registered, before any changes.
|
||||
* @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 ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$keys = array(
|
||||
'name' => 'name',
|
||||
'title' => 'title',
|
||||
'content' => 'content',
|
||||
'description' => 'description',
|
||||
'viewportWidth' => 'viewport_width',
|
||||
'inserter' => 'inserter',
|
||||
'categories' => 'categories',
|
||||
'keywords' => 'keywords',
|
||||
'blockTypes' => 'block_types',
|
||||
'postTypes' => 'post_types',
|
||||
'templateTypes' => 'template_types',
|
||||
'source' => 'source',
|
||||
);
|
||||
$data = array();
|
||||
foreach ( $keys as $item_key => $rest_key ) {
|
||||
if ( isset( $item[ $item_key ] ) && rest_is_field_included( $rest_key, $fields ) ) {
|
||||
$data[ $rest_key ] = $item[ $item_key ];
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block pattern schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.3.0 Added `source` property.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'block-pattern',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The pattern name.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'title' => array(
|
||||
'description' => __( 'The pattern title, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'content' => array(
|
||||
'description' => __( 'The pattern content.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The pattern detailed description.' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'viewport_width' => array(
|
||||
'description' => __( 'The pattern viewport width for inserter preview.' ),
|
||||
'type' => 'number',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'inserter' => array(
|
||||
'description' => __( 'Determines whether the pattern is visible in inserter.' ),
|
||||
'type' => 'boolean',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'categories' => array(
|
||||
'description' => __( 'The pattern category slugs.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'keywords' => array(
|
||||
'description' => __( 'The pattern keywords.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'block_types' => array(
|
||||
'description' => __( 'Block types that the pattern is intended to be used with.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'post_types' => array(
|
||||
'description' => __( 'An array of post types that the pattern is restricted to be used with.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'template_types' => array(
|
||||
'description' => __( 'An array of template types where the pattern fits.' ),
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'source' => array(
|
||||
'description' => __( 'Where the pattern comes from e.g. core' ),
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'enum' => array(
|
||||
'core',
|
||||
'plugin',
|
||||
'theme',
|
||||
'pattern-directory/core',
|
||||
'pattern-directory/theme',
|
||||
'pattern-directory/featured',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,224 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* Block Renderer REST API: WP_REST_Block_Renderer_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for rendering a block.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Renderer_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-renderer';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the necessary REST API routes, one for each dynamic block.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<name>[a-z0-9-]+/[a-z0-9-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'Unique registered name for the block.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => array( WP_REST_Server::READABLE, WP_REST_Server::CREATABLE ),
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
'attributes' => array(
|
||||
'description' => __( 'Attributes for the block.' ),
|
||||
'type' => 'object',
|
||||
'default' => array(),
|
||||
'validate_callback' => static function ( $value, $request ) {
|
||||
$block = WP_Block_Type_Registry::get_instance()->get_registered( $request['name'] );
|
||||
|
||||
if ( ! $block ) {
|
||||
// This will get rejected in ::get_item().
|
||||
return true;
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => $block->get_attributes(),
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
return rest_validate_value_from_schema( $value, $schema );
|
||||
},
|
||||
'sanitize_callback' => static function ( $value, $request ) {
|
||||
$block = WP_Block_Type_Registry::get_instance()->get_registered( $request['name'] );
|
||||
|
||||
if ( ! $block ) {
|
||||
// This will get rejected in ::get_item().
|
||||
return true;
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => $block->get_attributes(),
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
return rest_sanitize_value_from_schema( $value, $schema );
|
||||
},
|
||||
),
|
||||
'post_id' => array(
|
||||
'description' => __( 'ID of the post context.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read blocks.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @global WP_Post $post Global post object.
|
||||
*
|
||||
* @param WP_REST_Request $request Request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
global $post;
|
||||
|
||||
$post_id = isset( $request['post_id'] ) ? (int) $request['post_id'] : 0;
|
||||
|
||||
if ( $post_id > 0 ) {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
|
||||
return new WP_Error(
|
||||
'block_cannot_read',
|
||||
__( 'Sorry, you are not allowed to read blocks of this post.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
return new WP_Error(
|
||||
'block_cannot_read',
|
||||
__( 'Sorry, you are not allowed to read blocks as this user.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns block output from block's registered render_callback.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @global WP_Post $post Global post object.
|
||||
*
|
||||
* @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 ) {
|
||||
global $post;
|
||||
|
||||
$post_id = isset( $request['post_id'] ) ? (int) $request['post_id'] : 0;
|
||||
|
||||
if ( $post_id > 0 ) {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
// Set up postdata since this will be needed if post_id was set.
|
||||
setup_postdata( $post );
|
||||
}
|
||||
|
||||
$registry = WP_Block_Type_Registry::get_instance();
|
||||
$registered = $registry->get_registered( $request['name'] );
|
||||
|
||||
if ( null === $registered || ! $registered->is_dynamic() ) {
|
||||
return new WP_Error(
|
||||
'block_invalid',
|
||||
__( 'Invalid block.' ),
|
||||
array(
|
||||
'status' => 404,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$attributes = $request->get_param( 'attributes' );
|
||||
|
||||
// Create an array representation simulating the output of parse_blocks.
|
||||
$block = array(
|
||||
'blockName' => $request['name'],
|
||||
'attrs' => $attributes,
|
||||
'innerHTML' => '',
|
||||
'innerContent' => array(),
|
||||
);
|
||||
|
||||
// Render using render_block to ensure all relevant filters are used.
|
||||
$data = array(
|
||||
'rendered' => render_block( $block ),
|
||||
);
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves block's output schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
$this->schema = array(
|
||||
'$schema' => 'http://json-schema.org/schema#',
|
||||
'title' => 'rendered-block',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'rendered' => array(
|
||||
'description' => __( 'The rendered block.' ),
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'context' => array( 'edit' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Block Renderer REST API: WP_REST_Block_Renderer_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for rendering a block.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Block_Renderer_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'block-renderer';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the necessary REST API routes, one for each dynamic block.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<name>[a-z0-9-]+/[a-z0-9-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'Unique registered name for the block.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => array( WP_REST_Server::READABLE, WP_REST_Server::CREATABLE ),
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
'attributes' => array(
|
||||
'description' => __( 'Attributes for the block.' ),
|
||||
'type' => 'object',
|
||||
'default' => array(),
|
||||
'validate_callback' => static function ( $value, $request ) {
|
||||
$block = WP_Block_Type_Registry::get_instance()->get_registered( $request['name'] );
|
||||
|
||||
if ( ! $block ) {
|
||||
// This will get rejected in ::get_item().
|
||||
return true;
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => $block->get_attributes(),
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
return rest_validate_value_from_schema( $value, $schema );
|
||||
},
|
||||
'sanitize_callback' => static function ( $value, $request ) {
|
||||
$block = WP_Block_Type_Registry::get_instance()->get_registered( $request['name'] );
|
||||
|
||||
if ( ! $block ) {
|
||||
// This will get rejected in ::get_item().
|
||||
return true;
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => $block->get_attributes(),
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
return rest_sanitize_value_from_schema( $value, $schema );
|
||||
},
|
||||
),
|
||||
'post_id' => array(
|
||||
'description' => __( 'ID of the post context.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read blocks.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @global WP_Post $post Global post object.
|
||||
*
|
||||
* @param WP_REST_Request $request Request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
global $post;
|
||||
|
||||
$post_id = isset( $request['post_id'] ) ? (int) $request['post_id'] : 0;
|
||||
|
||||
if ( $post_id > 0 ) {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
|
||||
return new WP_Error(
|
||||
'block_cannot_read',
|
||||
__( 'Sorry, you are not allowed to read blocks of this post.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
return new WP_Error(
|
||||
'block_cannot_read',
|
||||
__( 'Sorry, you are not allowed to read blocks as this user.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns block output from block's registered render_callback.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @global WP_Post $post Global post object.
|
||||
*
|
||||
* @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 ) {
|
||||
global $post;
|
||||
|
||||
$post_id = isset( $request['post_id'] ) ? (int) $request['post_id'] : 0;
|
||||
|
||||
if ( $post_id > 0 ) {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
// Set up postdata since this will be needed if post_id was set.
|
||||
setup_postdata( $post );
|
||||
}
|
||||
|
||||
$registry = WP_Block_Type_Registry::get_instance();
|
||||
$registered = $registry->get_registered( $request['name'] );
|
||||
|
||||
if ( null === $registered || ! $registered->is_dynamic() ) {
|
||||
return new WP_Error(
|
||||
'block_invalid',
|
||||
__( 'Invalid block.' ),
|
||||
array(
|
||||
'status' => 404,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$attributes = $request->get_param( 'attributes' );
|
||||
|
||||
// Create an array representation simulating the output of parse_blocks.
|
||||
$block = array(
|
||||
'blockName' => $request['name'],
|
||||
'attrs' => $attributes,
|
||||
'innerHTML' => '',
|
||||
'innerContent' => array(),
|
||||
);
|
||||
|
||||
// Render using render_block to ensure all relevant filters are used.
|
||||
$data = array(
|
||||
'rendered' => render_block( $block ),
|
||||
);
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves block's output schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
$this->schema = array(
|
||||
'$schema' => 'http://json-schema.org/schema#',
|
||||
'title' => 'rendered-block',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'rendered' => array(
|
||||
'description' => __( 'The rendered block.' ),
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'context' => array( 'edit' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,100 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* Synced patterns REST API: WP_REST_Blocks_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides a REST endpoint for the editor to read, create,
|
||||
* edit, and delete synced patterns (formerly called reusable blocks).
|
||||
* Patterns are stored as posts with the wp_block post type.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Posts_Controller
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Blocks_Controller extends WP_REST_Posts_Controller {
|
||||
|
||||
/**
|
||||
* Checks if a pattern can be read.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_Post $post Post object that backs the block.
|
||||
* @return bool Whether the pattern can be read.
|
||||
*/
|
||||
public function check_read_permission( $post ) {
|
||||
// By default the read_post capability is mapped to edit_posts.
|
||||
if ( ! current_user_can( 'read_post', $post->ID ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::check_read_permission( $post );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a response based on the context defined in the schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 6.3.0 Adds the `wp_pattern_sync_status` postmeta property to the top level of response.
|
||||
*
|
||||
* @param array $data Response data to filter.
|
||||
* @param string $context Context defined in the schema.
|
||||
* @return array Filtered response.
|
||||
*/
|
||||
public function filter_response_by_context( $data, $context ) {
|
||||
$data = parent::filter_response_by_context( $data, $context );
|
||||
|
||||
/*
|
||||
* Remove `title.rendered` and `content.rendered` from the response.
|
||||
* It doesn't make sense for a pattern to have rendered content on its own,
|
||||
* since rendering a block requires it to be inside a post or a page.
|
||||
*/
|
||||
unset( $data['title']['rendered'] );
|
||||
unset( $data['content']['rendered'] );
|
||||
|
||||
// Add the core wp_pattern_sync_status meta as top level property to the response.
|
||||
$data['wp_pattern_sync_status'] = isset( $data['meta']['wp_pattern_sync_status'] ) ? $data['meta']['wp_pattern_sync_status'] : '';
|
||||
unset( $data['meta']['wp_pattern_sync_status'] );
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the pattern's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = parent::get_item_schema();
|
||||
|
||||
/*
|
||||
* Allow all contexts to access `title.raw` and `content.raw`.
|
||||
* Clients always need the raw markup of a pattern to do anything useful,
|
||||
* e.g. parse it or display it in an editor.
|
||||
*/
|
||||
$schema['properties']['title']['properties']['raw']['context'] = array( 'view', 'edit' );
|
||||
$schema['properties']['content']['properties']['raw']['context'] = array( 'view', 'edit' );
|
||||
|
||||
/*
|
||||
* Remove `title.rendered` and `content.rendered` from the schema.
|
||||
* It doesn't make sense for a pattern to have rendered content on its own,
|
||||
* since rendering a block requires it to be inside a post or a page.
|
||||
*/
|
||||
unset( $schema['properties']['title']['properties']['rendered'] );
|
||||
unset( $schema['properties']['content']['properties']['rendered'] );
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Synced patterns REST API: WP_REST_Blocks_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides a REST endpoint for the editor to read, create,
|
||||
* edit, and delete synced patterns (formerly called reusable blocks).
|
||||
* Patterns are stored as posts with the wp_block post type.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Posts_Controller
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Blocks_Controller extends WP_REST_Posts_Controller {
|
||||
|
||||
/**
|
||||
* Checks if a pattern can be read.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_Post $post Post object that backs the block.
|
||||
* @return bool Whether the pattern can be read.
|
||||
*/
|
||||
public function check_read_permission( $post ) {
|
||||
// By default the read_post capability is mapped to edit_posts.
|
||||
if ( ! current_user_can( 'read_post', $post->ID ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::check_read_permission( $post );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a response based on the context defined in the schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 6.3.0 Adds the `wp_pattern_sync_status` postmeta property to the top level of response.
|
||||
*
|
||||
* @param array $data Response data to filter.
|
||||
* @param string $context Context defined in the schema.
|
||||
* @return array Filtered response.
|
||||
*/
|
||||
public function filter_response_by_context( $data, $context ) {
|
||||
$data = parent::filter_response_by_context( $data, $context );
|
||||
|
||||
/*
|
||||
* Remove `title.rendered` and `content.rendered` from the response.
|
||||
* It doesn't make sense for a pattern to have rendered content on its own,
|
||||
* since rendering a block requires it to be inside a post or a page.
|
||||
*/
|
||||
unset( $data['title']['rendered'] );
|
||||
unset( $data['content']['rendered'] );
|
||||
|
||||
// Add the core wp_pattern_sync_status meta as top level property to the response.
|
||||
$data['wp_pattern_sync_status'] = isset( $data['meta']['wp_pattern_sync_status'] ) ? $data['meta']['wp_pattern_sync_status'] : '';
|
||||
unset( $data['meta']['wp_pattern_sync_status'] );
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the pattern's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = parent::get_item_schema();
|
||||
|
||||
/*
|
||||
* Allow all contexts to access `title.raw` and `content.raw`.
|
||||
* Clients always need the raw markup of a pattern to do anything useful,
|
||||
* e.g. parse it or display it in an editor.
|
||||
*/
|
||||
$schema['properties']['title']['properties']['raw']['context'] = array( 'view', 'edit' );
|
||||
$schema['properties']['content']['properties']['raw']['context'] = array( 'view', 'edit' );
|
||||
|
||||
/*
|
||||
* Remove `title.rendered` and `content.rendered` from the schema.
|
||||
* It doesn't make sense for a pattern to have rendered content on its own,
|
||||
* since rendering a block requires it to be inside a post or a page.
|
||||
*/
|
||||
unset( $schema['properties']['title']['properties']['rendered'] );
|
||||
unset( $schema['properties']['content']['properties']['rendered'] );
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,94 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Edit_Site_Export_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for exporting current templates
|
||||
* and template parts.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Edit_Site_Export_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp-block-editor/v1';
|
||||
$this->rest_base = 'export';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the site export route.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'export' ),
|
||||
'permission_callback' => array( $this, 'permissions_check' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to export.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @return WP_Error|true True if the request has access, or WP_Error object.
|
||||
*/
|
||||
public function permissions_check() {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_export_templates',
|
||||
__( 'Sorry, you are not allowed to export templates and template parts.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a ZIP file with an export of the current templates
|
||||
* and template parts from the site editor, and close the connection.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @return WP_Error|void
|
||||
*/
|
||||
public function export() {
|
||||
// Generate the export file.
|
||||
$filename = wp_generate_block_templates_export_file();
|
||||
|
||||
if ( is_wp_error( $filename ) ) {
|
||||
$filename->add_data( array( 'status' => 500 ) );
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$theme_name = basename( get_stylesheet() );
|
||||
header( 'Content-Type: application/zip' );
|
||||
header( 'Content-Disposition: attachment; filename=' . $theme_name . '.zip' );
|
||||
header( 'Content-Length: ' . filesize( $filename ) );
|
||||
flush();
|
||||
readfile( $filename );
|
||||
unlink( $filename );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Edit_Site_Export_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for exporting current templates
|
||||
* and template parts.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Edit_Site_Export_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp-block-editor/v1';
|
||||
$this->rest_base = 'export';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the site export route.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'export' ),
|
||||
'permission_callback' => array( $this, 'permissions_check' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to export.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @return WP_Error|true True if the request has access, or WP_Error object.
|
||||
*/
|
||||
public function permissions_check() {
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_export_templates',
|
||||
__( 'Sorry, you are not allowed to export templates and template parts.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a ZIP file with an export of the current templates
|
||||
* and template parts from the site editor, and close the connection.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @return WP_Error|void
|
||||
*/
|
||||
public function export() {
|
||||
// Generate the export file.
|
||||
$filename = wp_generate_block_templates_export_file();
|
||||
|
||||
if ( is_wp_error( $filename ) ) {
|
||||
$filename->add_data( array( 'status' => 500 ) );
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$theme_name = basename( get_stylesheet() );
|
||||
header( 'Content-Type: application/zip' );
|
||||
header( 'Content-Disposition: attachment; filename=' . $theme_name . '.zip' );
|
||||
header( 'Content-Length: ' . filesize( $filename ) );
|
||||
flush();
|
||||
readfile( $filename );
|
||||
unlink( $filename );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,322 +1,322 @@
|
||||
<?php
|
||||
/**
|
||||
* Rest Font Collections Controller.
|
||||
*
|
||||
* This file contains the class for the REST API Font Collections Controller.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.5.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Font Library Controller class.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
class WP_REST_Font_Collections_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->rest_base = 'font-collections';
|
||||
$this->namespace = 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
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>[\/\w-]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the font collections available.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$collections_all = WP_Font_Library::get_instance()->get_font_collections();
|
||||
|
||||
$page = $request['page'];
|
||||
$per_page = $request['per_page'];
|
||||
$total_items = count( $collections_all );
|
||||
$max_pages = (int) ceil( $total_items / $per_page );
|
||||
|
||||
if ( $page > $max_pages && $total_items > 0 ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_page_number',
|
||||
__( 'The page number requested is larger than the number of pages available.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$collections_page = array_slice( $collections_all, ( $page - 1 ) * $per_page, $per_page );
|
||||
|
||||
$items = array();
|
||||
foreach ( $collections_page as $collection ) {
|
||||
$item = $this->prepare_item_for_response( $collection, $request );
|
||||
|
||||
// If there's an error loading a collection, skip it and continue loading valid collections.
|
||||
if ( is_wp_error( $item ) ) {
|
||||
continue;
|
||||
}
|
||||
$item = $this->prepare_response_for_collection( $item );
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $items );
|
||||
|
||||
$response->header( 'X-WP-Total', (int) $total_items );
|
||||
$response->header( 'X-WP-TotalPages', $max_pages );
|
||||
|
||||
$request_params = $request->get_query_params();
|
||||
$collection_url = rest_url( $this->namespace . '/' . $this->rest_base );
|
||||
$base = add_query_arg( urlencode_deep( $request_params ), $collection_url );
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a font collection.
|
||||
*
|
||||
* @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_item( $request ) {
|
||||
$slug = $request->get_param( 'slug' );
|
||||
$collection = WP_Font_Library::get_instance()->get_font_collection( $slug );
|
||||
|
||||
if ( ! $collection ) {
|
||||
return new WP_Error( 'rest_font_collection_not_found', __( 'Font collection not found.' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return $this->prepare_item_for_response( $collection, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single collection output for response.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param WP_Font_Collection $item Font collection object.
|
||||
* @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 ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( rest_is_field_included( 'slug', $fields ) ) {
|
||||
$data['slug'] = $item->slug;
|
||||
}
|
||||
|
||||
// If any data fields are requested, get the collection data.
|
||||
$data_fields = array( 'name', 'description', 'font_families', 'categories' );
|
||||
if ( ! empty( array_intersect( $fields, $data_fields ) ) ) {
|
||||
$collection_data = $item->get_data();
|
||||
if ( is_wp_error( $collection_data ) ) {
|
||||
$collection_data->add_data( array( 'status' => 500 ) );
|
||||
return $collection_data;
|
||||
}
|
||||
|
||||
foreach ( $data_fields as $field ) {
|
||||
if ( rest_is_field_included( $field, $fields ) ) {
|
||||
$data[ $field ] = $collection_data[ $field ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) ) {
|
||||
$links = $this->prepare_links( $item );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$response->data = $this->add_additional_fields_to_object( $response->data, $request );
|
||||
$response->data = $this->filter_response_by_context( $response->data, $context );
|
||||
|
||||
/**
|
||||
* Filters the font collection data for a REST API response.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Font_Collection $item The font collection object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_font_collection', $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the font collection's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'font-collection',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'slug' => array(
|
||||
'description' => __( 'Unique identifier for the font collection.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The name for the font collection.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The description for the font collection.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'font_families' => array(
|
||||
'description' => __( 'The font families for the font collection.' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'categories' => array(
|
||||
'description' => __( 'The categories for the font collection.' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param WP_Font_Collection $collection Font collection data
|
||||
* @return array Links for the given font collection.
|
||||
*/
|
||||
protected function prepare_links( $collection ) {
|
||||
return array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $collection->slug ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the search params for the font collections.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
|
||||
unset( $query_params['search'] );
|
||||
|
||||
/**
|
||||
* Filters REST API collection parameters for the font collections controller.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param array $query_params JSON Schema-formatted collection parameters.
|
||||
*/
|
||||
return apply_filters( 'rest_font_collections_collection_params', $query_params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user has permissions to use the Fonts Collections.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_read',
|
||||
__( 'Sorry, you are not allowed to access font collections.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Rest Font Collections Controller.
|
||||
*
|
||||
* This file contains the class for the REST API Font Collections Controller.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.5.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Font Library Controller class.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
class WP_REST_Font_Collections_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->rest_base = 'font-collections';
|
||||
$this->namespace = 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
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>[\/\w-]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the font collections available.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$collections_all = WP_Font_Library::get_instance()->get_font_collections();
|
||||
|
||||
$page = $request['page'];
|
||||
$per_page = $request['per_page'];
|
||||
$total_items = count( $collections_all );
|
||||
$max_pages = (int) ceil( $total_items / $per_page );
|
||||
|
||||
if ( $page > $max_pages && $total_items > 0 ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_page_number',
|
||||
__( 'The page number requested is larger than the number of pages available.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$collections_page = array_slice( $collections_all, ( $page - 1 ) * $per_page, $per_page );
|
||||
|
||||
$items = array();
|
||||
foreach ( $collections_page as $collection ) {
|
||||
$item = $this->prepare_item_for_response( $collection, $request );
|
||||
|
||||
// If there's an error loading a collection, skip it and continue loading valid collections.
|
||||
if ( is_wp_error( $item ) ) {
|
||||
continue;
|
||||
}
|
||||
$item = $this->prepare_response_for_collection( $item );
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $items );
|
||||
|
||||
$response->header( 'X-WP-Total', (int) $total_items );
|
||||
$response->header( 'X-WP-TotalPages', $max_pages );
|
||||
|
||||
$request_params = $request->get_query_params();
|
||||
$collection_url = rest_url( $this->namespace . '/' . $this->rest_base );
|
||||
$base = add_query_arg( urlencode_deep( $request_params ), $collection_url );
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a font collection.
|
||||
*
|
||||
* @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_item( $request ) {
|
||||
$slug = $request->get_param( 'slug' );
|
||||
$collection = WP_Font_Library::get_instance()->get_font_collection( $slug );
|
||||
|
||||
if ( ! $collection ) {
|
||||
return new WP_Error( 'rest_font_collection_not_found', __( 'Font collection not found.' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return $this->prepare_item_for_response( $collection, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single collection output for response.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param WP_Font_Collection $item Font collection object.
|
||||
* @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 ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( rest_is_field_included( 'slug', $fields ) ) {
|
||||
$data['slug'] = $item->slug;
|
||||
}
|
||||
|
||||
// If any data fields are requested, get the collection data.
|
||||
$data_fields = array( 'name', 'description', 'font_families', 'categories' );
|
||||
if ( ! empty( array_intersect( $fields, $data_fields ) ) ) {
|
||||
$collection_data = $item->get_data();
|
||||
if ( is_wp_error( $collection_data ) ) {
|
||||
$collection_data->add_data( array( 'status' => 500 ) );
|
||||
return $collection_data;
|
||||
}
|
||||
|
||||
foreach ( $data_fields as $field ) {
|
||||
if ( rest_is_field_included( $field, $fields ) ) {
|
||||
$data[ $field ] = $collection_data[ $field ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) ) {
|
||||
$links = $this->prepare_links( $item );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$response->data = $this->add_additional_fields_to_object( $response->data, $request );
|
||||
$response->data = $this->filter_response_by_context( $response->data, $context );
|
||||
|
||||
/**
|
||||
* Filters the font collection data for a REST API response.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Font_Collection $item The font collection object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_font_collection', $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the font collection's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'font-collection',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'slug' => array(
|
||||
'description' => __( 'Unique identifier for the font collection.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The name for the font collection.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The description for the font collection.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'font_families' => array(
|
||||
'description' => __( 'The font families for the font collection.' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'categories' => array(
|
||||
'description' => __( 'The categories for the font collection.' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param WP_Font_Collection $collection Font collection data
|
||||
* @return array Links for the given font collection.
|
||||
*/
|
||||
protected function prepare_links( $collection ) {
|
||||
return array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $collection->slug ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the search params for the font collections.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
|
||||
unset( $query_params['search'] );
|
||||
|
||||
/**
|
||||
* Filters REST API collection parameters for the font collections controller.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param array $query_params JSON Schema-formatted collection parameters.
|
||||
*/
|
||||
return apply_filters( 'rest_font_collections_collection_params', $query_params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user has permissions to use the Fonts Collections.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( current_user_can( 'edit_theme_options' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_read',
|
||||
__( 'Sorry, you are not allowed to access font collections.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,304 +1,304 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Menu_Locations_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.9.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access menu locations via the REST API.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Menu_Locations_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Menu Locations Constructor.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'menu-locations';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
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<location>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'location' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the menu location.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read menu locations.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view menu locations.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all menu locations, depending on user context.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$data = array();
|
||||
|
||||
foreach ( get_registered_nav_menus() as $name => $description ) {
|
||||
$location = new stdClass();
|
||||
$location->name = $name;
|
||||
$location->description = $description;
|
||||
|
||||
$location = $this->prepare_item_for_response( $location, $request );
|
||||
$data[ $name ] = $this->prepare_response_for_collection( $location );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read a menu location.
|
||||
*
|
||||
* @since 5.9.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_item_permissions_check( $request ) {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view menu locations.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific menu location.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$registered_menus = get_registered_nav_menus();
|
||||
if ( ! array_key_exists( $request['location'], $registered_menus ) ) {
|
||||
return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$location = new stdClass();
|
||||
$location->name = $request['location'];
|
||||
$location->description = $registered_menus[ $location->name ];
|
||||
|
||||
$data = $this->prepare_item_for_response( $location, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a menu location object for serialization.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param stdClass $item Post status data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Menu location data.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$location = $item;
|
||||
|
||||
$locations = get_nav_menu_locations();
|
||||
$menu = isset( $locations[ $location->name ] ) ? $locations[ $location->name ] : 0;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( rest_is_field_included( 'name', $fields ) ) {
|
||||
$data['name'] = $location->name;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'description', $fields ) ) {
|
||||
$data['description'] = $location->description;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'menu', $fields ) ) {
|
||||
$data['menu'] = (int) $menu;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['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 );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $location ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters menu location data returned from the REST API.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $location The original location object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_menu_location', $response, $location, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param stdClass $location Menu location.
|
||||
* @return array Links for the given menu location.
|
||||
*/
|
||||
protected function prepare_links( $location ) {
|
||||
$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
|
||||
|
||||
// Entity meta.
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( trailingslashit( $base ) . $location->name ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( $base ),
|
||||
),
|
||||
);
|
||||
|
||||
$locations = get_nav_menu_locations();
|
||||
$menu = isset( $locations[ $location->name ] ) ? $locations[ $location->name ] : 0;
|
||||
if ( $menu ) {
|
||||
$path = rest_get_route_for_term( $menu );
|
||||
if ( $path ) {
|
||||
$url = rest_url( $path );
|
||||
|
||||
$links['https://api.w.org/menu'][] = array(
|
||||
'href' => $url,
|
||||
'embeddable' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the menu location's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.9.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' => 'menu-location',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The name of the menu location.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The description of the menu location.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'menu' => array(
|
||||
'description' => __( 'The ID of the assigned menu.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Menu_Locations_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.9.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access menu locations via the REST API.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Menu_Locations_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Menu Locations Constructor.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'menu-locations';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the objects of the controller.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
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<location>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'location' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the menu location.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read menu locations.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view menu locations.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all menu locations, depending on user context.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$data = array();
|
||||
|
||||
foreach ( get_registered_nav_menus() as $name => $description ) {
|
||||
$location = new stdClass();
|
||||
$location->name = $name;
|
||||
$location->description = $description;
|
||||
|
||||
$location = $this->prepare_item_for_response( $location, $request );
|
||||
$data[ $name ] = $this->prepare_response_for_collection( $location );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read a menu location.
|
||||
*
|
||||
* @since 5.9.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_item_permissions_check( $request ) {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to view menu locations.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific menu location.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$registered_menus = get_registered_nav_menus();
|
||||
if ( ! array_key_exists( $request['location'], $registered_menus ) ) {
|
||||
return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$location = new stdClass();
|
||||
$location->name = $request['location'];
|
||||
$location->description = $registered_menus[ $location->name ];
|
||||
|
||||
$data = $this->prepare_item_for_response( $location, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a menu location object for serialization.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param stdClass $item Post status data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Menu location data.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$location = $item;
|
||||
|
||||
$locations = get_nav_menu_locations();
|
||||
$menu = isset( $locations[ $location->name ] ) ? $locations[ $location->name ] : 0;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( rest_is_field_included( 'name', $fields ) ) {
|
||||
$data['name'] = $location->name;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'description', $fields ) ) {
|
||||
$data['description'] = $location->description;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'menu', $fields ) ) {
|
||||
$data['menu'] = (int) $menu;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['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 );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $location ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters menu location data returned from the REST API.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $location The original location object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_menu_location', $response, $location, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @param stdClass $location Menu location.
|
||||
* @return array Links for the given menu location.
|
||||
*/
|
||||
protected function prepare_links( $location ) {
|
||||
$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
|
||||
|
||||
// Entity meta.
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( trailingslashit( $base ) . $location->name ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( $base ),
|
||||
),
|
||||
);
|
||||
|
||||
$locations = get_nav_menu_locations();
|
||||
$menu = isset( $locations[ $location->name ] ) ? $locations[ $location->name ] : 0;
|
||||
if ( $menu ) {
|
||||
$path = rest_get_route_for_term( $menu );
|
||||
if ( $path ) {
|
||||
$url = rest_url( $path );
|
||||
|
||||
$links['https://api.w.org/menu'][] = array(
|
||||
'href' => $url,
|
||||
'embeddable' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the menu location's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.9.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' => 'menu-location',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The name of the menu location.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'The description of the menu location.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'menu' => array(
|
||||
'description' => __( 'The ID of the assigned menu.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 5.9.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,191 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
* WP_REST_Navigation_Fallback_Controller class
|
||||
*
|
||||
* REST Controller to create/fetch a fallback Navigation Menu.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* REST Controller to fetch a fallback Navigation Block Menu. If needed it creates one.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*/
|
||||
class WP_REST_Navigation_Fallback_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* The Post Type for the Controller
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $post_type;
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp-block-editor/v1';
|
||||
$this->rest_base = 'navigation-fallback';
|
||||
$this->post_type = 'wp_navigation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the controllers routes.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
// Lists a single nav item based on the given id or slug.
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::READABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read fallbacks.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
|
||||
$post_type = get_post_type_object( $this->post_type );
|
||||
|
||||
// Getting fallbacks requires creating and reading `wp_navigation` posts.
|
||||
if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( 'edit_theme_options' ) || ! current_user_can( 'edit_posts' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_create',
|
||||
__( 'Sorry, you are not allowed to create Navigation Menus as this user.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit Navigation Menus as this user.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most appropriate fallback Navigation Menu.
|
||||
*
|
||||
* @since 6.3.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 ) {
|
||||
$post = WP_Navigation_Fallback::get_fallback();
|
||||
|
||||
if ( empty( $post ) ) {
|
||||
return rest_ensure_response( new WP_Error( 'no_fallback_menu', __( 'No fallback menu found.' ), array( 'status' => 404 ) ) );
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the fallbacks' schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.3.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' => 'navigation-fallback',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The unique identifier for the Navigation Menu.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the post data to the schema we want.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param WP_Post $item The wp_navigation Post object whose response is being prepared.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response The response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$data = array();
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
if ( rest_is_field_included( 'id', $fields ) ) {
|
||||
$data['id'] = (int) $item->ID;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['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 );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $this->prepare_links( $item );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the links for the request.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param WP_Post $post the Navigation Menu post object.
|
||||
* @return array Links for the given request.
|
||||
*/
|
||||
private function prepare_links( $post ) {
|
||||
return array(
|
||||
'self' => array(
|
||||
'href' => rest_url( rest_get_route_for_post( $post->ID ) ),
|
||||
'embeddable' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* WP_REST_Navigation_Fallback_Controller class
|
||||
*
|
||||
* REST Controller to create/fetch a fallback Navigation Menu.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* REST Controller to fetch a fallback Navigation Block Menu. If needed it creates one.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*/
|
||||
class WP_REST_Navigation_Fallback_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* The Post Type for the Controller
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $post_type;
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp-block-editor/v1';
|
||||
$this->rest_base = 'navigation-fallback';
|
||||
$this->post_type = 'wp_navigation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the controllers routes.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
// Lists a single nav item based on the given id or slug.
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::READABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read fallbacks.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
|
||||
$post_type = get_post_type_object( $this->post_type );
|
||||
|
||||
// Getting fallbacks requires creating and reading `wp_navigation` posts.
|
||||
if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( 'edit_theme_options' ) || ! current_user_can( 'edit_posts' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_create',
|
||||
__( 'Sorry, you are not allowed to create Navigation Menus as this user.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit Navigation Menus as this user.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most appropriate fallback Navigation Menu.
|
||||
*
|
||||
* @since 6.3.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 ) {
|
||||
$post = WP_Navigation_Fallback::get_fallback();
|
||||
|
||||
if ( empty( $post ) ) {
|
||||
return rest_ensure_response( new WP_Error( 'no_fallback_menu', __( 'No fallback menu found.' ), array( 'status' => 404 ) ) );
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the fallbacks' schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.3.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' => 'navigation-fallback',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The unique identifier for the Navigation Menu.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the post data to the schema we want.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param WP_Post $item The wp_navigation Post object whose response is being prepared.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response The response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$data = array();
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
if ( rest_is_field_included( 'id', $fields ) ) {
|
||||
$data['id'] = (int) $item->ID;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['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 );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $this->prepare_links( $item );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the links for the request.
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param WP_Post $post the Navigation Menu post object.
|
||||
* @return array Links for the given request.
|
||||
*/
|
||||
private function prepare_links( $post ) {
|
||||
return array(
|
||||
'self' => array(
|
||||
'href' => rest_url( rest_get_route_for_post( $post->ID ) ),
|
||||
'embeddable' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,410 +1,410 @@
|
||||
<?php
|
||||
/**
|
||||
* Block Pattern Directory REST API: WP_REST_Pattern_Directory_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for block patterns.
|
||||
*
|
||||
* This simply proxies the endpoint at http://api.wordpress.org/patterns/1.0/. That isn't necessary for
|
||||
* functionality, but is desired for privacy. It prevents api.wordpress.org from knowing the user's IP address.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Pattern_Directory_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'pattern-directory';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the necessary REST API routes.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/patterns',
|
||||
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' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to view the local block pattern directory.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has permission, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
|
||||
if ( current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_pattern_directory_cannot_view',
|
||||
__( 'Sorry, you are not allowed to browse the local block pattern directory.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and retrieve block patterns metadata
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.0.0 Added 'slug' to request.
|
||||
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
|
||||
*
|
||||
* @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 ) {
|
||||
/*
|
||||
* Include an unmodified `$wp_version`, so the API can craft a response that's tailored to
|
||||
* it. Some plugins modify the version in a misguided attempt to improve security by
|
||||
* obscuring the version, which can cause invalid requests.
|
||||
*/
|
||||
require ABSPATH . WPINC . '/version.php';
|
||||
|
||||
$valid_query_args = array(
|
||||
'offset' => true,
|
||||
'order' => true,
|
||||
'orderby' => true,
|
||||
'page' => true,
|
||||
'per_page' => true,
|
||||
'search' => true,
|
||||
'slug' => true,
|
||||
);
|
||||
$query_args = array_intersect_key( $request->get_params(), $valid_query_args );
|
||||
|
||||
$query_args['locale'] = get_user_locale();
|
||||
$query_args['wp-version'] = $wp_version;
|
||||
$query_args['pattern-categories'] = isset( $request['category'] ) ? $request['category'] : false;
|
||||
$query_args['pattern-keywords'] = isset( $request['keyword'] ) ? $request['keyword'] : false;
|
||||
|
||||
$query_args = array_filter( $query_args );
|
||||
|
||||
$transient_key = $this->get_transient_key( $query_args );
|
||||
|
||||
/*
|
||||
* Use network-wide transient to improve performance. The locale is the only site
|
||||
* configuration that affects the response, and it's included in the transient key.
|
||||
*/
|
||||
$raw_patterns = get_site_transient( $transient_key );
|
||||
|
||||
if ( ! $raw_patterns ) {
|
||||
$api_url = 'http://api.wordpress.org/patterns/1.0/?' . build_query( $query_args );
|
||||
if ( wp_http_supports( array( 'ssl' ) ) ) {
|
||||
$api_url = set_url_scheme( $api_url, 'https' );
|
||||
}
|
||||
|
||||
/*
|
||||
* Default to a short TTL, to mitigate cache stampedes on high-traffic sites.
|
||||
* This assumes that most errors will be short-lived, e.g., packet loss that causes the
|
||||
* first request to fail, but a follow-up one will succeed. The value should be high
|
||||
* enough to avoid stampedes, but low enough to not interfere with users manually
|
||||
* re-trying a failed request.
|
||||
*/
|
||||
$cache_ttl = 5;
|
||||
$wporg_response = wp_remote_get( $api_url );
|
||||
$raw_patterns = json_decode( wp_remote_retrieve_body( $wporg_response ) );
|
||||
|
||||
if ( is_wp_error( $wporg_response ) ) {
|
||||
$raw_patterns = $wporg_response;
|
||||
|
||||
} elseif ( ! is_array( $raw_patterns ) ) {
|
||||
// HTTP request succeeded, but response data is invalid.
|
||||
$raw_patterns = new WP_Error(
|
||||
'pattern_api_failed',
|
||||
sprintf(
|
||||
/* translators: %s: Support forums URL. */
|
||||
__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
|
||||
__( 'https://wordpress.org/support/forums/' )
|
||||
),
|
||||
array(
|
||||
'response' => wp_remote_retrieve_body( $wporg_response ),
|
||||
)
|
||||
);
|
||||
|
||||
} else {
|
||||
// Response has valid data.
|
||||
$cache_ttl = HOUR_IN_SECONDS;
|
||||
}
|
||||
|
||||
set_site_transient( $transient_key, $raw_patterns, $cache_ttl );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $raw_patterns ) ) {
|
||||
$raw_patterns->add_data( array( 'status' => 500 ) );
|
||||
|
||||
return $raw_patterns;
|
||||
}
|
||||
|
||||
$response = array();
|
||||
|
||||
if ( $raw_patterns ) {
|
||||
foreach ( $raw_patterns as $pattern ) {
|
||||
$response[] = $this->prepare_response_for_collection(
|
||||
$this->prepare_item_for_response( $pattern, $request )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a raw block pattern before it gets output in a REST API response.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 5.9.0 Renamed `$raw_pattern` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param object $item Raw pattern from api.wordpress.org, before any changes.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$raw_pattern = $item;
|
||||
|
||||
$prepared_pattern = array(
|
||||
'id' => absint( $raw_pattern->id ),
|
||||
'title' => sanitize_text_field( $raw_pattern->title->rendered ),
|
||||
'content' => wp_kses_post( $raw_pattern->pattern_content ),
|
||||
'categories' => array_map( 'sanitize_title', $raw_pattern->category_slugs ),
|
||||
'keywords' => array_map( 'sanitize_text_field', explode( ',', $raw_pattern->meta->wpop_keywords ) ),
|
||||
'description' => sanitize_text_field( $raw_pattern->meta->wpop_description ),
|
||||
'viewport_width' => absint( $raw_pattern->meta->wpop_viewport_width ),
|
||||
'block_types' => array_map( 'sanitize_text_field', $raw_pattern->meta->wpop_block_types ),
|
||||
);
|
||||
|
||||
$prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request );
|
||||
|
||||
$response = new WP_REST_Response( $prepared_pattern );
|
||||
|
||||
/**
|
||||
* Filters the REST API response for a block pattern.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $raw_pattern The unprepared block pattern.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_block_pattern', $response, $raw_pattern, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block pattern's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.2.0 Added `'block_types'` to schema.
|
||||
*
|
||||
* @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' => 'pattern-directory-item',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The pattern ID.' ),
|
||||
'type' => 'integer',
|
||||
'minimum' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'title' => array(
|
||||
'description' => __( 'The pattern title, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'minLength' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'content' => array(
|
||||
'description' => __( 'The pattern content.' ),
|
||||
'type' => 'string',
|
||||
'minLength' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'categories' => array(
|
||||
'description' => __( "The pattern's category slugs." ),
|
||||
'type' => 'array',
|
||||
'uniqueItems' => true,
|
||||
'items' => array( 'type' => 'string' ),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'keywords' => array(
|
||||
'description' => __( "The pattern's keywords." ),
|
||||
'type' => 'array',
|
||||
'uniqueItems' => true,
|
||||
'items' => array( 'type' => 'string' ),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'description' => array(
|
||||
'description' => __( 'A description of the pattern.' ),
|
||||
'type' => 'string',
|
||||
'minLength' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'viewport_width' => array(
|
||||
'description' => __( 'The preferred width of the viewport when previewing a pattern, in pixels.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'block_types' => array(
|
||||
'description' => __( 'The block types which can use this pattern.' ),
|
||||
'type' => 'array',
|
||||
'uniqueItems' => true,
|
||||
'items' => array( 'type' => 'string' ),
|
||||
'context' => array( 'view', 'embed' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the search parameters for the block pattern's collection.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['per_page']['default'] = 100;
|
||||
$query_params['search']['minLength'] = 1;
|
||||
$query_params['context']['default'] = 'view';
|
||||
|
||||
$query_params['category'] = array(
|
||||
'description' => __( 'Limit results to those matching a category ID.' ),
|
||||
'type' => 'integer',
|
||||
'minimum' => 1,
|
||||
);
|
||||
|
||||
$query_params['keyword'] = array(
|
||||
'description' => __( 'Limit results to those matching a keyword ID.' ),
|
||||
'type' => 'integer',
|
||||
'minimum' => 1,
|
||||
);
|
||||
|
||||
$query_params['slug'] = array(
|
||||
'description' => __( 'Limit results to those matching a pattern (slug).' ),
|
||||
'type' => 'array',
|
||||
);
|
||||
|
||||
$query_params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.' ),
|
||||
'type' => 'integer',
|
||||
);
|
||||
|
||||
$query_params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
);
|
||||
|
||||
$query_params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by post attribute.' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'author',
|
||||
'date',
|
||||
'id',
|
||||
'include',
|
||||
'modified',
|
||||
'parent',
|
||||
'relevance',
|
||||
'slug',
|
||||
'include_slugs',
|
||||
'title',
|
||||
'favorite_count',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter collection parameters for the block pattern directory controller.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $query_params JSON Schema-formatted collection parameters.
|
||||
*/
|
||||
return apply_filters( 'rest_pattern_directory_collection_params', $query_params );
|
||||
}
|
||||
|
||||
/*
|
||||
* Include a hash of the query args, so that different requests are stored in
|
||||
* separate caches.
|
||||
*
|
||||
* MD5 is chosen for its speed, low-collision rate, universal availability, and to stay
|
||||
* under the character limit for `_site_transient_timeout_{...}` keys.
|
||||
*
|
||||
* @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param array $query_args Query arguments to generate a transient key from.
|
||||
* @return string Transient key.
|
||||
*/
|
||||
protected function get_transient_key( $query_args ) {
|
||||
|
||||
if ( isset( $query_args['slug'] ) ) {
|
||||
// This is an additional precaution because the "sort" function expects an array.
|
||||
$query_args['slug'] = wp_parse_list( $query_args['slug'] );
|
||||
|
||||
// Empty arrays should not affect the transient key.
|
||||
if ( empty( $query_args['slug'] ) ) {
|
||||
unset( $query_args['slug'] );
|
||||
} else {
|
||||
// Sort the array so that the transient key doesn't depend on the order of slugs.
|
||||
sort( $query_args['slug'] );
|
||||
}
|
||||
}
|
||||
|
||||
return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Block Pattern Directory REST API: WP_REST_Pattern_Directory_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller which provides REST endpoint for block patterns.
|
||||
*
|
||||
* This simply proxies the endpoint at http://api.wordpress.org/patterns/1.0/. That isn't necessary for
|
||||
* functionality, but is desired for privacy. It prevents api.wordpress.org from knowing the user's IP address.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Pattern_Directory_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'pattern-directory';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the necessary REST API routes.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/patterns',
|
||||
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' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to view the local block pattern directory.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has permission, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( current_user_can( 'edit_posts' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
|
||||
if ( current_user_can( $post_type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_pattern_directory_cannot_view',
|
||||
__( 'Sorry, you are not allowed to browse the local block pattern directory.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and retrieve block patterns metadata
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.0.0 Added 'slug' to request.
|
||||
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
|
||||
*
|
||||
* @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 ) {
|
||||
/*
|
||||
* Include an unmodified `$wp_version`, so the API can craft a response that's tailored to
|
||||
* it. Some plugins modify the version in a misguided attempt to improve security by
|
||||
* obscuring the version, which can cause invalid requests.
|
||||
*/
|
||||
require ABSPATH . WPINC . '/version.php';
|
||||
|
||||
$valid_query_args = array(
|
||||
'offset' => true,
|
||||
'order' => true,
|
||||
'orderby' => true,
|
||||
'page' => true,
|
||||
'per_page' => true,
|
||||
'search' => true,
|
||||
'slug' => true,
|
||||
);
|
||||
$query_args = array_intersect_key( $request->get_params(), $valid_query_args );
|
||||
|
||||
$query_args['locale'] = get_user_locale();
|
||||
$query_args['wp-version'] = $wp_version;
|
||||
$query_args['pattern-categories'] = isset( $request['category'] ) ? $request['category'] : false;
|
||||
$query_args['pattern-keywords'] = isset( $request['keyword'] ) ? $request['keyword'] : false;
|
||||
|
||||
$query_args = array_filter( $query_args );
|
||||
|
||||
$transient_key = $this->get_transient_key( $query_args );
|
||||
|
||||
/*
|
||||
* Use network-wide transient to improve performance. The locale is the only site
|
||||
* configuration that affects the response, and it's included in the transient key.
|
||||
*/
|
||||
$raw_patterns = get_site_transient( $transient_key );
|
||||
|
||||
if ( ! $raw_patterns ) {
|
||||
$api_url = 'http://api.wordpress.org/patterns/1.0/?' . build_query( $query_args );
|
||||
if ( wp_http_supports( array( 'ssl' ) ) ) {
|
||||
$api_url = set_url_scheme( $api_url, 'https' );
|
||||
}
|
||||
|
||||
/*
|
||||
* Default to a short TTL, to mitigate cache stampedes on high-traffic sites.
|
||||
* This assumes that most errors will be short-lived, e.g., packet loss that causes the
|
||||
* first request to fail, but a follow-up one will succeed. The value should be high
|
||||
* enough to avoid stampedes, but low enough to not interfere with users manually
|
||||
* re-trying a failed request.
|
||||
*/
|
||||
$cache_ttl = 5;
|
||||
$wporg_response = wp_remote_get( $api_url );
|
||||
$raw_patterns = json_decode( wp_remote_retrieve_body( $wporg_response ) );
|
||||
|
||||
if ( is_wp_error( $wporg_response ) ) {
|
||||
$raw_patterns = $wporg_response;
|
||||
|
||||
} elseif ( ! is_array( $raw_patterns ) ) {
|
||||
// HTTP request succeeded, but response data is invalid.
|
||||
$raw_patterns = new WP_Error(
|
||||
'pattern_api_failed',
|
||||
sprintf(
|
||||
/* translators: %s: Support forums URL. */
|
||||
__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
|
||||
__( 'https://wordpress.org/support/forums/' )
|
||||
),
|
||||
array(
|
||||
'response' => wp_remote_retrieve_body( $wporg_response ),
|
||||
)
|
||||
);
|
||||
|
||||
} else {
|
||||
// Response has valid data.
|
||||
$cache_ttl = HOUR_IN_SECONDS;
|
||||
}
|
||||
|
||||
set_site_transient( $transient_key, $raw_patterns, $cache_ttl );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $raw_patterns ) ) {
|
||||
$raw_patterns->add_data( array( 'status' => 500 ) );
|
||||
|
||||
return $raw_patterns;
|
||||
}
|
||||
|
||||
$response = array();
|
||||
|
||||
if ( $raw_patterns ) {
|
||||
foreach ( $raw_patterns as $pattern ) {
|
||||
$response[] = $this->prepare_response_for_collection(
|
||||
$this->prepare_item_for_response( $pattern, $request )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a raw block pattern before it gets output in a REST API response.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 5.9.0 Renamed `$raw_pattern` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param object $item Raw pattern from api.wordpress.org, before any changes.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$raw_pattern = $item;
|
||||
|
||||
$prepared_pattern = array(
|
||||
'id' => absint( $raw_pattern->id ),
|
||||
'title' => sanitize_text_field( $raw_pattern->title->rendered ),
|
||||
'content' => wp_kses_post( $raw_pattern->pattern_content ),
|
||||
'categories' => array_map( 'sanitize_title', $raw_pattern->category_slugs ),
|
||||
'keywords' => array_map( 'sanitize_text_field', explode( ',', $raw_pattern->meta->wpop_keywords ) ),
|
||||
'description' => sanitize_text_field( $raw_pattern->meta->wpop_description ),
|
||||
'viewport_width' => absint( $raw_pattern->meta->wpop_viewport_width ),
|
||||
'block_types' => array_map( 'sanitize_text_field', $raw_pattern->meta->wpop_block_types ),
|
||||
);
|
||||
|
||||
$prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request );
|
||||
|
||||
$response = new WP_REST_Response( $prepared_pattern );
|
||||
|
||||
/**
|
||||
* Filters the REST API response for a block pattern.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $raw_pattern The unprepared block pattern.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_block_pattern', $response, $raw_pattern, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block pattern's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.2.0 Added `'block_types'` to schema.
|
||||
*
|
||||
* @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' => 'pattern-directory-item',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The pattern ID.' ),
|
||||
'type' => 'integer',
|
||||
'minimum' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'title' => array(
|
||||
'description' => __( 'The pattern title, in human readable format.' ),
|
||||
'type' => 'string',
|
||||
'minLength' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'content' => array(
|
||||
'description' => __( 'The pattern content.' ),
|
||||
'type' => 'string',
|
||||
'minLength' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'categories' => array(
|
||||
'description' => __( "The pattern's category slugs." ),
|
||||
'type' => 'array',
|
||||
'uniqueItems' => true,
|
||||
'items' => array( 'type' => 'string' ),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'keywords' => array(
|
||||
'description' => __( "The pattern's keywords." ),
|
||||
'type' => 'array',
|
||||
'uniqueItems' => true,
|
||||
'items' => array( 'type' => 'string' ),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'description' => array(
|
||||
'description' => __( 'A description of the pattern.' ),
|
||||
'type' => 'string',
|
||||
'minLength' => 1,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'viewport_width' => array(
|
||||
'description' => __( 'The preferred width of the viewport when previewing a pattern, in pixels.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
|
||||
'block_types' => array(
|
||||
'description' => __( 'The block types which can use this pattern.' ),
|
||||
'type' => 'array',
|
||||
'uniqueItems' => true,
|
||||
'items' => array( 'type' => 'string' ),
|
||||
'context' => array( 'view', 'embed' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the search parameters for the block pattern's collection.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['per_page']['default'] = 100;
|
||||
$query_params['search']['minLength'] = 1;
|
||||
$query_params['context']['default'] = 'view';
|
||||
|
||||
$query_params['category'] = array(
|
||||
'description' => __( 'Limit results to those matching a category ID.' ),
|
||||
'type' => 'integer',
|
||||
'minimum' => 1,
|
||||
);
|
||||
|
||||
$query_params['keyword'] = array(
|
||||
'description' => __( 'Limit results to those matching a keyword ID.' ),
|
||||
'type' => 'integer',
|
||||
'minimum' => 1,
|
||||
);
|
||||
|
||||
$query_params['slug'] = array(
|
||||
'description' => __( 'Limit results to those matching a pattern (slug).' ),
|
||||
'type' => 'array',
|
||||
);
|
||||
|
||||
$query_params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.' ),
|
||||
'type' => 'integer',
|
||||
);
|
||||
|
||||
$query_params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
);
|
||||
|
||||
$query_params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by post attribute.' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'author',
|
||||
'date',
|
||||
'id',
|
||||
'include',
|
||||
'modified',
|
||||
'parent',
|
||||
'relevance',
|
||||
'slug',
|
||||
'include_slugs',
|
||||
'title',
|
||||
'favorite_count',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter collection parameters for the block pattern directory controller.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $query_params JSON Schema-formatted collection parameters.
|
||||
*/
|
||||
return apply_filters( 'rest_pattern_directory_collection_params', $query_params );
|
||||
}
|
||||
|
||||
/*
|
||||
* Include a hash of the query args, so that different requests are stored in
|
||||
* separate caches.
|
||||
*
|
||||
* MD5 is chosen for its speed, low-collision rate, universal availability, and to stay
|
||||
* under the character limit for `_site_transient_timeout_{...}` keys.
|
||||
*
|
||||
* @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses
|
||||
*
|
||||
* @since 6.0.0
|
||||
*
|
||||
* @param array $query_args Query arguments to generate a transient key from.
|
||||
* @return string Transient key.
|
||||
*/
|
||||
protected function get_transient_key( $query_args ) {
|
||||
|
||||
if ( isset( $query_args['slug'] ) ) {
|
||||
// This is an additional precaution because the "sort" function expects an array.
|
||||
$query_args['slug'] = wp_parse_list( $query_args['slug'] );
|
||||
|
||||
// Empty arrays should not affect the transient key.
|
||||
if ( empty( $query_args['slug'] ) ) {
|
||||
unset( $query_args['slug'] );
|
||||
} else {
|
||||
// Sort the array so that the transient key doesn't depend on the order of slugs.
|
||||
sort( $query_args['slug'] );
|
||||
}
|
||||
}
|
||||
|
||||
return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) );
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,373 +1,373 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Post_Statuses_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access post statuses via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'statuses';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for post statuses.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
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<status>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'status' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the status.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read post statuses.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( 'edit' === $request['context'] ) {
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( current_user_can( $type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to manage post statuses.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all post statuses, depending on user context.
|
||||
*
|
||||
* @since 4.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_items( $request ) {
|
||||
$data = array();
|
||||
$statuses = get_post_stati( array( 'internal' => false ), 'object' );
|
||||
$statuses['trash'] = get_post_status_object( 'trash' );
|
||||
|
||||
foreach ( $statuses as $slug => $obj ) {
|
||||
$ret = $this->check_read_permission( $obj );
|
||||
|
||||
if ( ! $ret ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = $this->prepare_item_for_response( $obj, $request );
|
||||
$data[ $obj->name ] = $this->prepare_response_for_collection( $status );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read a post status.
|
||||
*
|
||||
* @since 4.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_item_permissions_check( $request ) {
|
||||
$status = get_post_status_object( $request['status'] );
|
||||
|
||||
if ( empty( $status ) ) {
|
||||
return new WP_Error(
|
||||
'rest_status_invalid',
|
||||
__( 'Invalid status.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$check = $this->check_read_permission( $status );
|
||||
|
||||
if ( ! $check ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_read_status',
|
||||
__( 'Cannot view status.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given post status should be visible.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param object $status Post status.
|
||||
* @return bool True if the post status is visible, otherwise false.
|
||||
*/
|
||||
protected function check_read_permission( $status ) {
|
||||
if ( true === $status->public ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( false === $status->internal || 'trash' === $status->name ) {
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( current_user_can( $type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific post status.
|
||||
*
|
||||
* @since 4.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_item( $request ) {
|
||||
$obj = get_post_status_object( $request['status'] );
|
||||
|
||||
if ( empty( $obj ) ) {
|
||||
return new WP_Error(
|
||||
'rest_status_invalid',
|
||||
__( 'Invalid status.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $obj, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a post status object for serialization.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.9.0 Renamed `$status` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param stdClass $item Post status data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Post status data.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$status = $item;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( in_array( 'name', $fields, true ) ) {
|
||||
$data['name'] = $status->label;
|
||||
}
|
||||
|
||||
if ( in_array( 'private', $fields, true ) ) {
|
||||
$data['private'] = (bool) $status->private;
|
||||
}
|
||||
|
||||
if ( in_array( 'protected', $fields, true ) ) {
|
||||
$data['protected'] = (bool) $status->protected;
|
||||
}
|
||||
|
||||
if ( in_array( 'public', $fields, true ) ) {
|
||||
$data['public'] = (bool) $status->public;
|
||||
}
|
||||
|
||||
if ( in_array( 'queryable', $fields, true ) ) {
|
||||
$data['queryable'] = (bool) $status->publicly_queryable;
|
||||
}
|
||||
|
||||
if ( in_array( 'show_in_list', $fields, true ) ) {
|
||||
$data['show_in_list'] = (bool) $status->show_in_admin_all_list;
|
||||
}
|
||||
|
||||
if ( in_array( 'slug', $fields, true ) ) {
|
||||
$data['slug'] = $status->name;
|
||||
}
|
||||
|
||||
if ( in_array( 'date_floating', $fields, true ) ) {
|
||||
$data['date_floating'] = $status->date_floating;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['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 );
|
||||
|
||||
$rest_url = rest_url( rest_get_route_for_post_type_items( 'post' ) );
|
||||
if ( 'publish' === $status->name ) {
|
||||
$response->add_link( 'archives', $rest_url );
|
||||
} else {
|
||||
$response->add_link( 'archives', add_query_arg( 'status', $status->name, $rest_url ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a post status returned from the REST API.
|
||||
*
|
||||
* Allows modification of the status data right before it is returned.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $status The original post status object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_status', $response, $status, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the post status' schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'status',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The title for the status.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'private' => array(
|
||||
'description' => __( 'Whether posts with this status should be private.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'protected' => array(
|
||||
'description' => __( 'Whether posts with this status should be protected.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'public' => array(
|
||||
'description' => __( 'Whether posts of this status should be shown in the front end of the site.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'queryable' => array(
|
||||
'description' => __( 'Whether posts with this status should be publicly-queryable.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'show_in_list' => array(
|
||||
'description' => __( 'Whether to include posts in the edit listing for their post type.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the status.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_floating' => array(
|
||||
'description' => __( 'Whether posts of this status may have floating published dates.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Post_Statuses_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access post statuses via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'statuses';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for post statuses.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
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<status>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'status' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the status.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read post statuses.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( 'edit' === $request['context'] ) {
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( current_user_can( $type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to manage post statuses.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all post statuses, depending on user context.
|
||||
*
|
||||
* @since 4.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_items( $request ) {
|
||||
$data = array();
|
||||
$statuses = get_post_stati( array( 'internal' => false ), 'object' );
|
||||
$statuses['trash'] = get_post_status_object( 'trash' );
|
||||
|
||||
foreach ( $statuses as $slug => $obj ) {
|
||||
$ret = $this->check_read_permission( $obj );
|
||||
|
||||
if ( ! $ret ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = $this->prepare_item_for_response( $obj, $request );
|
||||
$data[ $obj->name ] = $this->prepare_response_for_collection( $status );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read a post status.
|
||||
*
|
||||
* @since 4.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_item_permissions_check( $request ) {
|
||||
$status = get_post_status_object( $request['status'] );
|
||||
|
||||
if ( empty( $status ) ) {
|
||||
return new WP_Error(
|
||||
'rest_status_invalid',
|
||||
__( 'Invalid status.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$check = $this->check_read_permission( $status );
|
||||
|
||||
if ( ! $check ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_read_status',
|
||||
__( 'Cannot view status.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given post status should be visible.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param object $status Post status.
|
||||
* @return bool True if the post status is visible, otherwise false.
|
||||
*/
|
||||
protected function check_read_permission( $status ) {
|
||||
if ( true === $status->public ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( false === $status->internal || 'trash' === $status->name ) {
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( current_user_can( $type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific post status.
|
||||
*
|
||||
* @since 4.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_item( $request ) {
|
||||
$obj = get_post_status_object( $request['status'] );
|
||||
|
||||
if ( empty( $obj ) ) {
|
||||
return new WP_Error(
|
||||
'rest_status_invalid',
|
||||
__( 'Invalid status.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $obj, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a post status object for serialization.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.9.0 Renamed `$status` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param stdClass $item Post status data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Post status data.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$status = $item;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( in_array( 'name', $fields, true ) ) {
|
||||
$data['name'] = $status->label;
|
||||
}
|
||||
|
||||
if ( in_array( 'private', $fields, true ) ) {
|
||||
$data['private'] = (bool) $status->private;
|
||||
}
|
||||
|
||||
if ( in_array( 'protected', $fields, true ) ) {
|
||||
$data['protected'] = (bool) $status->protected;
|
||||
}
|
||||
|
||||
if ( in_array( 'public', $fields, true ) ) {
|
||||
$data['public'] = (bool) $status->public;
|
||||
}
|
||||
|
||||
if ( in_array( 'queryable', $fields, true ) ) {
|
||||
$data['queryable'] = (bool) $status->publicly_queryable;
|
||||
}
|
||||
|
||||
if ( in_array( 'show_in_list', $fields, true ) ) {
|
||||
$data['show_in_list'] = (bool) $status->show_in_admin_all_list;
|
||||
}
|
||||
|
||||
if ( in_array( 'slug', $fields, true ) ) {
|
||||
$data['slug'] = $status->name;
|
||||
}
|
||||
|
||||
if ( in_array( 'date_floating', $fields, true ) ) {
|
||||
$data['date_floating'] = $status->date_floating;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['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 );
|
||||
|
||||
$rest_url = rest_url( rest_get_route_for_post_type_items( 'post' ) );
|
||||
if ( 'publish' === $status->name ) {
|
||||
$response->add_link( 'archives', $rest_url );
|
||||
} else {
|
||||
$response->add_link( 'archives', add_query_arg( 'status', $status->name, $rest_url ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a post status returned from the REST API.
|
||||
*
|
||||
* Allows modification of the status data right before it is returned.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $status The original post status object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_status', $response, $status, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the post status' schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'status',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'The title for the status.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'private' => array(
|
||||
'description' => __( 'Whether posts with this status should be private.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'protected' => array(
|
||||
'description' => __( 'Whether posts with this status should be protected.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'public' => array(
|
||||
'description' => __( 'Whether posts of this status should be shown in the front end of the site.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'queryable' => array(
|
||||
'description' => __( 'Whether posts with this status should be publicly-queryable.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'show_in_list' => array(
|
||||
'description' => __( 'Whether to include posts in the edit listing for their post type.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the status.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_floating' => array(
|
||||
'description' => __( 'Whether posts of this status may have floating published dates.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,430 +1,430 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Post_Types_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class to access post types via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'types';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for post types.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
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<type>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'type' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the post type.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read types.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( 'edit' === $request['context'] ) {
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( current_user_can( $type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to edit posts in this post type.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all public post types.
|
||||
*
|
||||
* @since 4.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_items( $request ) {
|
||||
$data = array();
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $type->cap->edit_posts ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$post_type = $this->prepare_item_for_response( $type, $request );
|
||||
$data[ $type->name ] = $this->prepare_response_for_collection( $post_type );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific post type.
|
||||
*
|
||||
* @since 4.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_item( $request ) {
|
||||
$obj = get_post_type_object( $request['type'] );
|
||||
|
||||
if ( empty( $obj ) ) {
|
||||
return new WP_Error(
|
||||
'rest_type_invalid',
|
||||
__( 'Invalid post type.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( empty( $obj->show_in_rest ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_read_type',
|
||||
__( 'Cannot view post type.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $obj->cap->edit_posts ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit posts in this post type.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $obj, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a post type object for serialization.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.9.0 Renamed `$post_type` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param WP_Post_Type $item Post type object.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$post_type = $item;
|
||||
|
||||
$taxonomies = wp_list_filter( get_object_taxonomies( $post_type->name, 'objects' ), array( 'show_in_rest' => true ) );
|
||||
$taxonomies = wp_list_pluck( $taxonomies, 'name' );
|
||||
$base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name;
|
||||
$namespace = ! empty( $post_type->rest_namespace ) ? $post_type->rest_namespace : 'wp/v2';
|
||||
$supports = get_all_post_type_supports( $post_type->name );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( rest_is_field_included( 'capabilities', $fields ) ) {
|
||||
$data['capabilities'] = $post_type->cap;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'description', $fields ) ) {
|
||||
$data['description'] = $post_type->description;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'hierarchical', $fields ) ) {
|
||||
$data['hierarchical'] = $post_type->hierarchical;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'has_archive', $fields ) ) {
|
||||
$data['has_archive'] = $post_type->has_archive;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'visibility', $fields ) ) {
|
||||
$data['visibility'] = array(
|
||||
'show_in_nav_menus' => (bool) $post_type->show_in_nav_menus,
|
||||
'show_ui' => (bool) $post_type->show_ui,
|
||||
);
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'viewable', $fields ) ) {
|
||||
$data['viewable'] = is_post_type_viewable( $post_type );
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'labels', $fields ) ) {
|
||||
$data['labels'] = $post_type->labels;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'name', $fields ) ) {
|
||||
$data['name'] = $post_type->label;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'slug', $fields ) ) {
|
||||
$data['slug'] = $post_type->name;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'icon', $fields ) ) {
|
||||
$data['icon'] = $post_type->menu_icon;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'supports', $fields ) ) {
|
||||
$data['supports'] = $supports;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'taxonomies', $fields ) ) {
|
||||
$data['taxonomies'] = array_values( $taxonomies );
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'rest_base', $fields ) ) {
|
||||
$data['rest_base'] = $base;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'rest_namespace', $fields ) ) {
|
||||
$data['rest_namespace'] = $namespace;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $post_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a post type returned from the REST API.
|
||||
*
|
||||
* Allows modification of the post type data right before it is returned.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post_Type $post_type The original post type object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_post_type', $response, $post_type, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param WP_Post_Type $post_type The post type.
|
||||
* @return array Links for the given post type.
|
||||
*/
|
||||
protected function prepare_links( $post_type ) {
|
||||
return array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'https://api.w.org/items' => array(
|
||||
'href' => rest_url( rest_get_route_for_post_type_items( $post_type->name ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the post type's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 4.8.0 The `supports` property was added.
|
||||
* @since 5.9.0 The `visibility` and `rest_namespace` properties were added.
|
||||
* @since 6.1.0 The `icon` property was added.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'type',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'capabilities' => array(
|
||||
'description' => __( 'All capabilities used by the post type.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A human-readable description of the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'hierarchical' => array(
|
||||
'description' => __( 'Whether or not the post type should have children.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'viewable' => array(
|
||||
'description' => __( 'Whether or not the post type can be viewed.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'labels' => array(
|
||||
'description' => __( 'Human-readable labels for the post type for various contexts.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The title for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'supports' => array(
|
||||
'description' => __( 'All features, supported by the post type.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'has_archive' => array(
|
||||
'description' => __( 'If the value is a string, the value will be used as the archive slug. If the value is false the post type has no archive.' ),
|
||||
'type' => array( 'string', 'boolean' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'taxonomies' => array(
|
||||
'description' => __( 'Taxonomies associated with post type.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_base' => array(
|
||||
'description' => __( 'REST base route for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_namespace' => array(
|
||||
'description' => __( 'REST route\'s namespace for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'visibility' => array(
|
||||
'description' => __( 'The visibility settings for the post type.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'show_ui' => array(
|
||||
'description' => __( 'Whether to generate a default UI for managing this post type.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_in_nav_menus' => array(
|
||||
'description' => __( 'Whether to make the post type available for selection in navigation menus.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
),
|
||||
'icon' => array(
|
||||
'description' => __( 'The icon for the post type.' ),
|
||||
'type' => array( 'string', 'null' ),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Post_Types_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class to access post types via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'types';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for post types.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
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<type>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'type' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the post type.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read types.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( 'edit' === $request['context'] ) {
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( current_user_can( $type->cap->edit_posts ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to edit posts in this post type.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all public post types.
|
||||
*
|
||||
* @since 4.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_items( $request ) {
|
||||
$data = array();
|
||||
$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $type->cap->edit_posts ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$post_type = $this->prepare_item_for_response( $type, $request );
|
||||
$data[ $type->name ] = $this->prepare_response_for_collection( $post_type );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific post type.
|
||||
*
|
||||
* @since 4.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_item( $request ) {
|
||||
$obj = get_post_type_object( $request['type'] );
|
||||
|
||||
if ( empty( $obj ) ) {
|
||||
return new WP_Error(
|
||||
'rest_type_invalid',
|
||||
__( 'Invalid post type.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( empty( $obj->show_in_rest ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_read_type',
|
||||
__( 'Cannot view post type.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $obj->cap->edit_posts ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to edit posts in this post type.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $obj, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a post type object for serialization.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.9.0 Renamed `$post_type` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param WP_Post_Type $item Post type object.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$post_type = $item;
|
||||
|
||||
$taxonomies = wp_list_filter( get_object_taxonomies( $post_type->name, 'objects' ), array( 'show_in_rest' => true ) );
|
||||
$taxonomies = wp_list_pluck( $taxonomies, 'name' );
|
||||
$base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name;
|
||||
$namespace = ! empty( $post_type->rest_namespace ) ? $post_type->rest_namespace : 'wp/v2';
|
||||
$supports = get_all_post_type_supports( $post_type->name );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( rest_is_field_included( 'capabilities', $fields ) ) {
|
||||
$data['capabilities'] = $post_type->cap;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'description', $fields ) ) {
|
||||
$data['description'] = $post_type->description;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'hierarchical', $fields ) ) {
|
||||
$data['hierarchical'] = $post_type->hierarchical;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'has_archive', $fields ) ) {
|
||||
$data['has_archive'] = $post_type->has_archive;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'visibility', $fields ) ) {
|
||||
$data['visibility'] = array(
|
||||
'show_in_nav_menus' => (bool) $post_type->show_in_nav_menus,
|
||||
'show_ui' => (bool) $post_type->show_ui,
|
||||
);
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'viewable', $fields ) ) {
|
||||
$data['viewable'] = is_post_type_viewable( $post_type );
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'labels', $fields ) ) {
|
||||
$data['labels'] = $post_type->labels;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'name', $fields ) ) {
|
||||
$data['name'] = $post_type->label;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'slug', $fields ) ) {
|
||||
$data['slug'] = $post_type->name;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'icon', $fields ) ) {
|
||||
$data['icon'] = $post_type->menu_icon;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'supports', $fields ) ) {
|
||||
$data['supports'] = $supports;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'taxonomies', $fields ) ) {
|
||||
$data['taxonomies'] = array_values( $taxonomies );
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'rest_base', $fields ) ) {
|
||||
$data['rest_base'] = $base;
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'rest_namespace', $fields ) ) {
|
||||
$data['rest_namespace'] = $namespace;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $post_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a post type returned from the REST API.
|
||||
*
|
||||
* Allows modification of the post type data right before it is returned.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post_Type $post_type The original post type object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_post_type', $response, $post_type, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param WP_Post_Type $post_type The post type.
|
||||
* @return array Links for the given post type.
|
||||
*/
|
||||
protected function prepare_links( $post_type ) {
|
||||
return array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'https://api.w.org/items' => array(
|
||||
'href' => rest_url( rest_get_route_for_post_type_items( $post_type->name ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the post type's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 4.8.0 The `supports` property was added.
|
||||
* @since 5.9.0 The `visibility` and `rest_namespace` properties were added.
|
||||
* @since 6.1.0 The `icon` property was added.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'type',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'capabilities' => array(
|
||||
'description' => __( 'All capabilities used by the post type.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A human-readable description of the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'hierarchical' => array(
|
||||
'description' => __( 'Whether or not the post type should have children.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'viewable' => array(
|
||||
'description' => __( 'Whether or not the post type can be viewed.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'labels' => array(
|
||||
'description' => __( 'Human-readable labels for the post type for various contexts.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The title for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'supports' => array(
|
||||
'description' => __( 'All features, supported by the post type.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'has_archive' => array(
|
||||
'description' => __( 'If the value is a string, the value will be used as the archive slug. If the value is false the post type has no archive.' ),
|
||||
'type' => array( 'string', 'boolean' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'taxonomies' => array(
|
||||
'description' => __( 'Taxonomies associated with post type.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_base' => array(
|
||||
'description' => __( 'REST base route for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_namespace' => array(
|
||||
'description' => __( 'REST route\'s namespace for the post type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'visibility' => array(
|
||||
'description' => __( 'The visibility settings for the post type.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'show_ui' => array(
|
||||
'description' => __( 'Whether to generate a default UI for managing this post type.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_in_nav_menus' => array(
|
||||
'description' => __( 'Whether to make the post type available for selection in navigation menus.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
),
|
||||
'icon' => array(
|
||||
'description' => __( 'The icon for the post type.' ),
|
||||
'type' => array( 'string', 'null' ),
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,408 +1,408 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Search_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class to search through all WordPress content via the REST API.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Search_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* ID property name.
|
||||
*/
|
||||
const PROP_ID = 'id';
|
||||
|
||||
/**
|
||||
* Title property name.
|
||||
*/
|
||||
const PROP_TITLE = 'title';
|
||||
|
||||
/**
|
||||
* URL property name.
|
||||
*/
|
||||
const PROP_URL = 'url';
|
||||
|
||||
/**
|
||||
* Type property name.
|
||||
*/
|
||||
const PROP_TYPE = 'type';
|
||||
|
||||
/**
|
||||
* Subtype property name.
|
||||
*/
|
||||
const PROP_SUBTYPE = 'subtype';
|
||||
|
||||
/**
|
||||
* Identifier for the 'any' type.
|
||||
*/
|
||||
const TYPE_ANY = 'any';
|
||||
|
||||
/**
|
||||
* Search handlers used by the controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var WP_REST_Search_Handler[]
|
||||
*/
|
||||
protected $search_handlers = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param array $search_handlers List of search handlers to use in the controller. Each search
|
||||
* handler instance must extend the `WP_REST_Search_Handler` class.
|
||||
*/
|
||||
public function __construct( array $search_handlers ) {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'search';
|
||||
|
||||
foreach ( $search_handlers as $search_handler ) {
|
||||
if ( ! $search_handler instanceof WP_REST_Search_Handler ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
/* translators: %s: PHP class name. */
|
||||
sprintf( __( 'REST search handlers must extend the %s class.' ), 'WP_REST_Search_Handler' ),
|
||||
'5.0.0'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->search_handlers[ $search_handler->get_type() ] = $search_handler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the search controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
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_permission_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to search content.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has search access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permission_check( $request ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a collection of search results.
|
||||
*
|
||||
* @since 5.0.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 ) {
|
||||
$handler = $this->get_search_handler( $request );
|
||||
if ( is_wp_error( $handler ) ) {
|
||||
return $handler;
|
||||
}
|
||||
|
||||
$result = $handler->search_items( $request );
|
||||
|
||||
if ( ! isset( $result[ WP_REST_Search_Handler::RESULT_IDS ] ) || ! is_array( $result[ WP_REST_Search_Handler::RESULT_IDS ] ) || ! isset( $result[ WP_REST_Search_Handler::RESULT_TOTAL ] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_search_handler_error',
|
||||
__( 'Internal search handler error.' ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
$ids = $result[ WP_REST_Search_Handler::RESULT_IDS ];
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ( $ids as $id ) {
|
||||
$data = $this->prepare_item_for_response( $id, $request );
|
||||
$results[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$total = (int) $result[ WP_REST_Search_Handler::RESULT_TOTAL ];
|
||||
$page = (int) $request['page'];
|
||||
$per_page = (int) $request['per_page'];
|
||||
$max_pages = (int) ceil( $total / $per_page );
|
||||
|
||||
if ( $page > $max_pages && $total > 0 ) {
|
||||
return new WP_Error(
|
||||
'rest_search_invalid_page_number',
|
||||
__( 'The page number requested is larger than the number of pages available.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $results );
|
||||
$response->header( 'X-WP-Total', $total );
|
||||
$response->header( 'X-WP-TotalPages', $max_pages );
|
||||
|
||||
$request_params = $request->get_query_params();
|
||||
$base = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_link = add_query_arg( 'page', $page - 1, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $page < $max_pages ) {
|
||||
$next_link = add_query_arg( 'page', $page + 1, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a single search result for response.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 5.6.0 The `$id` parameter can accept a string.
|
||||
* @since 5.9.0 Renamed `$id` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param int|string $item ID of the item to prepare.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$item_id = $item;
|
||||
|
||||
$handler = $this->get_search_handler( $request );
|
||||
if ( is_wp_error( $handler ) ) {
|
||||
return new WP_REST_Response();
|
||||
}
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
$data = $handler->prepare_item( $item_id, $fields );
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $handler->prepare_item_links( $item_id );
|
||||
$links['collection'] = array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
);
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$types = array();
|
||||
$subtypes = array();
|
||||
|
||||
foreach ( $this->search_handlers as $search_handler ) {
|
||||
$types[] = $search_handler->get_type();
|
||||
$subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
|
||||
}
|
||||
|
||||
$types = array_unique( $types );
|
||||
$subtypes = array_unique( $subtypes );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'search-result',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
self::PROP_ID => array(
|
||||
'description' => __( 'Unique identifier for the object.' ),
|
||||
'type' => array( 'integer', 'string' ),
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_TITLE => array(
|
||||
'description' => __( 'The title for the object.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_URL => array(
|
||||
'description' => __( 'URL to the object.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_TYPE => array(
|
||||
'description' => __( 'Object type.' ),
|
||||
'type' => 'string',
|
||||
'enum' => $types,
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_SUBTYPE => array(
|
||||
'description' => __( 'Object subtype.' ),
|
||||
'type' => 'string',
|
||||
'enum' => $subtypes,
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for the search results collection.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$types = array();
|
||||
$subtypes = array();
|
||||
|
||||
foreach ( $this->search_handlers as $search_handler ) {
|
||||
$types[] = $search_handler->get_type();
|
||||
$subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
|
||||
}
|
||||
|
||||
$types = array_unique( $types );
|
||||
$subtypes = array_unique( $subtypes );
|
||||
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['context']['default'] = 'view';
|
||||
|
||||
$query_params[ self::PROP_TYPE ] = array(
|
||||
'default' => $types[0],
|
||||
'description' => __( 'Limit results to items of an object type.' ),
|
||||
'type' => 'string',
|
||||
'enum' => $types,
|
||||
);
|
||||
|
||||
$query_params[ self::PROP_SUBTYPE ] = array(
|
||||
'default' => self::TYPE_ANY,
|
||||
'description' => __( 'Limit results to items of one or more object subtypes.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'enum' => array_merge( $subtypes, array( self::TYPE_ANY ) ),
|
||||
'type' => 'string',
|
||||
),
|
||||
'sanitize_callback' => array( $this, 'sanitize_subtypes' ),
|
||||
);
|
||||
|
||||
$query_params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific IDs.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'integer',
|
||||
),
|
||||
'default' => array(),
|
||||
);
|
||||
|
||||
$query_params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific IDs.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'integer',
|
||||
),
|
||||
'default' => array(),
|
||||
);
|
||||
|
||||
return $query_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the list of subtypes, to ensure only subtypes of the passed type are included.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param string|array $subtypes One or more subtypes.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @param string $parameter Parameter name.
|
||||
* @return string[]|WP_Error List of valid subtypes, or WP_Error object on failure.
|
||||
*/
|
||||
public function sanitize_subtypes( $subtypes, $request, $parameter ) {
|
||||
$subtypes = wp_parse_slug_list( $subtypes );
|
||||
|
||||
$subtypes = rest_parse_request_arg( $subtypes, $request, $parameter );
|
||||
if ( is_wp_error( $subtypes ) ) {
|
||||
return $subtypes;
|
||||
}
|
||||
|
||||
// 'any' overrides any other subtype.
|
||||
if ( in_array( self::TYPE_ANY, $subtypes, true ) ) {
|
||||
return array( self::TYPE_ANY );
|
||||
}
|
||||
|
||||
$handler = $this->get_search_handler( $request );
|
||||
if ( is_wp_error( $handler ) ) {
|
||||
return $handler;
|
||||
}
|
||||
|
||||
return array_intersect( $subtypes, $handler->get_subtypes() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the search handler to handle the current request.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Search_Handler|WP_Error Search handler for the request type, or WP_Error object on failure.
|
||||
*/
|
||||
protected function get_search_handler( $request ) {
|
||||
$type = $request->get_param( self::PROP_TYPE );
|
||||
|
||||
if ( ! $type || ! isset( $this->search_handlers[ $type ] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_search_invalid_type',
|
||||
__( 'Invalid type parameter.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->search_handlers[ $type ];
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Search_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class to search through all WordPress content via the REST API.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Search_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* ID property name.
|
||||
*/
|
||||
const PROP_ID = 'id';
|
||||
|
||||
/**
|
||||
* Title property name.
|
||||
*/
|
||||
const PROP_TITLE = 'title';
|
||||
|
||||
/**
|
||||
* URL property name.
|
||||
*/
|
||||
const PROP_URL = 'url';
|
||||
|
||||
/**
|
||||
* Type property name.
|
||||
*/
|
||||
const PROP_TYPE = 'type';
|
||||
|
||||
/**
|
||||
* Subtype property name.
|
||||
*/
|
||||
const PROP_SUBTYPE = 'subtype';
|
||||
|
||||
/**
|
||||
* Identifier for the 'any' type.
|
||||
*/
|
||||
const TYPE_ANY = 'any';
|
||||
|
||||
/**
|
||||
* Search handlers used by the controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @var WP_REST_Search_Handler[]
|
||||
*/
|
||||
protected $search_handlers = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param array $search_handlers List of search handlers to use in the controller. Each search
|
||||
* handler instance must extend the `WP_REST_Search_Handler` class.
|
||||
*/
|
||||
public function __construct( array $search_handlers ) {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'search';
|
||||
|
||||
foreach ( $search_handlers as $search_handler ) {
|
||||
if ( ! $search_handler instanceof WP_REST_Search_Handler ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
/* translators: %s: PHP class name. */
|
||||
sprintf( __( 'REST search handlers must extend the %s class.' ), 'WP_REST_Search_Handler' ),
|
||||
'5.0.0'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->search_handlers[ $search_handler->get_type() ] = $search_handler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the search controller.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
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_permission_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to search content.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has search access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permission_check( $request ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a collection of search results.
|
||||
*
|
||||
* @since 5.0.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 ) {
|
||||
$handler = $this->get_search_handler( $request );
|
||||
if ( is_wp_error( $handler ) ) {
|
||||
return $handler;
|
||||
}
|
||||
|
||||
$result = $handler->search_items( $request );
|
||||
|
||||
if ( ! isset( $result[ WP_REST_Search_Handler::RESULT_IDS ] ) || ! is_array( $result[ WP_REST_Search_Handler::RESULT_IDS ] ) || ! isset( $result[ WP_REST_Search_Handler::RESULT_TOTAL ] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_search_handler_error',
|
||||
__( 'Internal search handler error.' ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
$ids = $result[ WP_REST_Search_Handler::RESULT_IDS ];
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ( $ids as $id ) {
|
||||
$data = $this->prepare_item_for_response( $id, $request );
|
||||
$results[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$total = (int) $result[ WP_REST_Search_Handler::RESULT_TOTAL ];
|
||||
$page = (int) $request['page'];
|
||||
$per_page = (int) $request['per_page'];
|
||||
$max_pages = (int) ceil( $total / $per_page );
|
||||
|
||||
if ( $page > $max_pages && $total > 0 ) {
|
||||
return new WP_Error(
|
||||
'rest_search_invalid_page_number',
|
||||
__( 'The page number requested is larger than the number of pages available.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $results );
|
||||
$response->header( 'X-WP-Total', $total );
|
||||
$response->header( 'X-WP-TotalPages', $max_pages );
|
||||
|
||||
$request_params = $request->get_query_params();
|
||||
$base = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_link = add_query_arg( 'page', $page - 1, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $page < $max_pages ) {
|
||||
$next_link = add_query_arg( 'page', $page + 1, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a single search result for response.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @since 5.6.0 The `$id` parameter can accept a string.
|
||||
* @since 5.9.0 Renamed `$id` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param int|string $item ID of the item to prepare.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$item_id = $item;
|
||||
|
||||
$handler = $this->get_search_handler( $request );
|
||||
if ( is_wp_error( $handler ) ) {
|
||||
return new WP_REST_Response();
|
||||
}
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
$data = $handler->prepare_item( $item_id, $fields );
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $handler->prepare_item_links( $item_id );
|
||||
$links['collection'] = array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
);
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$types = array();
|
||||
$subtypes = array();
|
||||
|
||||
foreach ( $this->search_handlers as $search_handler ) {
|
||||
$types[] = $search_handler->get_type();
|
||||
$subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
|
||||
}
|
||||
|
||||
$types = array_unique( $types );
|
||||
$subtypes = array_unique( $subtypes );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'search-result',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
self::PROP_ID => array(
|
||||
'description' => __( 'Unique identifier for the object.' ),
|
||||
'type' => array( 'integer', 'string' ),
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_TITLE => array(
|
||||
'description' => __( 'The title for the object.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_URL => array(
|
||||
'description' => __( 'URL to the object.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_TYPE => array(
|
||||
'description' => __( 'Object type.' ),
|
||||
'type' => 'string',
|
||||
'enum' => $types,
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
self::PROP_SUBTYPE => array(
|
||||
'description' => __( 'Object subtype.' ),
|
||||
'type' => 'string',
|
||||
'enum' => $subtypes,
|
||||
'context' => array( 'view', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for the search results collection.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$types = array();
|
||||
$subtypes = array();
|
||||
|
||||
foreach ( $this->search_handlers as $search_handler ) {
|
||||
$types[] = $search_handler->get_type();
|
||||
$subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
|
||||
}
|
||||
|
||||
$types = array_unique( $types );
|
||||
$subtypes = array_unique( $subtypes );
|
||||
|
||||
$query_params = parent::get_collection_params();
|
||||
|
||||
$query_params['context']['default'] = 'view';
|
||||
|
||||
$query_params[ self::PROP_TYPE ] = array(
|
||||
'default' => $types[0],
|
||||
'description' => __( 'Limit results to items of an object type.' ),
|
||||
'type' => 'string',
|
||||
'enum' => $types,
|
||||
);
|
||||
|
||||
$query_params[ self::PROP_SUBTYPE ] = array(
|
||||
'default' => self::TYPE_ANY,
|
||||
'description' => __( 'Limit results to items of one or more object subtypes.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'enum' => array_merge( $subtypes, array( self::TYPE_ANY ) ),
|
||||
'type' => 'string',
|
||||
),
|
||||
'sanitize_callback' => array( $this, 'sanitize_subtypes' ),
|
||||
);
|
||||
|
||||
$query_params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific IDs.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'integer',
|
||||
),
|
||||
'default' => array(),
|
||||
);
|
||||
|
||||
$query_params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific IDs.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'integer',
|
||||
),
|
||||
'default' => array(),
|
||||
);
|
||||
|
||||
return $query_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the list of subtypes, to ensure only subtypes of the passed type are included.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param string|array $subtypes One or more subtypes.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @param string $parameter Parameter name.
|
||||
* @return string[]|WP_Error List of valid subtypes, or WP_Error object on failure.
|
||||
*/
|
||||
public function sanitize_subtypes( $subtypes, $request, $parameter ) {
|
||||
$subtypes = wp_parse_slug_list( $subtypes );
|
||||
|
||||
$subtypes = rest_parse_request_arg( $subtypes, $request, $parameter );
|
||||
if ( is_wp_error( $subtypes ) ) {
|
||||
return $subtypes;
|
||||
}
|
||||
|
||||
// 'any' overrides any other subtype.
|
||||
if ( in_array( self::TYPE_ANY, $subtypes, true ) ) {
|
||||
return array( self::TYPE_ANY );
|
||||
}
|
||||
|
||||
$handler = $this->get_search_handler( $request );
|
||||
if ( is_wp_error( $handler ) ) {
|
||||
return $handler;
|
||||
}
|
||||
|
||||
return array_intersect( $subtypes, $handler->get_subtypes() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the search handler to handle the current request.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Search_Handler|WP_Error Search handler for the request type, or WP_Error object on failure.
|
||||
*/
|
||||
protected function get_search_handler( $request ) {
|
||||
$type = $request->get_param( self::PROP_TYPE );
|
||||
|
||||
if ( ! $type || ! isset( $this->search_handlers[ $type ] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_search_invalid_type',
|
||||
__( 'Invalid type parameter.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->search_handlers[ $type ];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,342 +1,342 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Settings_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to manage a site's settings via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Settings_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the site's settings.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'args' => array(),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read and manage settings.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool True if the request has read access for the item, otherwise false.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the settings.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return array|WP_Error Array on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$options = $this->get_registered_options();
|
||||
$response = array();
|
||||
|
||||
foreach ( $options as $name => $args ) {
|
||||
/**
|
||||
* Filters the value of a setting recognized by the REST API.
|
||||
*
|
||||
* Allow hijacking the setting value and overriding the built-in behavior by returning a
|
||||
* non-null value. The returned value will be presented as the setting value instead.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param mixed $result Value to use for the requested setting. Can be a scalar
|
||||
* matching the registered schema for the setting, or null to
|
||||
* follow the default get_option() behavior.
|
||||
* @param string $name Setting name (as shown in REST API responses).
|
||||
* @param array $args Arguments passed to register_setting() for this setting.
|
||||
*/
|
||||
$response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args );
|
||||
|
||||
if ( is_null( $response[ $name ] ) ) {
|
||||
// Default to a null value as "null" in the response means "not set".
|
||||
$response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] );
|
||||
}
|
||||
|
||||
/*
|
||||
* Because get_option() is lossy, we have to
|
||||
* cast values to the type they are registered with.
|
||||
*/
|
||||
$response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a value for output based off a schema array.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param mixed $value Value to prepare.
|
||||
* @param array $schema Schema to match.
|
||||
* @return mixed The prepared value.
|
||||
*/
|
||||
protected function prepare_value( $value, $schema ) {
|
||||
/*
|
||||
* If the value is not valid by the schema, set the value to null.
|
||||
* Null values are specifically non-destructive, so this will not cause
|
||||
* overwriting the current invalid value to null.
|
||||
*/
|
||||
if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return rest_sanitize_value_from_schema( $value, $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates settings for the settings object.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return array|WP_Error Array on success, or error object on failure.
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$options = $this->get_registered_options();
|
||||
|
||||
$params = $request->get_params();
|
||||
|
||||
foreach ( $options as $name => $args ) {
|
||||
if ( ! array_key_exists( $name, $params ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters whether to preempt a setting value update via the REST API.
|
||||
*
|
||||
* Allows hijacking the setting update logic and overriding the built-in behavior by
|
||||
* returning true.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param bool $result Whether to override the default behavior for updating the
|
||||
* value of a setting.
|
||||
* @param string $name Setting name (as shown in REST API responses).
|
||||
* @param mixed $value Updated setting value.
|
||||
* @param array $args Arguments passed to register_setting() for this setting.
|
||||
*/
|
||||
$updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args );
|
||||
|
||||
if ( $updated ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A null value for an option would have the same effect as
|
||||
* deleting the option from the database, and relying on the
|
||||
* default value.
|
||||
*/
|
||||
if ( is_null( $request[ $name ] ) ) {
|
||||
/*
|
||||
* A null value is returned in the response for any option
|
||||
* that has a non-scalar value.
|
||||
*
|
||||
* To protect clients from accidentally including the null
|
||||
* values from a response object in a request, we do not allow
|
||||
* options with values that don't pass validation to be updated to null.
|
||||
* Without this added protection a client could mistakenly
|
||||
* delete all options that have invalid values from the
|
||||
* database.
|
||||
*/
|
||||
if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_stored_value',
|
||||
/* translators: %s: Property name. */
|
||||
sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
delete_option( $args['option_name'] );
|
||||
} else {
|
||||
update_option( $args['option_name'], $request[ $name ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->get_item( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all of the registered options for the Settings API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Array of registered options.
|
||||
*/
|
||||
protected function get_registered_options() {
|
||||
$rest_options = array();
|
||||
|
||||
foreach ( get_registered_settings() as $name => $args ) {
|
||||
if ( empty( $args['show_in_rest'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rest_args = array();
|
||||
|
||||
if ( is_array( $args['show_in_rest'] ) ) {
|
||||
$rest_args = $args['show_in_rest'];
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'name' => ! empty( $rest_args['name'] ) ? $rest_args['name'] : $name,
|
||||
'schema' => array(),
|
||||
);
|
||||
|
||||
$rest_args = array_merge( $defaults, $rest_args );
|
||||
|
||||
$default_schema = array(
|
||||
'type' => empty( $args['type'] ) ? null : $args['type'],
|
||||
'description' => empty( $args['description'] ) ? '' : $args['description'],
|
||||
'default' => isset( $args['default'] ) ? $args['default'] : null,
|
||||
);
|
||||
|
||||
$rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] );
|
||||
$rest_args['option_name'] = $name;
|
||||
|
||||
// Skip over settings that don't have a defined type in the schema.
|
||||
if ( empty( $rest_args['schema']['type'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the supported types for settings, as we don't want invalid types
|
||||
* to be updated with arbitrary values that we can't do decent sanitizing for.
|
||||
*/
|
||||
if ( ! in_array( $rest_args['schema']['type'], array( 'number', 'integer', 'string', 'boolean', 'array', 'object' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rest_args['schema'] = rest_default_additional_properties_to_false( $rest_args['schema'] );
|
||||
|
||||
$rest_options[ $rest_args['name'] ] = $rest_args;
|
||||
}
|
||||
|
||||
return $rest_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the site setting schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$options = $this->get_registered_options();
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'settings',
|
||||
'type' => 'object',
|
||||
'properties' => array(),
|
||||
);
|
||||
|
||||
foreach ( $options as $option_name => $option ) {
|
||||
$schema['properties'][ $option_name ] = $option['schema'];
|
||||
$schema['properties'][ $option_name ]['arg_options'] = array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_callback' ),
|
||||
);
|
||||
}
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom sanitize callback used for all options to allow the use of 'null'.
|
||||
*
|
||||
* By default, the schema of settings will throw an error if a value is set to
|
||||
* `null` as it's not a valid value for something like "type => string". We
|
||||
* provide a wrapper sanitizer to allow the use of `null`.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param mixed $value The value for the setting.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @param string $param The parameter name.
|
||||
* @return mixed|WP_Error
|
||||
*/
|
||||
public function sanitize_callback( $value, $request, $param ) {
|
||||
if ( is_null( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return rest_parse_request_arg( $value, $request, $param );
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively add additionalProperties = false to all objects in a schema
|
||||
* if no additionalProperties setting is specified.
|
||||
*
|
||||
* This is needed to restrict properties of objects in settings values to only
|
||||
* registered items, as the REST API will allow additional properties by
|
||||
* default.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @deprecated 6.1.0 Use {@see rest_default_additional_properties_to_false()} instead.
|
||||
*
|
||||
* @param array $schema The schema array.
|
||||
* @return array
|
||||
*/
|
||||
protected function set_additional_properties_to_false( $schema ) {
|
||||
_deprecated_function( __METHOD__, '6.1.0', 'rest_default_additional_properties_to_false()' );
|
||||
|
||||
return rest_default_additional_properties_to_false( $schema );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Settings_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to manage a site's settings via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Settings_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for the site's settings.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'args' => array(),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read and manage settings.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool True if the request has read access for the item, otherwise false.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the settings.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return array|WP_Error Array on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$options = $this->get_registered_options();
|
||||
$response = array();
|
||||
|
||||
foreach ( $options as $name => $args ) {
|
||||
/**
|
||||
* Filters the value of a setting recognized by the REST API.
|
||||
*
|
||||
* Allow hijacking the setting value and overriding the built-in behavior by returning a
|
||||
* non-null value. The returned value will be presented as the setting value instead.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param mixed $result Value to use for the requested setting. Can be a scalar
|
||||
* matching the registered schema for the setting, or null to
|
||||
* follow the default get_option() behavior.
|
||||
* @param string $name Setting name (as shown in REST API responses).
|
||||
* @param array $args Arguments passed to register_setting() for this setting.
|
||||
*/
|
||||
$response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args );
|
||||
|
||||
if ( is_null( $response[ $name ] ) ) {
|
||||
// Default to a null value as "null" in the response means "not set".
|
||||
$response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] );
|
||||
}
|
||||
|
||||
/*
|
||||
* Because get_option() is lossy, we have to
|
||||
* cast values to the type they are registered with.
|
||||
*/
|
||||
$response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a value for output based off a schema array.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param mixed $value Value to prepare.
|
||||
* @param array $schema Schema to match.
|
||||
* @return mixed The prepared value.
|
||||
*/
|
||||
protected function prepare_value( $value, $schema ) {
|
||||
/*
|
||||
* If the value is not valid by the schema, set the value to null.
|
||||
* Null values are specifically non-destructive, so this will not cause
|
||||
* overwriting the current invalid value to null.
|
||||
*/
|
||||
if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return rest_sanitize_value_from_schema( $value, $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates settings for the settings object.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return array|WP_Error Array on success, or error object on failure.
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$options = $this->get_registered_options();
|
||||
|
||||
$params = $request->get_params();
|
||||
|
||||
foreach ( $options as $name => $args ) {
|
||||
if ( ! array_key_exists( $name, $params ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters whether to preempt a setting value update via the REST API.
|
||||
*
|
||||
* Allows hijacking the setting update logic and overriding the built-in behavior by
|
||||
* returning true.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param bool $result Whether to override the default behavior for updating the
|
||||
* value of a setting.
|
||||
* @param string $name Setting name (as shown in REST API responses).
|
||||
* @param mixed $value Updated setting value.
|
||||
* @param array $args Arguments passed to register_setting() for this setting.
|
||||
*/
|
||||
$updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args );
|
||||
|
||||
if ( $updated ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A null value for an option would have the same effect as
|
||||
* deleting the option from the database, and relying on the
|
||||
* default value.
|
||||
*/
|
||||
if ( is_null( $request[ $name ] ) ) {
|
||||
/*
|
||||
* A null value is returned in the response for any option
|
||||
* that has a non-scalar value.
|
||||
*
|
||||
* To protect clients from accidentally including the null
|
||||
* values from a response object in a request, we do not allow
|
||||
* options with values that don't pass validation to be updated to null.
|
||||
* Without this added protection a client could mistakenly
|
||||
* delete all options that have invalid values from the
|
||||
* database.
|
||||
*/
|
||||
if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_stored_value',
|
||||
/* translators: %s: Property name. */
|
||||
sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
delete_option( $args['option_name'] );
|
||||
} else {
|
||||
update_option( $args['option_name'], $request[ $name ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->get_item( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all of the registered options for the Settings API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Array of registered options.
|
||||
*/
|
||||
protected function get_registered_options() {
|
||||
$rest_options = array();
|
||||
|
||||
foreach ( get_registered_settings() as $name => $args ) {
|
||||
if ( empty( $args['show_in_rest'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rest_args = array();
|
||||
|
||||
if ( is_array( $args['show_in_rest'] ) ) {
|
||||
$rest_args = $args['show_in_rest'];
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'name' => ! empty( $rest_args['name'] ) ? $rest_args['name'] : $name,
|
||||
'schema' => array(),
|
||||
);
|
||||
|
||||
$rest_args = array_merge( $defaults, $rest_args );
|
||||
|
||||
$default_schema = array(
|
||||
'type' => empty( $args['type'] ) ? null : $args['type'],
|
||||
'description' => empty( $args['description'] ) ? '' : $args['description'],
|
||||
'default' => isset( $args['default'] ) ? $args['default'] : null,
|
||||
);
|
||||
|
||||
$rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] );
|
||||
$rest_args['option_name'] = $name;
|
||||
|
||||
// Skip over settings that don't have a defined type in the schema.
|
||||
if ( empty( $rest_args['schema']['type'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the supported types for settings, as we don't want invalid types
|
||||
* to be updated with arbitrary values that we can't do decent sanitizing for.
|
||||
*/
|
||||
if ( ! in_array( $rest_args['schema']['type'], array( 'number', 'integer', 'string', 'boolean', 'array', 'object' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rest_args['schema'] = rest_default_additional_properties_to_false( $rest_args['schema'] );
|
||||
|
||||
$rest_options[ $rest_args['name'] ] = $rest_args;
|
||||
}
|
||||
|
||||
return $rest_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the site setting schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$options = $this->get_registered_options();
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'settings',
|
||||
'type' => 'object',
|
||||
'properties' => array(),
|
||||
);
|
||||
|
||||
foreach ( $options as $option_name => $option ) {
|
||||
$schema['properties'][ $option_name ] = $option['schema'];
|
||||
$schema['properties'][ $option_name ]['arg_options'] = array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_callback' ),
|
||||
);
|
||||
}
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom sanitize callback used for all options to allow the use of 'null'.
|
||||
*
|
||||
* By default, the schema of settings will throw an error if a value is set to
|
||||
* `null` as it's not a valid value for something like "type => string". We
|
||||
* provide a wrapper sanitizer to allow the use of `null`.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param mixed $value The value for the setting.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @param string $param The parameter name.
|
||||
* @return mixed|WP_Error
|
||||
*/
|
||||
public function sanitize_callback( $value, $request, $param ) {
|
||||
if ( is_null( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return rest_parse_request_arg( $value, $request, $param );
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively add additionalProperties = false to all objects in a schema
|
||||
* if no additionalProperties setting is specified.
|
||||
*
|
||||
* This is needed to restrict properties of objects in settings values to only
|
||||
* registered items, as the REST API will allow additional properties by
|
||||
* default.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @deprecated 6.1.0 Use {@see rest_default_additional_properties_to_false()} instead.
|
||||
*
|
||||
* @param array $schema The schema array.
|
||||
* @return array
|
||||
*/
|
||||
protected function set_additional_properties_to_false( $schema ) {
|
||||
_deprecated_function( __METHOD__, '6.1.0', 'rest_default_additional_properties_to_false()' );
|
||||
|
||||
return rest_default_additional_properties_to_false( $schema );
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,407 +1,407 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Site_Health_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class for interacting with Site Health tests.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Site_Health_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* An instance of the site health class.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @var WP_Site_Health
|
||||
*/
|
||||
private $site_health;
|
||||
|
||||
/**
|
||||
* Site Health controller constructor.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param WP_Site_Health $site_health An instance of the site health class.
|
||||
*/
|
||||
public function __construct( $site_health ) {
|
||||
$this->namespace = 'wp-site-health/v1';
|
||||
$this->rest_base = 'tests';
|
||||
|
||||
$this->site_health = $site_health;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers API routes.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @since 6.1.0 Adds page-cache async test.
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'background-updates'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_background_updates' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'background_updates' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'loopback-requests'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_loopback_requests' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'loopback_requests' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'https-status'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_https_status' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'https_status' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'dotorg-communication'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_dotorg_communication' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'dotorg_communication' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'authorization-header'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_authorization_header' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'authorization_header' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s',
|
||||
'directory-sizes'
|
||||
),
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_directory_sizes' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'directory_sizes' ) && ! is_multisite();
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'page-cache'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_page_cache' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'page_cache' );
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the current user can request this REST endpoint.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $check The endpoint check being ran.
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_request_permission( $check ) {
|
||||
$default_capability = 'view_site_health_checks';
|
||||
|
||||
/**
|
||||
* Filters the capability needed to run a given Site Health check.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $default_capability The default capability required for this check.
|
||||
* @param string $check The Site Health check being performed.
|
||||
*/
|
||||
$capability = apply_filters( "site_health_test_rest_capability_{$check}", $default_capability, $check );
|
||||
|
||||
return current_user_can( $capability );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if background updates work as expected.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_background_updates() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_background_updates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the site can reach the WordPress.org API.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_dotorg_communication() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_dotorg_communication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that loopbacks can be performed.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_loopback_requests() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_loopback_requests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the site's frontend can be accessed over HTTPS.
|
||||
*
|
||||
* @since 5.7.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_https_status() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_https_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the authorization header is valid.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_authorization_header() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_authorization_header();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that full page cache is active.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function test_page_cache() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current directory sizes for this install.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_directory_sizes() {
|
||||
if ( ! class_exists( 'WP_Debug_Data' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-debug-data.php';
|
||||
}
|
||||
|
||||
$this->load_admin_textdomain();
|
||||
|
||||
$sizes_data = WP_Debug_Data::get_sizes();
|
||||
$all_sizes = array( 'raw' => 0 );
|
||||
|
||||
foreach ( $sizes_data as $name => $value ) {
|
||||
$name = sanitize_text_field( $name );
|
||||
$data = array();
|
||||
|
||||
if ( isset( $value['size'] ) ) {
|
||||
if ( is_string( $value['size'] ) ) {
|
||||
$data['size'] = sanitize_text_field( $value['size'] );
|
||||
} else {
|
||||
$data['size'] = (int) $value['size'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $value['debug'] ) ) {
|
||||
if ( is_string( $value['debug'] ) ) {
|
||||
$data['debug'] = sanitize_text_field( $value['debug'] );
|
||||
} else {
|
||||
$data['debug'] = (int) $value['debug'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $value['raw'] ) ) {
|
||||
$data['raw'] = (int) $value['raw'];
|
||||
}
|
||||
|
||||
$all_sizes[ $name ] = $data;
|
||||
}
|
||||
|
||||
if ( isset( $all_sizes['total_size']['debug'] ) && 'not available' === $all_sizes['total_size']['debug'] ) {
|
||||
return new WP_Error( 'not_available', __( 'Directory sizes could not be returned.' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return $all_sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the admin textdomain for Site Health tests.
|
||||
*
|
||||
* The {@see WP_Site_Health} class is defined in WP-Admin, while the REST API operates in a front-end context.
|
||||
* This means that the translations for Site Health won't be loaded by default in {@see load_default_textdomain()}.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*/
|
||||
protected function load_admin_textdomain() {
|
||||
// Accounts for inner REST API requests in the admin.
|
||||
if ( ! is_admin() ) {
|
||||
$locale = determine_locale();
|
||||
load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo", $locale );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the schema for each site health test.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array The test schema.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
$this->schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'wp-site-health-test',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'test' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'The name of the test being run.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'label' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'A label describing the test.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'status' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'The status of the test.' ),
|
||||
'enum' => array( 'good', 'recommended', 'critical' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'badge' => array(
|
||||
'type' => 'object',
|
||||
'description' => __( 'The category this test is grouped in.' ),
|
||||
'properties' => array(
|
||||
'label' => array(
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
),
|
||||
'color' => array(
|
||||
'type' => 'string',
|
||||
'enum' => array( 'blue', 'orange', 'red', 'green', 'purple', 'gray' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'A more descriptive explanation of what the test looks for, and why it is important for the user.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'actions' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'HTML containing an action to direct the user to where they can resolve the issue.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Site_Health_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class for interacting with Site Health tests.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Site_Health_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* An instance of the site health class.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @var WP_Site_Health
|
||||
*/
|
||||
private $site_health;
|
||||
|
||||
/**
|
||||
* Site Health controller constructor.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param WP_Site_Health $site_health An instance of the site health class.
|
||||
*/
|
||||
public function __construct( $site_health ) {
|
||||
$this->namespace = 'wp-site-health/v1';
|
||||
$this->rest_base = 'tests';
|
||||
|
||||
$this->site_health = $site_health;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers API routes.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @since 6.1.0 Adds page-cache async test.
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'background-updates'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_background_updates' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'background_updates' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'loopback-requests'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_loopback_requests' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'loopback_requests' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'https-status'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_https_status' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'https_status' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'dotorg-communication'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_dotorg_communication' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'dotorg_communication' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'authorization-header'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_authorization_header' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'authorization_header' );
|
||||
},
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s',
|
||||
'directory-sizes'
|
||||
),
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_directory_sizes' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'directory_sizes' ) && ! is_multisite();
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/%s',
|
||||
$this->rest_base,
|
||||
'page-cache'
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'test_page_cache' ),
|
||||
'permission_callback' => function () {
|
||||
return $this->validate_request_permission( 'page_cache' );
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the current user can request this REST endpoint.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $check The endpoint check being ran.
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_request_permission( $check ) {
|
||||
$default_capability = 'view_site_health_checks';
|
||||
|
||||
/**
|
||||
* Filters the capability needed to run a given Site Health check.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $default_capability The default capability required for this check.
|
||||
* @param string $check The Site Health check being performed.
|
||||
*/
|
||||
$capability = apply_filters( "site_health_test_rest_capability_{$check}", $default_capability, $check );
|
||||
|
||||
return current_user_can( $capability );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if background updates work as expected.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_background_updates() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_background_updates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the site can reach the WordPress.org API.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_dotorg_communication() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_dotorg_communication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that loopbacks can be performed.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_loopback_requests() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_loopback_requests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the site's frontend can be accessed over HTTPS.
|
||||
*
|
||||
* @since 5.7.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_https_status() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_https_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the authorization header is valid.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_authorization_header() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_authorization_header();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that full page cache is active.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @return array The test result.
|
||||
*/
|
||||
public function test_page_cache() {
|
||||
$this->load_admin_textdomain();
|
||||
return $this->site_health->get_test_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current directory sizes for this install.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_directory_sizes() {
|
||||
if ( ! class_exists( 'WP_Debug_Data' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-debug-data.php';
|
||||
}
|
||||
|
||||
$this->load_admin_textdomain();
|
||||
|
||||
$sizes_data = WP_Debug_Data::get_sizes();
|
||||
$all_sizes = array( 'raw' => 0 );
|
||||
|
||||
foreach ( $sizes_data as $name => $value ) {
|
||||
$name = sanitize_text_field( $name );
|
||||
$data = array();
|
||||
|
||||
if ( isset( $value['size'] ) ) {
|
||||
if ( is_string( $value['size'] ) ) {
|
||||
$data['size'] = sanitize_text_field( $value['size'] );
|
||||
} else {
|
||||
$data['size'] = (int) $value['size'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $value['debug'] ) ) {
|
||||
if ( is_string( $value['debug'] ) ) {
|
||||
$data['debug'] = sanitize_text_field( $value['debug'] );
|
||||
} else {
|
||||
$data['debug'] = (int) $value['debug'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $value['raw'] ) ) {
|
||||
$data['raw'] = (int) $value['raw'];
|
||||
}
|
||||
|
||||
$all_sizes[ $name ] = $data;
|
||||
}
|
||||
|
||||
if ( isset( $all_sizes['total_size']['debug'] ) && 'not available' === $all_sizes['total_size']['debug'] ) {
|
||||
return new WP_Error( 'not_available', __( 'Directory sizes could not be returned.' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return $all_sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the admin textdomain for Site Health tests.
|
||||
*
|
||||
* The {@see WP_Site_Health} class is defined in WP-Admin, while the REST API operates in a front-end context.
|
||||
* This means that the translations for Site Health won't be loaded by default in {@see load_default_textdomain()}.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*/
|
||||
protected function load_admin_textdomain() {
|
||||
// Accounts for inner REST API requests in the admin.
|
||||
if ( ! is_admin() ) {
|
||||
$locale = determine_locale();
|
||||
load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo", $locale );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the schema for each site health test.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @return array The test schema.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
$this->schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'wp-site-health-test',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'test' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'The name of the test being run.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'label' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'A label describing the test.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'status' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'The status of the test.' ),
|
||||
'enum' => array( 'good', 'recommended', 'critical' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'badge' => array(
|
||||
'type' => 'object',
|
||||
'description' => __( 'The category this test is grouped in.' ),
|
||||
'properties' => array(
|
||||
'label' => array(
|
||||
'type' => 'string',
|
||||
'readonly' => true,
|
||||
),
|
||||
'color' => array(
|
||||
'type' => 'string',
|
||||
'enum' => array( 'blue', 'orange', 'red', 'green', 'purple', 'gray' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'A more descriptive explanation of what the test looks for, and why it is important for the user.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'actions' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'HTML containing an action to direct the user to where they can resolve the issue.' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,452 +1,452 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Taxonomies_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to manage taxonomies via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'taxonomies';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for taxonomies.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
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<taxonomy>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'taxonomy' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read taxonomies.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( 'edit' === $request['context'] ) {
|
||||
if ( ! empty( $request['type'] ) ) {
|
||||
$taxonomies = get_object_taxonomies( $request['type'], 'objects' );
|
||||
} else {
|
||||
$taxonomies = get_taxonomies( '', 'objects' );
|
||||
}
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
if ( ! empty( $taxonomy->show_in_rest ) && current_user_can( $taxonomy->cap->assign_terms ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to manage terms in this taxonomy.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all public taxonomies.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
|
||||
// Retrieve the list of registered collection query parameters.
|
||||
$registered = $this->get_collection_params();
|
||||
|
||||
if ( isset( $registered['type'] ) && ! empty( $request['type'] ) ) {
|
||||
$taxonomies = get_object_taxonomies( $request['type'], 'objects' );
|
||||
} else {
|
||||
$taxonomies = get_taxonomies( '', 'objects' );
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ( $taxonomies as $tax_type => $value ) {
|
||||
if ( empty( $value->show_in_rest ) || ( 'edit' === $request['context'] && ! current_user_can( $value->cap->assign_terms ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tax = $this->prepare_item_for_response( $value, $request );
|
||||
$tax = $this->prepare_response_for_collection( $tax );
|
||||
$data[ $tax_type ] = $tax;
|
||||
}
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
// Response should still be returned as a JSON object when it is empty.
|
||||
$data = (object) $data;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to a taxonomy.
|
||||
*
|
||||
* @since 4.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, otherwise false or WP_Error object.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
|
||||
$tax_obj = get_taxonomy( $request['taxonomy'] );
|
||||
|
||||
if ( $tax_obj ) {
|
||||
if ( empty( $tax_obj->show_in_rest ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->assign_terms ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to manage terms in this taxonomy.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific taxonomy.
|
||||
*
|
||||
* @since 4.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_item( $request ) {
|
||||
$tax_obj = get_taxonomy( $request['taxonomy'] );
|
||||
|
||||
if ( empty( $tax_obj ) ) {
|
||||
return new WP_Error(
|
||||
'rest_taxonomy_invalid',
|
||||
__( 'Invalid taxonomy.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $tax_obj, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a taxonomy object for serialization.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.9.0 Renamed `$taxonomy` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param WP_Taxonomy $item Taxonomy data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$taxonomy = $item;
|
||||
|
||||
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( in_array( 'name', $fields, true ) ) {
|
||||
$data['name'] = $taxonomy->label;
|
||||
}
|
||||
|
||||
if ( in_array( 'slug', $fields, true ) ) {
|
||||
$data['slug'] = $taxonomy->name;
|
||||
}
|
||||
|
||||
if ( in_array( 'capabilities', $fields, true ) ) {
|
||||
$data['capabilities'] = $taxonomy->cap;
|
||||
}
|
||||
|
||||
if ( in_array( 'description', $fields, true ) ) {
|
||||
$data['description'] = $taxonomy->description;
|
||||
}
|
||||
|
||||
if ( in_array( 'labels', $fields, true ) ) {
|
||||
$data['labels'] = $taxonomy->labels;
|
||||
}
|
||||
|
||||
if ( in_array( 'types', $fields, true ) ) {
|
||||
$data['types'] = array_values( $taxonomy->object_type );
|
||||
}
|
||||
|
||||
if ( in_array( 'show_cloud', $fields, true ) ) {
|
||||
$data['show_cloud'] = $taxonomy->show_tagcloud;
|
||||
}
|
||||
|
||||
if ( in_array( 'hierarchical', $fields, true ) ) {
|
||||
$data['hierarchical'] = $taxonomy->hierarchical;
|
||||
}
|
||||
|
||||
if ( in_array( 'rest_base', $fields, true ) ) {
|
||||
$data['rest_base'] = $base;
|
||||
}
|
||||
|
||||
if ( in_array( 'rest_namespace', $fields, true ) ) {
|
||||
$data['rest_namespace'] = $taxonomy->rest_namespace;
|
||||
}
|
||||
|
||||
if ( in_array( 'visibility', $fields, true ) ) {
|
||||
$data['visibility'] = array(
|
||||
'public' => (bool) $taxonomy->public,
|
||||
'publicly_queryable' => (bool) $taxonomy->publicly_queryable,
|
||||
'show_admin_column' => (bool) $taxonomy->show_admin_column,
|
||||
'show_in_nav_menus' => (bool) $taxonomy->show_in_nav_menus,
|
||||
'show_in_quick_edit' => (bool) $taxonomy->show_in_quick_edit,
|
||||
'show_ui' => (bool) $taxonomy->show_ui,
|
||||
);
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $taxonomy ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a taxonomy returned from the REST API.
|
||||
*
|
||||
* Allows modification of the taxonomy data right before it is returned.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Taxonomy $item The original taxonomy object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_taxonomy', $response, $taxonomy, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param WP_Taxonomy $taxonomy The taxonomy.
|
||||
* @return array Links for the given taxonomy.
|
||||
*/
|
||||
protected function prepare_links( $taxonomy ) {
|
||||
return array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'https://api.w.org/items' => array(
|
||||
'href' => rest_url( rest_get_route_for_taxonomy_items( $taxonomy->name ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the taxonomy's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.0.0 The `visibility` property was added.
|
||||
* @since 5.9.0 The `rest_namespace` property was added.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'taxonomy',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'capabilities' => array(
|
||||
'description' => __( 'All capabilities used by the taxonomy.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A human-readable description of the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'hierarchical' => array(
|
||||
'description' => __( 'Whether or not the taxonomy should have children.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'labels' => array(
|
||||
'description' => __( 'Human-readable labels for the taxonomy for various contexts.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The title for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'show_cloud' => array(
|
||||
'description' => __( 'Whether or not the term cloud should be displayed.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'types' => array(
|
||||
'description' => __( 'Types associated with the taxonomy.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_base' => array(
|
||||
'description' => __( 'REST base route for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_namespace' => array(
|
||||
'description' => __( 'REST namespace route for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'visibility' => array(
|
||||
'description' => __( 'The visibility settings for the taxonomy.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'public' => array(
|
||||
'description' => __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'publicly_queryable' => array(
|
||||
'description' => __( 'Whether the taxonomy is publicly queryable.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_ui' => array(
|
||||
'description' => __( 'Whether to generate a default UI for managing this taxonomy.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_admin_column' => array(
|
||||
'description' => __( 'Whether to allow automatic creation of taxonomy columns on associated post-types table.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_in_nav_menus' => array(
|
||||
'description' => __( 'Whether to make the taxonomy available for selection in navigation menus.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_in_quick_edit' => array(
|
||||
'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$new_params = array();
|
||||
$new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$new_params['type'] = array(
|
||||
'description' => __( 'Limit results to taxonomies associated with a specific post type.' ),
|
||||
'type' => 'string',
|
||||
);
|
||||
return $new_params;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Taxonomies_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 4.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to manage taxonomies via the REST API.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'taxonomies';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for taxonomies.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
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<taxonomy>[\w-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'taxonomy' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read taxonomies.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( 'edit' === $request['context'] ) {
|
||||
if ( ! empty( $request['type'] ) ) {
|
||||
$taxonomies = get_object_taxonomies( $request['type'], 'objects' );
|
||||
} else {
|
||||
$taxonomies = get_taxonomies( '', 'objects' );
|
||||
}
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
if ( ! empty( $taxonomy->show_in_rest ) && current_user_can( $taxonomy->cap->assign_terms ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'rest_cannot_view',
|
||||
__( 'Sorry, you are not allowed to manage terms in this taxonomy.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all public taxonomies.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
|
||||
// Retrieve the list of registered collection query parameters.
|
||||
$registered = $this->get_collection_params();
|
||||
|
||||
if ( isset( $registered['type'] ) && ! empty( $request['type'] ) ) {
|
||||
$taxonomies = get_object_taxonomies( $request['type'], 'objects' );
|
||||
} else {
|
||||
$taxonomies = get_taxonomies( '', 'objects' );
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ( $taxonomies as $tax_type => $value ) {
|
||||
if ( empty( $value->show_in_rest ) || ( 'edit' === $request['context'] && ! current_user_can( $value->cap->assign_terms ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tax = $this->prepare_item_for_response( $value, $request );
|
||||
$tax = $this->prepare_response_for_collection( $tax );
|
||||
$data[ $tax_type ] = $tax;
|
||||
}
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
// Response should still be returned as a JSON object when it is empty.
|
||||
$data = (object) $data;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to a taxonomy.
|
||||
*
|
||||
* @since 4.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, otherwise false or WP_Error object.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
|
||||
$tax_obj = get_taxonomy( $request['taxonomy'] );
|
||||
|
||||
if ( $tax_obj ) {
|
||||
if ( empty( $tax_obj->show_in_rest ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->assign_terms ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden_context',
|
||||
__( 'Sorry, you are not allowed to manage terms in this taxonomy.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific taxonomy.
|
||||
*
|
||||
* @since 4.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_item( $request ) {
|
||||
$tax_obj = get_taxonomy( $request['taxonomy'] );
|
||||
|
||||
if ( empty( $tax_obj ) ) {
|
||||
return new WP_Error(
|
||||
'rest_taxonomy_invalid',
|
||||
__( 'Invalid taxonomy.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $tax_obj, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a taxonomy object for serialization.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.9.0 Renamed `$taxonomy` to `$item` to match parent class for PHP 8 named parameter support.
|
||||
*
|
||||
* @param WP_Taxonomy $item Taxonomy data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Restores the more descriptive, specific name for use within this method.
|
||||
$taxonomy = $item;
|
||||
|
||||
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array();
|
||||
|
||||
if ( in_array( 'name', $fields, true ) ) {
|
||||
$data['name'] = $taxonomy->label;
|
||||
}
|
||||
|
||||
if ( in_array( 'slug', $fields, true ) ) {
|
||||
$data['slug'] = $taxonomy->name;
|
||||
}
|
||||
|
||||
if ( in_array( 'capabilities', $fields, true ) ) {
|
||||
$data['capabilities'] = $taxonomy->cap;
|
||||
}
|
||||
|
||||
if ( in_array( 'description', $fields, true ) ) {
|
||||
$data['description'] = $taxonomy->description;
|
||||
}
|
||||
|
||||
if ( in_array( 'labels', $fields, true ) ) {
|
||||
$data['labels'] = $taxonomy->labels;
|
||||
}
|
||||
|
||||
if ( in_array( 'types', $fields, true ) ) {
|
||||
$data['types'] = array_values( $taxonomy->object_type );
|
||||
}
|
||||
|
||||
if ( in_array( 'show_cloud', $fields, true ) ) {
|
||||
$data['show_cloud'] = $taxonomy->show_tagcloud;
|
||||
}
|
||||
|
||||
if ( in_array( 'hierarchical', $fields, true ) ) {
|
||||
$data['hierarchical'] = $taxonomy->hierarchical;
|
||||
}
|
||||
|
||||
if ( in_array( 'rest_base', $fields, true ) ) {
|
||||
$data['rest_base'] = $base;
|
||||
}
|
||||
|
||||
if ( in_array( 'rest_namespace', $fields, true ) ) {
|
||||
$data['rest_namespace'] = $taxonomy->rest_namespace;
|
||||
}
|
||||
|
||||
if ( in_array( 'visibility', $fields, true ) ) {
|
||||
$data['visibility'] = array(
|
||||
'public' => (bool) $taxonomy->public,
|
||||
'publicly_queryable' => (bool) $taxonomy->publicly_queryable,
|
||||
'show_admin_column' => (bool) $taxonomy->show_admin_column,
|
||||
'show_in_nav_menus' => (bool) $taxonomy->show_in_nav_menus,
|
||||
'show_in_quick_edit' => (bool) $taxonomy->show_in_quick_edit,
|
||||
'show_ui' => (bool) $taxonomy->show_ui,
|
||||
);
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$response->add_links( $this->prepare_links( $taxonomy ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a taxonomy returned from the REST API.
|
||||
*
|
||||
* Allows modification of the taxonomy data right before it is returned.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Taxonomy $item The original taxonomy object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_taxonomy', $response, $taxonomy, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param WP_Taxonomy $taxonomy The taxonomy.
|
||||
* @return array Links for the given taxonomy.
|
||||
*/
|
||||
protected function prepare_links( $taxonomy ) {
|
||||
return array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'https://api.w.org/items' => array(
|
||||
'href' => rest_url( rest_get_route_for_taxonomy_items( $taxonomy->name ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the taxonomy's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 4.7.0
|
||||
* @since 5.0.0 The `visibility` property was added.
|
||||
* @since 5.9.0 The `rest_namespace` property was added.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'taxonomy',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'capabilities' => array(
|
||||
'description' => __( 'All capabilities used by the taxonomy.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A human-readable description of the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'hierarchical' => array(
|
||||
'description' => __( 'Whether or not the taxonomy should have children.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'labels' => array(
|
||||
'description' => __( 'Human-readable labels for the taxonomy for various contexts.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The title for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'show_cloud' => array(
|
||||
'description' => __( 'Whether or not the term cloud should be displayed.' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'types' => array(
|
||||
'description' => __( 'Types associated with the taxonomy.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_base' => array(
|
||||
'description' => __( 'REST base route for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rest_namespace' => array(
|
||||
'description' => __( 'REST namespace route for the taxonomy.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'visibility' => array(
|
||||
'description' => __( 'The visibility settings for the taxonomy.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'public' => array(
|
||||
'description' => __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'publicly_queryable' => array(
|
||||
'description' => __( 'Whether the taxonomy is publicly queryable.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_ui' => array(
|
||||
'description' => __( 'Whether to generate a default UI for managing this taxonomy.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_admin_column' => array(
|
||||
'description' => __( 'Whether to allow automatic creation of taxonomy columns on associated post-types table.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_in_nav_menus' => array(
|
||||
'description' => __( 'Whether to make the taxonomy available for selection in navigation menus.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'show_in_quick_edit' => array(
|
||||
'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 4.7.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$new_params = array();
|
||||
$new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$new_params['type'] = array(
|
||||
'description' => __( 'Limit results to taxonomies associated with a specific post type.' ),
|
||||
'type' => 'string',
|
||||
);
|
||||
return $new_params;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,276 +1,276 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Template_Autosaves_Controller class.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.4.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access template autosaves via the REST API.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see WP_REST_Autosaves_Controller
|
||||
*/
|
||||
class WP_REST_Template_Autosaves_Controller extends WP_REST_Autosaves_Controller {
|
||||
/**
|
||||
* Parent post type.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_post_type;
|
||||
|
||||
/**
|
||||
* Parent post controller.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var WP_REST_Controller
|
||||
*/
|
||||
private $parent_controller;
|
||||
|
||||
/**
|
||||
* Revision controller.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var WP_REST_Revisions_Controller
|
||||
*/
|
||||
private $revisions_controller;
|
||||
|
||||
/**
|
||||
* The base of the parent controller's route.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_base;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param string $parent_post_type Post type of the parent.
|
||||
*/
|
||||
public function __construct( $parent_post_type ) {
|
||||
parent::__construct( $parent_post_type );
|
||||
$this->parent_post_type = $parent_post_type;
|
||||
$post_type_object = get_post_type_object( $parent_post_type );
|
||||
$parent_controller = $post_type_object->get_rest_controller();
|
||||
|
||||
if ( ! $parent_controller ) {
|
||||
$parent_controller = new WP_REST_Templates_Controller( $parent_post_type );
|
||||
}
|
||||
|
||||
$this->parent_controller = $parent_controller;
|
||||
|
||||
$revisions_controller = $post_type_object->get_revisions_rest_controller();
|
||||
if ( ! $revisions_controller ) {
|
||||
$revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
|
||||
}
|
||||
$this->revisions_controller = $revisions_controller;
|
||||
$this->rest_base = 'autosaves';
|
||||
$this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
|
||||
$this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for autosaves.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/(?P<id>%s%s)/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->parent_controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/(?P<parent>%s%s)/%s/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base,
|
||||
'(?P<id>[\d]+)'
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'The ID for the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the item for the REST response.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Post $item Post revision object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$template = _build_block_template_result_from_post( $item );
|
||||
$response = $this->parent_controller->prepare_item_for_response( $template, $request );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = $response->get_data();
|
||||
|
||||
if ( in_array( 'parent', $fields, true ) ) {
|
||||
$data['parent'] = (int) $item->post_parent;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = new WP_REST_Response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $this->prepare_links( $template );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the autosave, if the ID is valid.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Post|WP_Error Autosave post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$parent = $this->get_parent( $request['parent'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
$autosave = wp_get_post_autosave( $parent->ID );
|
||||
|
||||
if ( ! $autosave ) {
|
||||
return new WP_Error(
|
||||
'rest_post_no_autosave',
|
||||
__( 'There is no autosave revision for this template.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $autosave, $request );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent post.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param int $parent_id Supplied ID.
|
||||
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function get_parent( $parent_id ) {
|
||||
return $this->revisions_controller->get_parent( $parent_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Block_Template $template Template.
|
||||
* @return array Links for the given post.
|
||||
*/
|
||||
protected function prepare_links( $template ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ),
|
||||
),
|
||||
'parent' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the autosave's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$this->schema = $this->revisions_controller->get_item_schema();
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Template_Autosaves_Controller class.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.4.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access template autosaves via the REST API.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see WP_REST_Autosaves_Controller
|
||||
*/
|
||||
class WP_REST_Template_Autosaves_Controller extends WP_REST_Autosaves_Controller {
|
||||
/**
|
||||
* Parent post type.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_post_type;
|
||||
|
||||
/**
|
||||
* Parent post controller.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var WP_REST_Controller
|
||||
*/
|
||||
private $parent_controller;
|
||||
|
||||
/**
|
||||
* Revision controller.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var WP_REST_Revisions_Controller
|
||||
*/
|
||||
private $revisions_controller;
|
||||
|
||||
/**
|
||||
* The base of the parent controller's route.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_base;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param string $parent_post_type Post type of the parent.
|
||||
*/
|
||||
public function __construct( $parent_post_type ) {
|
||||
parent::__construct( $parent_post_type );
|
||||
$this->parent_post_type = $parent_post_type;
|
||||
$post_type_object = get_post_type_object( $parent_post_type );
|
||||
$parent_controller = $post_type_object->get_rest_controller();
|
||||
|
||||
if ( ! $parent_controller ) {
|
||||
$parent_controller = new WP_REST_Templates_Controller( $parent_post_type );
|
||||
}
|
||||
|
||||
$this->parent_controller = $parent_controller;
|
||||
|
||||
$revisions_controller = $post_type_object->get_revisions_rest_controller();
|
||||
if ( ! $revisions_controller ) {
|
||||
$revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
|
||||
}
|
||||
$this->revisions_controller = $revisions_controller;
|
||||
$this->rest_base = 'autosaves';
|
||||
$this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
|
||||
$this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for autosaves.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/(?P<id>%s%s)/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->parent_controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/(?P<parent>%s%s)/%s/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base,
|
||||
'(?P<id>[\d]+)'
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'The ID for the autosave.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the item for the REST response.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Post $item Post revision object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$template = _build_block_template_result_from_post( $item );
|
||||
$response = $this->parent_controller->prepare_item_for_response( $template, $request );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = $response->get_data();
|
||||
|
||||
if ( in_array( 'parent', $fields, true ) ) {
|
||||
$data['parent'] = (int) $item->post_parent;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = new WP_REST_Response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $this->prepare_links( $template );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the autosave, if the ID is valid.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Post|WP_Error Autosave post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$parent = $this->get_parent( $request['parent'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
$autosave = wp_get_post_autosave( $parent->ID );
|
||||
|
||||
if ( ! $autosave ) {
|
||||
return new WP_Error(
|
||||
'rest_post_no_autosave',
|
||||
__( 'There is no autosave revision for this template.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $autosave, $request );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent post.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param int $parent_id Supplied ID.
|
||||
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function get_parent( $parent_id ) {
|
||||
return $this->revisions_controller->get_parent( $parent_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Block_Template $template Template.
|
||||
* @return array Links for the given post.
|
||||
*/
|
||||
protected function prepare_links( $template ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ),
|
||||
),
|
||||
'parent' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the autosave's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$this->schema = $this->revisions_controller->get_item_schema();
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,297 +1,297 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Template_Revisions_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.4.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access template revisions via the REST API.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Template_Revisions_Controller extends WP_REST_Revisions_Controller {
|
||||
/**
|
||||
* Parent post type.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_post_type;
|
||||
|
||||
/**
|
||||
* Parent controller.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var WP_REST_Controller
|
||||
*/
|
||||
private $parent_controller;
|
||||
|
||||
/**
|
||||
* The base of the parent controller's route.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_base;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param string $parent_post_type Post type of the parent.
|
||||
*/
|
||||
public function __construct( $parent_post_type ) {
|
||||
parent::__construct( $parent_post_type );
|
||||
$this->parent_post_type = $parent_post_type;
|
||||
$post_type_object = get_post_type_object( $parent_post_type );
|
||||
$parent_controller = $post_type_object->get_rest_controller();
|
||||
|
||||
if ( ! $parent_controller ) {
|
||||
$parent_controller = new WP_REST_Templates_Controller( $parent_post_type );
|
||||
}
|
||||
|
||||
$this->parent_controller = $parent_controller;
|
||||
$this->rest_base = 'revisions';
|
||||
$this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
|
||||
$this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for revisions based on post types supporting revisions.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/(?P<parent>%s%s)/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
),
|
||||
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,
|
||||
sprintf(
|
||||
'/%s/(?P<parent>%s%s)/%s/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base,
|
||||
'(?P<id>[\d]+)'
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the revision.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as revisions do not support trashing.' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent post, if the ID is valid.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param int $parent_post_id Supplied ID.
|
||||
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function get_parent( $parent_post_id ) {
|
||||
$template = get_block_template( $parent_post_id, $this->parent_post_type );
|
||||
|
||||
if ( ! $template ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_parent',
|
||||
__( 'Invalid template parent ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
return get_post( $template->wp_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the item for the REST response.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Post $item Post revision object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$template = _build_block_template_result_from_post( $item );
|
||||
$response = $this->parent_controller->prepare_item_for_response( $template, $request );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = $response->get_data();
|
||||
|
||||
if ( in_array( 'parent', $fields, true ) ) {
|
||||
$data['parent'] = (int) $item->post_parent;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = new WP_REST_Response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $this->prepare_links( $template );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to delete a revision.
|
||||
*
|
||||
* @since 6.4.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 ) {
|
||||
$parent = $this->get_parent( $request['parent'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'delete_post', $parent->ID ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_delete',
|
||||
__( 'Sorry, you are not allowed to delete revisions of this post.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
$revision = $this->get_revision( $request['id'] );
|
||||
if ( is_wp_error( $revision ) ) {
|
||||
return $revision;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_delete',
|
||||
__( 'Sorry, you are not allowed to delete this revision.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Block_Template $template Template.
|
||||
* @return array Links for the given post.
|
||||
*/
|
||||
protected function prepare_links( $template ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ),
|
||||
),
|
||||
'parent' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = $this->parent_controller->get_item_schema();
|
||||
|
||||
$schema['properties']['parent'] = array(
|
||||
'description' => __( 'The ID for the parent of the revision.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Template_Revisions_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 6.4.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to access template revisions via the REST API.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Template_Revisions_Controller extends WP_REST_Revisions_Controller {
|
||||
/**
|
||||
* Parent post type.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_post_type;
|
||||
|
||||
/**
|
||||
* Parent controller.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var WP_REST_Controller
|
||||
*/
|
||||
private $parent_controller;
|
||||
|
||||
/**
|
||||
* The base of the parent controller's route.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @var string
|
||||
*/
|
||||
private $parent_base;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param string $parent_post_type Post type of the parent.
|
||||
*/
|
||||
public function __construct( $parent_post_type ) {
|
||||
parent::__construct( $parent_post_type );
|
||||
$this->parent_post_type = $parent_post_type;
|
||||
$post_type_object = get_post_type_object( $parent_post_type );
|
||||
$parent_controller = $post_type_object->get_rest_controller();
|
||||
|
||||
if ( ! $parent_controller ) {
|
||||
$parent_controller = new WP_REST_Templates_Controller( $parent_post_type );
|
||||
}
|
||||
|
||||
$this->parent_controller = $parent_controller;
|
||||
$this->rest_base = 'revisions';
|
||||
$this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
|
||||
$this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the routes for revisions based on post types supporting revisions.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
sprintf(
|
||||
'/%s/(?P<parent>%s%s)/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
),
|
||||
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,
|
||||
sprintf(
|
||||
'/%s/(?P<parent>%s%s)/%s/%s',
|
||||
$this->parent_base,
|
||||
/*
|
||||
* Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
|
||||
* Excludes invalid directory name characters: `/:<>*?"|`.
|
||||
*/
|
||||
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
|
||||
// Matches the template name.
|
||||
'[\/\w%-]+',
|
||||
$this->rest_base,
|
||||
'(?P<id>[\d]+)'
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'parent' => array(
|
||||
'description' => __( 'The id of a template' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
|
||||
),
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the revision.' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as revisions do not support trashing.' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent post, if the ID is valid.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param int $parent_post_id Supplied ID.
|
||||
* @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function get_parent( $parent_post_id ) {
|
||||
$template = get_block_template( $parent_post_id, $this->parent_post_type );
|
||||
|
||||
if ( ! $template ) {
|
||||
return new WP_Error(
|
||||
'rest_post_invalid_parent',
|
||||
__( 'Invalid template parent ID.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
return get_post( $template->wp_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the item for the REST response.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Post $item Post revision object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response Response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$template = _build_block_template_result_from_post( $item );
|
||||
$response = $this->parent_controller->prepare_item_for_response( $template, $request );
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = $response->get_data();
|
||||
|
||||
if ( in_array( 'parent', $fields, true ) ) {
|
||||
$data['parent'] = (int) $item->post_parent;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = new WP_REST_Response( $data );
|
||||
|
||||
if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
|
||||
$links = $this->prepare_links( $template );
|
||||
$response->add_links( $links );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to delete a revision.
|
||||
*
|
||||
* @since 6.4.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 ) {
|
||||
$parent = $this->get_parent( $request['parent'] );
|
||||
if ( is_wp_error( $parent ) ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'delete_post', $parent->ID ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_delete',
|
||||
__( 'Sorry, you are not allowed to delete revisions of this post.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
$revision = $this->get_revision( $request['id'] );
|
||||
if ( is_wp_error( $revision ) ) {
|
||||
return $revision;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_delete',
|
||||
__( 'Sorry, you are not allowed to delete this revision.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the request.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @param WP_Block_Template $template Template.
|
||||
* @return array Links for the given post.
|
||||
*/
|
||||
protected function prepare_links( $template ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ),
|
||||
),
|
||||
'parent' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = $this->parent_controller->get_item_schema();
|
||||
|
||||
$schema['properties']['parent'] = array(
|
||||
'description' => __( 'The ID for the parent of the revision.' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user