2020-06-26 02:46:07 +02:00
< ? php
/**
* REST API : WP_REST_Block_Types_Controller class
*
* @ package WordPress
* @ subpackage REST_API
* @ since 5.5 . 0
*/
/**
* Core class used to access block types via the REST API .
*
* @ since 5.5 . 0
*
* @ see WP_REST_Controller
*/
class WP_REST_Block_Types_Controller extends WP_REST_Controller {
/**
* Instance of WP_Block_Type_Registry .
*
* @ since 5.5 . 0
* @ var WP_Block_Type_Registry
*/
protected $block_registry ;
/**
* Instance of WP_Block_Styles_Registry .
*
* @ since 5.5 . 0
* @ var WP_Block_Styles_Registry
*/
protected $style_registry ;
/**
* Constructor .
*
* @ since 5.5 . 0
*/
public function __construct () {
$this -> namespace = 'wp/v2' ;
$this -> rest_base = 'block-types' ;
$this -> block_registry = WP_Block_Type_Registry :: get_instance ();
$this -> style_registry = WP_Block_Styles_Registry :: get_instance ();
}
/**
2021-05-25 15:12:58 +02:00
* Registers the routes for block types .
2020-06-26 02:46:07 +02:00
*
* @ since 5.5 . 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<namespace>[a-zA-Z0-9_-]+)' ,
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<namespace>[a-zA-Z0-9_-]+)/(?P<name>[a-zA-Z0-9_-]+)' ,
array (
'args' => array (
'name' => array (
2020-09-16 21:23:07 +02:00
'description' => __ ( 'Block name.' ),
2020-06-26 02:46:07 +02:00
'type' => 'string' ,
),
'namespace' => array (
2020-09-16 21:23:07 +02:00
'description' => __ ( 'Block namespace.' ),
2020-06-26 02:46:07 +02:00
'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 block types .
*
* @ since 5.5 . 0
*
* @ param WP_REST_Request $request Full details about the request .
2021-01-04 18:18:04 +01:00
* @ return true | WP_Error True if the request has read access , WP_Error object otherwise .
2020-06-26 02:46:07 +02:00
*/
public function get_items_permissions_check ( $request ) {
return $this -> check_read_permission ();
}
/**
* Retrieves all post block types , depending on user context .
*
* @ since 5.5 . 0
*
* @ param WP_REST_Request $request Full details about the request .
2021-01-04 18:18:04 +01:00
* @ return WP_REST_Response | WP_Error Response object on success , or WP_Error object on failure .
2020-06-26 02:46:07 +02:00
*/
public function get_items ( $request ) {
$data = array ();
$block_types = $this -> block_registry -> get_all_registered ();
// Retrieve the list of registered collection query parameters.
$registered = $this -> get_collection_params ();
$namespace = '' ;
if ( isset ( $registered [ 'namespace' ] ) && ! empty ( $request [ 'namespace' ] ) ) {
$namespace = $request [ 'namespace' ];
}
foreach ( $block_types as $slug => $obj ) {
if ( $namespace ) {
list ( $block_namespace ) = explode ( '/' , $obj -> name );
if ( $namespace !== $block_namespace ) {
continue ;
}
}
$block_type = $this -> prepare_item_for_response ( $obj , $request );
$data [] = $this -> prepare_response_for_collection ( $block_type );
}
return rest_ensure_response ( $data );
}
/**
* Checks if a given request has access to read a block type .
*
* @ since 5.5 . 0
*
* @ param WP_REST_Request $request Full details about the request .
2021-01-04 18:18:04 +01:00
* @ return true | WP_Error True if the request has read access for the item , WP_Error object otherwise .
2020-06-26 02:46:07 +02:00
*/
public function get_item_permissions_check ( $request ) {
$check = $this -> check_read_permission ();
if ( is_wp_error ( $check ) ) {
return $check ;
}
$block_name = sprintf ( '%s/%s' , $request [ 'namespace' ], $request [ 'name' ] );
$block_type = $this -> get_block ( $block_name );
if ( is_wp_error ( $block_type ) ) {
return $block_type ;
}
return true ;
}
/**
* Checks whether a given block type should be visible .
*
* @ since 5.5 . 0
*
2021-01-04 18:18:04 +01:00
* @ return true | WP_Error True if the block type is visible , WP_Error otherwise .
2020-06-26 02:46:07 +02:00
*/
protected function check_read_permission () {
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_block_type_cannot_view' , __ ( 'Sorry, you are not allowed to manage block types.' ), array ( 'status' => rest_authorization_required_code () ) );
}
/**
* Get the block , if the name is valid .
*
* @ since 5.5 . 0
*
* @ param string $name Block name .
* @ return WP_Block_Type | WP_Error Block type object if name is valid , WP_Error otherwise .
*/
protected function get_block ( $name ) {
$block_type = $this -> block_registry -> get_registered ( $name );
if ( empty ( $block_type ) ) {
return new WP_Error ( 'rest_block_type_invalid' , __ ( 'Invalid block type.' ), array ( 'status' => 404 ) );
}
return $block_type ;
}
/**
* Retrieves a specific block type .
*
* @ since 5.5 . 0
*
* @ param WP_REST_Request $request Full details about the request .
2021-01-04 18:18:04 +01:00
* @ return WP_REST_Response | WP_Error Response object on success , or WP_Error object on failure .
2020-06-26 02:46:07 +02:00
*/
public function get_item ( $request ) {
$block_name = sprintf ( '%s/%s' , $request [ 'namespace' ], $request [ 'name' ] );
$block_type = $this -> get_block ( $block_name );
if ( is_wp_error ( $block_type ) ) {
return $block_type ;
}
$data = $this -> prepare_item_for_response ( $block_type , $request );
return rest_ensure_response ( $data );
}
/**
* Prepares a block type object for serialization .
*
* @ since 5.5 . 0
*
* @ param WP_Block_Type $block_type Block type data .
* @ param WP_REST_Request $request Full details about the request .
* @ return WP_REST_Response Block type data .
*/
public function prepare_item_for_response ( $block_type , $request ) {
$fields = $this -> get_fields_for_response ( $request );
$data = array ();
if ( rest_is_field_included ( 'attributes' , $fields ) ) {
$data [ 'attributes' ] = $block_type -> get_attributes ();
}
if ( rest_is_field_included ( 'is_dynamic' , $fields ) ) {
$data [ 'is_dynamic' ] = $block_type -> is_dynamic ();
}
$schema = $this -> get_item_schema ();
$extra_fields = array (
2020-10-20 09:54:10 +02:00
'api_version' ,
2020-06-26 02:46:07 +02:00
'name' ,
'title' ,
'description' ,
'icon' ,
'category' ,
'keywords' ,
'parent' ,
'provides_context' ,
'uses_context' ,
'supports' ,
'styles' ,
'textdomain' ,
'example' ,
'editor_script' ,
'script' ,
2021-07-28 12:06:59 +02:00
'view_script' ,
2020-06-26 02:46:07 +02:00
'editor_style' ,
'style' ,
2021-03-12 14:35:09 +01:00
'variations' ,
2020-06-26 02:46:07 +02:00
);
foreach ( $extra_fields as $extra_field ) {
if ( rest_is_field_included ( $extra_field , $fields ) ) {
if ( isset ( $block_type -> $extra_field ) ) {
$field = $block_type -> $extra_field ;
} elseif ( array_key_exists ( 'default' , $schema [ 'properties' ][ $extra_field ] ) ) {
$field = $schema [ 'properties' ][ $extra_field ][ 'default' ];
} else {
$field = '' ;
}
$data [ $extra_field ] = rest_sanitize_value_from_schema ( $field , $schema [ 'properties' ][ $extra_field ] );
}
}
if ( rest_is_field_included ( 'styles' , $fields ) ) {
$styles = $this -> style_registry -> get_registered_styles_for_block ( $block_type -> name );
$styles = array_values ( $styles );
$data [ 'styles' ] = wp_parse_args ( $styles , $data [ 'styles' ] );
$data [ 'styles' ] = array_filter ( $data [ 'styles' ] );
}
$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 );
$response -> add_links ( $this -> prepare_links ( $block_type ) );
/**
* Filters a block type returned from the REST API .
*
* Allows modification of the block type data right before it is returned .
*
* @ since 5.5 . 0
*
* @ param WP_REST_Response $response The response object .
* @ param WP_Block_Type $block_type The original block type object .
* @ param WP_REST_Request $request Request used to generate the response .
*/
return apply_filters ( 'rest_prepare_block_type' , $response , $block_type , $request );
}
/**
* Prepares links for the request .
*
* @ since 5.5 . 0
*
* @ param WP_Block_Type $block_type Block type data .
* @ return array Links for the given block type .
*/
protected function prepare_links ( $block_type ) {
list ( $namespace ) = explode ( '/' , $block_type -> name );
$links = array (
'collection' => array (
'href' => rest_url ( sprintf ( '%s/%s' , $this -> namespace , $this -> rest_base ) ),
),
'self' => array (
'href' => rest_url ( sprintf ( '%s/%s/%s' , $this -> namespace , $this -> rest_base , $block_type -> name ) ),
),
'up' => array (
'href' => rest_url ( sprintf ( '%s/%s/%s' , $this -> namespace , $this -> rest_base , $namespace ) ),
),
);
if ( $block_type -> is_dynamic () ) {
$links [ 'https://api.w.org/render-block' ] = array (
'href' => add_query_arg ( 'context' , 'edit' , rest_url ( sprintf ( '%s/%s/%s' , 'wp/v2' , 'block-renderer' , $block_type -> name ) ) ),
);
}
return $links ;
}
/**
* Retrieves the block type ' 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 );
}
2021-03-13 11:25:06 +01:00
// rest_validate_value_from_schema doesn't understand $refs, pull out reused definitions for readability.
2021-03-12 14:35:09 +01:00
$inner_blocks_definition = array (
'description' => __ ( 'The list of inner blocks used in the example.' ),
'type' => 'array' ,
'items' => array (
'type' => 'object' ,
'properties' => array (
'name' => array (
'description' => __ ( 'The name of the inner block.' ),
'type' => 'string' ,
),
'attributes' => array (
'description' => __ ( 'The attributes of the inner block.' ),
'type' => 'object' ,
),
'innerBlocks' => array (
'description' => __ ( " A list of the inner block's own inner blocks. This is a recursive definition following the parent innerBlocks schema. " ),
'type' => 'array' ,
),
),
),
);
2021-04-01 17:54:04 +02:00
$example_definition = array (
2021-03-12 14:35:09 +01:00
'description' => __ ( 'Block example.' ),
'type' => array ( 'object' , 'null' ),
'default' => null ,
'properties' => array (
'attributes' => array (
'description' => __ ( 'The attributes used in the example.' ),
'type' => 'object' ,
),
'innerBlocks' => $inner_blocks_definition ,
),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
);
$keywords_definition = array (
'description' => __ ( 'Block keywords.' ),
'type' => 'array' ,
'items' => array (
'type' => 'string' ,
),
'default' => array (),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
);
$icon_definition = array (
'description' => __ ( 'Icon of block type.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
);
$category_definition = array (
'description' => __ ( 'Block category.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
);
2020-06-26 02:46:07 +02:00
$schema = array (
'$schema' => 'http://json-schema.org/draft-04/schema#' ,
'title' => 'block-type' ,
'type' => 'object' ,
'properties' => array (
2020-10-20 15:29:08 +02:00
'api_version' => array (
2020-10-20 09:54:10 +02:00
'description' => __ ( 'Version of block API.' ),
'type' => 'integer' ,
'default' => 1 ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
2020-06-26 02:46:07 +02:00
'title' => array (
'description' => __ ( 'Title of block type.' ),
'type' => 'string' ,
'default' => '' ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'name' => array (
'description' => __ ( 'Unique name identifying the block type.' ),
'type' => 'string' ,
'default' => '' ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'description' => array (
'description' => __ ( 'Description of block type.' ),
'type' => 'string' ,
'default' => '' ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
2021-03-12 14:35:09 +01:00
'icon' => $icon_definition ,
2020-06-26 02:46:07 +02:00
'attributes' => array (
'description' => __ ( 'Block attributes.' ),
'type' => array ( 'object' , 'null' ),
'properties' => array (),
'default' => null ,
'additionalProperties' => array (
'type' => 'object' ,
),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'provides_context' => array (
'description' => __ ( 'Context provided by blocks of this type.' ),
'type' => 'object' ,
'properties' => array (),
'additionalProperties' => array (
'type' => 'string' ,
),
'default' => array (),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'uses_context' => array (
'description' => __ ( 'Context values inherited by blocks of this type.' ),
'type' => 'array' ,
'default' => array (),
'items' => array (
'type' => 'string' ,
),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'supports' => array (
'description' => __ ( 'Block supports.' ),
'type' => 'object' ,
'default' => array (),
'properties' => array (),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
2021-03-12 14:35:09 +01:00
'category' => $category_definition ,
2020-06-26 02:46:07 +02:00
'is_dynamic' => array (
'description' => __ ( 'Is the block dynamically rendered.' ),
'type' => 'boolean' ,
'default' => false ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'editor_script' => array (
'description' => __ ( 'Editor script handle.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'script' => array (
2021-07-28 12:06:59 +02:00
'description' => __ ( 'Public facing and editor script handle.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
2021-08-30 16:09:58 +02:00
'view_script' => array (
2020-06-26 02:46:07 +02:00
'description' => __ ( 'Public facing script handle.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'editor_style' => array (
'description' => __ ( 'Editor style handle.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'style' => array (
2021-07-28 12:06:59 +02:00
'description' => __ ( 'Public facing and editor style handle.' ),
2020-06-26 02:46:07 +02:00
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'styles' => array (
'description' => __ ( 'Block style variations.' ),
'type' => 'array' ,
'items' => array (
'type' => 'object' ,
'properties' => array (
'name' => array (
'description' => __ ( 'Unique name identifying the style.' ),
'type' => 'string' ,
'required' => true ,
),
'label' => array (
'description' => __ ( 'The human-readable label for the style.' ),
'type' => 'string' ,
),
'inline_style' => array (
'description' => __ ( 'Inline CSS code that registers the CSS class required for the style.' ),
'type' => 'string' ,
),
'style_handle' => array (
'description' => __ ( 'Contains the handle that defines the block style.' ),
'type' => 'string' ,
),
),
),
'default' => array (),
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
2021-03-12 14:35:09 +01:00
'variations' => array (
'description' => __ ( 'Block variations.' ),
'type' => 'array' ,
'items' => array (
'type' => 'object' ,
'properties' => array (
'name' => array (
'description' => __ ( 'The unique and machine-readable name.' ),
'type' => 'string' ,
'required' => true ,
),
'title' => array (
'description' => __ ( 'A human-readable variation title.' ),
'type' => 'string' ,
'required' => true ,
),
'description' => array (
'description' => __ ( 'A detailed variation description.' ),
'type' => 'string' ,
'required' => false ,
),
'category' => $category_definition ,
'icon' => $icon_definition ,
'isDefault' => array (
'description' => __ ( 'Indicates whether the current variation is the default one.' ),
'type' => 'boolean' ,
'required' => false ,
'default' => false ,
),
'attributes' => array (
'description' => __ ( 'The initial values for attributes.' ),
'type' => 'object' ,
),
'innerBlocks' => $inner_blocks_definition ,
'example' => $example_definition ,
'scope' => array (
'description' => __ ( 'The list of scopes where the variation is applicable. When not provided, it assumes all available scopes.' ),
'type' => array ( 'array' , 'null' ),
'default' => null ,
'items' => array (
'type' => 'string' ,
'enum' => array ( 'block' , 'inserter' , 'transform' ),
),
'readonly' => true ,
),
'keywords' => $keywords_definition ,
),
),
'readonly' => true ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'default' => null ,
),
2020-06-26 02:46:07 +02:00
'textdomain' => array (
'description' => __ ( 'Public text domain.' ),
'type' => array ( 'string' , 'null' ),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
'parent' => array (
'description' => __ ( 'Parent blocks.' ),
'type' => array ( 'array' , 'null' ),
'items' => array (
'type' => 'string' ,
),
'default' => null ,
'context' => array ( 'embed' , 'view' , 'edit' ),
'readonly' => true ,
),
2021-03-12 14:35:09 +01:00
'keywords' => $keywords_definition ,
'example' => $example_definition ,
2020-06-26 02:46:07 +02:00
),
);
$this -> schema = $schema ;
return $this -> add_additional_fields_schema ( $this -> schema );
}
/**
* Retrieves the query params for collections .
*
* @ since 5.5 . 0
*
* @ return array Collection parameters .
*/
public function get_collection_params () {
return array (
'context' => $this -> get_context_param ( array ( 'default' => 'view' ) ),
'namespace' => array (
'description' => __ ( 'Block namespace.' ),
'type' => 'string' ,
),
);
}
}