Make WordPress Core

source: tags/6.0.2/src/wp-includes/rest-api/endpoints/class-wp-rest-widgets-controller.php

Last change on this file was 52362, checked in by SergeyBiryukov, 12 months ago

Docs: Capitalize "ID", when referring to a widget ID or sidebar ID, in a more consistent way.

Follow-up to [48104], [52357], [52361].

See #53399.

  • Property svn:eol-style set to native
File size: 25.7 KB
Line 
1<?php
2/**
3 * REST API: WP_REST_Widgets_Controller class
4 *
5 * @package WordPress
6 * @subpackage REST_API
7 * @since 5.8.0
8 */
9
10/**
11 * Core class to access widgets via the REST API.
12 *
13 * @since 5.8.0
14 *
15 * @see WP_REST_Controller
16 */
17class WP_REST_Widgets_Controller extends WP_REST_Controller {
18
19        /**
20         * Tracks whether {@see retrieve_widgets()} has been called in the current request.
21         *
22         * @since 5.9.0
23         * @var bool
24         */
25        protected $widgets_retrieved = false;
26
27        /**
28         * Whether the controller supports batching.
29         *
30         * @since 5.9.0
31         * @var array
32         */
33        protected $allow_batch = array( 'v1' => true );
34
35        /**
36         * Widgets controller constructor.
37         *
38         * @since 5.8.0
39         */
40        public function __construct() {
41                $this->namespace = 'wp/v2';
42                $this->rest_base = 'widgets';
43        }
44
45        /**
46         * Registers the widget routes for the controller.
47         *
48         * @since 5.8.0
49         */
50        public function register_routes() {
51                register_rest_route(
52                        $this->namespace,
53                        $this->rest_base,
54                        array(
55                                array(
56                                        'methods'             => WP_REST_Server::READABLE,
57                                        'callback'            => array( $this, 'get_items' ),
58                                        'permission_callback' => array( $this, 'get_items_permissions_check' ),
59                                        'args'                => $this->get_collection_params(),
60                                ),
61                                array(
62                                        'methods'             => WP_REST_Server::CREATABLE,
63                                        'callback'            => array( $this, 'create_item' ),
64                                        'permission_callback' => array( $this, 'create_item_permissions_check' ),
65                                        'args'                => $this->get_endpoint_args_for_item_schema(),
66                                ),
67                                'allow_batch' => $this->allow_batch,
68                                'schema'      => array( $this, 'get_public_item_schema' ),
69                        )
70                );
71
72                register_rest_route(
73                        $this->namespace,
74                        $this->rest_base . '/(?P<id>[\w\-]+)',
75                        array(
76                                array(
77                                        'methods'             => WP_REST_Server::READABLE,
78                                        'callback'            => array( $this, 'get_item' ),
79                                        'permission_callback' => array( $this, 'get_item_permissions_check' ),
80                                        'args'                => array(
81                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
82                                        ),
83                                ),
84                                array(
85                                        'methods'             => WP_REST_Server::EDITABLE,
86                                        'callback'            => array( $this, 'update_item' ),
87                                        'permission_callback' => array( $this, 'update_item_permissions_check' ),
88                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
89                                ),
90                                array(
91                                        'methods'             => WP_REST_Server::DELETABLE,
92                                        'callback'            => array( $this, 'delete_item' ),
93                                        'permission_callback' => array( $this, 'delete_item_permissions_check' ),
94                                        'args'                => array(
95                                                'force' => array(
96                                                        'description' => __( 'Whether to force removal of the widget, or move it to the inactive sidebar.' ),
97                                                        'type'        => 'boolean',
98                                                ),
99                                        ),
100                                ),
101                                'allow_batch' => $this->allow_batch,
102                                'schema'      => array( $this, 'get_public_item_schema' ),
103                        )
104                );
105        }
106
107        /**
108         * Checks if a given request has access to get widgets.
109         *
110         * @since 5.8.0
111         *
112         * @param WP_REST_Request $request Full details about the request.
113         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
114         */
115        public function get_items_permissions_check( $request ) {
116                $this->retrieve_widgets();
117                if ( isset( $request['sidebar'] ) && $this->check_read_sidebar_permission( $request['sidebar'] ) ) {
118                        return true;
119                }
120
121                foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
122                        if ( $this->check_read_sidebar_permission( $sidebar_id ) ) {
123                                return true;
124                        }
125                }
126
127                return $this->permissions_check( $request );
128        }
129
130        /**
131         * Retrieves a collection of widgets.
132         *
133         * @since 5.8.0
134         *
135         * @param WP_REST_Request $request Full details about the request.
136         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
137         */
138        public function get_items( $request ) {
139                $this->retrieve_widgets();
140
141                $prepared          = array();
142                $permissions_check = $this->permissions_check( $request );
143
144                foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
145                        if ( isset( $request['sidebar'] ) && $sidebar_id !== $request['sidebar'] ) {
146                                continue;
147                        }
148
149                        if ( is_wp_error( $permissions_check ) && ! $this->check_read_sidebar_permission( $sidebar_id ) ) {
150                                continue;
151                        }
152
153                        foreach ( $widget_ids as $widget_id ) {
154                                $response = $this->prepare_item_for_response( compact( 'sidebar_id', 'widget_id' ), $request );
155
156                                if ( ! is_wp_error( $response ) ) {
157                                        $prepared[] = $this->prepare_response_for_collection( $response );
158                                }
159                        }
160                }
161
162                return new WP_REST_Response( $prepared );
163        }
164
165        /**
166         * Checks if a given request has access to get a widget.
167         *
168         * @since 5.8.0
169         *
170         * @param WP_REST_Request $request Full details about the request.
171         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
172         */
173        public function get_item_permissions_check( $request ) {
174                $this->retrieve_widgets();
175
176                $widget_id  = $request['id'];
177                $sidebar_id = wp_find_widgets_sidebar( $widget_id );
178
179                if ( $sidebar_id && $this->check_read_sidebar_permission( $sidebar_id ) ) {
180                        return true;
181                }
182
183                return $this->permissions_check( $request );
184        }
185
186        /**
187         * Checks if a sidebar can be read publicly.
188         *
189         * @since 5.9.0
190         *
191         * @param string $sidebar_id The sidebar ID.
192         * @return bool Whether the sidebar can be read.
193         */
194        protected function check_read_sidebar_permission( $sidebar_id ) {
195                $sidebar = wp_get_sidebar( $sidebar_id );
196
197                return ! empty( $sidebar['show_in_rest'] );
198        }
199
200        /**
201         * Gets an individual widget.
202         *
203         * @since 5.8.0
204         *
205         * @param WP_REST_Request $request Full details about the request.
206         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
207         */
208        public function get_item( $request ) {
209                $this->retrieve_widgets();
210
211                $widget_id  = $request['id'];
212                $sidebar_id = wp_find_widgets_sidebar( $widget_id );
213
214                if ( is_null( $sidebar_id ) ) {
215                        return new WP_Error(
216                                'rest_widget_not_found',
217                                __( 'No widget was found with that id.' ),
218                                array( 'status' => 404 )
219                        );
220                }
221
222                return $this->prepare_item_for_response( compact( 'widget_id', 'sidebar_id' ), $request );
223        }
224
225        /**
226         * Checks if a given request has access to create widgets.
227         *
228         * @since 5.8.0
229         *
230         * @param WP_REST_Request $request Full details about the request.
231         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
232         */
233        public function create_item_permissions_check( $request ) {
234                return $this->permissions_check( $request );
235        }
236
237        /**
238         * Creates a widget.
239         *
240         * @since 5.8.0
241         *
242         * @param WP_REST_Request $request Full details about the request.
243         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
244         */
245        public function create_item( $request ) {
246                $sidebar_id = $request['sidebar'];
247
248                $widget_id = $this->save_widget( $request, $sidebar_id );
249
250                if ( is_wp_error( $widget_id ) ) {
251                        return $widget_id;
252                }
253
254                wp_assign_widget_to_sidebar( $widget_id, $sidebar_id );
255
256                $request['context'] = 'edit';
257
258                $response = $this->prepare_item_for_response( compact( 'sidebar_id', 'widget_id' ), $request );
259
260                if ( is_wp_error( $response ) ) {
261                        return $response;
262                }
263
264                $response->set_status( 201 );
265
266                return $response;
267        }
268
269        /**
270         * Checks if a given request has access to update widgets.
271         *
272         * @since 5.8.0
273         *
274         * @param WP_REST_Request $request Full details about the request.
275         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
276         */
277        public function update_item_permissions_check( $request ) {
278                return $this->permissions_check( $request );
279        }
280
281        /**
282         * Updates an existing widget.
283         *
284         * @since 5.8.0
285         *
286         * @global WP_Widget_Factory $wp_widget_factory
287         *
288         * @param WP_REST_Request $request Full details about the request.
289         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
290         */
291        public function update_item( $request ) {
292                global $wp_widget_factory;
293
294                /*
295                 * retrieve_widgets() contains logic to move "hidden" or "lost" widgets to the
296                 * wp_inactive_widgets sidebar based on the contents of the $sidebars_widgets global.
297                 *
298                 * When batch requests are processed, this global is not properly updated by previous
299                 * calls, resulting in widgets incorrectly being moved to the wp_inactive_widgets
300                 * sidebar.
301                 *
302                 * See https://core.trac.wordpress.org/ticket/53657.
303                 */
304                wp_get_sidebars_widgets();
305                $this->retrieve_widgets();
306
307                $widget_id  = $request['id'];
308                $sidebar_id = wp_find_widgets_sidebar( $widget_id );
309
310                // Allow sidebar to be unset or missing when widget is not a WP_Widget.
311                $parsed_id     = wp_parse_widget_id( $widget_id );
312                $widget_object = $wp_widget_factory->get_widget_object( $parsed_id['id_base'] );
313                if ( is_null( $sidebar_id ) && $widget_object ) {
314                        return new WP_Error(
315                                'rest_widget_not_found',
316                                __( 'No widget was found with that id.' ),
317                                array( 'status' => 404 )
318                        );
319                }
320
321                if (
322                        $request->has_param( 'instance' ) ||
323                        $request->has_param( 'form_data' )
324                ) {
325                        $maybe_error = $this->save_widget( $request, $sidebar_id );
326                        if ( is_wp_error( $maybe_error ) ) {
327                                return $maybe_error;
328                        }
329                }
330
331                if ( $request->has_param( 'sidebar' ) ) {
332                        if ( $sidebar_id !== $request['sidebar'] ) {
333                                $sidebar_id = $request['sidebar'];
334                                wp_assign_widget_to_sidebar( $widget_id, $sidebar_id );
335                        }
336                }
337
338                $request['context'] = 'edit';
339
340                return $this->prepare_item_for_response( compact( 'widget_id', 'sidebar_id' ), $request );
341        }
342
343        /**
344         * Checks if a given request has access to delete widgets.
345         *
346         * @since 5.8.0
347         *
348         * @param WP_REST_Request $request Full details about the request.
349         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
350         */
351        public function delete_item_permissions_check( $request ) {
352                return $this->permissions_check( $request );
353        }
354
355        /**
356         * Deletes a widget.
357         *
358         * @since 5.8.0
359         *
360         * @global WP_Widget_Factory $wp_widget_factory
361         * @global array             $wp_registered_widget_updates The registered widget update functions.
362         *
363         * @param WP_REST_Request $request Full details about the request.
364         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
365         */
366        public function delete_item( $request ) {
367                global $wp_widget_factory, $wp_registered_widget_updates;
368
369                /*
370                 * retrieve_widgets() contains logic to move "hidden" or "lost" widgets to the
371                 * wp_inactive_widgets sidebar based on the contents of the $sidebars_widgets global.
372                 *
373                 * When batch requests are processed, this global is not properly updated by previous
374                 * calls, resulting in widgets incorrectly being moved to the wp_inactive_widgets
375                 * sidebar.
376                 *
377                 * See https://core.trac.wordpress.org/ticket/53657.
378                 */
379                wp_get_sidebars_widgets();
380                $this->retrieve_widgets();
381
382                $widget_id  = $request['id'];
383                $sidebar_id = wp_find_widgets_sidebar( $widget_id );
384
385                if ( is_null( $sidebar_id ) ) {
386                        return new WP_Error(
387                                'rest_widget_not_found',
388                                __( 'No widget was found with that id.' ),
389                                array( 'status' => 404 )
390                        );
391                }
392
393                $request['context'] = 'edit';
394
395                if ( $request['force'] ) {
396                        $response = $this->prepare_item_for_response( compact( 'widget_id', 'sidebar_id' ), $request );
397
398                        $parsed_id = wp_parse_widget_id( $widget_id );
399                        $id_base   = $parsed_id['id_base'];
400
401                        $original_post    = $_POST;
402                        $original_request = $_REQUEST;
403
404                        $_POST    = array(
405                                'sidebar'         => $sidebar_id,
406                                "widget-$id_base" => array(),
407                                'the-widget-id'   => $widget_id,
408                                'delete_widget'   => '1',
409                        );
410                        $_REQUEST = $_POST;
411
412                        /** This action is documented in wp-admin/widgets-form.php */
413                        do_action( 'delete_widget', $widget_id, $sidebar_id, $id_base );
414
415                        $callback = $wp_registered_widget_updates[ $id_base ]['callback'];
416                        $params   = $wp_registered_widget_updates[ $id_base ]['params'];
417
418                        if ( is_callable( $callback ) ) {
419                                ob_start();
420                                call_user_func_array( $callback, $params );
421                                ob_end_clean();
422                        }
423
424                        $_POST    = $original_post;
425                        $_REQUEST = $original_request;
426
427                        $widget_object = $wp_widget_factory->get_widget_object( $id_base );
428
429                        if ( $widget_object ) {
430                                /*
431                                 * WP_Widget sets `updated = true` after an update to prevent more than one widget
432                                 * from being saved per request. This isn't what we want in the REST API, though,
433                                 * as we support batch requests.
434                                 */
435                                $widget_object->updated = false;
436                        }
437
438                        wp_assign_widget_to_sidebar( $widget_id, '' );
439
440                        $response->set_data(
441                                array(
442                                        'deleted'  => true,
443                                        'previous' => $response->get_data(),
444                                )
445                        );
446                } else {
447                        wp_assign_widget_to_sidebar( $widget_id, 'wp_inactive_widgets' );
448
449                        $response = $this->prepare_item_for_response(
450                                array(
451                                        'sidebar_id' => 'wp_inactive_widgets',
452                                        'widget_id'  => $widget_id,
453                                ),
454                                $request
455                        );
456                }
457
458                /**
459                 * Fires after a widget is deleted via the REST API.
460                 *
461                 * @since 5.8.0
462                 *
463                 * @param string                    $widget_id  ID of the widget marked for deletion.
464                 * @param string                    $sidebar_id ID of the sidebar the widget was deleted from.
465                 * @param WP_REST_Response|WP_Error $response   The response data, or WP_Error object on failure.
466                 * @param WP_REST_Request           $request    The request sent to the API.
467                 */
468                do_action( 'rest_delete_widget', $widget_id, $sidebar_id, $response, $request );
469
470                return $response;
471        }
472
473        /**
474         * Performs a permissions check for managing widgets.
475         *
476         * @since 5.8.0
477         *
478         * @param WP_REST_Request $request Full details about the request.
479         * @return true|WP_Error
480         */
481        protected function permissions_check( $request ) {
482                if ( ! current_user_can( 'edit_theme_options' ) ) {
483                        return new WP_Error(
484                                'rest_cannot_manage_widgets',
485                                __( 'Sorry, you are not allowed to manage widgets on this site.' ),
486                                array(
487                                        'status' => rest_authorization_required_code(),
488                                )
489                        );
490                }
491
492                return true;
493        }
494
495        /**
496         * Looks for "lost" widgets once per request.
497         *
498         * @since 5.9.0
499         *
500         * @see retrieve_widgets()
501         */
502        protected function retrieve_widgets() {
503                if ( ! $this->widgets_retrieved ) {
504                        retrieve_widgets();
505                        $this->widgets_retrieved = true;
506                }
507        }
508
509        /**
510         * Saves the widget in the request object.
511         *
512         * @since 5.8.0
513         *
514         * @global WP_Widget_Factory $wp_widget_factory
515         * @global array             $wp_registered_widget_updates The registered widget update functions.
516         *
517         * @param WP_REST_Request $request    Full details about the request.
518         * @param string          $sidebar_id ID of the sidebar the widget belongs to.
519         * @return string|WP_Error The saved widget ID.
520         */
521        protected function save_widget( $request, $sidebar_id ) {
522                global $wp_widget_factory, $wp_registered_widget_updates;
523
524                require_once ABSPATH . 'wp-admin/includes/widgets.php'; // For next_widget_id_number().
525
526                if ( isset( $request['id'] ) ) {
527                        // Saving an existing widget.
528                        $id            = $request['id'];
529                        $parsed_id     = wp_parse_widget_id( $id );
530                        $id_base       = $parsed_id['id_base'];
531                        $number        = isset( $parsed_id['number'] ) ? $parsed_id['number'] : null;
532                        $widget_object = $wp_widget_factory->get_widget_object( $id_base );
533                        $creating      = false;
534                } elseif ( $request['id_base'] ) {
535                        // Saving a new widget.
536                        $id_base       = $request['id_base'];
537                        $widget_object = $wp_widget_factory->get_widget_object( $id_base );
538                        $number        = $widget_object ? next_widget_id_number( $id_base ) : null;
539                        $id            = $widget_object ? $id_base . '-' . $number : $id_base;
540                        $creating      = true;
541                } else {
542                        return new WP_Error(
543                                'rest_invalid_widget',
544                                __( 'Widget type (id_base) is required.' ),
545                                array( 'status' => 400 )
546                        );
547                }
548
549                if ( ! isset( $wp_registered_widget_updates[ $id_base ] ) ) {
550                        return new WP_Error(
551                                'rest_invalid_widget',
552                                __( 'The provided widget type (id_base) cannot be updated.' ),
553                                array( 'status' => 400 )
554                        );
555                }
556
557                if ( isset( $request['instance'] ) ) {
558                        if ( ! $widget_object ) {
559                                return new WP_Error(
560                                        'rest_invalid_widget',
561                                        __( 'Cannot set instance on a widget that does not extend WP_Widget.' ),
562                                        array( 'status' => 400 )
563                                );
564                        }
565
566                        if ( isset( $request['instance']['raw'] ) ) {
567                                if ( empty( $widget_object->widget_options['show_instance_in_rest'] ) ) {
568                                        return new WP_Error(
569                                                'rest_invalid_widget',
570                                                __( 'Widget type does not support raw instances.' ),
571                                                array( 'status' => 400 )
572                                        );
573                                }
574                                $instance = $request['instance']['raw'];
575                        } elseif ( isset( $request['instance']['encoded'], $request['instance']['hash'] ) ) {
576                                $serialized_instance = base64_decode( $request['instance']['encoded'] );
577                                if ( ! hash_equals( wp_hash( $serialized_instance ), $request['instance']['hash'] ) ) {
578                                        return new WP_Error(
579                                                'rest_invalid_widget',
580                                                __( 'The provided instance is malformed.' ),
581                                                array( 'status' => 400 )
582                                        );
583                                }
584                                $instance = unserialize( $serialized_instance );
585                        } else {
586                                return new WP_Error(
587                                        'rest_invalid_widget',
588                                        __( 'The provided instance is invalid. Must contain raw OR encoded and hash.' ),
589                                        array( 'status' => 400 )
590                                );
591                        }
592
593                        $form_data = array(
594                                "widget-$id_base" => array(
595                                        $number => $instance,
596                                ),
597                                'sidebar'         => $sidebar_id,
598                        );
599                } elseif ( isset( $request['form_data'] ) ) {
600                        $form_data = $request['form_data'];
601                } else {
602                        $form_data = array();
603                }
604
605                $original_post    = $_POST;
606                $original_request = $_REQUEST;
607
608                foreach ( $form_data as $key => $value ) {
609                        $slashed_value    = wp_slash( $value );
610                        $_POST[ $key ]    = $slashed_value;
611                        $_REQUEST[ $key ] = $slashed_value;
612                }
613
614                $callback = $wp_registered_widget_updates[ $id_base ]['callback'];
615                $params   = $wp_registered_widget_updates[ $id_base ]['params'];
616
617                if ( is_callable( $callback ) ) {
618                        ob_start();
619                        call_user_func_array( $callback, $params );
620                        ob_end_clean();
621                }
622
623                $_POST    = $original_post;
624                $_REQUEST = $original_request;
625
626                if ( $widget_object ) {
627                        // Register any multi-widget that the update callback just created.
628                        $widget_object->_set( $number );
629                        $widget_object->_register_one( $number );
630
631                        /*
632                         * WP_Widget sets `updated = true` after an update to prevent more than one widget
633                         * from being saved per request. This isn't what we want in the REST API, though,
634                         * as we support batch requests.
635                         */
636                        $widget_object->updated = false;
637                }
638
639                /**
640                 * Fires after a widget is created or updated via the REST API.
641                 *
642                 * @since 5.8.0
643                 *
644                 * @param string          $id         ID of the widget being saved.
645                 * @param string          $sidebar_id ID of the sidebar containing the widget being saved.
646                 * @param WP_REST_Request $request    Request object.
647                 * @param bool            $creating   True when creating a widget, false when updating.
648                 */
649                do_action( 'rest_after_save_widget', $id, $sidebar_id, $request, $creating );
650
651                return $id;
652        }
653
654        /**
655         * Prepares the widget for the REST response.
656         *
657         * @since 5.8.0
658         *
659         * @global WP_Widget_Factory $wp_widget_factory
660         * @global array             $wp_registered_widgets The registered widgets.
661         *
662         * @param array           $item    An array containing a widget_id and sidebar_id.
663         * @param WP_REST_Request $request Request object.
664         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
665         */
666        public function prepare_item_for_response( $item, $request ) {
667                global $wp_widget_factory, $wp_registered_widgets;
668
669                $widget_id  = $item['widget_id'];
670                $sidebar_id = $item['sidebar_id'];
671
672                if ( ! isset( $wp_registered_widgets[ $widget_id ] ) ) {
673                        return new WP_Error(
674                                'rest_invalid_widget',
675                                __( 'The requested widget is invalid.' ),
676                                array( 'status' => 500 )
677                        );
678                }
679
680                $widget    = $wp_registered_widgets[ $widget_id ];
681                $parsed_id = wp_parse_widget_id( $widget_id );
682                $fields    = $this->get_fields_for_response( $request );
683
684                $prepared = array(
685                        'id'            => $widget_id,
686                        'id_base'       => $parsed_id['id_base'],
687                        'sidebar'       => $sidebar_id,
688                        'rendered'      => '',
689                        'rendered_form' => null,
690                        'instance'      => null,
691                );
692
693                if (
694                        rest_is_field_included( 'rendered', $fields ) &&
695                        'wp_inactive_widgets' !== $sidebar_id
696                ) {
697                        $prepared['rendered'] = trim( wp_render_widget( $widget_id, $sidebar_id ) );
698                }
699
700                if ( rest_is_field_included( 'rendered_form', $fields ) ) {
701                        $rendered_form = wp_render_widget_control( $widget_id );
702                        if ( ! is_null( $rendered_form ) ) {
703                                $prepared['rendered_form'] = trim( $rendered_form );
704                        }
705                }
706
707                if ( rest_is_field_included( 'instance', $fields ) ) {
708                        $widget_object = $wp_widget_factory->get_widget_object( $parsed_id['id_base'] );
709                        if ( $widget_object && isset( $parsed_id['number'] ) ) {
710                                $all_instances                   = $widget_object->get_settings();
711                                $instance                        = $all_instances[ $parsed_id['number'] ];
712                                $serialized_instance             = serialize( $instance );
713                                $prepared['instance']['encoded'] = base64_encode( $serialized_instance );
714                                $prepared['instance']['hash']    = wp_hash( $serialized_instance );
715
716                                if ( ! empty( $widget_object->widget_options['show_instance_in_rest'] ) ) {
717                                        // Use new stdClass so that JSON result is {} and not [].
718                                        $prepared['instance']['raw'] = empty( $instance ) ? new stdClass : $instance;
719                                }
720                        }
721                }
722
723                $context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
724                $prepared = $this->add_additional_fields_to_object( $prepared, $request );
725                $prepared = $this->filter_response_by_context( $prepared, $context );
726
727                $response = rest_ensure_response( $prepared );
728
729                $response->add_links( $this->prepare_links( $prepared ) );
730
731                /**
732                 * Filters the REST API response for a widget.
733                 *
734                 * @since 5.8.0
735                 *
736                 * @param WP_REST_Response|WP_Error $response The response object, or WP_Error object on failure.
737                 * @param array                     $widget   The registered widget data.
738                 * @param WP_REST_Request           $request  Request used to generate the response.
739                 */
740                return apply_filters( 'rest_prepare_widget', $response, $widget, $request );
741        }
742
743        /**
744         * Prepares links for the widget.
745         *
746         * @since 5.8.0
747         *
748         * @param array $prepared Widget.
749         * @return array Links for the given widget.
750         */
751        protected function prepare_links( $prepared ) {
752                $id_base = ! empty( $prepared['id_base'] ) ? $prepared['id_base'] : $prepared['id'];
753
754                return array(
755                        'self'                      => array(
756                                'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $prepared['id'] ) ),
757                        ),
758                        'collection'                => array(
759                                'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
760                        ),
761                        'about'                     => array(
762                                'href'       => rest_url( sprintf( 'wp/v2/widget-types/%s', $id_base ) ),
763                                'embeddable' => true,
764                        ),
765                        'https://api.w.org/sidebar' => array(
766                                'href' => rest_url( sprintf( 'wp/v2/sidebars/%s/', $prepared['sidebar'] ) ),
767                        ),
768                );
769        }
770
771        /**
772         * Gets the list of collection params.
773         *
774         * @since 5.8.0
775         *
776         * @return array[]
777         */
778        public function get_collection_params() {
779                return array(
780                        'context' => $this->get_context_param( array( 'default' => 'view' ) ),
781                        'sidebar' => array(
782                                'description' => __( 'The sidebar to return widgets for.' ),
783                                'type'        => 'string',
784                        ),
785                );
786        }
787
788        /**
789         * Retrieves the widget's schema, conforming to JSON Schema.
790         *
791         * @since 5.8.0
792         *
793         * @return array Item schema data.
794         */
795        public function get_item_schema() {
796                if ( $this->schema ) {
797                        return $this->add_additional_fields_schema( $this->schema );
798                }
799
800                $this->schema = array(
801                        '$schema'    => 'http://json-schema.org/draft-04/schema#',
802                        'title'      => 'widget',
803                        'type'       => 'object',
804                        'properties' => array(
805                                'id'            => array(
806                                        'description' => __( 'Unique identifier for the widget.' ),
807                                        'type'        => 'string',
808                                        'context'     => array( 'view', 'edit', 'embed' ),
809                                ),
810                                'id_base'       => array(
811                                        'description' => __( 'The type of the widget. Corresponds to ID in widget-types endpoint.' ),
812                                        'type'        => 'string',
813                                        'context'     => array( 'view', 'edit', 'embed' ),
814                                ),
815                                'sidebar'       => array(
816                                        'description' => __( 'The sidebar the widget belongs to.' ),
817                                        'type'        => 'string',
818                                        'default'     => 'wp_inactive_widgets',
819                                        'required'    => true,
820                                        'context'     => array( 'view', 'edit', 'embed' ),
821                                ),
822                                'rendered'      => array(
823                                        'description' => __( 'HTML representation of the widget.' ),
824                                        'type'        => 'string',
825                                        'context'     => array( 'view', 'edit', 'embed' ),
826                                        'readonly'    => true,
827                                ),
828                                'rendered_form' => array(
829                                        'description' => __( 'HTML representation of the widget admin form.' ),
830                                        'type'        => 'string',
831                                        'context'     => array( 'edit' ),
832                                        'readonly'    => true,
833                                ),
834                                'instance'      => array(
835                                        'description' => __( 'Instance settings of the widget, if supported.' ),
836                                        'type'        => 'object',
837                                        'context'     => array( 'edit' ),
838                                        'default'     => null,
839                                        'properties'  => array(
840                                                'encoded' => array(
841                                                        'description' => __( 'Base64 encoded representation of the instance settings.' ),
842                                                        'type'        => 'string',
843                                                        'context'     => array( 'edit' ),
844                                                ),
845                                                'hash'    => array(
846                                                        'description' => __( 'Cryptographic hash of the instance settings.' ),
847                                                        'type'        => 'string',
848                                                        'context'     => array( 'edit' ),
849                                                ),
850                                                'raw'     => array(
851                                                        'description' => __( 'Unencoded instance settings, if supported.' ),
852                                                        'type'        => 'object',
853                                                        'context'     => array( 'edit' ),
854                                                ),
855                                        ),
856                                ),
857                                'form_data'     => array(
858                                        'description' => __( 'URL-encoded form data from the widget admin form. Used to update a widget that does not support instance. Write only.' ),
859                                        'type'        => 'string',
860                                        'context'     => array(),
861                                        'arg_options' => array(
862                                                'sanitize_callback' => static function( $string ) {
863                                                        $array = array();
864                                                        wp_parse_str( $string, $array );
865                                                        return $array;
866                                                },
867                                        ),
868                                ),
869                        ),
870                );
871
872                return $this->add_additional_fields_schema( $this->schema );
873        }
874}
Note: See TracBrowser for help on using the repository browser.