PluginProbe
Gutenberg / 8.3.0
Gutenberg v8.3.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 8.3.0, at lib/class-wp-rest-block-types-controller.php

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