Skip to content

Commit

Permalink
Added is_callback_widget to the controller arguments; Added required …
Browse files Browse the repository at this point in the history
…flag for the identifier. (+2 squashed commits)

Squashed commits:
[86539f0] Remove hasEditForm attribute
[3045a2d] Fix: Legacy widget: allow to use the edit form of callback widgets with a custom form.
  • Loading branch information
jorgefilipecosta committed May 23, 2019
1 parent 392df98 commit bba10fa
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 55 deletions.
160 changes: 128 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,18 @@ 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 . '/(?P<identifier>[\w-_]+)/',
array(
'args' => array(
'identifier' => array(
'identifier' => array(
'description' => __( 'Class name of the widget.', 'gutenberg' ),
'type' => 'string',
'required' => true,
),
'is_callback_widget' => array(
'description' => __( 'Flag indicating if the widget is registered using register_sidebar_widget or is a subclass of WP_Widget.', 'gutenberg' ),
'type' => 'boolean',
'default' => false,
),
),
array(
Expand Down Expand Up @@ -76,53 +81,110 @@ 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 Widget id for callback widgets or widget class name for class widgets.
* @param boolean $is_callback_widget If true the widget is a back widget if false the widget is a class widget.
*
* @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, $is_callback_widget ) {
if ( $is_callback_widget ) {
global $wp_registered_widgets;
return isset( $wp_registered_widgets[ $identifier ] );
}
global $wp_widget_factory;
return isset( $wp_widget_factory->widgets[ $identifier ] ) &&
( $wp_widget_factory->widgets[ $identifier ] instanceof WP_Widget );
}

/**
* 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 bew callback widget form.
*
* @since 5.7.0
* @param string $identifier Widget id for callback widgets or widget class name for class widgets.
* @param array $instance_changes Array with the form values being being changed.
*
* @return WP_REST_Response Response object.
*/
private function compute_new_widget_handle_callback_widgets( $identifier, $instance_changes ) {
global $wp_registered_widget_controls;
$form = '';
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 ];
$_POST = array_merge( $_POST, $instance_changes );
ob_start();
call_user_func_array( $control['callback'], $control['params'] );
$form = ob_get_clean();
}

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

$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 $identifier 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 compute_new_widget_handle_class_widgets( $identifier, $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[ $identifier ];

$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,10 +228,7 @@ 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(
Expand All @@ -180,6 +239,43 @@ public function compute_new_widget( $request ) {
)
);
}

/**
* 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' );
$is_callback_widget = $request->get_param( 'is_callback_widget' );

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

if ( $is_callback_widget ) {
return $this->compute_new_widget_handle_callback_widgets(
$identifier,
$request->get_param( 'instance_changes' )
);
}
return $this->compute_new_widget_handle_class_widgets(
$identifier,
$request->get_param( 'instance' ),
$request->get_param( 'instance_changes' ),
$request->get_param( 'id_to_use' )
);
}
}
/**
* End: Include for phase 2
Expand Down
14 changes: 5 additions & 9 deletions packages/block-library/src/legacy-widget/edit/dom-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,28 +110,24 @@ class LegacyWidgetEditDomManager extends Component {

retrieveUpdatedInstance() {
if ( this.formRef.current ) {
const { idBase, widgetNumber } = this.props;
const form = this.formRef.current;
const formData = new window.FormData( form );
const updatedInstance = {};
const keyPrefixLength = `widget-${ idBase }[${ widgetNumber }][`.length;
const keySuffixLength = `]`.length;
for ( const rawKey of formData.keys() ) {
for ( const key of formData.keys() ) {
// This fields are added to the form because the widget JavaScript code may use this values.
// They are not relevant for the update mechanism.
if ( includes(
[ 'widget-id', 'id_base', 'widget_number', 'multi_number', 'add_new' ],
rawKey,
key,
) ) {
continue;
}
const keyParsed = rawKey.substring( keyPrefixLength, rawKey.length - keySuffixLength );

const value = formData.getAll( rawKey );
const value = formData.getAll( key );
if ( value.length > 1 ) {
updatedInstance[ keyParsed ] = value;
updatedInstance[ key ] = value;
} else {
updatedInstance[ keyParsed ] = value[ 0 ];
updatedInstance[ key ] = value[ 0 ];
}
}
return updatedInstance;
Expand Down
13 changes: 9 additions & 4 deletions packages/block-library/src/legacy-widget/edit/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ class LegacyWidgetEditHandler extends Component {

componentDidMount() {
this.isStillMounted = true;
this.requestWidgetUpdater();
this.requestWidgetUpdater( undefined, ( response ) => {
this.props.onInstanceChange( null, !! response.form );
} );
}

componentDidUpdate( prevProps ) {
if (
prevProps.instance !== this.props.instance &&
this.instanceUpdating !== this.props.instance
) {
this.requestWidgetUpdater();
this.requestWidgetUpdater( undefined, ( response ) => {
this.props.onInstanceChange( null, !! response.form );
} );
}
if ( this.instanceUpdating === this.props.instance ) {
this.instanceUpdating = null;
Expand Down Expand Up @@ -81,12 +85,12 @@ class LegacyWidgetEditHandler extends Component {
onInstanceChange( instanceChanges ) {
this.requestWidgetUpdater( instanceChanges, ( response ) => {
this.instanceUpdating = response.instance;
this.props.onInstanceChange( response.instance );
this.props.onInstanceChange( response.instance, !! response.form );
} );
}

requestWidgetUpdater( instanceChanges, callback ) {
const { identifier, instanceId, instance } = this.props;
const { identifier, instanceId, instance, isCallbackWidget } = this.props;
if ( ! identifier ) {
return;
}
Expand All @@ -98,6 +102,7 @@ class LegacyWidgetEditHandler extends Component {
instance,
// use negative ids to make sure the id does not exist on the database.
id_to_use: instanceId * -1,
is_callback_widget: isCallbackWidget,
instance_changes: instanceChanges,
},
method: 'POST',
Expand Down
32 changes: 22 additions & 10 deletions packages/block-library/src/legacy-widget/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class LegacyWidgetEdit extends Component {
constructor() {
super( ...arguments );
this.state = {
hasEditForm: true,
isPreview: false,
};
this.switchToEdit = this.switchToEdit.bind( this );
Expand All @@ -47,7 +48,7 @@ class LegacyWidgetEdit extends Component {
hasPermissionsToManageWidgets,
setAttributes,
} = this.props;
const { isPreview } = this.state;
const { isPreview, hasEditForm } = this.state;
const { identifier, isCallbackWidget } = attributes;
const widgetObject = identifier && availableLegacyWidgets[ identifier ];
if ( ! widgetObject ) {
Expand Down Expand Up @@ -115,8 +116,8 @@ class LegacyWidgetEdit extends Component {
icon="update"
>
</IconButton>
{ ! isCallbackWidget && (
<>
{ hasEditForm && (
<Fragment>
<Button
className={ `components-tab-button ${ ! isPreview ? 'is-active' : '' }` }
onClick={ this.switchToEdit }
Expand All @@ -129,26 +130,34 @@ class LegacyWidgetEdit extends Component {
>
<span>{ __( 'Preview' ) }</span>
</Button>
</>
</Fragment>
) }
</Toolbar>
</BlockControls>
{ inspectorControls }
{ ! isCallbackWidget && (
{ hasEditForm && (
<LegacyWidgetEditHandler
isVisible={ ! isPreview }
identifier={ attributes.identifier }
instance={ attributes.instance }
isCallbackWidget={ isCallbackWidget }
onInstanceChange={
( newInstance ) => {
this.props.setAttributes( {
instance: newInstance,
} );
( newInstance, newHasEditForm ) => {
if ( newInstance ) {
this.props.setAttributes( {
instance: newInstance,
} );
}
if ( newHasEditForm !== this.hasEditForm ) {
this.setState( {
hasEditForm: newHasEditForm,
} );
}
}
}
/>
) }
{ ( isPreview || isCallbackWidget ) && this.renderWidgetPreview() }
{ ( isPreview || ! hasEditForm ) && this.renderWidgetPreview() }
</>
);
}
Expand All @@ -159,6 +168,9 @@ class LegacyWidgetEdit extends Component {
instance: {},
identifier: undefined,
} );
this.setState( {
hasEditForm: true,
} );
}

switchToEdit() {
Expand Down

0 comments on commit bba10fa

Please sign in to comment.