Skip to content

Commit

Permalink
Implement widget references in legacy widget using ajax-admin calls. …
Browse files Browse the repository at this point in the history
…(+2 squashed commits)

Squashed commits:
[f15e512e1] solved
[ae4935e52] Add legacy widget by id
  • Loading branch information
jorgefilipecosta committed May 24, 2019
1 parent e81f191 commit 24127bd
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 124 deletions.
2 changes: 1 addition & 1 deletion lib/class-experimental-wp-widget-blocks-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private static function get_widget_class( $widget_id ) {
* @param string $id Idenfitier of the widget instance.
* @return array Array containing the widget instance.
*/
private static function get_sidebar_widget_instance( $sidebar, $id ) {
public static function get_sidebar_widget_instance( $sidebar, $id ) {
list( $object, $number, $name ) = self::get_widget_info( $id );
if ( ! $object ) {
return array();
Expand Down
180 changes: 148 additions & 32 deletions lib/class-wp-rest-widget-updater-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,30 @@ public function __construct() {
public function register_routes() {
register_rest_route(
$this->namespace,
// Regex representing a PHP class extracted from http://php.net/manual/en/language.oop5.basic.php.
'/' . $this->rest_base . '/(?P<identifier>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/',
'/' . $this->rest_base . '/',
array(
'args' => array(
'identifier' => array(
'widget_class' => array(
'description' => __( 'Class name of the widget.', 'gutenberg' ),
'type' => 'string',
'required' => false,
'default' => null,
),
'identifier' => array(
'description' => __( 'Identifier of the widget.', 'gutenberg' ),
'type' => 'string',
'required' => false,
'default' => null,
),
'instance' => array(
'description' => __( 'Current widget instance', 'gutenberg' ),
'type' => 'object',
'default' => array(),
),
'instance_changes' => array(
'description' => __( 'Array of instance changes', 'gutenberg' ),
'type' => 'object',
'default' => array(),
),
),
array(
Expand Down Expand Up @@ -76,53 +93,120 @@ public function compute_new_widget_permissions_check() {
}

/**
* Returns the new widget instance and the form that represents it.
* Checks if the widget being referenced is valid.
*
* @since 5.2.0
* @access public
* @param string $identifier Instance identifier of the widget.
* @param string $widget_class Name of the class the widget references.
*
* @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.
* @return boolean True if the widget being referenced exists and false otherwise.
*/
public function compute_new_widget( $request ) {
$url_params = $request->get_url_params();

$widget = $request->get_param( 'identifier' );
private function is_valid_widget( $identifier, $widget_class ) {
global $wp_widget_factory, $wp_registered_widgets;
if ( ! $identifier && ! $widget_class ) {
return false;
}
if ( $identifier ) {
return isset( $wp_registered_widgets[ $identifier ] );
}
return isset( $wp_widget_factory->widgets[ $widget_class ] ) &&
( $wp_widget_factory->widgets[ $widget_class ] instanceof WP_Widget );
}

global $wp_widget_factory;
/**
* Computes an array with instance changes cleaned of widget specific prefixes and sufixes.
*
* @since 5.7.0
* @param string $id_base Widget ID Base.
* @param string $id Widget instance identifier.
* @param array $instance_changes Array with the form values being being changed.
*
* @return array An array based on $instance_changes whose keys have the widget specific sufixes and prefixes removed.
*/
private function parse_instance_changes( $id_base, $id, $instance_changes ) {
$instance_changes_parsed = array();
$start_position = strlen( 'widget-' . $id_base . '[' . $id . '][' );
foreach ( $instance_changes as $key => $value ) {
$key_parsed = substr( $key, $start_position, -1 );
$instance_changes_parsed[ $key_parsed ] = $value;
}
return $instance_changes_parsed;
}

/**
* Returns the edit form of the widget being referenced.
*
* @since 5.7.0
* @param string $identifier Instance identifier of the widget.
*
* @return WP_REST_Response Response object.
*/
private function handle_reference_widgets( $identifier ) {
global $wp_registered_widget_controls;
$form = '';
$id_base = $identifier;
$id = $identifier;
$number = null;
if (
null === $widget ||
! isset( $wp_widget_factory->widgets[ $widget ] ) ||
! ( $wp_widget_factory->widgets[ $widget ] instanceof WP_Widget )
isset( $wp_registered_widget_controls[ $identifier ]['callback'] ) &&
is_callable( $wp_registered_widget_controls[ $identifier ]['callback'] )
) {
return new WP_Error(
'widget_invalid',
__( 'Invalid widget.', 'gutenberg' ),
array(
'status' => 404,
)
);
$control = $wp_registered_widget_controls[ $identifier ];
ob_start();
call_user_func_array( $control['callback'], $control['params'] );
$form = ob_get_clean();
if ( isset( $control['id_base'] ) ) {
$id_base = $control['id_base'];
}
if ( isset( $control['params'][0]['number'] ) ) {
$number = $control['params'][0]['number'];
}
}

$widget_obj = $wp_widget_factory->widgets[ $widget ];
return rest_ensure_response(
array(
'instance' => array(),
'form' => $form,
'id_base' => $id_base,
'id' => $id,
'number' => $number,
)
);
}

$instance = $request->get_param( 'instance' );
/**
* Returns the new class widget instance and the form that represents it.
*
* @since 5.7.0
* @access public
*
* @param string $widget_class Widget id for callback widgets or widget class name for class widgets.
* @param array $instance Previous widget instance.
* @param array $instance_changes Array with the form values being being changed.
* @param string $id_to_use Identifier of the specific widget instance.
* @return WP_REST_Response Response object on success, or WP_Error object on failure.
*/
private function handle_class_widgets( $widget_class, $instance, $instance_changes, $id_to_use ) {
if ( null === $instance ) {
$instance = array();
}
$id_to_use = $request->get_param( 'id_to_use' );
if ( null === $id_to_use ) {
$id_to_use = -1;
}

global $wp_widget_factory;
$widget_obj = $wp_widget_factory->widgets[ $widget_class ];

$widget_obj->_set( $id_to_use );
$id_base = $widget_obj->id_base;
$id = $widget_obj->id;
ob_start();

$instance_changes = $request->get_param( 'instance_changes' );
if ( null !== $instance_changes ) {
$old_instance = $instance;
$instance = $widget_obj->update( $instance_changes, $old_instance );
$instance_changes = $this->parse_instance_changes( $id_base, $id_to_use, $instance_changes );
$old_instance = $instance;
$instance = $widget_obj->update( $instance_changes, $old_instance );

/**
* Filters a widget's settings before saving.
*
Expand Down Expand Up @@ -166,20 +250,52 @@ public function compute_new_widget( $request ) {
*/
do_action_ref_array( 'in_widget_form', array( &$widget_obj, &$return, $instance ) );
}

$id_base = $widget_obj->id_base;
$id = $widget_obj->id;
$form = ob_get_clean();
$form = ob_get_clean();

return rest_ensure_response(
array(
'instance' => $instance,
'form' => $form,
'id_base' => $id_base,
'id' => $id,
'number' => $id_to_use,
)
);
}

/**
* Returns the new widget instance and the form that represents it.
*
* @since 5.7.0
* @access public
*
* @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 compute_new_widget( $request ) {
$identifier = $request->get_param( 'identifier' );
$widget_class = $request->get_param( 'widget_class' );

if ( ! $this->is_valid_widget( $identifier, $widget_class ) ) {
return new WP_Error(
'widget_invalid',
__( 'Invalid widget.', 'gutenberg' ),
array(
'status' => 404,
)
);
}

if ( $identifier ) {
return $this->handle_reference_widgets( $identifier );
}
return $this->handle_class_widgets(
$widget_class,
$request->get_param( 'instance' ),
$request->get_param( 'instance_changes' ),
$request->get_param( 'id_to_use' )
);
}
}
/**
* End: Include for phase 2
Expand Down
2 changes: 2 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
}
if ( ! class_exists( 'WP_REST_Widget_Areas_Controller' ) ) {
require dirname( __FILE__ ) . '/class-experimental-wp-widget-blocks-manager.php';
}
if ( ! class_exists( 'WP_REST_Widget_Areas_Controller' ) ) {
require dirname( __FILE__ ) . '/class-wp-rest-widget-areas-controller.php';
}
/**
Expand Down
35 changes: 23 additions & 12 deletions lib/widgets.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ function gutenberg_block_editor_admin_print_footer_scripts() {
*/
function gutenberg_block_editor_admin_footer() {
if ( gutenberg_is_block_editor() ) {
echo '<form method="post">';
echo wp_nonce_field( 'save-sidebar-widgets', '_wpnonce_widgets', false );
echo '</form>';
/** This action is documented in wp-admin/admin-footer.php */
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
do_action( 'admin_footer-widgets.php' );
Expand Down Expand Up @@ -102,18 +105,17 @@ function gutenberg_get_legacy_widget_settings() {
$available_legacy_widgets = array();
global $wp_widget_factory, $wp_registered_widgets;
foreach ( $wp_widget_factory->widgets as $class => $widget_obj ) {
if ( ! in_array( $class, $core_widgets ) ) {
$available_legacy_widgets[ $class ] = array(
'name' => html_entity_decode( $widget_obj->name ),
// wp_widget_description is not being used because its input parameter is a Widget Id.
// Widgets id's reference to a specific widget instance.
// Here we are iterating on all the available widget classes even if no widget instance exists for them.
'description' => isset( $widget_obj->widget_options['description'] ) ?
html_entity_decode( $widget_obj->widget_options['description'] ) :
null,
'isCallbackWidget' => false,
);
}
$available_legacy_widgets[ $class ] = array(
'name' => html_entity_decode( $widget_obj->name ),
// wp_widget_description is not being used because its input parameter is a Widget Id.
// Widgets id's reference to a specific widget instance.
// Here we are iterating on all the available widget classes even if no widget instance exists for them.
'description' => isset( $widget_obj->widget_options['description'] ) ?
html_entity_decode( $widget_obj->widget_options['description'] ) :
null,
'isCallbackWidget' => false,
'isHidden' => in_array( $class, $core_widgets, true ),
);
}
foreach ( $wp_registered_widgets as $widget_id => $widget_obj ) {
if (
Expand Down Expand Up @@ -204,3 +206,12 @@ function gutenberg_create_wp_area_post_type() {
add_action( 'init', 'gutenberg_create_wp_area_post_type' );

add_filter( 'sidebars_widgets', 'Experimental_WP_Widget_Blocks_Manager::swap_out_sidebars_blocks_for_block_widgets' );

/**
* Function to enqueue admin-widgets as part of the block editor assets.
*/
function gutenberg_enqueue_widget_scripts() {
wp_enqueue_script( 'admin-widgets' );
}

add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_widget_scripts' );
Loading

0 comments on commit 24127bd

Please sign in to comment.