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