mirror of
https://github.com/WordPress/WordPress.git
synced 2024-12-22 17:18:32 +01:00
REST API: Add widget endpoints
Adds the sidebars, widgets and widget-types REST API endpoints from the Gutenberg plugin. Fixes #41683. Props TimothyBlynJacobs. Built from https://develop.svn.wordpress.org/trunk@50993 git-svn-id: http://core.svn.wordpress.org/trunk@50602 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
2b7ad8ad4f
commit
2a4e1e0c04
@ -102,4 +102,22 @@ class WP_Widget_Factory {
|
||||
$this->widgets[ $key ]->_register();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered WP_Widget object for the given widget type.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $id_base Widget type ID.
|
||||
* @return WP_Widget|null
|
||||
*/
|
||||
public function get_widget_object( $id_base ) {
|
||||
foreach ( $this->widgets as $widget_object ) {
|
||||
if ( $widget_object->id_base === $id_base ) {
|
||||
return $widget_object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -63,3 +63,6 @@ require_once ABSPATH . WPINC . '/widgets/class-wp-nav-menu-widget.php';
|
||||
|
||||
/** WP_Widget_Custom_HTML class */
|
||||
require_once ABSPATH . WPINC . '/widgets/class-wp-widget-custom-html.php';
|
||||
|
||||
/** WP_Widget_Block class */
|
||||
require_once ABSPATH . WPINC . '/widgets/class-wp-widget-block.php';
|
||||
|
@ -313,6 +313,18 @@ function create_initial_rest_routes() {
|
||||
$controller = new WP_REST_Plugins_Controller();
|
||||
$controller->register_routes();
|
||||
|
||||
// Sidebars.
|
||||
$controller = new WP_REST_Sidebars_Controller();
|
||||
$controller->register_routes();
|
||||
|
||||
// Widget Types.
|
||||
$controller = new WP_REST_Widget_Types_Controller();
|
||||
$controller->register_routes();
|
||||
|
||||
// Widgets.
|
||||
$controller = new WP_REST_Widgets_Controller();
|
||||
$controller->register_routes();
|
||||
|
||||
// Block Directory.
|
||||
$controller = new WP_REST_Block_Directory_Controller();
|
||||
$controller->register_routes();
|
||||
|
@ -0,0 +1,459 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Sidebars_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.8.0
|
||||
*
|
||||
* Copyright (C) 2015 Martin Pettersson
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Martin Pettersson <martin_pettersson@outlook.com>
|
||||
* @copyright 2015 Martin Pettersson
|
||||
* @license GPLv2
|
||||
* @link https://github.com/martin-pettersson/wp-rest-api-sidebars
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to manage a site's sidebars.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Sidebars_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Sidebars controller constructor.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'sidebars';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the controllers routes.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<id>[\w-]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The id of a registered sidebar' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to get sidebars.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
return $this->do_permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of sidebars (active or inactive).
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$data = array();
|
||||
foreach ( (array) wp_get_sidebars_widgets() as $id => $widgets ) {
|
||||
$sidebar = $this->get_sidebar( $id );
|
||||
|
||||
if ( ! $sidebar ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[] = $this->prepare_response_for_collection(
|
||||
$this->prepare_item_for_response( $sidebar, $request )
|
||||
);
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to get a single sidebar.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return $this->do_permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves one sidebar from the collection.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$sidebar = $this->get_sidebar( $request['id'] );
|
||||
|
||||
if ( ! $sidebar ) {
|
||||
return new WP_Error( 'rest_sidebar_not_found', __( 'No sidebar exists with that id.' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return $this->prepare_item_for_response( $sidebar, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to update sidebars.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
return $this->do_permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a sidebar.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
if ( isset( $request['widgets'] ) ) {
|
||||
$sidebars = wp_get_sidebars_widgets();
|
||||
|
||||
foreach ( $sidebars as $sidebar_id => $widgets ) {
|
||||
foreach ( $widgets as $i => $widget_id ) {
|
||||
// This automatically removes the passed widget ids from any other sidebars in use.
|
||||
if ( $sidebar_id !== $request['id'] && in_array( $widget_id, $request['widgets'], true ) ) {
|
||||
unset( $sidebars[ $sidebar_id ][ $i ] );
|
||||
}
|
||||
|
||||
// This automatically removes omitted widget ids to the inactive sidebar.
|
||||
if ( $sidebar_id === $request['id'] && ! in_array( $widget_id, $request['widgets'], true ) ) {
|
||||
$sidebars['wp_inactive_widgets'][] = $widget_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sidebars[ $request['id'] ] = $request['widgets'];
|
||||
|
||||
wp_set_sidebars_widgets( $sidebars );
|
||||
}
|
||||
|
||||
$request['context'] = 'edit';
|
||||
|
||||
$sidebar = $this->get_sidebar( $request['id'] );
|
||||
|
||||
return $this->prepare_item_for_response( $sidebar, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has permissions to make the request.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
protected function do_permissions_check() {
|
||||
// Verify if the current user has edit_theme_options capability.
|
||||
// This capability is required to access the widgets screen.
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_manage_widgets',
|
||||
__( 'Sorry, you are not allowed to manage widgets on this site.' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the registered sidebar with the given id.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @global array $wp_registered_sidebars The registered sidebars.
|
||||
*
|
||||
* @param string|int $id ID of the sidebar.
|
||||
* @return array|null The discovered sidebar, or null if it is not registered.
|
||||
*/
|
||||
protected function get_sidebar( $id ) {
|
||||
global $wp_registered_sidebars;
|
||||
|
||||
foreach ( (array) $wp_registered_sidebars as $sidebar ) {
|
||||
if ( $sidebar['id'] === $id ) {
|
||||
return $sidebar;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'wp_inactive_widgets' === $id ) {
|
||||
return array(
|
||||
'id' => 'wp_inactive_widgets',
|
||||
'name' => __( 'Inactive widgets' ),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a single sidebar output for response.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @global array $wp_registered_sidebars The registered sidebars.
|
||||
* @global array $wp_registered_widgets The registered widgets.
|
||||
*
|
||||
* @param array $raw_sidebar Sidebar instance.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
*
|
||||
* @return WP_REST_Response Prepared response object.
|
||||
*/
|
||||
public function prepare_item_for_response( $raw_sidebar, $request ) {
|
||||
global $wp_registered_sidebars, $wp_registered_widgets;
|
||||
|
||||
$id = $raw_sidebar['id'];
|
||||
$sidebar = array( 'id' => $id );
|
||||
|
||||
if ( isset( $wp_registered_sidebars[ $id ] ) ) {
|
||||
$registered_sidebar = $wp_registered_sidebars[ $id ];
|
||||
|
||||
$sidebar['status'] = 'active';
|
||||
$sidebar['name'] = isset( $registered_sidebar['name'] ) ? $registered_sidebar['name'] : '';
|
||||
$sidebar['description'] = isset( $registered_sidebar['description'] ) ? $registered_sidebar['description'] : '';
|
||||
$sidebar['class'] = isset( $registered_sidebar['class'] ) ? $registered_sidebar['class'] : '';
|
||||
$sidebar['before_widget'] = isset( $registered_sidebar['before_widget'] ) ? $registered_sidebar['before_widget'] : '';
|
||||
$sidebar['after_widget'] = isset( $registered_sidebar['after_widget'] ) ? $registered_sidebar['after_widget'] : '';
|
||||
$sidebar['before_title'] = isset( $registered_sidebar['before_title'] ) ? $registered_sidebar['before_title'] : '';
|
||||
$sidebar['after_title'] = isset( $registered_sidebar['after_title'] ) ? $registered_sidebar['after_title'] : '';
|
||||
} else {
|
||||
$sidebar['status'] = 'inactive';
|
||||
$sidebar['name'] = $raw_sidebar['name'];
|
||||
$sidebar['description'] = '';
|
||||
$sidebar['class'] = '';
|
||||
}
|
||||
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
if ( rest_is_field_included( 'widgets', $fields ) ) {
|
||||
$sidebars = wp_get_sidebars_widgets();
|
||||
$widgets = array_filter(
|
||||
isset( $sidebars[ $sidebar['id'] ] ) ? $sidebars[ $sidebar['id'] ] : array(),
|
||||
static function ( $widget_id ) use ( $wp_registered_widgets ) {
|
||||
return isset( $wp_registered_widgets[ $widget_id ] );
|
||||
}
|
||||
);
|
||||
|
||||
$sidebar['widgets'] = $widgets;
|
||||
}
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
$data = array();
|
||||
foreach ( $schema['properties'] as $property_id => $property ) {
|
||||
if ( isset( $sidebar[ $property_id ] ) && true === rest_validate_value_from_schema( $sidebar[ $property_id ], $property ) ) {
|
||||
$data[ $property_id ] = $sidebar[ $property_id ];
|
||||
} elseif ( isset( $property['default'] ) ) {
|
||||
$data[ $property_id ] = $property['default'];
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $sidebar ) );
|
||||
|
||||
/**
|
||||
* Filters the REST API response for a sidebar.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param array $raw_sidebar The raw sidebar data.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_sidebar', $response, $raw_sidebar, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the sidebar.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $sidebar Sidebar.
|
||||
*
|
||||
* @return array Links for the given widget.
|
||||
*/
|
||||
protected function prepare_links( $sidebar ) {
|
||||
return array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $sidebar['id'] ) ),
|
||||
),
|
||||
'https://api.w.org/widget' => array(
|
||||
'href' => add_query_arg( 'sidebar', $sidebar['id'], rest_url( '/wp/v2/widgets' ) ),
|
||||
'embeddable' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block type' schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'sidebar',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'ID of sidebar.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Unique name identifying the sidebar.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'Description of sidebar.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'class' => array(
|
||||
'description' => __( 'Extra CSS class to assign to the sidebar in the Widgets interface.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'before_widget' => array(
|
||||
'description' => __( 'HTML content to prepend to each widget\'s HTML output when assigned to this sidebar. Default is an opening list item element.' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'after_widget' => array(
|
||||
'description' => __( 'HTML content to append to each widget\'s HTML output when assigned to this sidebar. Default is a closing list item element.' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'before_title' => array(
|
||||
'description' => __( 'HTML content to prepend to the sidebar title when displayed. Default is an opening h2 element.' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'after_title' => array(
|
||||
'description' => __( 'HTML content to append to the sidebar title when displayed. Default is a closing h2 element.' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => __( 'Status of sidebar.' ),
|
||||
'type' => 'string',
|
||||
'enum' => array( 'active', 'inactive' ),
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'widgets' => array(
|
||||
'description' => __( 'Nested widgets.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => array( 'object', 'string' ),
|
||||
),
|
||||
'default' => array(),
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
@ -0,0 +1,551 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Widget_Types_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class to access widget types via the REST API.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Widget_Types_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'widget-types';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the widget type routes.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see register_rest_route()
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<id>[a-zA-Z0-9_-]+)',
|
||||
array(
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The widget type id.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/(?P<id>[a-zA-Z0-9_-]+)/encode',
|
||||
array(
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'The widget type id.' ),
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
'instance' => array(
|
||||
'description' => __( 'Current instance settings of the widget.' ),
|
||||
'type' => 'object',
|
||||
),
|
||||
'form_data' => array(
|
||||
'description' => __( 'Serialized widget form data to encode into instance settings.' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => function( $string ) {
|
||||
$array = array();
|
||||
wp_parse_str( $string, $array );
|
||||
return $array;
|
||||
},
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'callback' => array( $this, 'encode_form_data' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given request has permission to read widget types.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
return $this->check_read_permission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of all widget types.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$data = array();
|
||||
foreach ( $this->get_widgets() as $widget ) {
|
||||
$widget_type = $this->prepare_item_for_response( $widget, $request );
|
||||
$data[] = $this->prepare_response_for_collection( $widget_type );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to read a widget type.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$check = $this->check_read_permission();
|
||||
if ( is_wp_error( $check ) ) {
|
||||
return $check;
|
||||
}
|
||||
$widget_id = $request['id'];
|
||||
$widget_type = $this->get_widget( $widget_id );
|
||||
if ( is_wp_error( $widget_type ) ) {
|
||||
return $widget_type;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user can read widget types.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return WP_Error|bool True if the widget type is visible, WP_Error otherwise.
|
||||
*/
|
||||
protected function check_read_permission() {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_manage_widgets',
|
||||
__( 'Sorry, you are not allowed to manage widgets on this site.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the details about the requested widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $id The widget type id.
|
||||
* @return array|WP_Error The array of widget data if the name is valid, WP_Error otherwise.
|
||||
*/
|
||||
public function get_widget( $id ) {
|
||||
foreach ( $this->get_widgets() as $widget ) {
|
||||
if ( $id === $widget['id'] ) {
|
||||
return $widget;
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error( 'rest_widget_type_invalid', __( 'Invalid widget type.' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize array of widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @global array $wp_registered_widgets The list of registered widgets.
|
||||
*
|
||||
* @return array Array of widgets.
|
||||
*/
|
||||
protected function get_widgets() {
|
||||
global $wp_widget_factory, $wp_registered_widgets;
|
||||
|
||||
$widgets = array();
|
||||
|
||||
foreach ( $wp_registered_widgets as $widget ) {
|
||||
$parsed_id = wp_parse_widget_id( $widget['id'] );
|
||||
$widget_object = $wp_widget_factory->get_widget_object( $parsed_id['id_base'] );
|
||||
|
||||
$widget['id'] = $parsed_id['id_base'];
|
||||
$widget['is_multi'] = (bool) $widget_object;
|
||||
|
||||
unset( $widget['callback'] );
|
||||
|
||||
$classname = '';
|
||||
foreach ( (array) $widget['classname'] as $cn ) {
|
||||
if ( is_string( $cn ) ) {
|
||||
$classname .= '_' . $cn;
|
||||
} elseif ( is_object( $cn ) ) {
|
||||
$classname .= '_' . get_class( $cn );
|
||||
}
|
||||
}
|
||||
$widget['classname'] = ltrim( $classname, '_' );
|
||||
|
||||
$widgets[] = $widget;
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single widget type from the collection.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$widget_id = $request['id'];
|
||||
$widget_type = $this->get_widget( $widget_id );
|
||||
if ( is_wp_error( $widget_type ) ) {
|
||||
return $widget_type;
|
||||
}
|
||||
$data = $this->prepare_item_for_response( $widget_type, $request );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a widget type object for serialization.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $widget_type Widget type data.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response Widget type data.
|
||||
*/
|
||||
public function prepare_item_for_response( $widget_type, $request ) {
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
$data = array(
|
||||
'id' => $widget_type['id'],
|
||||
);
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
$extra_fields = array(
|
||||
'name',
|
||||
'description',
|
||||
'is_multi',
|
||||
'classname',
|
||||
'widget_class',
|
||||
'option_name',
|
||||
'customize_selective_refresh',
|
||||
);
|
||||
|
||||
foreach ( $extra_fields as $extra_field ) {
|
||||
if ( ! rest_is_field_included( $extra_field, $fields ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $widget_type[ $extra_field ] ) ) {
|
||||
$field = $widget_type[ $extra_field ];
|
||||
} elseif ( array_key_exists( 'default', $schema['properties'][ $extra_field ] ) ) {
|
||||
$field = $schema['properties'][ $extra_field ]['default'];
|
||||
} else {
|
||||
$field = '';
|
||||
}
|
||||
|
||||
$data[ $extra_field ] = rest_sanitize_value_from_schema( $field, $schema['properties'][ $extra_field ] );
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $widget_type ) );
|
||||
|
||||
/**
|
||||
* Filters the REST API response for a widget type.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param array $widget_type The array of widget data.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_widget_type', $response, $widget_type, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the widget type.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $widget_type Widget type data.
|
||||
* @return array Links for the given widget type.
|
||||
*/
|
||||
protected function prepare_links( $widget_type ) {
|
||||
return array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $widget_type['id'] ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the widget type's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'widget-type',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique slug identifying the widget type.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Human-readable name identifying the widget type.' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'Description of the widget.' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'is_multi' => array(
|
||||
'description' => __( 'Whether the widget supports multiple instances' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'classname' => array(
|
||||
'description' => __( 'Class name' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
'context' => array( 'embed', 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* An RPC-style endpoint which can be used by clients to turn user input in
|
||||
* a widget admin form into an encoded instance object.
|
||||
*
|
||||
* Accepts:
|
||||
*
|
||||
* - id: A widget type ID.
|
||||
* - instance: A widget's encoded instance object. Optional.
|
||||
* - form_data: Form data from submitting a widget's admin form. Optional.
|
||||
*
|
||||
* Returns:
|
||||
* - instance: The encoded instance object after updating the widget with
|
||||
* the given form data.
|
||||
* - form: The widget's admin form after updating the widget with the
|
||||
* given form data.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function encode_form_data( $request ) {
|
||||
global $wp_widget_factory;
|
||||
|
||||
$id = $request['id'];
|
||||
$widget_object = $wp_widget_factory->get_widget_object( $id );
|
||||
|
||||
if ( ! $widget_object ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'Cannot preview a widget that does not extend WP_Widget.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
// Set the widget's number so that the id attributes in the HTML that we
|
||||
// return are predictable.
|
||||
if ( isset( $request['number'] ) && is_numeric( $request['number'] ) ) {
|
||||
$widget_object->_set( (int) $request['number'] );
|
||||
} else {
|
||||
$widget_object->_set( -1 );
|
||||
}
|
||||
|
||||
if ( isset( $request['instance']['encoded'], $request['instance']['hash'] ) ) {
|
||||
$serialized_instance = base64_decode( $request['instance']['encoded'] );
|
||||
if ( ! hash_equals( wp_hash( $serialized_instance ), $request['instance']['hash'] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'The provided instance is malformed.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$instance = unserialize( $serialized_instance );
|
||||
} else {
|
||||
$instance = array();
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $request['form_data'][ "widget-$id" ] ) &&
|
||||
is_array( $request['form_data'][ "widget-$id" ] )
|
||||
) {
|
||||
$new_instance = array_values( $request['form_data'][ "widget-$id" ] )[0];
|
||||
$old_instance = $instance;
|
||||
|
||||
$instance = $widget_object->update( $new_instance, $old_instance );
|
||||
|
||||
/** This filter is documented in wp-includes/class-wp-widget.php */
|
||||
$instance = apply_filters(
|
||||
'widget_update_callback',
|
||||
$instance,
|
||||
$new_instance,
|
||||
$old_instance,
|
||||
$widget_object
|
||||
);
|
||||
}
|
||||
|
||||
$serialized_instance = serialize( $instance );
|
||||
|
||||
$response = array(
|
||||
'form' => trim(
|
||||
$this->get_widget_form(
|
||||
$widget_object,
|
||||
$instance
|
||||
)
|
||||
),
|
||||
'preview' => trim(
|
||||
$this->get_widget_preview(
|
||||
$widget_object,
|
||||
$instance
|
||||
)
|
||||
),
|
||||
'instance' => array(
|
||||
'encoded' => base64_encode( $serialized_instance ),
|
||||
'hash' => wp_hash( $serialized_instance ),
|
||||
),
|
||||
);
|
||||
|
||||
if ( ! empty( $widget_object->widget_options['show_instance_in_rest'] ) ) {
|
||||
// Use new stdClass so that JSON result is {} and not [].
|
||||
$response['instance']['raw'] = empty( $instance ) ? new stdClass : $instance;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output of WP_Widget::widget() when called with the provided
|
||||
* instance. Used by encode_form_data() to preview a widget.
|
||||
|
||||
* @param WP_Widget $widget_object Widget object to call widget() on.
|
||||
* @param array $instance Widget instance settings.
|
||||
* @return string
|
||||
*/
|
||||
private function get_widget_preview( $widget_object, $instance ) {
|
||||
ob_start();
|
||||
the_widget( get_class( $widget_object ), $instance );
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output of WP_Widget::form() when called with the provided
|
||||
* instance. Used by encode_form_data() to preview a widget's form.
|
||||
*
|
||||
* @param WP_Widget $widget_object Widget object to call widget() on.
|
||||
* @param array $instance Widget instance settings.
|
||||
* @return string
|
||||
*/
|
||||
private function get_widget_form( $widget_object, $instance ) {
|
||||
ob_start();
|
||||
|
||||
/** This filter is documented in wp-includes/class-wp-widget.php */
|
||||
$instance = apply_filters(
|
||||
'widget_form_callback',
|
||||
$instance,
|
||||
$widget_object
|
||||
);
|
||||
|
||||
if ( false !== $instance ) {
|
||||
$return = $widget_object->form( $instance );
|
||||
|
||||
/** This filter is documented in wp-includes/class-wp-widget.php */
|
||||
do_action_ref_array(
|
||||
'in_widget_form',
|
||||
array( &$widget_object, &$return, $instance )
|
||||
);
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the query params for collections.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return array Collection parameters.
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,693 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API: WP_REST_Widgets_Controller class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage REST_API
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class to access widgets via the REST API.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see WP_REST_Controller
|
||||
*/
|
||||
class WP_REST_Widgets_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Widgets controller constructor.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->namespace = 'wp/v2';
|
||||
$this->rest_base = 'widgets';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the widget routes for the controller.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
$this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema(),
|
||||
),
|
||||
'allow_batch' => array( 'v1' => true ),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
$this->rest_base . '/(?P<id>[\w\-]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'description' => __( 'Whether to force removal of the widget, or move it to the inactive sidebar.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
),
|
||||
'allow_batch' => array( 'v1' => true ),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to get widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
return $this->permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a collection of widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$prepared = array();
|
||||
|
||||
foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
|
||||
if ( isset( $request['sidebar'] ) && $sidebar_id !== $request['sidebar'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $widget_ids as $widget_id ) {
|
||||
$response = $this->prepare_item_for_response( compact( 'sidebar_id', 'widget_id' ), $request );
|
||||
|
||||
if ( ! is_wp_error( $response ) ) {
|
||||
$prepared[] = $this->prepare_response_for_collection( $response );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $prepared );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to get a widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return $this->permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an individual widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$widget_id = $request['id'];
|
||||
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
|
||||
|
||||
if ( is_null( $sidebar_id ) ) {
|
||||
return new WP_Error(
|
||||
'rest_widget_not_found',
|
||||
__( 'No widget was found with that id.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->prepare_item_for_response( compact( 'widget_id', 'sidebar_id' ), $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to create widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
return $this->permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
$sidebar_id = $request['sidebar'];
|
||||
|
||||
$widget_id = $this->save_widget( $request );
|
||||
|
||||
if ( is_wp_error( $widget_id ) ) {
|
||||
return $widget_id;
|
||||
}
|
||||
|
||||
wp_assign_widget_to_sidebar( $widget_id, $sidebar_id );
|
||||
|
||||
$request['context'] = 'edit';
|
||||
|
||||
$response = $this->prepare_item_for_response( compact( 'sidebar_id', 'widget_id' ), $request );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response->set_status( 201 );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to update widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
return $this->permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
global $wp_widget_factory;
|
||||
|
||||
$widget_id = $request['id'];
|
||||
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
|
||||
|
||||
// Allow sidebar to be unset or missing when widget is not a WP_Widget.
|
||||
$parsed_id = wp_parse_widget_id( $widget_id );
|
||||
$widget_object = $wp_widget_factory->get_widget_object( $parsed_id['id_base'] );
|
||||
if ( is_null( $sidebar_id ) && $widget_object ) {
|
||||
return new WP_Error(
|
||||
'rest_widget_not_found',
|
||||
__( 'No widget was found with that id.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$request->has_param( 'instance' ) ||
|
||||
$request->has_param( 'form_data' )
|
||||
) {
|
||||
$maybe_error = $this->save_widget( $request );
|
||||
if ( is_wp_error( $maybe_error ) ) {
|
||||
return $maybe_error;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $request->has_param( 'sidebar' ) ) {
|
||||
if ( $sidebar_id !== $request['sidebar'] ) {
|
||||
$sidebar_id = $request['sidebar'];
|
||||
wp_assign_widget_to_sidebar( $widget_id, $sidebar_id );
|
||||
}
|
||||
}
|
||||
|
||||
$request['context'] = 'edit';
|
||||
|
||||
return $this->prepare_item_for_response( compact( 'widget_id', 'sidebar_id' ), $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given request has access to delete widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
return $this->permissions_check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
$widget_id = $request['id'];
|
||||
$sidebar_id = wp_find_widgets_sidebar( $widget_id );
|
||||
|
||||
if ( is_null( $sidebar_id ) ) {
|
||||
return new WP_Error(
|
||||
'rest_widget_not_found',
|
||||
__( 'No widget was found with that id.' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$request['context'] = 'edit';
|
||||
|
||||
if ( $request['force'] ) {
|
||||
$prepared = $this->prepare_item_for_response( compact( 'widget_id', 'sidebar_id' ), $request );
|
||||
wp_assign_widget_to_sidebar( $widget_id, '' );
|
||||
$prepared->set_data(
|
||||
array(
|
||||
'deleted' => true,
|
||||
'previous' => $prepared->get_data(),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_assign_widget_to_sidebar( $widget_id, 'wp_inactive_widgets' );
|
||||
$prepared = $this->prepare_item_for_response(
|
||||
array(
|
||||
'sidebar_id' => 'wp_inactive_widgets',
|
||||
'widget_id' => $widget_id,
|
||||
),
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
return $prepared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a permissions check for managing widgets.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return true|WP_Error
|
||||
*/
|
||||
protected function permissions_check() {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_cannot_manage_widgets',
|
||||
__( 'Sorry, you are not allowed to manage widgets on this site.' ),
|
||||
array(
|
||||
'status' => rest_authorization_required_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the widget in the request object.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
*
|
||||
* @return string|WP_Error The saved widget ID.
|
||||
*/
|
||||
protected function save_widget( $request ) {
|
||||
global $wp_widget_factory, $wp_registered_widget_updates;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/widgets.php'; // For next_widget_id_number().
|
||||
|
||||
if ( isset( $request['id'] ) ) {
|
||||
// Saving an existing widget.
|
||||
$id = $request['id'];
|
||||
$parsed_id = wp_parse_widget_id( $id );
|
||||
$id_base = $parsed_id['id_base'];
|
||||
$number = isset( $parsed_id['number'] ) ? $parsed_id['number'] : null;
|
||||
$widget_object = $wp_widget_factory->get_widget_object( $id_base );
|
||||
} elseif ( $request['id_base'] ) {
|
||||
// Saving a new widget.
|
||||
$id_base = $request['id_base'];
|
||||
$widget_object = $wp_widget_factory->get_widget_object( $id_base );
|
||||
$number = $widget_object ? next_widget_id_number( $id_base ) : null;
|
||||
$id = $widget_object ? $id_base . '-' . $number : $id_base;
|
||||
} else {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'Widget type (id_base) is required.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! isset( $wp_registered_widget_updates[ $id_base ] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'The provided widget type (id_base) cannot be updated.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $request['instance'] ) ) {
|
||||
if ( ! $widget_object ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'Cannot set instance on a widget that does not extend WP_Widget.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $request['instance']['raw'] ) ) {
|
||||
if ( empty( $widget_object->widget_options['show_instance_in_rest'] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'Widget type does not support raw instances.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$instance = $request['instance']['raw'];
|
||||
} elseif ( isset( $request['instance']['encoded'], $request['instance']['hash'] ) ) {
|
||||
$serialized_instance = base64_decode( $request['instance']['encoded'] );
|
||||
if ( ! hash_equals( wp_hash( $serialized_instance ), $request['instance']['hash'] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'The provided instance is malformed.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$instance = unserialize( $serialized_instance );
|
||||
} else {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'The provided instance is invalid. Must contain raw OR encoded and hash.' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$form_data = array(
|
||||
"widget-$id_base" => array(
|
||||
$number => $instance,
|
||||
),
|
||||
);
|
||||
} elseif ( isset( $request['form_data'] ) ) {
|
||||
$form_data = $request['form_data'];
|
||||
} else {
|
||||
$form_data = array();
|
||||
}
|
||||
|
||||
$original_post = $_POST;
|
||||
$original_request = $_REQUEST;
|
||||
|
||||
foreach ( $form_data as $key => $value ) {
|
||||
$slashed_value = wp_slash( $value );
|
||||
$_POST[ $key ] = $slashed_value;
|
||||
$_REQUEST[ $key ] = $slashed_value;
|
||||
}
|
||||
|
||||
$callback = $wp_registered_widget_updates[ $id_base ]['callback'];
|
||||
$params = $wp_registered_widget_updates[ $id_base ]['params'];
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
ob_start();
|
||||
call_user_func_array( $callback, $params );
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
$_POST = $original_post;
|
||||
$_REQUEST = $original_request;
|
||||
|
||||
if ( $widget_object ) {
|
||||
// Register any multi-widget that the update callback just created.
|
||||
$widget_object->_set( $number );
|
||||
$widget_object->_register_one( $number );
|
||||
|
||||
// WP_Widget sets updated = true after an update to prevent more
|
||||
// than one widget from being saved per request. This isn't what we
|
||||
// want in the REST API, though, as we support batch requests.
|
||||
$widget_object->updated = false;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the widget for the REST response.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @global array $wp_registered_sidebars The registered sidebars.
|
||||
* @global array $wp_registered_widgets The registered widgets.
|
||||
* @global array $wp_registered_widget_controls The registered widget controls.
|
||||
*
|
||||
* @param array $item An array containing a widget_id and sidebar_id.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
global $wp_widget_factory, $wp_registered_widgets;
|
||||
|
||||
$widget_id = $item['widget_id'];
|
||||
$sidebar_id = $item['sidebar_id'];
|
||||
|
||||
if ( ! isset( $wp_registered_widgets[ $widget_id ] ) ) {
|
||||
return new WP_Error(
|
||||
'rest_invalid_widget',
|
||||
__( 'The requested widget is invalid.' ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
$widget = $wp_registered_widgets[ $widget_id ];
|
||||
$parsed_id = wp_parse_widget_id( $widget_id );
|
||||
$fields = $this->get_fields_for_response( $request );
|
||||
|
||||
$prepared = array(
|
||||
'id' => $widget_id,
|
||||
'id_base' => $parsed_id['id_base'],
|
||||
'sidebar' => $sidebar_id,
|
||||
'rendered' => '',
|
||||
'rendered_form' => null,
|
||||
'instance' => null,
|
||||
);
|
||||
|
||||
if (
|
||||
rest_is_field_included( 'rendered', $fields ) &&
|
||||
'wp_inactive_widgets' !== $sidebar_id
|
||||
) {
|
||||
$prepared['rendered'] = trim( wp_render_widget( $widget_id, $sidebar_id ) );
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'rendered_form', $fields ) ) {
|
||||
$rendered_form = wp_render_widget_control( $widget_id );
|
||||
if ( ! is_null( $rendered_form ) ) {
|
||||
$prepared['rendered_form'] = trim( $rendered_form );
|
||||
}
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'instance', $fields ) ) {
|
||||
$widget_object = $wp_widget_factory->get_widget_object( $parsed_id['id_base'] );
|
||||
if ( $widget_object && isset( $parsed_id['number'] ) ) {
|
||||
$all_instances = $widget_object->get_settings();
|
||||
$instance = $all_instances[ $parsed_id['number'] ];
|
||||
$serialized_instance = serialize( $instance );
|
||||
$prepared['instance']['encoded'] = base64_encode( $serialized_instance );
|
||||
$prepared['instance']['hash'] = wp_hash( $serialized_instance );
|
||||
|
||||
if ( ! empty( $widget_object->widget_options['show_instance_in_rest'] ) ) {
|
||||
// Use new stdClass so that JSON result is {} and not [].
|
||||
$prepared['instance']['raw'] = empty( $instance ) ? new stdClass : $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$prepared = $this->add_additional_fields_to_object( $prepared, $request );
|
||||
$prepared = $this->filter_response_by_context( $prepared, $context );
|
||||
|
||||
$response = rest_ensure_response( $prepared );
|
||||
|
||||
$response->add_links( $this->prepare_links( $prepared ) );
|
||||
|
||||
/**
|
||||
* Filters the REST API response for a widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param array $widget The registered widget data.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'rest_prepare_widget', $response, $widget, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares links for the widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $prepared Widget.
|
||||
* @return array Links for the given widget.
|
||||
*/
|
||||
protected function prepare_links( $prepared ) {
|
||||
$id_base = ! empty( $prepared['id_base'] ) ? $prepared['id_base'] : $prepared['id'];
|
||||
|
||||
return array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $prepared['id'] ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
'about' => array(
|
||||
'href' => rest_url( sprintf( 'wp/v2/widget-types/%s', $id_base ) ),
|
||||
'embeddable' => true,
|
||||
),
|
||||
'https://api.w.org/sidebar' => array(
|
||||
'href' => rest_url( sprintf( 'wp/v2/sidebars/%s/', $prepared['sidebar'] ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of collection params.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
'sidebar' => array(
|
||||
'description' => __( 'The sidebar to return widgets for.' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the widget's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @return array Item schema data.
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
if ( $this->schema ) {
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
|
||||
$this->schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'widget',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the widget.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'id_base' => array(
|
||||
'description' => __( 'The type of the widget. Corresponds to ID in widget-types endpoint.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'sidebar' => array(
|
||||
'description' => __( 'The sidebar the widget belongs to.' ),
|
||||
'type' => 'string',
|
||||
'default' => 'wp_inactive_widgets',
|
||||
'required' => true,
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'rendered' => array(
|
||||
'description' => __( 'HTML representation of the widget.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rendered_form' => array(
|
||||
'description' => __( 'HTML representation of the widget admin form.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'instance' => array(
|
||||
'description' => __( 'Instance settings of the widget, if supported.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'default' => null,
|
||||
'properties' => array(
|
||||
'encoded' => array(
|
||||
'description' => __( 'Base64 encoded representation of the instance settings.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'hash' => array(
|
||||
'description' => __( 'Cryptographic hash of the instance settings.' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'raw' => array(
|
||||
'description' => __( 'Unencoded instance settings, if supported.' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'form_data' => array(
|
||||
'description' => __( 'URL-encoded form data from the widget admin form. Used to update a widget that does not support instance. Write only.' ),
|
||||
'type' => 'string',
|
||||
'context' => array(),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => function( $string ) {
|
||||
$array = array();
|
||||
wp_parse_str( $string, $array );
|
||||
return $array;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '5.8-alpha-50992';
|
||||
$wp_version = '5.8-alpha-50993';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
@ -357,6 +357,7 @@ function is_registered_sidebar( $sidebar_id ) {
|
||||
* @since 2.2.0
|
||||
* @since 5.3.0 Formalized the existing and already documented `...$params` parameter
|
||||
* by adding it to the function signature.
|
||||
* @since 5.8.0 Added show_instance_in_rest option.
|
||||
*
|
||||
* @global array $wp_registered_widgets Uses stored registered widgets.
|
||||
* @global array $wp_registered_widget_controls Stores the registered widget controls (options).
|
||||
@ -373,6 +374,8 @@ function is_registered_sidebar( $sidebar_id ) {
|
||||
* version of the output callback name.
|
||||
* @type string $description Widget description for display in the widget administration
|
||||
* panel and/or theme.
|
||||
* @type bool $show_instance_in_rest Whether to show the widget's instance settings in the REST API.
|
||||
* Only available for WP_Widget based widgets.
|
||||
* }
|
||||
* @param mixed ...$params Optional additional parameters to pass to the callback function when it's called.
|
||||
*/
|
||||
@ -1796,6 +1799,8 @@ function wp_widgets_init() {
|
||||
|
||||
register_widget( 'WP_Widget_Custom_HTML' );
|
||||
|
||||
register_widget( 'WP_Widget_Block' );
|
||||
|
||||
/**
|
||||
* Fires after all default WordPress widgets have been registered.
|
||||
*
|
||||
@ -1803,3 +1808,166 @@ function wp_widgets_init() {
|
||||
*/
|
||||
do_action( 'widgets_init' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a widget ID into its id_base and number components.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $id Widget ID.
|
||||
* @return array Array containing a widget's id_base and number components.
|
||||
*/
|
||||
function wp_parse_widget_id( $id ) {
|
||||
$parsed = array();
|
||||
|
||||
if ( preg_match( '/^(.+)-(\d+)$/', $id, $matches ) ) {
|
||||
$parsed['id_base'] = $matches[1];
|
||||
$parsed['number'] = (int) $matches[2];
|
||||
} else {
|
||||
// Likely an old single widget.
|
||||
$parsed['id_base'] = $id;
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the sidebar that a given widget belongs to.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $widget_id The widget id to look for.
|
||||
* @return string|null The found sidebar's id, or null if it was not found.
|
||||
*/
|
||||
function wp_find_widgets_sidebar( $widget_id ) {
|
||||
foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
|
||||
foreach ( $widget_ids as $maybe_widget_id ) {
|
||||
if ( $maybe_widget_id === $widget_id ) {
|
||||
return (string) $sidebar_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a widget to the given sidebar.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $widget_id The widget id to assign.
|
||||
* @param string $sidebar_id The sidebar id to assign to. If empty, the widget won't be added to any sidebar.
|
||||
*/
|
||||
function wp_assign_widget_to_sidebar( $widget_id, $sidebar_id ) {
|
||||
$sidebars = wp_get_sidebars_widgets();
|
||||
|
||||
foreach ( $sidebars as $maybe_sidebar_id => $widgets ) {
|
||||
foreach ( $widgets as $i => $maybe_widget_id ) {
|
||||
if ( $widget_id === $maybe_widget_id && $sidebar_id !== $maybe_sidebar_id ) {
|
||||
unset( $sidebars[ $maybe_sidebar_id ][ $i ] );
|
||||
// We could technically break 2 here, but continue looping in case the id is duplicated.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $sidebar_id ) {
|
||||
$sidebars[ $sidebar_id ][] = $widget_id;
|
||||
}
|
||||
|
||||
wp_set_sidebars_widgets( $sidebars );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the render callback of a widget and returns the output.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $widget_id Widget ID.
|
||||
* @param string $sidebar_id Sidebar ID.
|
||||
* @return string
|
||||
*/
|
||||
function wp_render_widget( $widget_id, $sidebar_id ) {
|
||||
global $wp_registered_widgets, $wp_registered_sidebars;
|
||||
|
||||
if ( ! isset( $wp_registered_widgets[ $widget_id ] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) {
|
||||
$sidebar = $wp_registered_sidebars[ $sidebar_id ];
|
||||
} elseif ( 'wp_inactive_widgets' === $sidebar_id ) {
|
||||
$sidebar = array();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
$params = array_merge(
|
||||
array(
|
||||
array_merge(
|
||||
$sidebar,
|
||||
array(
|
||||
'widget_id' => $widget_id,
|
||||
'widget_name' => $wp_registered_widgets[ $widget_id ]['name'],
|
||||
)
|
||||
),
|
||||
),
|
||||
(array) $wp_registered_widgets[ $widget_id ]['params']
|
||||
);
|
||||
|
||||
// Substitute HTML `id` and `class` attributes into `before_widget`.
|
||||
$classname_ = '';
|
||||
foreach ( (array) $wp_registered_widgets[ $widget_id ]['classname'] as $cn ) {
|
||||
if ( is_string( $cn ) ) {
|
||||
$classname_ .= '_' . $cn;
|
||||
} elseif ( is_object( $cn ) ) {
|
||||
$classname_ .= '_' . get_class( $cn );
|
||||
}
|
||||
}
|
||||
$classname_ = ltrim( $classname_, '_' );
|
||||
$params[0]['before_widget'] = sprintf( $params[0]['before_widget'], $widget_id, $classname_ );
|
||||
|
||||
/** This filter is documented in wp-includes/widgets.php */
|
||||
$params = apply_filters( 'dynamic_sidebar_params', $params );
|
||||
|
||||
$callback = $wp_registered_widgets[ $widget_id ]['callback'];
|
||||
|
||||
ob_start();
|
||||
|
||||
/** This filter is documented in wp-includes/widgets.php */
|
||||
do_action( 'dynamic_sidebar', $wp_registered_widgets[ $widget_id ] );
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
call_user_func_array( $callback, $params );
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the control callback of a widget and returns the output.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $id Widget ID.
|
||||
* @return string|null
|
||||
*/
|
||||
function wp_render_widget_control( $id ) {
|
||||
global $wp_registered_widget_controls;
|
||||
|
||||
if ( ! isset( $wp_registered_widget_controls[ $id ]['callback'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$callback = $wp_registered_widget_controls[ $id ]['callback'];
|
||||
$params = $wp_registered_widget_controls[ $id ]['params'];
|
||||
|
||||
ob_start();
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
call_user_func_array( $callback, $params );
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ class WP_Nav_Menu_Widget extends WP_Widget {
|
||||
$widget_ops = array(
|
||||
'description' => __( 'Add a navigation menu to your sidebar.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'nav_menu', __( 'Navigation Menu' ), $widget_ops );
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class WP_Widget_Archives extends WP_Widget {
|
||||
'classname' => 'widget_archive',
|
||||
'description' => __( 'A monthly archive of your site’s Posts.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'archives', __( 'Archives' ), $widget_ops );
|
||||
}
|
||||
|
224
wp-includes/widgets/class-wp-widget-block.php
Normal file
224
wp-includes/widgets/class-wp-widget-block.php
Normal file
@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* Widget API: WP_Widget_Block class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Widgets
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to implement a Block widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @see WP_Widget
|
||||
*/
|
||||
class WP_Widget_Block extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Default instance.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @var array
|
||||
*/
|
||||
protected $default_instance = array(
|
||||
'content' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets up a new Block widget instance.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public function __construct() {
|
||||
$widget_ops = array(
|
||||
'classname' => 'widget_block',
|
||||
'description' => __( 'A widget containing a block.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
$control_ops = array(
|
||||
'width' => 400,
|
||||
'height' => 350,
|
||||
);
|
||||
parent::__construct( 'block', __( 'Block' ), $widget_ops, $control_ops );
|
||||
add_filter( 'is_wide_widget_in_customizer', array( $this, 'set_is_wide_widget_in_customizer' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the content for the current Block widget instance.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $args Display arguments including 'before_title', 'after_title',
|
||||
* 'before_widget', and 'after_widget'.
|
||||
* @param array $instance Settings for the current Block widget instance.
|
||||
*
|
||||
* @global WP_Post $post Global post object.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$instance = wp_parse_args( $instance, $this->default_instance );
|
||||
|
||||
echo str_replace(
|
||||
'widget_block',
|
||||
$this->get_dynamic_classname( $instance['content'] ),
|
||||
$args['before_widget']
|
||||
);
|
||||
|
||||
// Handle embeds for block widgets.
|
||||
//
|
||||
// When this feature is added to core it may need to be implemented
|
||||
// differently. WP_Widget_Text is a good reference, that applies a
|
||||
// filter for its content, which WP_Embed uses in its constructor.
|
||||
// See https://core.trac.wordpress.org/ticket/51566.
|
||||
global $wp_embed;
|
||||
$content = $wp_embed->run_shortcode( $instance['content'] );
|
||||
$content = $wp_embed->autoembed( $content );
|
||||
|
||||
$content = do_blocks( $content );
|
||||
$content = do_shortcode( $content );
|
||||
|
||||
echo $content;
|
||||
|
||||
echo $args['after_widget'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the classname to use in the block widget's container HTML.
|
||||
*
|
||||
* Usually this is set to $this->widget_options['classname'] by
|
||||
* dynamic_sidebar(). In this case, however, we want to set the classname
|
||||
* dynamically depending on the block contained by this block widget.
|
||||
*
|
||||
* If a block widget contains a block that has an equivalent legacy widget,
|
||||
* we display that legacy widget's class name. This helps with theme
|
||||
* backwards compatibility.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $content The HTML content of the current block widget.
|
||||
*
|
||||
* @return string The classname to use in the block widget's container HTML.
|
||||
*/
|
||||
private function get_dynamic_classname( $content ) {
|
||||
$blocks = parse_blocks( $content );
|
||||
|
||||
$block_name = isset( $blocks[0] ) ? $blocks[0]['blockName'] : null;
|
||||
|
||||
switch ( $block_name ) {
|
||||
case 'core/paragraph':
|
||||
$classname = 'widget_block widget_text';
|
||||
break;
|
||||
case 'core/calendar':
|
||||
$classname = 'widget_block widget_calendar';
|
||||
break;
|
||||
case 'core/search':
|
||||
$classname = 'widget_block widget_search';
|
||||
break;
|
||||
case 'core/html':
|
||||
$classname = 'widget_block widget_custom_html';
|
||||
break;
|
||||
case 'core/archives':
|
||||
$classname = 'widget_block widget_archive';
|
||||
break;
|
||||
case 'core/latest-posts':
|
||||
$classname = 'widget_block widget_recent_entries';
|
||||
break;
|
||||
case 'core/latest-comments':
|
||||
$classname = 'widget_block widget_recent_comments';
|
||||
break;
|
||||
case 'core/tag-cloud':
|
||||
$classname = 'widget_block widget_tag_cloud';
|
||||
break;
|
||||
case 'core/categories':
|
||||
$classname = 'widget_block widget_categories';
|
||||
break;
|
||||
case 'core/audio':
|
||||
$classname = 'widget_block widget_media_audio';
|
||||
break;
|
||||
case 'core/video':
|
||||
$classname = 'widget_block widget_media_video';
|
||||
break;
|
||||
case 'core/image':
|
||||
$classname = 'widget_block widget_media_image';
|
||||
break;
|
||||
case 'core/gallery':
|
||||
$classname = 'widget_block widget_media_gallery';
|
||||
break;
|
||||
case 'core/rss':
|
||||
$classname = 'widget_block widget_rss';
|
||||
break;
|
||||
default:
|
||||
$classname = 'widget_block';
|
||||
}
|
||||
|
||||
/**
|
||||
* The classname used in the block widget's container HTML.
|
||||
*
|
||||
* This can be set according to the name of the block contained by the
|
||||
* block widget.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $classname The classname to be used in the block widget's container HTML, e.g. 'widget_block widget_text'.
|
||||
* @param string $block_name The name of the block contained by the block widget, e.g. 'core/paragraph'.
|
||||
*/
|
||||
return apply_filters( 'widget_block_dynamic_classname', $classname, $block_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles updating settings for the current Block widget instance.
|
||||
*
|
||||
* @since 5.8.0
|
||||
|
||||
* @param array $new_instance New settings for this instance as input by the user via
|
||||
* WP_Widget::form().
|
||||
* @param array $old_instance Old settings for this instance.
|
||||
*
|
||||
* @return array Settings to save or bool false to cancel saving.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$instance = array_merge( $this->default_instance, $old_instance );
|
||||
$instance['content'] = $new_instance['content'];
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the Block widget settings form.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array $instance Current instance.
|
||||
*
|
||||
* @see WP_Widget_Custom_HTML::render_control_template_scripts()
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->default_instance );
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id( 'content' ); ?>"><?php echo __( 'Block HTML:' ); ?></label>
|
||||
<textarea id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>" rows="6" cols="50" class="widefat code"><?php echo esc_textarea( $instance['content'] ); ?></textarea>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure no block widget is considered to be wide.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param boolean $is_wide Is regarded wide.
|
||||
* @param string $widget_id Widget ID.
|
||||
*
|
||||
* @return bool Updated is_wide value.
|
||||
*/
|
||||
public function set_is_wide_widget_in_customizer( $is_wide, $widget_id ) {
|
||||
if ( strpos( $widget_id, 'block-' ) === 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $is_wide;
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ class WP_Widget_Calendar extends WP_Widget {
|
||||
'classname' => 'widget_calendar',
|
||||
'description' => __( 'A calendar of your site’s posts.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'calendar', __( 'Calendar' ), $widget_ops );
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class WP_Widget_Categories extends WP_Widget {
|
||||
'classname' => 'widget_categories',
|
||||
'description' => __( 'A list or dropdown of categories.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'categories', __( 'Categories' ), $widget_ops );
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ class WP_Widget_Custom_HTML extends WP_Widget {
|
||||
'classname' => 'widget_custom_html',
|
||||
'description' => __( 'Arbitrary HTML code.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
$control_ops = array(
|
||||
'width' => 400,
|
||||
|
@ -59,6 +59,7 @@ abstract class WP_Widget_Media extends WP_Widget {
|
||||
array(
|
||||
'description' => __( 'A media item.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
'mime_type' => '',
|
||||
)
|
||||
);
|
||||
|
@ -28,6 +28,7 @@ class WP_Widget_Meta extends WP_Widget {
|
||||
'classname' => 'widget_meta',
|
||||
'description' => __( 'Login, RSS, & WordPress.org links.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'meta', __( 'Meta' ), $widget_ops );
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class WP_Widget_Pages extends WP_Widget {
|
||||
'classname' => 'widget_pages',
|
||||
'description' => __( 'A list of your site’s Pages.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'pages', __( 'Pages' ), $widget_ops );
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class WP_Widget_Recent_Comments extends WP_Widget {
|
||||
'classname' => 'widget_recent_comments',
|
||||
'description' => __( 'Your site’s most recent comments.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'recent-comments', __( 'Recent Comments' ), $widget_ops );
|
||||
$this->alt_option_name = 'widget_recent_comments';
|
||||
|
@ -26,6 +26,7 @@ class WP_Widget_Recent_Posts extends WP_Widget {
|
||||
'classname' => 'widget_recent_entries',
|
||||
'description' => __( 'Your site’s most recent Posts.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'recent-posts', __( 'Recent Posts' ), $widget_ops );
|
||||
$this->alt_option_name = 'widget_recent_entries';
|
||||
|
@ -25,6 +25,8 @@ class WP_Widget_RSS extends WP_Widget {
|
||||
$widget_ops = array(
|
||||
'description' => __( 'Entries from any RSS or Atom feed.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
|
||||
);
|
||||
$control_ops = array(
|
||||
'width' => 400,
|
||||
|
@ -26,6 +26,7 @@ class WP_Widget_Search extends WP_Widget {
|
||||
'classname' => 'widget_search',
|
||||
'description' => __( 'A search form for your site.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'search', _x( 'Search', 'Search widget' ), $widget_ops );
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ class WP_Widget_Tag_Cloud extends WP_Widget {
|
||||
$widget_ops = array(
|
||||
'description' => __( 'A cloud of your most used tags.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
parent::__construct( 'tag_cloud', __( 'Tag Cloud' ), $widget_ops );
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class WP_Widget_Text extends WP_Widget {
|
||||
'classname' => 'widget_text',
|
||||
'description' => __( 'Arbitrary text.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
'show_instance_in_rest' => true,
|
||||
);
|
||||
$control_ops = array(
|
||||
'width' => 400,
|
||||
|
@ -265,6 +265,9 @@ require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-plugins-controller.
|
||||
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-block-directory-controller.php';
|
||||
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-application-passwords-controller.php';
|
||||
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-site-health-controller.php';
|
||||
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-sidebars-controller.php';
|
||||
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-widget-types-controller.php';
|
||||
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-widgets-controller.php';
|
||||
require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php';
|
||||
require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php';
|
||||
require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php';
|
||||
|
Loading…
Reference in New Issue
Block a user