PluginProbe
Gutenberg / 8.9.2
Gutenberg v8.9.2
24.0.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 All 403 releases
gutenberg / lib / class-wp-rest-sidebars-controller.php

class-wp-rest-sidebars-controller.php in Gutenberg 8.9.2, at lib/class-wp-rest-sidebars-controller.php

636 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * An extension for the WP REST API that exposes endpoints for sidebars and widgets.
4 *
5 * PHP version 5.4.0
6 *
7 * Copyright (C) 2015 Martin Pettersson
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * @author Martin Pettersson <martin_pettersson@outlook.com>
23 * @copyright 2015 Martin Pettersson
24 * @license GPLv2
25 * @link https://github.com/martin-pettersson/wp-rest-api-sidebars
26 * @package gutenberg
27 */
28
29 /**
30 * Class Sidebars_Controller
31 *
32 * @package WP_API_Sidebars\Controllers
33 */
34 class WP_REST_Sidebars_Controller extends WP_REST_Controller {
35
36 /**
37 * Plugins controller constructor.
38 *
39 * @since 5.5.0
40 */
41 public function __construct() {
42 $this->namespace = '__experimental';
43 $this->rest_base = 'sidebars';
44 }
45
46 /**
47 * Registers the controllers routes.
48 *
49 * @return void
50 */
51 public function register_routes() {
52 // Lists all sidebars.
53 register_rest_route(
54 $this->namespace,
55 '/' . $this->rest_base,
56 array(
57 array(
58 'methods' => WP_REST_Server::READABLE,
59 'callback' => array( $this, 'get_items' ),
60 'permission_callback' => array( $this, 'permissions_check' ),
61 ),
62 'schema' => array( $this, 'get_public_item_schema' ),
63 )
64 );
65
66 // Lists/updates a single sidebar based on the given id.
67 register_rest_route(
68 $this->namespace,
69 '/' . $this->rest_base . '/(?P<id>[\w-]+)',
70 array(
71 array(
72 'methods' => WP_REST_Server::READABLE,
73 'callback' => array( $this, 'get_item' ),
74 'permission_callback' => array( $this, 'permissions_check' ),
75 'args' => array(
76 'id' => array(
77 'description' => __( 'The id of a registered sidebar', 'gutenberg' ),
78 'type' => 'string',
79 'validate_callback' => function ( $id ) {
80 return self::get_sidebar( $id )[0];
81 },
82 ),
83 ),
84 ),
85 array(
86 'methods' => WP_REST_Server::EDITABLE,
87 'callback' => array( $this, 'update_item' ),
88 'permission_callback' => array( $this, 'permissions_check' ),
89 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
90 ),
91 'schema' => array( $this, 'get_public_item_schema' ),
92 )
93 );
94 }
95
96 /**
97 * Checks if the user has permissions to make the request.
98 *
99 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
100 * @since 5.6.0
101 * @access public
102 */
103 public function permissions_check() {
104 // Verify if the current user has edit_theme_options capability.
105 // This capability is required to access the widgets screen.
106 if ( ! current_user_can( 'edit_theme_options' ) ) {
107 return new WP_Error(
108 'widgets_cannot_access',
109 __( 'Sorry, you are not allowed to access widgets on this site.', 'gutenberg' ),
110 array(
111 'status' => rest_authorization_required_code(),
112 )
113 );
114 }
115
116 return true;
117 }
118
119
120 /**
121 * Updates the sidebar.
122 *
123 * @param WP_REST_Request $request The request instance.
124 *
125 * @return WP_REST_Response
126 * @global array $wp_registered_widget_updates
127 */
128 public function update_item( $request ) {
129 global $wp_registered_widget_updates, $wp_registered_widgets;
130 $sidebar_id = $request['id'];
131 $input_widgets = $request['widgets'];
132
133 // Initialize $numbers.
134 $numbers = array();
135 foreach ( $wp_registered_widget_updates as $id_base => $control ) {
136 if ( is_array( $control['callback'] ) ) {
137 $numbers[ $id_base ] = $control['callback'][0]->number + 1;
138 }
139 }
140
141 // Create and update widgets.
142 $sidebar_widgets_ids = array();
143 foreach ( $input_widgets as $input_widget ) {
144 ob_start();
145 if ( isset( $input_widget['id_base'] ) && isset( $wp_registered_widget_updates[ $input_widget['id_base'] ] ) ) {
146 // Class-based widget.
147 $update_control = $wp_registered_widget_updates[ $input_widget['id_base'] ];
148 if ( ! isset( $input_widget['id'] ) ) {
149 $number = $numbers[ $input_widget['id_base'] ] ++;
150 $id = $input_widget['id_base'] . '-' . $number;
151
152 $input_widget['id'] = $id;
153 $input_widget['number'] = $number;
154 }
155 $field = 'widget-' . $input_widget['id_base'];
156 $number = $input_widget['number'];
157 $_POST = $input_widget;
158 $_POST[ $field ][ $number ] = wp_slash( $input_widget['settings'] );
159 call_user_func( $update_control['callback'] );
160 $update_control['callback'][0]->updated = false;
161
162 // Just because we saved new widget doesn't mean it was added to $wp_registered_widgets.
163 // Let's make sure it's there so that it's included in the response.
164 if ( ! isset( $wp_registered_widgets[ $input_widget['id'] ] ) ) {
165 $first_widget_id = substr( $input_widget['id'], 0, strrpos( $input_widget['id'], '-' ) ) . '-1';
166
167 if ( isset( $wp_registered_widgets[ $first_widget_id ] ) ) {
168 $wp_registered_widgets[ $input_widget['id'] ] = $wp_registered_widgets[ $first_widget_id ];
169 $widget_class = get_class( $update_control['callback'][0] );
170 $new_object = new $widget_class(
171 $input_widget['id_base'],
172 $input_widget['name'],
173 $input_widget['settings']
174 );
175 $new_object->_register();
176 $wp_registered_widgets[ $input_widget['id'] ]['callback'][0] = $new_object;
177 }
178 }
179 } else {
180 $registered_widget_id = null;
181 if ( isset( $wp_registered_widget_updates[ $input_widget['id'] ] ) ) {
182 $registered_widget_id = $input_widget['id'];
183 } else {
184 $numberless_id = substr( $input_widget['id'], 0, strrpos( $input_widget['id'], '-' ) );
185 if ( isset( $wp_registered_widget_updates[ $numberless_id ] ) ) {
186 $registered_widget_id = $numberless_id;
187 }
188 }
189
190 if ( $registered_widget_id ) {
191 // Old-style widget.
192 $update_control = $wp_registered_widget_updates[ $registered_widget_id ];
193 $_POST = wp_slash( $input_widget['settings'] );
194 call_user_func( $update_control['callback'] );
195 }
196 }
197 ob_end_clean();
198
199 $sidebar_widgets_ids[] = $input_widget['id'];
200 }
201
202 // Update sidebar to only consist of the widgets we just processed.
203 $sidebars = wp_get_sidebars_widgets();
204 $sidebars[ $sidebar_id ] = $sidebar_widgets_ids;
205 wp_set_sidebars_widgets( $sidebars );
206
207 $request = new WP_REST_Request( 'GET' );
208 $request->set_param( 'id', $sidebar_id );
209
210 return $this->get_item( $request );
211 }
212
213 /**
214 * Returns a list of sidebars (active or inactive)
215 *
216 * @param WP_REST_Request $request The request instance.
217 *
218 * @return WP_REST_Response
219 * @global array $wp_registered_sidebars
220 */
221 public function get_items( $request ) {
222 $data = array();
223 foreach ( (array) wp_get_sidebars_widgets() as $id => $widgets ) {
224 $sidebar = self::get_sidebar( $id )[1];
225
226 $data[] = $this->prepare_item_for_response( $sidebar, $request )->get_data();
227 }
228
229 return rest_ensure_response( $data );
230 }
231
232 /**
233 * Returns the given sidebar
234 *
235 * @param WP_REST_Request $request The request instance.
236 *
237 * @return WP_REST_Response
238 */
239 public function get_item( $request ) {
240 $sidebar = self::get_sidebar( $request['id'] )[1];
241
242 return $this->prepare_item_for_response( $sidebar, $request );
243 }
244
245 /**
246 * Returns a sidebar for the given id or null if not found
247 *
248 * Note: The id can be either an index, the id or the name of a sidebar
249 *
250 * @param string|int $id ID of the sidebar.
251 *
252 * @return array|null
253 * @global array $wp_registered_sidebars
254 */
255 public static function get_sidebar( $id ) {
256 global $wp_registered_sidebars;
257
258 if ( is_int( $id ) ) {
259 $id = 'sidebar-' . $id;
260 } else {
261 $id = sanitize_title( $id );
262
263 foreach ( (array) $wp_registered_sidebars as $key => $sidebar ) {
264 if ( sanitize_title( $sidebar['name'] ) === $id ) {
265 return array( true, $sidebar );
266 }
267 }
268 }
269
270 foreach ( (array) $wp_registered_sidebars as $key => $sidebar ) {
271 if ( $key === $id ) {
272 return array( true, $sidebar );
273 }
274 }
275
276 return array( false, array( 'id' => $id ) );
277 }
278
279 /**
280 * Returns a list of widgets for the given sidebar id
281 *
282 * @param string $sidebar_id ID of the sidebar.
283 * @param WP_REST_Request $request Request object.
284 *
285 * @return array
286 * @global array $wp_registered_widgets
287 * @global array $wp_registered_sidebars
288 */
289 public static function get_widgets( $sidebar_id, $request ) {
290 global $wp_registered_widgets, $wp_registered_sidebars, $wp_registered_widget_controls;
291
292 $widgets = array();
293 $sidebars_widgets = (array) wp_get_sidebars_widgets();
294 $registered_sidebar = isset( $wp_registered_sidebars[ $sidebar_id ] )
295 ? $wp_registered_sidebars[ $sidebar_id ]
296 : (
297 'wp_inactive_widgets' === $sidebar_id ? array() : null
298 );
299
300 if ( null !== $registered_sidebar && isset( $sidebars_widgets[ $sidebar_id ] ) ) {
301 foreach ( $sidebars_widgets[ $sidebar_id ] as $widget_id ) {
302 // Just to be sure.
303 if ( isset( $wp_registered_widgets[ $widget_id ] ) ) {
304 $widget = $wp_registered_widgets[ $widget_id ];
305
306 // Get the widget output.
307 if ( is_callable( $widget['callback'] ) ) {
308 // @note: everything up to ob_start is taken from the dynamic_sidebar function.
309 $widget_parameters = array_merge(
310 array(
311 array_merge(
312 $registered_sidebar,
313 array(
314 'widget_id' => $widget_id,
315 'widget_name' => $widget['name'],
316 )
317 ),
318 ),
319 (array) $widget['params']
320 );
321
322 $classname = '';
323 foreach ( (array) $widget['classname'] as $cn ) {
324 if ( is_string( $cn ) ) {
325 $classname .= '_' . $cn;
326 } elseif ( is_object( $cn ) ) {
327 $classname .= '_' . get_class( $cn );
328 }
329 }
330 $classname = ltrim( $classname, '_' );
331 if ( isset( $widget_parameters[0]['before_widget'] ) ) {
332 $widget_parameters[0]['before_widget'] = sprintf(
333 $widget_parameters[0]['before_widget'],
334 $widget_id,
335 $classname
336 );
337 }
338
339 ob_start();
340 call_user_func_array( $widget['callback'], $widget_parameters );
341 $widget['rendered'] = trim( ob_get_clean() );
342 }
343
344 if ( is_array( $widget['callback'] ) && isset( $widget['callback'][0] ) ) {
345 $instance = $widget['callback'][0];
346 $widget['widget_class'] = get_class( $instance );
347 $widget['settings'] = static::get_sidebar_widget_instance(
348 $registered_sidebar,
349 $widget_id
350 );
351 $widget['number'] = (int) $widget['params'][0]['number'];
352 $widget['id_base'] = $instance->id_base;
353 }
354
355 if ( 'edit' === $request['context'] && isset( $wp_registered_widget_controls[ $widget_id ]['callback'] ) ) {
356 $control = $wp_registered_widget_controls[ $widget_id ];
357 $arguments = array();
358 if ( ! empty( $widget['number'] ) ) {
359 $arguments[0] = array( 'number' => $widget['number'] );
360 }
361 ob_start();
362 call_user_func_array( $control['callback'], $arguments );
363 $widget['rendered_form'] = trim( ob_get_clean() );
364 }
365
366 unset( $widget['params'] );
367 unset( $widget['callback'] );
368
369 $widgets[] = $widget;
370 }
371 }
372 }
373
374 return $widgets;
375 }
376
377 /**
378 * Prepare a single sidebar output for response
379 *
380 * @param array $raw_sidebar Sidebar instance.
381 * @param WP_REST_Request $request Request object.
382 *
383 * @return WP_REST_Response $data
384 */
385 public function prepare_item_for_response( $raw_sidebar, $request ) {
386 global $wp_registered_sidebars;
387
388 $id = $raw_sidebar['id'];
389 $sidebar = array( 'id' => $id );
390
391 if ( isset( $wp_registered_sidebars[ $id ] ) ) {
392 $registered_sidebar = $wp_registered_sidebars[ $id ];
393
394 $sidebar['status'] = 'active';
395 $sidebar['name'] = isset( $registered_sidebar['name'] ) ? $registered_sidebar['name'] : '';
396 $sidebar['description'] = isset( $registered_sidebar['description'] ) ? $registered_sidebar['description'] : '';
397 } else {
398 $sidebar['status'] = 'inactive';
399 }
400
401 if ( 'wp_inactive_widgets' === $sidebar['id'] && empty( $sidebar['name'] ) ) {
402 $sidebar['name'] = __( 'Inactive widgets', 'gutenberg' );
403 }
404
405 $fields = $this->get_fields_for_response( $request );
406 if ( rest_is_field_included( 'widgets', $fields ) ) {
407 $sidebar['widgets'] = self::get_widgets( $sidebar['id'], $request );
408 }
409
410 $schema = $this->get_item_schema();
411 $data = array();
412 foreach ( $schema['properties'] as $property_id => $property ) {
413 if ( isset( $sidebar[ $property_id ] ) && gettype( $sidebar[ $property_id ] ) === $property['type'] ) {
414 $data[ $property_id ] = $sidebar[ $property_id ];
415 } elseif ( isset( $property['default'] ) ) {
416 $data[ $property_id ] = $property['default'];
417 }
418 }
419
420 foreach ( $sidebar['widgets'] as $widget_id => $widget ) {
421 $widget_data = array();
422 foreach ( $schema['properties']['widgets']['items']['properties'] as $property_id => $property ) {
423 if ( isset( $widget[ $property_id ] ) && gettype( $widget[ $property_id ] ) === $property['type'] ) {
424 $widget_data[ $property_id ] = $widget[ $property_id ];
425 } elseif ( 'settings' === $property_id && isset( $widget[ $property_id ] ) && 'array' === gettype( $widget[ $property_id ] ) ) {
426 $widget_data[ $property_id ] = $widget['settings'];
427 } elseif ( isset( $property['default'] ) ) {
428 $widget_data[ $property_id ] = $property['default'];
429 }
430 }
431 $data['widgets'][ $widget_id ] = $widget_data;
432 }
433
434 $response = rest_ensure_response( $data );
435
436 /**
437 * Filters a sidebar location returned from the REST API.
438 *
439 * Allows modification of the menu location data right before it is
440 * returned.
441 *
442 * @param WP_REST_Response $response The response object.
443 * @param object $sidebar The original status object.
444 * @param WP_REST_Request $request Request used to generate the response.
445 */
446 return apply_filters( 'rest_prepare_sidebar', $response, $sidebar, $request );
447 }
448
449 /**
450 * Retrieves the block type' schema, conforming to JSON Schema.
451 *
452 * @return array Item schema data.
453 */
454 public function get_item_schema() {
455 if ( $this->schema ) {
456 return $this->add_additional_fields_schema( $this->schema );
457 }
458
459 $schema = array(
460 '$schema' => 'http://json-schema.org/draft-04/schema#',
461 'title' => 'sidebar',
462 'type' => 'object',
463 'properties' => array(
464 'id' => array(
465 'description' => __( 'ID of sidebar.', 'gutenberg' ),
466 'type' => 'string',
467 'default' => '',
468 'context' => array( 'embed', 'view', 'edit' ),
469 'readonly' => true,
470 ),
471 'name' => array(
472 'description' => __( 'Unique name identifying the sidebar.', 'gutenberg' ),
473 'type' => 'string',
474 'default' => '',
475 'context' => array( 'embed', 'view', 'edit' ),
476 'readonly' => true,
477 ),
478 'description' => array(
479 'description' => __( 'Description of sidebar.', 'gutenberg' ),
480 'type' => 'string',
481 'default' => '',
482 'context' => array( 'embed', 'view', 'edit' ),
483 'readonly' => true,
484 ),
485 'status' => array(
486 'description' => __( 'Status of sidebar.', 'gutenberg' ),
487 'type' => 'string',
488 'enum' => array( 'active', 'inactive' ),
489 'default' => '',
490 'context' => array( 'embed', 'view', 'edit' ),
491 'readonly' => true,
492 ),
493 'widgets' => array(
494 'description' => __( 'Nested widgets.', 'gutenberg' ),
495 'type' => 'array',
496 'items' => array(
497 'type' => 'object',
498 'properties' => array(
499 'id' => array(
500 'description' => __( 'Unique identifier for the widget.', 'gutenberg' ),
501 'type' => 'string',
502 'context' => array( 'view', 'edit', 'embed' ),
503 ),
504 'id_base' => array(
505 'description' => __( 'Type of widget for the object.', 'gutenberg' ),
506 'type' => 'string',
507 'context' => array( 'view', 'edit', 'embed' ),
508 ),
509 'widget_class' => array(
510 'description' => __( 'Class name of the widget implementation.', 'gutenberg' ),
511 'type' => 'string',
512 'context' => array( 'view', 'edit', 'embed' ),
513 ),
514 'name' => array(
515 'description' => __( 'Name of the widget.', 'gutenberg' ),
516 'type' => 'string',
517 'context' => array( 'view', 'edit', 'embed' ),
518 ),
519 'description' => array(
520 'description' => __( 'Description of the widget.', 'gutenberg' ),
521 'type' => 'string',
522 'context' => array( 'view', 'edit', 'embed' ),
523 ),
524 'number' => array(
525 'description' => __( 'Number of the widget.', 'gutenberg' ),
526 'type' => 'integer',
527 'context' => array( 'view', 'edit', 'embed' ),
528 ),
529 'rendered' => array(
530 'description' => __( 'HTML representation of the widget.', 'gutenberg' ),
531 'type' => 'string',
532 'context' => array( 'view', 'embed' ),
533 'readonly' => true,
534 ),
535 'rendered_form' => array(
536 'description' => __( 'HTML representation of the widget admin form.', 'gutenberg' ),
537 'type' => 'string',
538 'context' => array( 'edit' ),
539 'readonly' => true,
540 ),
541 'settings' => array(
542 'description' => __( 'Settings of the widget.', 'gutenberg' ),
543 'type' => 'object',
544 'context' => array( 'view', 'edit', 'embed' ),
545 'default' => array(),
546 ),
547 ),
548 ),
549 'default' => array(),
550 'context' => array( 'embed', 'view', 'edit' ),
551 ),
552 ),
553 );
554
555 $this->schema = $schema;
556
557 return $this->add_additional_fields_schema( $this->schema );
558 }
559
560 /**
561 * Retrieves a widget instance.
562 *
563 * @param array $sidebar sidebar data available at $wp_registered_sidebars.
564 * @param string $id Identifier of the widget instance.
565 *
566 * @return array Array containing the widget instance.
567 * @since 5.7.0
568 */
569 public static function get_sidebar_widget_instance( $sidebar, $id ) {
570 list( $object, $number, $name ) = static::get_widget_info( $id );
571 if ( ! $object ) {
572 return array();
573 }
574
575 $object->_set( $number );
576
577 $instances = $object->get_settings();
578 $instance = $instances[ $number ];
579
580 $args = array_merge(
581 $sidebar,
582 array(
583 'widget_id' => $id,
584 'widget_name' => $name,
585 )
586 );
587
588 /**
589 * Filters the settings for a particular widget instance.
590 *
591 * Returning false will effectively short-circuit display of the widget.
592 *
593 * @param array $instance The current widget instance's settings.
594 * @param WP_Widget $this The current widget instance.
595 * @param array $args An array of default widget arguments.
596 *
597 * @since 2.8.0
598 */
599 $instance = apply_filters( 'widget_display_callback', $instance, $object, $args );
600
601 if ( false === $instance ) {
602 return array();
603 }
604
605 return $instance;
606 }
607
608 /**
609 * Given a widget id returns an array containing information about the widget.
610 *
611 * @param string $widget_id Identifier of the widget.
612 *
613 * @return array Array containing the the widget object, the number, and the name.
614 * @since 5.7.0
615 */
616 private static function get_widget_info( $widget_id ) {
617 global $wp_registered_widgets;
618
619 if (
620 ! isset( $wp_registered_widgets[ $widget_id ]['callback'][0] ) ||
621 ! isset( $wp_registered_widgets[ $widget_id ]['params'][0]['number'] ) ||
622 ! isset( $wp_registered_widgets[ $widget_id ]['name'] ) ||
623 ! ( $wp_registered_widgets[ $widget_id ]['callback'][0] instanceof WP_Widget )
624 ) {
625 return array( null, null, null );
626 }
627
628 $object = $wp_registered_widgets[ $widget_id ]['callback'][0];
629 $number = $wp_registered_widgets[ $widget_id ]['params'][0]['number'];
630 $name = $wp_registered_widgets[ $widget_id ]['name'];
631
632 return array( $object, $number, $name );
633 }
634
635 }
636