-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Customizer: Add widget blocks section. #16204
Conversation
ddb4d54
to
bb27097
Compare
The issues have been fixed. They were caused by an incompatibility between how widget blocks are stored and legacy widgets sanitization for the Customizer. Next up, live-preview enhancements! Relevant commit:
|
.gitignore
Outdated
@@ -12,6 +12,7 @@ phpcs.xml | |||
yarn.lock | |||
docker-compose.override.yml | |||
/wordpress | |||
.vscode |
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.
You can create a global gitignore file instead to so you don't have to include IDE specific ignores to each project:
https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore
We would have to keep a long list of IDE specific files for each WordPress project instead :)
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.
Done, thanks 👍
* Start: Include for phase 2 | ||
* | ||
* @package gutenberg | ||
* @since x.x.x |
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.
Next planned Gutenberg release is 6.0.
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.
👍
lib/widgets-page.php
Outdated
@@ -58,15 +58,21 @@ function gutenberg_widgets_init( $hook ) { | |||
wp_add_inline_script( | |||
'wp-edit-widgets', | |||
sprintf( | |||
'wp.editWidgets.initialize( "widgets-editor", %s );', | |||
'window.addEventListener( "load", function() { | |||
wp.editWidgets.initialize( "widgets-editor", %s ); |
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 think the theory here is that editWidgets
is specific to the widgets screen and we didn't initially think of using it in the customizer directly.
I can see how it's a quick way to arrive to something usable, the issue though is that it forces us to play with CSS to have a sidebar with the "save" button...
I think ideally, we'd:
- Extract the common parts between the customizer and the widgets screen into a
@wordpress/widgets
or@wordpress/widget-areas
package - Build a new
widgets-customizer-panel
package that use the common parts in order to bootstrap the customizer package. - Try to merges the save button with the customizer's save button. (We shouldn't need two separate saving actions)
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.
What if we do that all in the same, current package. Merging the save buttons when rendering in the customizer should be simpler than splitting this into multiple packages.
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.
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.
Hi @epiqueras, thank you for working on this task! There is good progress here in a short amount of time :)
I guess having a separate package for the customizer is going to be beneficial down the road as @youknowriad suggested.
I think we should also have a customizer.php where we add the customizer specific PHP code.
lib/widgets-page.php
Outdated
'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');' | ||
); | ||
|
||
if ( 'gutenberg_customizer' !== $hook ) { // `get_block_editor_server_block_settings` is undefined in the Customizer. |
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.
It will be a complex problem to solve; we can implement a core patch to make the function available in the customizer. But users of the plugin using the current WP version would notice some crashes, maybe there is some init function we can call, but I guess for now we can add a "Todo:" so we don't miss the need to fix this.
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.
Adding the comment for now.
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.
What does this mean exactly? Block registered server side won't be available in the customizer?
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't we just "include" the file where it's defined?
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.
What does this mean exactly? Block registered server side won't be available in the customizer?
Yes
Can't we just "include" the file where it's defined?
Huh, I got rid of the if guard and it worked. I don't see any obvious changes in Core that would have fixed this 🤔
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.
gutenberg.php
Outdated
* @param \WP_Customize_Manager $wp_customize An instance of the class that controls most of the Theme Customization API for WordPress 3.4 and newer. | ||
* @since x.x.x | ||
*/ | ||
function gutenberg_customize_register( $wp_customize ) { |
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.
This code is experimental, and if we need to merge the plugin to the core while we are not finished yet having the code in the middle of the file may make the task harder and more error-prone. I guess we can follow a similar approach to what we did with the widgets screen and have a separate file called customizer.php that contains this code and then we can require this code in load.php.
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.
Good idea.
lib/widgets-page.php
Outdated
@@ -58,15 +58,21 @@ function gutenberg_widgets_init( $hook ) { | |||
wp_add_inline_script( | |||
'wp-edit-widgets', | |||
sprintf( | |||
'wp.editWidgets.initialize( "widgets-editor", %s );', | |||
'window.addEventListener( "load", function() { |
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.
The core version to load the block editor uses:
wp.domReady( function() {
resolve( wp.editPost.initializeEditor( 'editor', "%s", %d, %s, %s ) );
} );
I guess we can use the same function here.
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.
Awesome, didn't know we had this!
lib/widgets.php
Outdated
@@ -213,3 +213,23 @@ 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' ); | |||
|
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 guess we can move this logic to a customizer.php file.
For reference, this code was added because of the global $wp_registered_widgets used in this function:
public function sanitize_sidebar_widgets_js_instance( $widget_ids ) {
global $wp_registered_widgets;
$widget_ids = array_values( array_intersect( $widget_ids, array_keys( $wp_registered_widgets ) ) );
return $widget_ids;
}
Does not contains the old format (an array of widgets) but contains the new format (a post id), I guess the best solution is to undestand why the global does not contain the backcompatible format and fix that problem as it may cause problems with plugins. If that is not possible I guess we can use this filter but instead of unsetting sanitize_js_callback I think we can make it point to new function where we compute the correct inputs based on the post id and we pass this values to the function that was set in $args['sanitize_js_callback'] before.
This does not seem a problem form this PR so I think we can address it separately provided we have a "Todo:" note to not miss this task.
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.
Yeah I wrote that in the documentation:
Lines 218 to 221 in 4899f9a
* Filters the Customizer widget settings arguments. | |
* This is needed because the Customizer registers settings for the raw registered widgets, without going through the `sidebars_widgets` filter. | |
* The `WP_Customize_Widgets` class expects sidebars to have an array of widgets registered, not a post ID. | |
* This results in the value passed to `sanitize_js_callback` being `null` and throwing an error. |
But, the issue is that $widget_ids
is an int when it should be an array.
This filter,
public static function swap_out_sidebars_blocks_for_block_widgets( $sidebars_widgets_input ) { |
I've moved it to customizer.php and added a TODO.
@jorgefilipecosta 's feedback. Relevant commit:
The next tasks would be not rendering the |
Also remove the `.vscode` entry in .gitignore.
…lly. Also add missing TODOs.
7fa5a38
to
271a49f
Compare
Saving customizer changes now also saves widget block edits and live preview is working:
|
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 took this for a spin, and it's great. I feel with the saving and previewing we kind of addressed the most difficult challenges of this screen.
Some things that would be good to improve.
-
I think the "Inspector" should probably be shown in a modal instead of a sidebar in that screen. This is doable by adding a button to toggle the modal to the Block settings menu (there's a lot for that). By I feel it could be left for a separate PR and just remove the inspector from the initial implementation.
-
Instead of reusing
wp.editWidgets.initialize( "widgets-editor", %s );
and trying to detect whether we're the customizer or widgets screen using DOM or availability of globals, I think we should have:- a separate wp.editCustomizerWidgetsPanel function to render a separate React Tree that is directly optimized for the customizer screen.
- reuse as much as we can from the edit-widgets Tree of components (WidgetArea...)
- Avoid rendering the Inspector in that customizer tree (we could use a modal but could be a separate PR)
-
I think there are some z-index issues for the block toolbar and styling issues for the areas (the width is too small by default) but these tweaks can also be addressed separately.
What do you think?
I also had some PHP notices while saving the areas
|
Yeah, that would look a lot better. I'll follow up this PR with that as suggested.
This makes a lot of sense, specially because the current tree is still being modified in parallel, but what if we have
I'll fix those in the new component tree in this PR, should be simple.
Looks like new Customizer PHP incompatibilities with post IDs being used as sidebar content. There's probably a hook to bailout from whatever it's trying to do, I'll look into it after the other changes. |
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.
This seems almost ready for a v1.
I'd appreciate a second review @jorgefilipecosta @aduth and others.
|
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.
Hi @epiqueras, excellent work here, it is fantastic that you managed to find solutions that allow us to test complex feature like the customizer previews 👍
I think this is almost ready to merge; I left some comments that I think we should consider before the merge.
They are mostly changes to reduce the impact on users that are not trying the block widgets experiments.
wp_register_widget_control( | ||
$widget_id, | ||
__( 'Blocks Area', 'gutenberg' ), | ||
'noop', |
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'm getting the following warning:
call_user_func_array() expects parameter 1 to be a valid callback, function 'noop' not found or invalid function name
I guess in PHP we don't have a defined noop callback.
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 wonder why I didn't get that error.
I'll change it to 'echo'.
// Check that all the necessary globals are present. | ||
if ( window.wp && window.wp.customize && window.wp.data ) { | ||
// Wait for the Customizer to finish bootstrapping. | ||
window.wp.customize.bind( 'ready', () => |
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.
Would it make sense to just perform this initialization when the user opens the "Block Widgets" section?
Right now for users that never used our experimental block widget features and have normal widgets we are breaking the ability for them to preview the widgets. previewBlocksInWidgetArea ends being called with a set of legacy widget blocks (just HTML comments) this changes the dom the Widgets section was expecting and makes it impossible to see the previews.
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 am changing it to run after the editor finishes setup so that it only fires after the first edit.
`[data-customize-partial-placement-context*='"sidebar_id":"${ id }"']` | ||
); | ||
if ( widgetArea ) { | ||
widgetArea.innerHTML = serialize( blocks ); |
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.
Related to another comment, maybe if all the blocks are legacy widget blocks, we should not update the preview. Users that just have normal widgets and never used our new block mechanism may open the Block Widgets section, and when they do it, they will lose the widget previews right away even if they go back to the standard widget sections, this check will reduce a little bit that problem.
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.
That fixes this too, but widget block changes will still take precedence over legacy widget ones. I think we should just show one or the other, having both is confusing.
lib/customizer.php
Outdated
); | ||
$wp_customize->add_section( | ||
'gutenberg_widget_blocks', | ||
array( 'title' => __( 'Widget Blocks', 'gutenberg' ) ) |
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.
Would it make sense to name this section 'Widget Blocks (Experimental)', to make users more aware of the experimental nature of this section? Similar to what we did with the widget blocks screen and the legacy widgets block.
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.
Yes
* @yields {Object} Action object. | ||
*/ | ||
export function* setupWidgetAreas() { | ||
export function* setupWidgetAreas( savedWidgetAreas ) { |
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.
It seems we are calling setupWidgetAreas just as a way to replace the blocks with the customizer changeset version. Can't we just do a loop call to updateBlocksInWidgetArea to replace blocks with customizer changeset version, avoiding the need to change this action?
if ( null === self::$unfiltered_sidebar_widgets ) { | ||
self::$unfiltered_sidebar_widgets = $sidebars_widgets; | ||
} | ||
$changeset_data = null; | ||
if ( is_customize_preview() ) { |
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.
Maybe a function exists check on is_customize_preview provides extra security to not break special sites that may not have this function defined.
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.
It seems we are calling setupWidgetAreas just as a way to replace the blocks with the customizer changeset version. Can't we just do a loop call to updateBlocksInWidgetArea to replace blocks with customizer changeset version, avoiding the need to change this action?
Good catch! This is actually not needed anymore after the changes for supporting share URLs. Changeset data is already rendered on the server.
Maybe a function exists check on is_customize_preview provides extra security to not break special sites that may not have this function defined.
Yes!
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.
Changeset data is already rendered on the server.
Actually, no, because the store loads from an API that doesn't go through that filter, I'll go with the updateBlocksInWidgetArea
approach after setup and before the subscription.
- Guard for undefined functions. - Add "experimental" warning to section title. - Don't let customizer syncing overwrite legacy widget previews until after the first edit.
649b375
to
a86e801
Compare
@jorgefilipecosta 's feedback
|
wp_register_widget_control( | ||
$widget_id, | ||
__( 'Blocks Area', 'gutenberg' ), | ||
'echo', |
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.
This still triggers an error:
call_user_func_array() expects parameter 1 to be a valid callback, function 'echo' not found or invalid function name
It seems echo is not considered a callback function, but using a function that prints something would be dangerous anyway as printing something during REST request may make the requests invalid.
It seems our best bet is to create a simple noop function ourselves and pass its name here.
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.
Will do that 👍
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'm noticing a bug that was not happening in the tests yesterday, not sure if it is something in my setup, it would be good to have a double check here:
If we use the normal widget screen to set up widgets and then we go to the customizer we delete the legacy widget blocks, we add some blocks (paragraphs) and we press publish right away without saving a draft first, the saving does not happen.
It works as expected for me. But I think one changeset has to be taking precedence over the other. I don't think showing both edit options at the same time is a good idea. It will confuse users at best, and cause bugs at worst. Can we hide the legacy option if the new one is enabled?
|
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.
Thank you for all the work, the iterations and diving deep on this feature 🥇. The initial objective was to have something experimental that we can test and iterate on, and I think we achieved this objective. This is a complex problem, and we have a good first step here.
I think we can merge (right after Travis is happy) and continue the development in other PR's 💥🎉.
Travis is identifying a lint problem on FILE: /app/lib/customizer.php. We should also update the "since versions" on some functions. Feel free to apply these changes and merge after.
For future reference/tests, I'm also writing here potential issues found during my tests. I don't think they are blockers.
Some issues I noticed that I think it is because we did not have yet a mechanism to handle the case:
- When someone does some changes in the widgets tab (e.g., add a new widget) even if they are saved as drafts our block widget section does not use it.
- When we preview another theme in the customizer, the standard widgets section updates to reflect the areas of the new theme but the new section doesn’t.
Things that may not be related/may be something on my setup/ plugins I have, etc:
- There is a strange problem if we add blocks in a widget section and then we reload the old widget screen two times the blocks get removed, and some widgets get used (may not be related to this PR, previously a similar problem was happening but just when a changed was applied on the widget screen).
- Adding blocks in the customizer on the 2014 theme may not work well. Problem described in more detail above; it works well when the area contains blocks, the problem only happens when the area contains standard widgets, and we are saving blocks for the first time (the problem seems intermittent).
Another issue I noticed is that if we add blocks to an area. We save everything; we reload the customizer we go to the standard widgets section; we delete the "Blocks Area" widget, and we try to re-add other widgets, the widget add does not work, and it is impossible for the user to re-add widgets using just the customizer. So if a user tries our experimental section and then wants to revert the only option is using the old widget screen, it is impossible using just the customizer, and some users may not be aware the widgets screen exist and use the customizer all the time. I guess this issue is the one with the most priority.
|
Congrats for your first PR @epiqueras 🎉 |
cc @aduth @jorgefilipecosta
Closes #13205
Description
This PR adds the widget blocks editor to a new section in the Customizer. The
wp-edit-widgets
package that powers the new widgets page in WPAdmin has been used for this.This is still a work in progress as
wp-edit-widgets
appears to have compatibility issues with legacy widgets. Some combinations of legacy widgets and widget blocks in some themes, e.g. Twenty Nineteen, break the Customizer or sometimes don't save the widgets for the right widget content area. All of the issues seem to be related to howwp-edit-widgets
persists data.After those issues are resolved, more work needs to be done to make the live preview work without page refreshes.
Updates
How has this been tested?
This has only been tested manually.
Screenshots
Types of Changes
New Feature: Add a widget blocks section to the Customizer.
Checklist: