Skip to content
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
1 change: 1 addition & 0 deletions backport-changelog/6.9/9469.md
Original file line number Diff line number Diff line change
@@ -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
35 changes: 24 additions & 11 deletions lib/compat/wordpress-6.9/block-bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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. <DIV>).
*/
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;
Expand Down
31 changes: 26 additions & 5 deletions phpunit/block-bindings-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
);
}

/**
Expand Down Expand Up @@ -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(
Expand All @@ -275,8 +291,8 @@ public function test_update_block_with_value_from_source_image_placeholder() {
);

$block_content = <<<HTML
<!-- wp:image {"metadata":{"bindings":{"url":{"source":"test/source"}}}} -->
<figure class="wp-block-image"><img alt=""/></figure>
<!-- wp:image {"metadata":{"bindings":{"url":{"source":"test/source"},"caption":{"source":"test/source"}}}} -->
<figure class="wp-block-image"><img alt=""/><figcaption class="wp-element-caption"></figcaption></figure>
<!-- /wp:image -->
HTML;
$parsed_blocks = parse_blocks( $block_content );
Expand All @@ -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(
'<figure class="wp-block-image"><img src="https://example.com/image.jpg" alt=""/></figure>',
'Example Image',
$block->attributes['caption'],
"The 'caption' attribute should be updated with the value returned by the source."
);
$this->assertSame(
'<figure class="wp-block-image"><img src="https://example.com/image.jpg" alt=""/><figcaption class="wp-element-caption">Example Image</figcaption></figure>',
trim( $result ),
'The block content should be updated with the value returned by the source.'
);
Expand Down
Loading