-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Meta Box back compat and fall backs #3554
Changes from all commits
95465e6
557bb01
996faed
08c322a
8a80393
1b1cd73
f030f01
305fb48
91c06fc
21a2375
b38729d
a160c28
fdae2d4
0b22b58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,38 @@ the superior developer and user experience of blocks however, especially once, | |
block templates are available, **converting PHP meta boxes to blocks is highly | ||
encouraged!** | ||
|
||
### Testing, Converting, and Maintaining Existing Meta Boxes | ||
|
||
Before converting meta boxes to blocks, it may be easier to test if a meta box works with Gutenberg, and explicitly mark it as such. | ||
|
||
If a meta box *doesn't* work with in Gutenberg, and updating it to work correctly is not an option, the next step is to add the `__block_editor_compatible_meta_box` argument to the meta box declaration: | ||
|
||
```php | ||
add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback', | ||
null, 'normal', 'high', | ||
array( | ||
'__block_editor_compatible_meta_box' => false, | ||
) | ||
); | ||
``` | ||
|
||
This will cause WordPress to fall back to the Classic editor, where the meta box will continue working as before. | ||
|
||
Explicitly setting `__block_editor_compatible_meta_box` to `true` will cause WordPress to stay in Gutenberg (assuming another meta box doesn't cause a fallback, of course). | ||
|
||
After a meta box is converted to a block, it can be declared as existing for backwards compatibility: | ||
|
||
```php | ||
add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback', | ||
null, 'normal', 'high', | ||
array( | ||
'__back_compat_meta_box' => false, | ||
) | ||
); | ||
``` | ||
|
||
When Gutenberg is run, this meta box will no longer be displayed in the meta box area, as it now only exists for backwards compatibility purposes. It will continue to be displayed correctly in the Classic editor, should some other meta box cause a fallback. | ||
|
||
### Meta Box Data Collection | ||
|
||
On each Gutenberg page load, the global state of post.php is mimicked, this is | ||
|
@@ -22,7 +54,9 @@ namely `add_meta_boxes`, `add_meta_boxes_{$post->post_type}`, and `do_meta_boxes | |
|
||
A copy of the global `$wp_meta_boxes` is made then filtered through | ||
`apply_filters( 'filter_gutenberg_meta_boxes', $_meta_boxes_copy );`, which will | ||
strip out any core meta boxes along with standard custom taxonomy meta boxes. | ||
strip out any core meta boxes, standard custom taxonomy meta boxes, and any meta | ||
boxes that have declared themselves as only existing for backwards compatibility | ||
purposes. | ||
|
||
Then each location for this particular type of meta box is checked for whether it | ||
is active. If it is not empty a value of true is stored, if it is empty a value | ||
|
@@ -36,7 +70,7 @@ have to do, unless we want to move `createEditorInstance()` to fire in the foote | |
or at some point after `admin_head`. With recent changes to editor bootstrapping | ||
this might now be possible. Test with ACF to make sure. | ||
|
||
### Redux and React Meta Box Management. | ||
### Redux and React Meta Box Management | ||
|
||
*The Redux store by default will hold all meta boxes as inactive*. When | ||
`INITIALIZE_META_BOX_STATE` comes in, the store will update any active meta box | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated but we should add this file to the Gutenberg Handbook. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, I didn't even notice that it wasn't there. 🙂 Added! |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -515,6 +515,10 @@ function gutenberg_filter_meta_boxes( $meta_boxes ) { | |
if ( isset( $data['callback'] ) && in_array( $data['callback'], $taxonomy_callbacks_to_unset ) ) { | ||
unset( $meta_boxes[ $page ][ $context ][ $priority ][ $name ] ); | ||
} | ||
// Filter out meta boxes that are just registered for back compat. | ||
if ( isset( $data['args']['__back_compat_meta_box'] ) && $data['args']['__back_compat_meta_box'] ) { | ||
unset( $meta_boxes[ $page ][ $context ][ $priority ][ $name ] ); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -550,3 +554,115 @@ function gutenberg_is_meta_box_empty( $meta_boxes, $context, $post_type ) { | |
} | ||
|
||
add_filter( 'filter_gutenberg_meta_boxes', 'gutenberg_filter_meta_boxes' ); | ||
|
||
/** | ||
* Go through the global metaboxes, and override the render callback, so we can trigger our warning if needed. | ||
* | ||
* @since 1.8.0 | ||
*/ | ||
function gutenberg_intercept_meta_box_render() { | ||
global $wp_meta_boxes; | ||
|
||
foreach ( $wp_meta_boxes as $post_type => $contexts ) { | ||
foreach ( $contexts as $context => $priorities ) { | ||
foreach ( $priorities as $priority => $boxes ) { | ||
foreach ( $boxes as $id => $box ) { | ||
if ( ! is_array( $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args'] ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we replace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can really only replace the first instance - There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too many JS in my head :) |
||
$wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args'] = array(); | ||
} | ||
if ( ! isset( $wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args']['__original_callback'] ) ) { | ||
$wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['args']['__original_callback'] = $box['callback']; | ||
$wp_meta_boxes[ $post_type ][ $context ][ $priority ][ $id ]['callback'] = 'gutenberg_override_meta_box_callback'; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
add_action( 'submitpost_box', 'gutenberg_intercept_meta_box_render' ); | ||
add_action( 'submitpage_box', 'gutenberg_intercept_meta_box_render' ); | ||
add_action( 'edit_page_form', 'gutenberg_intercept_meta_box_render' ); | ||
add_action( 'edit_form_advanced', 'gutenberg_intercept_meta_box_render' ); | ||
|
||
/** | ||
* Check if this metabox only exists for back compat purposes, show a warning if it doesn't. | ||
* | ||
* @since 1.8.0 | ||
* | ||
* @param mixed $object The object being operated on, on this screen. | ||
* @param array $box The current meta box definition. | ||
*/ | ||
function gutenberg_override_meta_box_callback( $object, $box ) { | ||
$callback = $box['args']['__original_callback']; | ||
unset( $box['args']['__original_callback'] ); | ||
|
||
$block_compatible = true; | ||
if ( isset( $box['args']['__block_editor_compatible_meta_box'] ) ) { | ||
$block_compatible = (bool) $box['args']['__block_editor_compatible_meta_box']; | ||
unset( $box['args']['__block_editor_compatible_meta_box'] ); | ||
} | ||
|
||
if ( isset( $box['args']['__back_compat_meta_box'] ) ) { | ||
$block_compatible |= (bool) $box['args']['__back_compat_meta_box']; | ||
unset( $box['args']['__back_compat_meta_box'] ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just per curiosity, why do we unset these keys? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copying the same pattern from Core - the meta box render function doesn't need to know about these keys. |
||
} | ||
|
||
if ( ! $block_compatible ) { | ||
gutenberg_show_meta_box_warning( $callback ); | ||
} | ||
|
||
call_user_func( $callback, $object, $box ); | ||
} | ||
|
||
/** | ||
* Display a warning in the metabox that the current plugin is causing the fallback to the old editor. | ||
* | ||
* @since 1.8.0 | ||
* | ||
* @param callable $callback The function that a plugin has defined to render a meta box. | ||
*/ | ||
function gutenberg_show_meta_box_warning( $callback ) { | ||
// Only show the warning when WP_DEBUG is enabled. | ||
if ( ! WP_DEBUG ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should not show it even in "prod" while we're on the plugin phase and introduce this change later. To encourage people to check their metaboxes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did this way while we're still working out the path for plugin devs to move their meta boxes to blocks - it doesn't seem fair to show a warning that the dev can't do anything to fix. Once there are well documented upgrade paths, we can take away this check. Once it comes time to merge, we can evaluate how we want to merge it. It might even be valuable to have a "plugin x caused this fall back" message in prod, so users know what happened, and a "here are links to documentation and tutorials to upgrade your meta boxes" when |
||
return; | ||
} | ||
|
||
// Don't show in the Gutenberg meta box UI. | ||
if ( ! isset( $_REQUEST['classic-editor'] ) ) { | ||
return; | ||
} | ||
|
||
if ( is_array( $callback ) ) { | ||
$reflection = new ReflectionMethod( $callback[0], $callback[1] ); | ||
} else { | ||
$reflection = new ReflectionFunction( $callback ); | ||
} | ||
|
||
if ( $reflection->isInternal() ) { | ||
return; | ||
} | ||
|
||
$filename = $reflection->getFileName(); | ||
if ( strpos( $filename, WP_PLUGIN_DIR ) !== 0 ) { | ||
return; | ||
} | ||
|
||
$filename = str_replace( WP_PLUGIN_DIR, '', $filename ); | ||
$filename = preg_replace( '|^/([^/]*/).*$|', '\\1', $filename ); | ||
|
||
$plugins = get_plugins(); | ||
foreach ( $plugins as $name => $plugin ) { | ||
if ( strpos( $name, $filename ) === 0 ) { | ||
?> | ||
<div class="error inline"> | ||
<p> | ||
<?php | ||
/* translators: %s is the name of the plugin that generated this meta box. */ | ||
printf( __( 'Gutenberg incompatible meta box, from the "%s" plugin.', 'gutenberg' ), $plugin['Name'] ); | ||
?> | ||
</p> | ||
</div> | ||
<?php | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -219,10 +219,40 @@ function gutenberg_collect_meta_box_data() { | |
|
||
// If the meta box should be empty set to false. | ||
foreach ( $locations as $location ) { | ||
if ( isset( $_meta_boxes_copy[ $post->post_type ][ $location ] ) && gutenberg_is_meta_box_empty( $_meta_boxes_copy, $location, $post->post_type ) ) { | ||
if ( gutenberg_is_meta_box_empty( $_meta_boxes_copy, $location, $post->post_type ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This call is always returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, I might have had a weird setup at that moment. It's working as expected right now. |
||
$meta_box_data[ $location ] = false; | ||
} else { | ||
$meta_box_data[ $location ] = true; | ||
$incompatible_meta_box = false; | ||
// Check if we have a meta box that has declared itself incompatible with the block editor. | ||
foreach ( $_meta_boxes_copy[ $post->post_type ][ $location ] as $boxes ) { | ||
foreach ( $boxes as $box ) { | ||
/* | ||
* If __block_editor_compatible_meta_box is declared as a false-y value, | ||
* the meta box is not compatible with the block editor. | ||
*/ | ||
if ( is_array( $box['args'] ) | ||
&& isset( $box['args']['__block_editor_compatible_meta_box'] ) | ||
&& ! $box['args']['__block_editor_compatible_meta_box'] ) { | ||
$incompatible_meta_box = true; | ||
break 2; | ||
} | ||
} | ||
} | ||
|
||
// Incompatible meta boxes require an immediate redirect to the classic editor. | ||
if ( $incompatible_meta_box ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we not perform the redirect server-side here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the current code, no. |
||
?> | ||
<script type="text/javascript"> | ||
var joiner = '?'; | ||
if ( window.location.search ) { | ||
joiner = '&'; | ||
} | ||
window.location.href += joiner + 'classic-editor'; | ||
</script> | ||
<?php | ||
exit; | ||
} | ||
} | ||
} | ||
|
||
|
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.
Mixed whitespace, tabs and spaces.
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.
Noice. #3684.