-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Iterate on widget REST API endpoints #29649
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
Conversation
|
Size Change: +4.09 kB (0%) Total Size: 1.4 MB
ℹ️ View Unchanged
|
|
These changes are looking great to me. Will review more carefully after the tests are updated. |
|
This is going great! 👏 |
|
I've fixed all test failures and as such will mark this as ready to review. I'm still working on adding new tests for the new functionality. |
|
OK, I added a few tests which cover the new functionality. I couldn't figure out how to write a test for It would be nice to DRY up the unit tests. Probably we can use data providers to test all of the combinations of methods ( |
|
Checks are failing because of #29752. |
spacedmonkey
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please provide more detail on this change? Most of the changes seem to make this less RESTFUL and remove data from the api. I don't see what problem you are trying to solve here. What exactly was wrong with the apis in the first place?
I added a little more justification to each of the dot points underneath "Changes" in this PR's description. Let me know if you'd like to elaborate on anything in particular there. |
|
Spoke to @TimothyBJacobs about making this more RESTful. Let's:
|
Makes the /wp/v2/widget and /wp/v2/widget-types endpoints: - Work with widget instance attributes that cannot be serialized to JSON - Less reliant on widget implementation details - Work better with widgets that don't extend WP_Widget
6737d4b to
1fa6816
Compare
We can be reasonably confident that this doesn't break any existing functionality on the frontend because there are E2E tests in adding-widgets.test.js that cover displaying widgets, saving widgets, etc. |
|
I think it would be helpful to call out what is being deprecated in the Gutenberg plugin release notes, so I've added Here's what can be included in the release notes:
|
|
This is ready for re-review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also have one question, but I think this is looking good.
|
Thanks for reviewing! |
spacedmonkey
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good
|
Thank you @TimothyBJacobs and @spacedmonkey! |
Summary
Makes the
/wp/v2/widgetand/wp/v2/widget-typesendpoints:WP_Widget.All changes are backwards compatible so that changes to the frontend can be made in a separate PR.
Changes
GET /wp/v2/widgets&GET /wp/v2/widgets/:idsettingsin favour ofinstance.instancesupports attributes that cannot be serialised to JSON viainstance.encodedandinstance.hash.Why: Not all widgets have settings that can be represented as JSON. See Legacy Widget add support for widgets with attributes that can't be serialised into JSON #28902.
instancesupports opt-in raw representation of attributes that can be serialised to JSON viainstance.raw.Why: If we know that a widget does have settings that can be represented as JSON, we should expose that so that the block editor can perform e.g. transformations.
instanceis set tonullwhen widget does not extendWP_Widget.Why: If a widget does not extend
WP_Widget, we cannot tell ahead of time what settings it has. All that we can say is that it has a HTML form and can accept form data from that HTML form.renderedandrendered_formto work for widgets that don't extendWP_Widgetby using lower level APIs.Why: If a widget does not extend
WP_Widget, we need to e.g. invoke the callback in$wp_registered_widgetsto display it instead of callingWP_Widget::display().widget_class.Why: Widgets that do not extend
WP_Widgetdo not have a class. Also, clients should not need to know about PHP class names.number.Why: Widgets that do not extend
WP_Widgetdo not have a number. Also,idcan already be used to uniquely identify the widget.nameanddescription.Why: These are redundant with fields that are in
/wp/v2/widget-types.widgets-api.php.Why: This allows for less code duplication when merged to Core.
POST /wp/v2/widgets&PUT /wp/v2/widgets/:idsettingsin favour ofinstance.instancesupports attributes that cannot be serialised to JSON viainstance.encodedandinstance.hash.Why: Not all widgets have settings that can be represented as JSON. See Legacy Widget add support for widgets with attributes that can't be serialised into JSON #28902.
instancesupports opt-in raw representation of attributes that can be serialised to JSON viainstance.raw.Why: If we know that a widget does have settings that can be represented as JSON, we should expose that so that the block editor can perform e.g. transformations.
instancecannot be set when the widget does not extendWP_Widget.Why: If a widget does not extend
WP_Widget, we cannot tell ahead of time what settings it has. All that we can say is that it has a HTML form and can accept form data from that HTML form.form_data.Why: This lets clients create an update widgets that do not extend
WP_Widgetby taking the form fromrendered_formand submitting it to the endpoint.$wp_registered_widget_updatesAPI.Why: Not all widgets extend
WP_Widgetand so we cannot useWP_Widget::update.widgets-api.php.Why: This allows for less code duplication when merged to Core.
GET /wp/v2/widget-types&GET /wp/v2/widget-types/:idoption_name.Why: This is an implementation detail and cannot be known ahead of time when the widget does not extend
WP_Widget.widget_class.Why: Widgets that do not extend
WP_Widgetdo not have a class. Also, clients should not need to know about PHP class names.customize_selective_refresh.Why: This data is only ever needed by the Customizer.
$wp_registered_widgetsAPI.Why: Not all widgets extend
WP_Widgetand so we cannot useWP_Widget::id_base.POST /wp/v2/widget-types/:id/form-renderer/wp/v2/widget-types/:id/encode.Why: The primary purpose of this endpoint now is to transform widget form data into an encoded instance object. This is how the Legacy Widget block will interact with the REST API. See Legacy Widget add support for widgets with attributes that can't be serialised into JSON #28902 (comment).
Examples
View examples
Using https://httpie.io. I've manually removed headers,
_links, and deprecated properties for brevity.Viewing a widget that extends
WP_Widget{ "id": "archives-3", "id_base": "archives", "instance": { "encoded": "YTozOntzOjU6InRpdGxlIjtzOjA6IiI7czo1OiJjb3VudCI7aTowO3M6ODoiZHJvcGRvd24iO2k6MDt9", "hash": "973cb1e41bcdaebe3d3d074f17842461" }, "rendered": "<section id=\"archives-3\" class=\"widget widget_archive\"><h2 class=\"widget-title\">Archives</h2><nav role=\"navigation\" aria-label=\"Archives\">\n\t\t\t<ul>\n\t\t\t\t\t<li><a href='http://wp-git-build.test/2021/03/'>March 2021</a></li>\n\t<li><a href='http://wp-git-build.test/2021/02/'>February 2021</a></li>\n\t\t\t</ul>\n\n\t\t\t</nav></section>", "rendered_form": "<p>\n\t\t\t<label for=\"widget-archives-3-title\">Title:</label>\n\t\t\t<input class=\"widefat\" id=\"widget-archives-3-title\" name=\"widget-archives[3][title]\" type=\"text\" value=\"\" />\n\t\t</p>\n\t\t<p>\n\t\t\t<input class=\"checkbox\" type=\"checkbox\" id=\"widget-archives-3-dropdown\" name=\"widget-archives[3][dropdown]\" />\n\t\t\t<label for=\"widget-archives-3-dropdown\">Display as dropdown</label>\n\t\t\t<br/>\n\t\t\t<input class=\"checkbox\" type=\"checkbox\" id=\"widget-archives-3-count\" name=\"widget-archives[3][count]\" />\n\t\t\t<label for=\"widget-archives-3-count\">Show post counts</label>\n\t\t</p>", "sidebar": "sidebar-1" }Viewing a widget that supports raw instance attributes
{ "id": "block-7", "id_base": "block", "instance": { "encoded": "YToxOntzOjc6ImNvbnRlbnQiO3M6NDk3OiI8IS0tIHdwOnBhcmFncmFwaCAtLT4KPHA+TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLjwvcD4KPCEtLSAvd3A6cGFyYWdyYXBoIC0tPiI7fQ==", "hash": "f8a6e0fd69fa7db77418b2835cfbfe7d", "raw": { "content": "<!-- wp:paragraph -->\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n<!-- /wp:paragraph -->" } }, "rendered": "<section id=\"block-7\" class=\"widget widget_block widget_text\">\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n</section>", "rendered_form": "<textarea id=\"widget-block-7-content\" name=\"widget-block[7][content]\" rows=\"6\" cols=\"50\" class=\"widefat text wp-block-widget-textarea\"><!-- wp:paragraph -->\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n<!-- /wp:paragraph --></textarea>", "sidebar": "sidebar-1" }Viewing a widget that does not extend
WP_Widget{ "id": "marquee_greeting", "id_base": "marquee_greeting", "instance": null, "rendered": "<marquee>Hey there!!</marquee>", "rendered_form": "<p>\n\t\t\t\t<label for=\"marquee-greeting\">Greeting:</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"marquee-greeting\"\n\t\t\t\t\tclass=\"widefat\"\n\t\t\t\t\tname=\"marquee-greeting\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tvalue=\"Hey there!!\"\n\t\t\t\t\tplaceholder=\"Hello!\"\n\t\t\t\t/>\n\t\t\t</p>", "sidebar": "sidebar-1" }Updating a widget that extends
WP_Widget{ "id": "archives-3", "id_base": "archives", "instance": { "encoded": "YTozOntzOjU6InRpdGxlIjtzOjEzOiJVcGRhdGVkIHRpdGxlIjtzOjU6ImNvdW50IjtpOjA7czo4OiJkcm9wZG93biI7aTowO30=", "hash": "85edac770804891295ed387be4c2d35a" }, "rendered": "<section id=\"archives-3\" class=\"widget widget_archive\"><h2 class=\"widget-title\">Updated title</h2><nav role=\"navigation\" aria-label=\"Updated title\">\n\t\t\t<ul>\n\t\t\t\t\t<li><a href='http://wp-git-build.test/2021/03/'>March 2021</a></li>\n\t<li><a href='http://wp-git-build.test/2021/02/'>February 2021</a></li>\n\t\t\t</ul>\n\n\t\t\t</nav></section>", "rendered_form": "<p>\n\t\t\t<label for=\"widget-archives-3-title\">Title:</label>\n\t\t\t<input class=\"widefat\" id=\"widget-archives-3-title\" name=\"widget-archives[3][title]\" type=\"text\" value=\"Updated title\" />\n\t\t</p>\n\t\t<p>\n\t\t\t<input class=\"checkbox\" type=\"checkbox\" id=\"widget-archives-3-dropdown\" name=\"widget-archives[3][dropdown]\" />\n\t\t\t<label for=\"widget-archives-3-dropdown\">Display as dropdown</label>\n\t\t\t<br/>\n\t\t\t<input class=\"checkbox\" type=\"checkbox\" id=\"widget-archives-3-count\" name=\"widget-archives[3][count]\" />\n\t\t\t<label for=\"widget-archives-3-count\">Show post counts</label>\n\t\t</p>", "sidebar": "sidebar-1" }Updating a widget that supports raw instance attributes
{ "id": "block-7", "id_base": "block", "instance": { "encoded": "YToxOntzOjc6ImNvbnRlbnQiO3M6MTc6IjwhLS0gd3A6bW9yZSAvLS0+Ijt9", "hash": "039ef226dee3600721702d1ffc731718", "raw": { "content": "<!-- wp:more /-->" } }, "rendered": "<section id=\"block-7\" class=\"widget widget_block\"></section>", "rendered_form": "<textarea id=\"widget-block-7-content\" name=\"widget-block[7][content]\" rows=\"6\" cols=\"50\" class=\"widefat text wp-block-widget-textarea\"><!-- wp:more /--></textarea>", "sidebar": "sidebar-1" }Updating a widget that does not extend
WP_Widget{ "id": "marquee_greeting", "id_base": "marquee_greeting", "instance": null, "rendered": "<marquee>Updated</marquee>", "rendered_form": "<p>\n\t\t\t\t<label for=\"marquee-greeting\">Greeting:</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"marquee-greeting\"\n\t\t\t\t\tclass=\"widefat\"\n\t\t\t\t\tname=\"marquee-greeting\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tvalue=\"Updated\"\n\t\t\t\t\tplaceholder=\"Hello!\"\n\t\t\t\t/>\n\t\t\t</p>", "sidebar": "sidebar-1" }Transforming form data into an encoded instance
{ "form": "<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=\"Hello there\" />\n\t\t</p>", "instance": { "encoded": "YToxOntzOjU6InRpdGxlIjtzOjExOiJIZWxsbyB0aGVyZSI7fQ==", "hash": "b106809b8ab93ea67141fe00767f371f" } }