PluginProbe
Gutenberg / 9.6.0
Gutenberg v9.6.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-widget-types-controller.php

class-wp-rest-widget-types-controller.php in Gutenberg 9.6.0, at lib/class-wp-rest-widget-types-controller.php

459 lines 12.8 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_Widget_Types_Controller class
4 *
5 * @package WordPress
6 * @subpackage REST_API
7 * @since x.x.0
8 */
9
10 /**
11 * Core class used to access widget types via the REST API.
12 *
13 * @since x.x.0
14 *
15 * @see WP_REST_Controller
16 */
17 class WP_REST_Widget_Types_Controller extends WP_REST_Controller {
18
19 /**
20 * Constructor.
21 *
22 * @since x.x.0
23 */
24 public function __construct() {
25 $this->namespace = 'wp/v2';
26 $this->rest_base = 'widget-types';
27 }
28
29 /**
30 * Registers the routes for the objects of the controller.
31 *
32 * @since x.x.0
33 *
34 * @see register_rest_route()
35 */
36 public function register_routes() {
37
38 register_rest_route(
39 $this->namespace,
40 '/' . $this->rest_base,
41 array(
42 array(
43 'methods' => WP_REST_Server::READABLE,
44 'callback' => array( $this, 'get_items' ),
45 'permission_callback' => array( $this, 'get_items_permissions_check' ),
46 'args' => $this->get_collection_params(),
47 ),
48 'schema' => array( $this, 'get_public_item_schema' ),
49 )
50 );
51
52 register_rest_route(
53 $this->namespace,
54 '/' . $this->rest_base . '/(?P<name>[a-zA-Z0-9_-]+)/form-renderer',
55 array(
56 'args' => array(
57 'name' => array(
58 'description' => __( 'Name of the widget.', 'gutenberg' ),
59 'type' => 'string',
60 'required' => true,
61 ),
62 'instance' => array(
63 'description' => __( 'Current widget instance', 'gutenberg' ),
64 'type' => 'object',
65 'default' => array(),
66 ),
67 ),
68 array(
69 'methods' => WP_REST_Server::CREATABLE,
70 'permission_callback' => array( $this, 'get_item_permissions_check' ),
71 'callback' => array( $this, 'get_widget_form' ),
72 'args' => array(
73 'context' => $this->get_context_param( array( 'default' => 'edit' ) ),
74 ),
75 ),
76 )
77 );
78
79 register_rest_route(
80 $this->namespace,
81 '/' . $this->rest_base . '/(?P<name>[a-zA-Z0-9_-]+)',
82 array(
83 'args' => array(
84 'name' => array(
85 'description' => __( 'Widget name.', 'gutenberg' ),
86 'type' => 'string',
87 ),
88 ),
89 array(
90 'methods' => WP_REST_Server::READABLE,
91 'callback' => array( $this, 'get_item' ),
92 'permission_callback' => array( $this, 'get_item_permissions_check' ),
93 'args' => $this->get_collection_params(),
94 ),
95 'schema' => array( $this, 'get_public_item_schema' ),
96 )
97 );
98 }
99
100 /**
101 * Checks whether a given request has permission to read post widget types.
102 *
103 * @since x.x.0
104 *
105 * @param WP_REST_Request $request Full details about the request.
106 * @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
107 */
108 public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
109 return $this->check_read_permission();
110 }
111
112 /**
113 * Retrieves all post widget types, depending on user context.
114 *
115 * @since x.x.0
116 *
117 * @param WP_REST_Request $request Full details about the request.
118 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
119 */
120 public function get_items( $request ) {
121 $data = array();
122 foreach ( $this->get_widgets() as $widget ) {
123 $widget_type = $this->prepare_item_for_response( $widget, $request );
124 $data[] = $this->prepare_response_for_collection( $widget_type );
125 }
126
127 return rest_ensure_response( $data );
128 }
129
130 /**
131 * Checks if a given request has access to read a widget type.
132 *
133 * @since x.x.0
134 *
135 * @param WP_REST_Request $request Full details about the request.
136 * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
137 */
138 public function get_item_permissions_check( $request ) {
139 $check = $this->check_read_permission();
140 if ( is_wp_error( $check ) ) {
141 return $check;
142 }
143 $widget_name = $request['name'];
144 $widget_type = $this->get_widget( $widget_name );
145 if ( is_wp_error( $widget_type ) ) {
146 return $widget_type;
147 }
148
149 return true;
150 }
151
152 /**
153 * Checks whether a given widget type should be visible.
154 *
155 * @since x.x.0
156 *
157 * @return WP_Error|bool True if the widget type is visible, WP_Error otherwise.
158 */
159 protected function check_read_permission() {
160 if ( ! current_user_can( 'edit_theme_options' ) ) {
161 return new WP_Error(
162 'widgets_cannot_access',
163 __( 'Sorry, you are not allowed to access widgets on this site.', 'gutenberg' ),
164 array(
165 'status' => rest_authorization_required_code(),
166 )
167 );
168 }
169
170 return true;
171 }
172
173 /**
174 * Get the widget, if the name is valid.
175 *
176 * @since x.x.0
177 *
178 * @param string $name Widget name.
179 * @return WP_Widget|WP_Error Widget type object if name is valid, WP_Error otherwise.
180 */
181 public function get_widget( $name ) {
182 foreach ( $this->get_widgets() as $widget ) {
183 if ( $name === $widget['id'] ) {
184 return $widget;
185 }
186 }
187
188 return new WP_Error( 'rest_widget_type_invalid', __( 'Invalid widget type.', 'gutenberg' ), array( 'status' => 404 ) );
189 }
190
191 /**
192 * Normalize array of widgets.
193 *
194 * @return array Array of widgets.
195 */
196 protected function get_widgets() {
197 global $wp_registered_widgets;
198
199 $widgets = array();
200 foreach ( $wp_registered_widgets as $widget ) {
201 $widget_callback = $widget['callback'];
202 unset( $widget['callback'] );
203
204 if ( is_array( $widget_callback ) && $widget_callback[0] instanceof WP_Widget ) {
205 $widget_class = $widget_callback[0];
206 $widget_array = (array) $widget_class;
207 $widget = array_merge( $widget, $widget_array );
208 $widget['id'] = $widget['id_base'];
209 $widget['widget_class'] = get_class( $widget_class );
210 } else {
211 unset( $widget['classname'] );
212 }
213 $widgets[] = $widget;
214 }
215
216 return $widgets;
217 }
218
219 /**
220 * Retrieves a specific widget type.
221 *
222 * @since x.x.0
223 *
224 * @param WP_REST_Request $request Full details about the request.
225 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
226 */
227 public function get_item( $request ) {
228 $widget_name = $request['name'];
229 $widget_type = $this->get_widget( $widget_name );
230 if ( is_wp_error( $widget_type ) ) {
231 return $widget_type;
232 }
233 $data = $this->prepare_item_for_response( $widget_type, $request );
234
235 return rest_ensure_response( $data );
236 }
237
238 /**
239 * Prepares a widget type object for serialization.
240 *
241 * @since x.x.0
242 *
243 * @param array $widget_type Widget type data.
244 * @param WP_REST_Request $request Full details about the request.
245 * @return WP_REST_Response Widget type data.
246 */
247 public function prepare_item_for_response( $widget_type, $request ) {
248
249 $fields = $this->get_fields_for_response( $request );
250 $data = array();
251
252 $schema = $this->get_item_schema();
253 $extra_fields = array(
254 'name',
255 'id',
256 'description',
257 'classname',
258 'widget_class',
259 'option_name',
260 'customize_selective_refresh',
261 );
262
263 foreach ( $extra_fields as $extra_field ) {
264 if ( rest_is_field_included( $extra_field, $fields ) ) {
265 if ( isset( $widget_type[ $extra_field ] ) ) {
266 $field = $widget_type[ $extra_field ];
267 } elseif ( array_key_exists( 'default', $schema['properties'][ $extra_field ] ) ) {
268 $field = $schema['properties'][ $extra_field ]['default'];
269 } else {
270 $field = '';
271 }
272
273 $data[ $extra_field ] = rest_sanitize_value_from_schema( $field, $schema['properties'][ $extra_field ] );
274 }
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( $widget_type ) );
284
285 /**
286 * Filters a widget type returned from the REST API.
287 *
288 * Allows modification of the widget type data right before it is returned.
289 *
290 * @since x.x.0
291 *
292 * @param WP_REST_Response $response The response object.
293 * @param WP_Widget $widget_type The original widget type object.
294 * @param WP_REST_Request $request Request used to generate the response.
295 */
296 return apply_filters( 'rest_prepare_widget_type', $response, $widget_type, $request );
297 }
298
299 /**
300 * Prepares links for the request.
301 *
302 * @since x.x.0
303 *
304 * @param WP_Widget $widget_type Widget type data.
305 * @return array Links for the given widget type.
306 */
307 protected function prepare_links( $widget_type ) {
308 $links = array(
309 'collection' => array(
310 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
311 ),
312 'self' => array(
313 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $widget_type['id'] ) ),
314 ),
315 );
316
317 return $links;
318 }
319
320 /**
321 * Retrieves the widget type' schema, conforming to JSON Schema.
322 *
323 * @since x.x.0
324 *
325 * @return array Item schema data.
326 */
327 public function get_item_schema() {
328 if ( $this->schema ) {
329 return $this->add_additional_fields_schema( $this->schema );
330 }
331
332 $schema = array(
333 '$schema' => 'http://json-schema.org/draft-04/schema#',
334 'title' => 'widget-type',
335 'type' => 'object',
336 'properties' => array(
337 'name' => array(
338 'description' => __( 'Unique name identifying the widget type.', 'gutenberg' ),
339 'type' => 'string',
340 'default' => '',
341 'context' => array( 'embed', 'view', 'edit' ),
342 'readonly' => true,
343 ),
344 'id' => array(
345 'description' => __( 'Unique name identifying the widget type.', 'gutenberg' ),
346 'type' => 'string',
347 'default' => '',
348 'context' => array( 'embed', 'view', 'edit' ),
349 'readonly' => true,
350 ),
351 'description' => array(
352 'description' => __( 'Description of the widget.', 'gutenberg' ),
353 'type' => 'string',
354 'default' => '',
355 'context' => array( 'view', 'edit', 'embed' ),
356 ),
357 'classname' => array(
358 'description' => __( 'Class name', 'gutenberg' ),
359 'type' => 'string',
360 'default' => '',
361 'context' => array( 'embed', 'view', 'edit' ),
362 'readonly' => true,
363 ),
364 'option_name' => array(
365 'description' => __( 'Option name.', 'gutenberg' ),
366 'type' => 'string',
367 'default' => '',
368 'context' => array( 'embed', 'view', 'edit' ),
369 'readonly' => true,
370 ),
371 'widget_class' => array(
372 'description' => __( 'Widget class name.', 'gutenberg' ),
373 'type' => 'string',
374 'default' => '',
375 'context' => array( 'embed', 'view', 'edit' ),
376 'readonly' => true,
377 ),
378 'customize_selective_refresh' => array(
379 'description' => __( 'Customize selective refresh.', 'gutenberg' ),
380 'type' => 'boolean',
381 'default' => false,
382 'context' => array( 'embed', 'view', 'edit' ),
383 'readonly' => true,
384 ),
385 ),
386 );
387
388 $this->schema = $schema;
389
390 return $this->add_additional_fields_schema( $this->schema );
391 }
392
393 /**
394 * Returns the new widget instance and the form that represents it.
395 *
396 * @param WP_REST_Request $request Full details about the request.
397 *
398 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
399 * @since 5.7.0
400 * @access public
401 */
402 public function get_widget_form( $request ) {
403 $instance = $request->get_param( 'instance' );
404
405 $widget_name = $request['name'];
406 $widget = $this->get_widget( $widget_name );
407 $widget_obj = new $widget['widget_class'];
408 $widget_obj->_set( -1 );
409 ob_start();
410
411 $instance = apply_filters( 'widget_form_callback', $instance, $widget_obj );
412
413 $return = null;
414 if ( false !== $instance ) {
415 $return = $widget_obj->form( $instance );
416
417 /**
418 * Fires at the end of the widget control form.
419 *
420 * Use this hook to add extra fields to the widget form. The hook
421 * is only fired if the value passed to the 'widget_form_callback'
422 * hook is not false.
423 *
424 * Note: If the widget has no form, the text echoed from the default
425 * form method can be hidden using CSS.
426 *
427 * @param WP_Widget $widget_obj The widget instance (passed by reference).
428 * @param null $return Return null if new fields are added.
429 * @param array $instance An array of the widget's settings.
430 *
431 * @since 5.2.0
432 */
433 do_action_ref_array( 'in_widget_form', array( &$widget_obj, &$return, $instance ) );
434 }
435 $form = ob_get_clean();
436
437 return rest_ensure_response(
438 array(
439 'instance' => $instance,
440 'form' => $form,
441 )
442 );
443 }
444
445 /**
446 * Retrieves the query params for collections.
447 *
448 * @since 5.5.0
449 *
450 * @return array Collection parameters.
451 */
452 public function get_collection_params() {
453 return array(
454 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
455 );
456 }
457
458 }
459