angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-rest-block-types-controller.php (20330B)


      1 <?php
      2 /**
      3  * REST API: WP_REST_Block_Types_Controller class
      4  *
      5  * @package WordPress
      6  * @subpackage REST_API
      7  * @since 5.5.0
      8  */
      9 
     10 /**
     11  * Core class used to access block types via the REST API.
     12  *
     13  * @since 5.5.0
     14  *
     15  * @see WP_REST_Controller
     16  */
     17 class WP_REST_Block_Types_Controller extends WP_REST_Controller {
     18 
     19 	/**
     20 	 * Instance of WP_Block_Type_Registry.
     21 	 *
     22 	 * @since 5.5.0
     23 	 * @var WP_Block_Type_Registry
     24 	 */
     25 	protected $block_registry;
     26 
     27 	/**
     28 	 * Instance of WP_Block_Styles_Registry.
     29 	 *
     30 	 * @since 5.5.0
     31 	 * @var WP_Block_Styles_Registry
     32 	 */
     33 	protected $style_registry;
     34 
     35 	/**
     36 	 * Constructor.
     37 	 *
     38 	 * @since 5.5.0
     39 	 */
     40 	public function __construct() {
     41 		$this->namespace      = 'wp/v2';
     42 		$this->rest_base      = 'block-types';
     43 		$this->block_registry = WP_Block_Type_Registry::get_instance();
     44 		$this->style_registry = WP_Block_Styles_Registry::get_instance();
     45 	}
     46 
     47 	/**
     48 	 * Registers the routes for block types.
     49 	 *
     50 	 * @since 5.5.0
     51 	 *
     52 	 * @see register_rest_route()
     53 	 */
     54 	public function register_routes() {
     55 
     56 		register_rest_route(
     57 			$this->namespace,
     58 			'/' . $this->rest_base,
     59 			array(
     60 				array(
     61 					'methods'             => WP_REST_Server::READABLE,
     62 					'callback'            => array( $this, 'get_items' ),
     63 					'permission_callback' => array( $this, 'get_items_permissions_check' ),
     64 					'args'                => $this->get_collection_params(),
     65 				),
     66 				'schema' => array( $this, 'get_public_item_schema' ),
     67 			)
     68 		);
     69 
     70 		register_rest_route(
     71 			$this->namespace,
     72 			'/' . $this->rest_base . '/(?P<namespace>[a-zA-Z0-9_-]+)',
     73 			array(
     74 				array(
     75 					'methods'             => WP_REST_Server::READABLE,
     76 					'callback'            => array( $this, 'get_items' ),
     77 					'permission_callback' => array( $this, 'get_items_permissions_check' ),
     78 					'args'                => $this->get_collection_params(),
     79 				),
     80 				'schema' => array( $this, 'get_public_item_schema' ),
     81 			)
     82 		);
     83 
     84 		register_rest_route(
     85 			$this->namespace,
     86 			'/' . $this->rest_base . '/(?P<namespace>[a-zA-Z0-9_-]+)/(?P<name>[a-zA-Z0-9_-]+)',
     87 			array(
     88 				'args'   => array(
     89 					'name'      => array(
     90 						'description' => __( 'Block name.' ),
     91 						'type'        => 'string',
     92 					),
     93 					'namespace' => array(
     94 						'description' => __( 'Block namespace.' ),
     95 						'type'        => 'string',
     96 					),
     97 				),
     98 				array(
     99 					'methods'             => WP_REST_Server::READABLE,
    100 					'callback'            => array( $this, 'get_item' ),
    101 					'permission_callback' => array( $this, 'get_item_permissions_check' ),
    102 					'args'                => array(
    103 						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
    104 					),
    105 				),
    106 				'schema' => array( $this, 'get_public_item_schema' ),
    107 			)
    108 		);
    109 	}
    110 
    111 	/**
    112 	 * Checks whether a given request has permission to read post block types.
    113 	 *
    114 	 * @since 5.5.0
    115 	 *
    116 	 * @param WP_REST_Request $request Full details about the request.
    117 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
    118 	 */
    119 	public function get_items_permissions_check( $request ) {
    120 		return $this->check_read_permission();
    121 	}
    122 
    123 	/**
    124 	 * Retrieves all post block types, depending on user context.
    125 	 *
    126 	 * @since 5.5.0
    127 	 *
    128 	 * @param WP_REST_Request $request Full details about the request.
    129 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
    130 	 */
    131 	public function get_items( $request ) {
    132 		$data        = array();
    133 		$block_types = $this->block_registry->get_all_registered();
    134 
    135 		// Retrieve the list of registered collection query parameters.
    136 		$registered = $this->get_collection_params();
    137 		$namespace  = '';
    138 		if ( isset( $registered['namespace'] ) && ! empty( $request['namespace'] ) ) {
    139 			$namespace = $request['namespace'];
    140 		}
    141 
    142 		foreach ( $block_types as $slug => $obj ) {
    143 			if ( $namespace ) {
    144 				list ( $block_namespace ) = explode( '/', $obj->name );
    145 
    146 				if ( $namespace !== $block_namespace ) {
    147 					continue;
    148 				}
    149 			}
    150 			$block_type = $this->prepare_item_for_response( $obj, $request );
    151 			$data[]     = $this->prepare_response_for_collection( $block_type );
    152 		}
    153 
    154 		return rest_ensure_response( $data );
    155 	}
    156 
    157 	/**
    158 	 * Checks if a given request has access to read a block type.
    159 	 *
    160 	 * @since 5.5.0
    161 	 *
    162 	 * @param WP_REST_Request $request Full details about the request.
    163 	 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
    164 	 */
    165 	public function get_item_permissions_check( $request ) {
    166 		$check = $this->check_read_permission();
    167 		if ( is_wp_error( $check ) ) {
    168 			return $check;
    169 		}
    170 		$block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] );
    171 		$block_type = $this->get_block( $block_name );
    172 		if ( is_wp_error( $block_type ) ) {
    173 			return $block_type;
    174 		}
    175 
    176 		return true;
    177 	}
    178 
    179 	/**
    180 	 * Checks whether a given block type should be visible.
    181 	 *
    182 	 * @since 5.5.0
    183 	 *
    184 	 * @return true|WP_Error True if the block type is visible, WP_Error otherwise.
    185 	 */
    186 	protected function check_read_permission() {
    187 		if ( current_user_can( 'edit_posts' ) ) {
    188 			return true;
    189 		}
    190 		foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
    191 			if ( current_user_can( $post_type->cap->edit_posts ) ) {
    192 				return true;
    193 			}
    194 		}
    195 
    196 		return new WP_Error( 'rest_block_type_cannot_view', __( 'Sorry, you are not allowed to manage block types.' ), array( 'status' => rest_authorization_required_code() ) );
    197 	}
    198 
    199 	/**
    200 	 * Get the block, if the name is valid.
    201 	 *
    202 	 * @since 5.5.0
    203 	 *
    204 	 * @param string $name Block name.
    205 	 * @return WP_Block_Type|WP_Error Block type object if name is valid, WP_Error otherwise.
    206 	 */
    207 	protected function get_block( $name ) {
    208 		$block_type = $this->block_registry->get_registered( $name );
    209 		if ( empty( $block_type ) ) {
    210 			return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.' ), array( 'status' => 404 ) );
    211 		}
    212 
    213 		return $block_type;
    214 	}
    215 
    216 	/**
    217 	 * Retrieves a specific block type.
    218 	 *
    219 	 * @since 5.5.0
    220 	 *
    221 	 * @param WP_REST_Request $request Full details about the request.
    222 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
    223 	 */
    224 	public function get_item( $request ) {
    225 		$block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] );
    226 		$block_type = $this->get_block( $block_name );
    227 		if ( is_wp_error( $block_type ) ) {
    228 			return $block_type;
    229 		}
    230 		$data = $this->prepare_item_for_response( $block_type, $request );
    231 
    232 		return rest_ensure_response( $data );
    233 	}
    234 
    235 	/**
    236 	 * Prepares a block type object for serialization.
    237 	 *
    238 	 * @since 5.5.0
    239 	 *
    240 	 * @param WP_Block_Type   $block_type Block type data.
    241 	 * @param WP_REST_Request $request    Full details about the request.
    242 	 * @return WP_REST_Response Block type data.
    243 	 */
    244 	public function prepare_item_for_response( $block_type, $request ) {
    245 
    246 		$fields = $this->get_fields_for_response( $request );
    247 		$data   = array();
    248 
    249 		if ( rest_is_field_included( 'attributes', $fields ) ) {
    250 			$data['attributes'] = $block_type->get_attributes();
    251 		}
    252 
    253 		if ( rest_is_field_included( 'is_dynamic', $fields ) ) {
    254 			$data['is_dynamic'] = $block_type->is_dynamic();
    255 		}
    256 
    257 		$schema       = $this->get_item_schema();
    258 		$extra_fields = array(
    259 			'api_version',
    260 			'name',
    261 			'title',
    262 			'description',
    263 			'icon',
    264 			'category',
    265 			'keywords',
    266 			'parent',
    267 			'provides_context',
    268 			'uses_context',
    269 			'supports',
    270 			'styles',
    271 			'textdomain',
    272 			'example',
    273 			'editor_script',
    274 			'script',
    275 			'editor_style',
    276 			'style',
    277 			'variations',
    278 		);
    279 		foreach ( $extra_fields as $extra_field ) {
    280 			if ( rest_is_field_included( $extra_field, $fields ) ) {
    281 				if ( isset( $block_type->$extra_field ) ) {
    282 					$field = $block_type->$extra_field;
    283 				} elseif ( array_key_exists( 'default', $schema['properties'][ $extra_field ] ) ) {
    284 					$field = $schema['properties'][ $extra_field ]['default'];
    285 				} else {
    286 					$field = '';
    287 				}
    288 				$data[ $extra_field ] = rest_sanitize_value_from_schema( $field, $schema['properties'][ $extra_field ] );
    289 			}
    290 		}
    291 
    292 		if ( rest_is_field_included( 'styles', $fields ) ) {
    293 			$styles         = $this->style_registry->get_registered_styles_for_block( $block_type->name );
    294 			$styles         = array_values( $styles );
    295 			$data['styles'] = wp_parse_args( $styles, $data['styles'] );
    296 			$data['styles'] = array_filter( $data['styles'] );
    297 		}
    298 
    299 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
    300 		$data    = $this->add_additional_fields_to_object( $data, $request );
    301 		$data    = $this->filter_response_by_context( $data, $context );
    302 
    303 		$response = rest_ensure_response( $data );
    304 
    305 		$response->add_links( $this->prepare_links( $block_type ) );
    306 
    307 		/**
    308 		 * Filters a block type returned from the REST API.
    309 		 *
    310 		 * Allows modification of the block type data right before it is returned.
    311 		 *
    312 		 * @since 5.5.0
    313 		 *
    314 		 * @param WP_REST_Response $response   The response object.
    315 		 * @param WP_Block_Type    $block_type The original block type object.
    316 		 * @param WP_REST_Request  $request    Request used to generate the response.
    317 		 */
    318 		return apply_filters( 'rest_prepare_block_type', $response, $block_type, $request );
    319 	}
    320 
    321 	/**
    322 	 * Prepares links for the request.
    323 	 *
    324 	 * @since 5.5.0
    325 	 *
    326 	 * @param WP_Block_Type $block_type Block type data.
    327 	 * @return array Links for the given block type.
    328 	 */
    329 	protected function prepare_links( $block_type ) {
    330 		list( $namespace ) = explode( '/', $block_type->name );
    331 
    332 		$links = array(
    333 			'collection' => array(
    334 				'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
    335 			),
    336 			'self'       => array(
    337 				'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $block_type->name ) ),
    338 			),
    339 			'up'         => array(
    340 				'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $namespace ) ),
    341 			),
    342 		);
    343 
    344 		if ( $block_type->is_dynamic() ) {
    345 			$links['https://api.w.org/render-block'] = array(
    346 				'href' => add_query_arg( 'context', 'edit', rest_url( sprintf( '%s/%s/%s', 'wp/v2', 'block-renderer', $block_type->name ) ) ),
    347 			);
    348 		}
    349 
    350 		return $links;
    351 	}
    352 
    353 	/**
    354 	 * Retrieves the block type' schema, conforming to JSON Schema.
    355 	 *
    356 	 * @since 5.5.0
    357 	 *
    358 	 * @return array Item schema data.
    359 	 */
    360 	public function get_item_schema() {
    361 		if ( $this->schema ) {
    362 			return $this->add_additional_fields_schema( $this->schema );
    363 		}
    364 
    365 		// rest_validate_value_from_schema doesn't understand $refs, pull out reused definitions for readability.
    366 		$inner_blocks_definition = array(
    367 			'description' => __( 'The list of inner blocks used in the example.' ),
    368 			'type'        => 'array',
    369 			'items'       => array(
    370 				'type'       => 'object',
    371 				'properties' => array(
    372 					'name'        => array(
    373 						'description' => __( 'The name of the inner block.' ),
    374 						'type'        => 'string',
    375 					),
    376 					'attributes'  => array(
    377 						'description' => __( 'The attributes of the inner block.' ),
    378 						'type'        => 'object',
    379 					),
    380 					'innerBlocks' => array(
    381 						'description' => __( "A list of the inner block's own inner blocks. This is a recursive definition following the parent innerBlocks schema." ),
    382 						'type'        => 'array',
    383 					),
    384 				),
    385 			),
    386 		);
    387 
    388 		$example_definition = array(
    389 			'description' => __( 'Block example.' ),
    390 			'type'        => array( 'object', 'null' ),
    391 			'default'     => null,
    392 			'properties'  => array(
    393 				'attributes'  => array(
    394 					'description' => __( 'The attributes used in the example.' ),
    395 					'type'        => 'object',
    396 				),
    397 				'innerBlocks' => $inner_blocks_definition,
    398 			),
    399 			'context'     => array( 'embed', 'view', 'edit' ),
    400 			'readonly'    => true,
    401 		);
    402 
    403 		$keywords_definition = array(
    404 			'description' => __( 'Block keywords.' ),
    405 			'type'        => 'array',
    406 			'items'       => array(
    407 				'type' => 'string',
    408 			),
    409 			'default'     => array(),
    410 			'context'     => array( 'embed', 'view', 'edit' ),
    411 			'readonly'    => true,
    412 		);
    413 
    414 		$icon_definition = array(
    415 			'description' => __( 'Icon of block type.' ),
    416 			'type'        => array( 'string', 'null' ),
    417 			'default'     => null,
    418 			'context'     => array( 'embed', 'view', 'edit' ),
    419 			'readonly'    => true,
    420 		);
    421 
    422 		$category_definition = array(
    423 			'description' => __( 'Block category.' ),
    424 			'type'        => array( 'string', 'null' ),
    425 			'default'     => null,
    426 			'context'     => array( 'embed', 'view', 'edit' ),
    427 			'readonly'    => true,
    428 		);
    429 
    430 		$schema = array(
    431 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
    432 			'title'      => 'block-type',
    433 			'type'       => 'object',
    434 			'properties' => array(
    435 				'api_version'      => array(
    436 					'description' => __( 'Version of block API.' ),
    437 					'type'        => 'integer',
    438 					'default'     => 1,
    439 					'context'     => array( 'embed', 'view', 'edit' ),
    440 					'readonly'    => true,
    441 				),
    442 				'title'            => array(
    443 					'description' => __( 'Title of block type.' ),
    444 					'type'        => 'string',
    445 					'default'     => '',
    446 					'context'     => array( 'embed', 'view', 'edit' ),
    447 					'readonly'    => true,
    448 				),
    449 				'name'             => array(
    450 					'description' => __( 'Unique name identifying the block type.' ),
    451 					'type'        => 'string',
    452 					'default'     => '',
    453 					'context'     => array( 'embed', 'view', 'edit' ),
    454 					'readonly'    => true,
    455 				),
    456 				'description'      => array(
    457 					'description' => __( 'Description of block type.' ),
    458 					'type'        => 'string',
    459 					'default'     => '',
    460 					'context'     => array( 'embed', 'view', 'edit' ),
    461 					'readonly'    => true,
    462 				),
    463 				'icon'             => $icon_definition,
    464 				'attributes'       => array(
    465 					'description'          => __( 'Block attributes.' ),
    466 					'type'                 => array( 'object', 'null' ),
    467 					'properties'           => array(),
    468 					'default'              => null,
    469 					'additionalProperties' => array(
    470 						'type' => 'object',
    471 					),
    472 					'context'              => array( 'embed', 'view', 'edit' ),
    473 					'readonly'             => true,
    474 				),
    475 				'provides_context' => array(
    476 					'description'          => __( 'Context provided by blocks of this type.' ),
    477 					'type'                 => 'object',
    478 					'properties'           => array(),
    479 					'additionalProperties' => array(
    480 						'type' => 'string',
    481 					),
    482 					'default'              => array(),
    483 					'context'              => array( 'embed', 'view', 'edit' ),
    484 					'readonly'             => true,
    485 				),
    486 				'uses_context'     => array(
    487 					'description' => __( 'Context values inherited by blocks of this type.' ),
    488 					'type'        => 'array',
    489 					'default'     => array(),
    490 					'items'       => array(
    491 						'type' => 'string',
    492 					),
    493 					'context'     => array( 'embed', 'view', 'edit' ),
    494 					'readonly'    => true,
    495 				),
    496 				'supports'         => array(
    497 					'description' => __( 'Block supports.' ),
    498 					'type'        => 'object',
    499 					'default'     => array(),
    500 					'properties'  => array(),
    501 					'context'     => array( 'embed', 'view', 'edit' ),
    502 					'readonly'    => true,
    503 				),
    504 				'category'         => $category_definition,
    505 				'is_dynamic'       => array(
    506 					'description' => __( 'Is the block dynamically rendered.' ),
    507 					'type'        => 'boolean',
    508 					'default'     => false,
    509 					'context'     => array( 'embed', 'view', 'edit' ),
    510 					'readonly'    => true,
    511 				),
    512 				'editor_script'    => array(
    513 					'description' => __( 'Editor script handle.' ),
    514 					'type'        => array( 'string', 'null' ),
    515 					'default'     => null,
    516 					'context'     => array( 'embed', 'view', 'edit' ),
    517 					'readonly'    => true,
    518 				),
    519 				'script'           => array(
    520 					'description' => __( 'Public facing script handle.' ),
    521 					'type'        => array( 'string', 'null' ),
    522 					'default'     => null,
    523 					'context'     => array( 'embed', 'view', 'edit' ),
    524 					'readonly'    => true,
    525 				),
    526 				'editor_style'     => array(
    527 					'description' => __( 'Editor style handle.' ),
    528 					'type'        => array( 'string', 'null' ),
    529 					'default'     => null,
    530 					'context'     => array( 'embed', 'view', 'edit' ),
    531 					'readonly'    => true,
    532 				),
    533 				'style'            => array(
    534 					'description' => __( 'Public facing style handle.' ),
    535 					'type'        => array( 'string', 'null' ),
    536 					'default'     => null,
    537 					'context'     => array( 'embed', 'view', 'edit' ),
    538 					'readonly'    => true,
    539 				),
    540 				'styles'           => array(
    541 					'description' => __( 'Block style variations.' ),
    542 					'type'        => 'array',
    543 					'items'       => array(
    544 						'type'       => 'object',
    545 						'properties' => array(
    546 							'name'         => array(
    547 								'description' => __( 'Unique name identifying the style.' ),
    548 								'type'        => 'string',
    549 								'required'    => true,
    550 							),
    551 							'label'        => array(
    552 								'description' => __( 'The human-readable label for the style.' ),
    553 								'type'        => 'string',
    554 							),
    555 							'inline_style' => array(
    556 								'description' => __( 'Inline CSS code that registers the CSS class required for the style.' ),
    557 								'type'        => 'string',
    558 							),
    559 							'style_handle' => array(
    560 								'description' => __( 'Contains the handle that defines the block style.' ),
    561 								'type'        => 'string',
    562 							),
    563 						),
    564 					),
    565 					'default'     => array(),
    566 					'context'     => array( 'embed', 'view', 'edit' ),
    567 					'readonly'    => true,
    568 				),
    569 				'variations'       => array(
    570 					'description' => __( 'Block variations.' ),
    571 					'type'        => 'array',
    572 					'items'       => array(
    573 						'type'       => 'object',
    574 						'properties' => array(
    575 							'name'        => array(
    576 								'description' => __( 'The unique and machine-readable name.' ),
    577 								'type'        => 'string',
    578 								'required'    => true,
    579 							),
    580 							'title'       => array(
    581 								'description' => __( 'A human-readable variation title.' ),
    582 								'type'        => 'string',
    583 								'required'    => true,
    584 							),
    585 							'description' => array(
    586 								'description' => __( 'A detailed variation description.' ),
    587 								'type'        => 'string',
    588 								'required'    => false,
    589 							),
    590 							'category'    => $category_definition,
    591 							'icon'        => $icon_definition,
    592 							'isDefault'   => array(
    593 								'description' => __( 'Indicates whether the current variation is the default one.' ),
    594 								'type'        => 'boolean',
    595 								'required'    => false,
    596 								'default'     => false,
    597 							),
    598 							'attributes'  => array(
    599 								'description' => __( 'The initial values for attributes.' ),
    600 								'type'        => 'object',
    601 							),
    602 							'innerBlocks' => $inner_blocks_definition,
    603 							'example'     => $example_definition,
    604 							'scope'       => array(
    605 								'description' => __( 'The list of scopes where the variation is applicable. When not provided, it assumes all available scopes.' ),
    606 								'type'        => array( 'array', 'null' ),
    607 								'default'     => null,
    608 								'items'       => array(
    609 									'type' => 'string',
    610 									'enum' => array( 'block', 'inserter', 'transform' ),
    611 								),
    612 								'readonly'    => true,
    613 							),
    614 							'keywords'    => $keywords_definition,
    615 						),
    616 					),
    617 					'readonly'    => true,
    618 					'context'     => array( 'embed', 'view', 'edit' ),
    619 					'default'     => null,
    620 				),
    621 				'textdomain'       => array(
    622 					'description' => __( 'Public text domain.' ),
    623 					'type'        => array( 'string', 'null' ),
    624 					'default'     => null,
    625 					'context'     => array( 'embed', 'view', 'edit' ),
    626 					'readonly'    => true,
    627 				),
    628 				'parent'           => array(
    629 					'description' => __( 'Parent blocks.' ),
    630 					'type'        => array( 'array', 'null' ),
    631 					'items'       => array(
    632 						'type' => 'string',
    633 					),
    634 					'default'     => null,
    635 					'context'     => array( 'embed', 'view', 'edit' ),
    636 					'readonly'    => true,
    637 				),
    638 				'keywords'         => $keywords_definition,
    639 				'example'          => $example_definition,
    640 			),
    641 		);
    642 
    643 		$this->schema = $schema;
    644 
    645 		return $this->add_additional_fields_schema( $this->schema );
    646 	}
    647 
    648 	/**
    649 	 * Retrieves the query params for collections.
    650 	 *
    651 	 * @since 5.5.0
    652 	 *
    653 	 * @return array Collection parameters.
    654 	 */
    655 	public function get_collection_params() {
    656 		return array(
    657 			'context'   => $this->get_context_param( array( 'default' => 'view' ) ),
    658 			'namespace' => array(
    659 				'description' => __( 'Block namespace.' ),
    660 				'type'        => 'string',
    661 			),
    662 		);
    663 	}
    664 
    665 }