Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legacy Widget: Ensure the control form's IDs have a consistent number #31485

Merged
merged 2 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lib/class-wp-rest-widget-types-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,13 @@ public function encode_form_data( $request ) {
);
}

// Set the widget's number to 1 so that the `id` attributes in the HTML
// that we return are predictable.
$widget_object->_set( 1 );
// 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'] );
Expand Down
48 changes: 26 additions & 22 deletions packages/block-library/src/legacy-widget/edit/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export default class Control {
this.onChangeHasPreview = onChangeHasPreview;
this.onError = onError;

// We can't use the real widget number as this is calculated by the
// server and we may not ever *actually* save this widget. Instead, use
// a fake but unique number.
this.number = ++lastNumber;

this.handleFormChange = debounce( this.saveForm.bind( this ), 200 );
this.handleFormSubmit = this.handleFormSubmit.bind( this );

Expand All @@ -73,11 +78,6 @@ export default class Control {
* @access private
*/
initDOM() {
// We can't use the real widget number as this is calculated by the
// server and we may not ever *actually* save this widget. Instead, use
// a fake but unique number.
const number = ++lastNumber;

this.element = el( 'div', { class: 'widget open' }, [
el( 'div', { class: 'widget-inside' }, [
( this.form = el( 'form', { class: 'form', method: 'post' }, [
Expand All @@ -87,7 +87,7 @@ export default class Control {
class: 'widget-id',
type: 'hidden',
name: 'widget-id',
value: this.id ?? `${ this.idBase }-${ number }`,
value: this.id ?? `${ this.idBase }-${ this.number }`,
} ),
el( 'input', {
class: 'id_base',
Expand All @@ -111,7 +111,7 @@ export default class Control {
class: 'widget_number',
type: 'hidden',
name: 'widget_number',
value: this.idBase ? number.toString() : '',
value: this.idBase ? this.number.toString() : '',
} ),
( this.content = el( 'div', { class: 'widget-content' } ) ),
// Non-multi widgets can be saved via a Save button.
Expand Down Expand Up @@ -179,21 +179,23 @@ export default class Control {
const { form } = await saveWidget( this.id );
this.content.innerHTML = form;
} else if ( this.idBase ) {
const { form, preview } = await encodeWidget(
this.idBase,
this.instance
);
const { form, preview } = await encodeWidget( {
idBase: this.idBase,
instance: this.instance,
number: this.number,
} );
this.content.innerHTML = form;
this.hasPreview = ! isEmptyHTML( preview );

// If we don't have an instance, perform a save right away. This
// happens when creating a new Legacy Widget block.
if ( ! this.instance.hash ) {
const { instance } = await encodeWidget(
this.idBase,
this.instance,
serializeForm( this.form )
);
const { instance } = await encodeWidget( {
idBase: this.idBase,
instance: this.instance,
number: this.number,
formData: serializeForm( this.form ),
} );
this.instance = instance;
}
}
Expand Down Expand Up @@ -244,11 +246,12 @@ export default class Control {
] );
}
} else if ( this.idBase ) {
const { instance, preview } = await encodeWidget(
this.idBase,
this.instance,
formData
);
const { instance, preview } = await encodeWidget( {
idBase: this.idBase,
instance: this.instance,
number: this.number,
formData,
} );
this.instance = instance;
this.hasPreview = ! isEmptyHTML( preview );
}
Expand Down Expand Up @@ -338,12 +341,13 @@ async function saveWidget( id, formData = null ) {
return { form: widget.rendered_form };
}

async function encodeWidget( idBase, instance, formData = null ) {
async function encodeWidget( { idBase, instance, number, formData = null } ) {
const response = await apiFetch( {
path: `/wp/v2/widget-types/${ idBase }/encode`,
method: 'POST',
data: {
instance,
number,
form_data: formData,
},
} );
Expand Down
51 changes: 42 additions & 9 deletions phpunit/class-rest-widget-types-controller-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,41 @@ public function test_encode_form_data_with_no_input() {
$data = $response->get_data();
$this->assertEquals(
"<p>\n" .
"\t\t\t<label for=\"widget-search-1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search-1-title\" name=\"widget-search[1][title]\" type=\"text\" value=\"\" />\n" .
"\t\t\t<label for=\"widget-search--1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search--1-title\" name=\"widget-search[-1][title]\" type=\"text\" value=\"\" />\n" .
"\t\t</p>",
$data['form']
);
$this->assertStringMatchesFormat(
"<div class=\"widget widget_search\"><form role=\"search\" method=\"get\" id=\"searchform\" class=\"searchform\" action=\"%s\">\n" .
"\t\t\t\t<div>\n" .
"\t\t\t\t\t<label class=\"screen-reader-text\" for=\"s\">Search for:</label>\n" .
"\t\t\t\t\t<input type=\"text\" value=\"\" name=\"s\" id=\"s\" />\n" .
"\t\t\t\t\t<input type=\"submit\" id=\"searchsubmit\" value=\"Search\" />\n" .
"\t\t\t\t</div>\n" .
"\t\t\t</form></div>",
$data['preview']
);
$this->assertEqualSets(
array(
'encoded' => base64_encode( serialize( array() ) ),
'hash' => wp_hash( serialize( array() ) ),
'raw' => new stdClass,
),
$data['instance']
);
}

public function test_encode_form_data_with_number() {
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/widget-types/search/encode' );
$request->set_param( 'number', 8 );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertEquals(
"<p>\n" .
"\t\t\t<label for=\"widget-search-8-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search-8-title\" name=\"widget-search[8][title]\" type=\"text\" value=\"\" />\n" .
"\t\t</p>",
$data['form']
);
Expand Down Expand Up @@ -299,8 +332,8 @@ public function test_encode_form_data_with_instance() {
$data = $response->get_data();
$this->assertEquals(
"<p>\n" .
"\t\t\t<label for=\"widget-search-1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search-1-title\" name=\"widget-search[1][title]\" type=\"text\" value=\"Test title\" />\n" .
"\t\t\t<label for=\"widget-search--1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search--1-title\" name=\"widget-search[-1][title]\" type=\"text\" value=\"Test title\" />\n" .
"\t\t</p>",
$data['form']
);
Expand All @@ -327,13 +360,13 @@ public function test_encode_form_data_with_instance() {
public function test_encode_form_data_with_form_data() {
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/widget-types/search/encode' );
$request->set_param( 'form_data', 'widget-search[1][title]=Updated+title' );
$request->set_param( 'form_data', 'widget-search[-1][title]=Updated+title' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertEquals(
"<p>\n" .
"\t\t\t<label for=\"widget-search-1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search-1-title\" name=\"widget-search[1][title]\" type=\"text\" value=\"Updated title\" />\n" .
"\t\t\t<label for=\"widget-search--1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search--1-title\" name=\"widget-search[-1][title]\" type=\"text\" value=\"Updated title\" />\n" .
"\t\t</p>",
$data['form']
);
Expand Down Expand Up @@ -373,8 +406,8 @@ public function test_encode_form_data_no_raw() {
$data = $response->get_data();
$this->assertEquals(
"<p>\n" .
"\t\t\t<label for=\"widget-search-1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search-1-title\" name=\"widget-search[1][title]\" type=\"text\" value=\"Test title\" />\n" .
"\t\t\t<label for=\"widget-search--1-title\">Title:</label>\n" .
"\t\t\t<input class=\"widefat\" id=\"widget-search--1-title\" name=\"widget-search[-1][title]\" type=\"text\" value=\"Test title\" />\n" .
"\t\t</p>",
$data['form']
);
Expand Down