diff --git a/backport-changelog/6.9/9469.md b/backport-changelog/6.9/9469.md index 26fa84c5dd18fe..5880ac5d4d013a 100644 --- a/backport-changelog/6.9/9469.md +++ b/backport-changelog/6.9/9469.md @@ -1,3 +1,4 @@ https://github.com/WordPress/wordpress-develop/pull/9469 * https://github.com/WordPress/gutenberg/pull/71389 +* https://github.com/WordPress/gutenberg/pull/71691 diff --git a/lib/compat/wordpress-6.9/block-bindings.php b/lib/compat/wordpress-6.9/block-bindings.php index 403c3b1f3db24d..245b962757f4bf 100644 --- a/lib/compat/wordpress-6.9/block-bindings.php +++ b/lib/compat/wordpress-6.9/block-bindings.php @@ -36,26 +36,39 @@ function gutenberg_block_bindings_render_block( $block_content, $block, $instanc return $block_content; } - $attributes = $instance->parsed_block['attrs']; // Process the block bindings and get attributes updated with the values from the sources. $computed_attributes = gutenberg_process_block_bindings( $instance ); if ( empty( $computed_attributes ) ) { return $block_content; } - // Merge the computed attributes with the original attributes. - $instance->attributes = array_merge( $attributes, $computed_attributes ); + /* + * Merge the computed attributes with the original attributes. + * + * Note that this is not a recursive merge, meaning that nested attributes -- + * such as block bindings metadata -- will be completely replaced. + * This is desirable. At this point, Core has already processed any block + * bindings that it supports. What remains to be processed are only the attributes + * for which support was added later (through the `block_bindings_supported_attributes` + * filter). To do so, we'll run `$instance->render()` once more + * so the block can update its content based on those attributes. + */ + $instance->attributes = array_merge( $instance->attributes, $computed_attributes ); + + /* + * If we're dealing with the Button block, we remove the bindings metadata + * in order to avoid having it reprocessed, which would lead to Core + * capitalizing the wrapper tag (e.g.
). + */ + if ( 'core/button' === $instance->name ) { + unset( $instance->parsed_block['attrs']['metadata']['bindings'] ); + } /** - * This filter is called from WP_Block::render(), after the block content has - * already been rendered. However, dynamic blocks expect their render() method - * to receive block attributes to have their bound values. This means that we have - * to re-render the block here. - * To do so, we'll set a flag that this filter checks when invoked to avoid infinite - * recursion. Furthermore, we can unset all of the block's bindings, as we know that - * they have been processed by the time we reach this point. + * This filter (`gutenberg_block_bindings_render_block`) is called from `WP_Block::render()`. + * To avoid infinite recursion, we set a flag that this filter checks when invoked which tells + * it to exit early. */ - unset( $instance->parsed_block['attrs']['metadata']['bindings'] ); $inside_block_bindings_render = true; $block_content = $instance->render(); $inside_block_bindings_render = false; diff --git a/phpunit/block-bindings-test.php b/phpunit/block-bindings-test.php index ff0d24aa190188..f868bba9537e42 100644 --- a/phpunit/block-bindings-test.php +++ b/phpunit/block-bindings-test.php @@ -45,6 +45,14 @@ function ( $supported_attributes ) { return $supported_attributes; } ); + + add_filter( + 'block_bindings_supported_attributes_core/image', + function ( $supported_attributes ) { + $supported_attributes[] = 'caption'; + return $supported_attributes; + } + ); } /** @@ -259,11 +267,19 @@ public function test_passing_uses_context_to_source() { * Tests if the block content is updated with the value returned by the source * for the Image block in the placeholder state. * + * Furthermore tests if the caption attribute, for which support is added via the + * `block_bindings_supported_attributes_core/image` filter, is correctly processed. + * * @covers ::register_block_bindings_source */ public function test_update_block_with_value_from_source_image_placeholder() { - $get_value_callback = function () { - return 'https://example.com/image.jpg'; + $get_value_callback = function ( $source_args, $block_instance, $attribute_name ) { + if ( 'url' === $attribute_name ) { + return 'https://example.com/image.jpg'; + } + if ( 'caption' === $attribute_name ) { + return 'Example Image'; + } }; register_block_bindings_source( @@ -275,8 +291,8 @@ public function test_update_block_with_value_from_source_image_placeholder() { ); $block_content = << -
+ +
HTML; $parsed_blocks = parse_blocks( $block_content ); @@ -289,7 +305,12 @@ public function test_update_block_with_value_from_source_image_placeholder() { "The 'url' attribute should be updated with the value returned by the source." ); $this->assertSame( - '
', + 'Example Image', + $block->attributes['caption'], + "The 'caption' attribute should be updated with the value returned by the source." + ); + $this->assertSame( + '
Example Image
', trim( $result ), 'The block content should be updated with the value returned by the source.' );