[ Index ]

PHP Cross Reference of WordPress

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|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  }


Generated: Thu Nov 21 01:00:03 2024 Cross-referenced by PHPXref 0.7.1