class-wp-rest-widgets-controller.php (24642B)
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 }