[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-widgets-controller.php (source)

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


Generated : Sun Apr 27 08:20:01 2025 Cross-referenced by PHPXref