PluginProbe
Gutenberg / 9.5.0
Gutenberg v9.5.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / class-wp-rest-block-types-controller.php

class-wp-rest-block-types-controller.php in Gutenberg 9.5.0, at lib/class-wp-rest-block-types-controller.php

529 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * REST API: WP_REST_Block_Types_Controller class
4 *
5 * @since 5.5.0
6 * @subpackage REST_API
7 * @package WordPress
8 */
9
10 /**
11 * Core class used to access block types via the REST API.
12 *
13 * This class can be removed when plugin support requires WordPress 5.5.0+.
14 *
15 * @see https://core.trac.wordpress.org/ticket/47620
16 *
17 * @see WP_REST_Controller
18 */
19 class WP_REST_Block_Types_Controller extends WP_REST_Controller {
20
21 /**
22 * Instance of WP_Block_Type_Registry.
23 *
24 * @var WP_Block_Type_Registry
25 */
26 protected $block_registry;
27
28 /**
29 * Instance of WP_Block_Styles_Registry.
30 *
31 * @var WP_Block_Styles_Registry
32 */
33 protected $style_registry;
34
35 /**
36 * Constructor.
37 */
38 public function __construct() {
39 $this->namespace = '__experimental';
40 $this->rest_base = 'block-types';
41 $this->block_registry = WP_Block_Type_Registry::get_instance();
42 $this->style_registry = WP_Block_Styles_Registry::get_instance();
43 }
44
45 /**
46 * Registers the routes for the objects of the controller.
47 *
48 * @see register_rest_route()
49 */
50 public function register_routes() {
51
52 register_rest_route(
53 $this->namespace,
54 '/' . $this->rest_base,
55 array(
56 array(
57 'methods' => WP_REST_Server::READABLE,
58 'callback' => array( $this, 'get_items' ),
59 'permission_callback' => array( $this, 'get_items_permissions_check' ),
60 'args' => $this->get_collection_params(),
61 ),
62 'schema' => array( $this, 'get_public_item_schema' ),
63 )
64 );
65
66 register_rest_route(
67 $this->namespace,
68 '/' . $this->rest_base . '/(?P<namespace>[a-zA-Z0-9_-]+)',
69 array(
70 array(
71 'methods' => WP_REST_Server::READABLE,
72 'callback' => array( $this, 'get_items' ),
73 'permission_callback' => array( $this, 'get_items_permissions_check' ),
74 'args' => $this->get_collection_params(),
75 ),
76 'schema' => array( $this, 'get_public_item_schema' ),
77 )
78 );
79
80 register_rest_route(
81 $this->namespace,
82 '/' . $this->rest_base . '/(?P<namespace>[a-zA-Z0-9_-]+)/(?P<name>[a-zA-Z0-9_-]+)',
83 array(
84 'args' => array(
85 'name' => array(
86 'description' => __( 'Block name.', 'gutenberg' ),
87 'type' => 'string',
88 ),
89 'namespace' => array(
90 'description' => __( 'Block namespace.', 'gutenberg' ),
91 'type' => 'string',
92 ),
93 ),
94 array(
95 'methods' => WP_REST_Server::READABLE,
96 'callback' => array( $this, 'get_item' ),
97 'permission_callback' => array( $this, 'get_item_permissions_check' ),
98 'args' => array(
99 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
100 ),
101 ),
102 'schema' => array( $this, 'get_public_item_schema' ),
103 )
104 );
105 }
106
107 /**
108 * Checks whether a given request has permission to read post block types.
109 *
110 * @param WP_REST_Request $request Full details about the request.
111 *
112 * @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
113 */
114 public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
115 return $this->check_read_permission();
116 }
117
118 /**
119 * Retrieves all post block types, depending on user context.
120 *
121 * @param WP_REST_Request $request Full details about the request.
122 *
123 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
124 */
125 public function get_items( $request ) {
126 $data = array();
127 $block_types = $this->block_registry->get_all_registered();
128
129 // Retrieve the list of registered collection query parameters.
130 $registered = $this->get_collection_params();
131 $namespace = '';
132 if ( isset( $registered['namespace'] ) && ! empty( $request['namespace'] ) ) {
133 $namespace = $request['namespace'];
134 }
135
136 foreach ( $block_types as $obj ) {
137 if ( $namespace ) {
138 $pieces = explode( '/', $obj->name );
139 $block_namespace = $pieces[0];
140 if ( $namespace !== $block_namespace ) {
141 continue;
142 }
143 }
144 $block_type = $this->prepare_item_for_response( $obj, $request );
145 $data[] = $this->prepare_response_for_collection( $block_type );
146 }
147
148 return rest_ensure_response( $data );
149 }
150
151 /**
152 * Checks if a given request has access to read a block type.
153 *
154 * @param WP_REST_Request $request Full details about the request.
155 *
156 * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
157 */
158 public function get_item_permissions_check( $request ) {
159 $check = $this->check_read_permission();
160 if ( is_wp_error( $check ) ) {
161 return $check;
162 }
163 $block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] );
164 $block_type = $this->get_block( $block_name );
165 if ( is_wp_error( $block_type ) ) {
166 return $block_type;
167 }
168
169 return true;
170 }
171
172 /**
173 * Checks whether a given block type should be visible.
174 *
175 * @return WP_Error|bool True if the block type is visible, otherwise false.
176 */
177 protected function check_read_permission() {
178 if ( current_user_can( 'edit_posts' ) ) {
179 return true;
180 }
181 foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
182 if ( current_user_can( $post_type->cap->edit_posts ) ) {
183 return true;
184 }
185 }
186
187 return new WP_Error( 'rest_block_type_cannot_view', __( 'Sorry, you are not allowed to manage block types.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
188 }
189
190 /**
191 * Get the block, if the name is valid.
192 *
193 * @param string $name Block name.
194 * @return WP_Block_Type|WP_Error Block type object if name is valid, WP_Error otherwise.
195 */
196 protected function get_block( $name ) {
197 $block_type = $this->block_registry->get_registered( $name );
198 if ( empty( $block_type ) ) {
199 return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.', 'gutenberg' ), array( 'status' => 404 ) );
200 }
201
202 return $block_type;
203 }
204
205 /**
206 * Retrieves a specific block type.
207 *
208 * @param WP_REST_Request $request Full details about the request.
209 *
210 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
211 */
212 public function get_item( $request ) {
213 $block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] );
214 $block_type = $this->get_block( $block_name );
215 if ( is_wp_error( $block_type ) ) {
216 return $block_type;
217 }
218 $data = $this->prepare_item_for_response( $block_type, $request );
219
220 return rest_ensure_response( $data );
221 }
222
223 /**
224 * Prepares a block type object for serialization.
225 *
226 * @param WP_Block_Type $block_type block type data.
227 * @param WP_REST_Request $request Full details about the request.
228 *
229 * @return WP_REST_Response block type data.
230 */
231 public function prepare_item_for_response( $block_type, $request ) {
232
233 $fields = $this->get_fields_for_response( $request );
234 $data = array();
235
236 if ( rest_is_field_included( 'attributes', $fields ) ) {
237 $data['attributes'] = $block_type->get_attributes();
238 }
239
240 if ( rest_is_field_included( 'is_dynamic', $fields ) ) {
241 $data['is_dynamic'] = $block_type->is_dynamic();
242 }
243
244 $schema = $this->get_item_schema();
245 $extra_fields = array(
246 'name' => 'name',
247 'title' => 'title',
248 'description' => 'description',
249 'icon' => 'icon',
250 'category' => 'category',
251 'keywords' => 'keywords',
252 'parent' => 'parent',
253 'provides_context' => 'provides_context',
254 'uses_context' => 'uses_context',
255 'supports' => 'supports',
256 'styles' => 'styles',
257 'textdomain' => 'textdomain',
258 'example' => 'example',
259 'editor_script' => 'editor_script',
260 'script' => 'script',
261 'editor_style' => 'editor_style',
262 'style' => 'style',
263 );
264 foreach ( $extra_fields as $key => $extra_field ) {
265 if ( rest_is_field_included( $key, $fields ) ) {
266 if ( isset( $block_type->$extra_field ) ) {
267 $field = $block_type->$extra_field;
268 } elseif ( array_key_exists( 'default', $schema['properties'][ $key ] ) ) {
269 $field = $schema['properties'][ $key ]['default'];
270 } else {
271 $field = '';
272 }
273 $data[ $key ] = rest_sanitize_value_from_schema( $field, $schema['properties'][ $key ] );
274 }
275 }
276
277 if ( rest_is_field_included( 'styles', $fields ) ) {
278 $styles = $this->style_registry->get_registered_styles_for_block( $block_type->name );
279 $styles = array_values( $styles );
280 $data['styles'] = wp_parse_args( $styles, $data['styles'] );
281 }
282
283 $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
284 $data = $this->add_additional_fields_to_object( $data, $request );
285 $data = $this->filter_response_by_context( $data, $context );
286
287 $response = rest_ensure_response( $data );
288
289 $response->add_links( $this->prepare_links( $block_type ) );
290
291 /**
292 * Filters a block type returned from the REST API.
293 *
294 * Allows modification of the block type data right before it is returned.
295 *
296 * @param WP_REST_Response $response The response object.
297 * @param object $block_type The original block type object.
298 * @param WP_REST_Request $request Request used to generate the response.
299 */
300 return apply_filters( 'rest_prepare_block_type', $response, $block_type, $request );
301 }
302
303 /**
304 * Prepares links for the request.
305 *
306 * @param WP_Block_Type $block_type block type data.
307 * @return array Links for the given block type.
308 */
309 protected function prepare_links( $block_type ) {
310 $pieces = explode( '/', $block_type->name );
311 $namespace = $pieces[0];
312 $links = array(
313 'collection' => array(
314 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
315 ),
316 'self' => array(
317 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $block_type->name ) ),
318 ),
319 'up' => array(
320 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $namespace ) ),
321 ),
322 );
323
324 if ( $block_type->is_dynamic() ) {
325 $links['https://api.w.org/render-block']['href'] = add_query_arg( 'context', 'edit', rest_url( sprintf( '%s/%s/%s', 'wp/v2', 'block-renderer', $block_type->name ) ) );
326 }
327
328 return $links;
329 }
330
331 /**
332 * Retrieves the block type' schema, conforming to JSON Schema.
333 *
334 * @return array Item schema data.
335 */
336 public function get_item_schema() {
337 if ( $this->schema ) {
338 return $this->add_additional_fields_schema( $this->schema );
339 }
340
341 $schema = array(
342 '$schema' => 'http://json-schema.org/draft-04/schema#',
343 'title' => 'block-type',
344 'type' => 'object',
345 'properties' => array(
346 'title' => array(
347 'description' => __( 'Title of block type.', 'gutenberg' ),
348 'type' => 'string',
349 'default' => '',
350 'context' => array( 'embed', 'view', 'edit' ),
351 'readonly' => true,
352 ),
353 'name' => array(
354 'description' => __( 'Unique name identifying the block type.', 'gutenberg' ),
355 'type' => 'string',
356 'default' => '',
357 'context' => array( 'embed', 'view', 'edit' ),
358 'readonly' => true,
359 ),
360 'description' => array(
361 'description' => __( 'Description of block type.', 'gutenberg' ),
362 'type' => 'string',
363 'default' => '',
364 'context' => array( 'embed', 'view', 'edit' ),
365 'readonly' => true,
366 ),
367 'icon' => array(
368 'description' => __( 'Icon of block type.', 'gutenberg' ),
369 'type' => array( 'string', 'null' ),
370 'default' => null,
371 'context' => array( 'embed', 'view', 'edit' ),
372 'readonly' => true,
373 ),
374 'attributes' => array(
375 'description' => __( 'Block attributes.', 'gutenberg' ),
376 'type' => array( 'object', 'null' ),
377 'properties' => array(),
378 'default' => null,
379 'additionalProperties' => array(
380 'type' => 'object',
381 ),
382 'context' => array( 'embed', 'view', 'edit' ),
383 'readonly' => true,
384 ),
385 'provides_context' => array(
386 'description' => __( 'Context provided by blocks of this type.', 'gutenberg' ),
387 'type' => 'object',
388 'properties' => array(),
389 'additionalProperties' => array(
390 'type' => 'string',
391 ),
392 'default' => array(),
393 'context' => array( 'embed', 'view', 'edit' ),
394 'readonly' => true,
395 ),
396 'uses_context' => array(
397 'description' => __( 'Context values inherited by blocks of this type.', 'gutenberg' ),
398 'type' => 'array',
399 'default' => array(),
400 'items' => array(
401 'type' => 'string',
402 ),
403 'context' => array( 'embed', 'view', 'edit' ),
404 'readonly' => true,
405 ),
406 'supports' => array(
407 'description' => __( 'Block supports.', 'gutenberg' ),
408 'type' => 'object',
409 'default' => array(),
410 'properties' => array(),
411 'context' => array( 'embed', 'view', 'edit' ),
412 'readonly' => true,
413 ),
414 'category' => array(
415 'description' => __( 'Block category.', 'gutenberg' ),
416 'type' => array( 'string', null ),
417 'default' => null,
418 'context' => array( 'embed', 'view', 'edit' ),
419 'readonly' => true,
420 ),
421 'is_dynamic' => array(
422 'description' => __( 'Is the block dynamically rendered.', 'gutenberg' ),
423 'type' => 'boolean',
424 'default' => false,
425 'context' => array( 'embed', 'view', 'edit' ),
426 'readonly' => true,
427 ),
428 'editor_script' => array(
429 'description' => __( 'Editor script handle.', 'gutenberg' ),
430 'type' => array( 'string', null ),
431 'default' => null,
432 'context' => array( 'embed', 'view', 'edit' ),
433 'readonly' => true,
434 ),
435 'script' => array(
436 'description' => __( 'Public facing script handle.', 'gutenberg' ),
437 'type' => array( 'string', null ),
438 'default' => null,
439 'context' => array( 'embed', 'view', 'edit' ),
440 'readonly' => true,
441 ),
442 'editor_style' => array(
443 'description' => __( 'Editor style handle.', 'gutenberg' ),
444 'type' => array( 'string', null ),
445 'default' => null,
446 'context' => array( 'embed', 'view', 'edit' ),
447 'readonly' => true,
448 ),
449 'style' => array(
450 'description' => __( 'Public facing style handle.', 'gutenberg' ),
451 'type' => array( 'string', null ),
452 'default' => null,
453 'context' => array( 'embed', 'view', 'edit' ),
454 'readonly' => true,
455 ),
456 'styles' => array(
457 'description' => __( 'Block style variations.', 'gutenberg' ),
458 'type' => 'array',
459 'properties' => array(),
460 'additionalProperties' => array(
461 'type' => 'object',
462 ),
463 'default' => array(),
464 'context' => array( 'embed', 'view', 'edit' ),
465 'readonly' => true,
466 ),
467 'textdomain' => array(
468 'description' => __( 'Public text domain.', 'gutenberg' ),
469 'type' => array( 'string', 'null' ),
470 'default' => null,
471 'context' => array( 'embed', 'view', 'edit' ),
472 'readonly' => true,
473 ),
474 'parent' => array(
475 'description' => __( 'Parent blocks.', 'gutenberg' ),
476 'type' => array( 'array', 'null' ),
477 'items' => array(
478 'type' => 'string',
479 ),
480 'default' => null,
481 'context' => array( 'embed', 'view', 'edit' ),
482 'readonly' => true,
483 ),
484 'keywords' => array(
485 'description' => __( 'Block keywords.', 'gutenberg' ),
486 'type' => 'array',
487 'items' => array(
488 'type' => 'string',
489 ),
490 'default' => array(),
491 'context' => array( 'embed', 'view', 'edit' ),
492 'readonly' => true,
493 ),
494 'example' => array(
495 'description' => __( 'Block example.', 'gutenberg' ),
496 'type' => array( 'object', 'null' ),
497 'default' => null,
498 'properties' => array(),
499 'additionalProperties' => array(
500 'type' => 'object',
501 ),
502 'context' => array( 'embed', 'view', 'edit' ),
503 'readonly' => true,
504 ),
505 ),
506 );
507
508 $this->schema = $schema;
509
510 return $this->add_additional_fields_schema( $this->schema );
511 }
512
513 /**
514 * Retrieves the query params for collections.
515 *
516 * @return array Collection parameters.
517 */
518 public function get_collection_params() {
519 $new_params = array();
520 $new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
521 $new_params['namespace'] = array(
522 'description' => __( 'Block namespace.', 'gutenberg' ),
523 'type' => 'string',
524 );
525 return $new_params;
526 }
527
528 }
529