diff --git a/src/wp-includes/class-wp-block.php b/src/wp-includes/class-wp-block.php index c6ca8a3bd42be..b6631016b3e9f 100644 --- a/src/wp-includes/class-wp-block.php +++ b/src/wp-includes/class-wp-block.php @@ -246,7 +246,7 @@ private function process_block_bindings() { $supported_block_attributes = array( 'core/paragraph' => array( 'content' ), 'core/heading' => array( 'content' ), - 'core/image' => array( 'id', 'url', 'title', 'alt' ), + 'core/image' => array( 'id', 'url', 'title', 'alt', 'caption' ), 'core/button' => array( 'url', 'text', 'linkTarget', 'rel' ), ); @@ -333,6 +333,67 @@ private function replace_html( string $block_content, string $attribute_name, $s switch ( $block_type->attributes[ $attribute_name ]['source'] ) { case 'html': case 'rich-text': + if ( 'core/image' === $this->name && 'caption' === $attribute_name ) { + // Create private anonymous class until the HTML API provides `set_inner_html` method. + $bindings_processor_builder = new class( + 'Do not use this, it will not work. It is only here to create a subclass and call the static creator method', + WP_HTML_Processor::CONSTRUCTOR_UNLOCK_CODE + ) extends WP_HTML_Processor { + /** + * Replace the inner content of a figcaption element with the passed content. + * + * DO NOT COPY THIS METHOD. + * THE HTML PROCESSOR WILL HAVE A PROPER METHOD. + * USE IT INSTEAD. + * + * @since 6.7.0 + * + * @param string $new_content New content to insert in the figcaption element. + * @return bool Whether the inner content was properly replaced. + */ + public function set_figcaption_inner_html( $new_content ) { + // Check that the processor is paused on an opener tag. + if ( 'FIGCAPTION' !== $this->get_tag() || $this->is_tag_closer() ) { + return false; + } + + // Set position of the opening tag. + $this->set_bookmark( 'opening' ); + + // Once this element closes the depth will be one shallower than it is now. + $depth = $this->get_current_depth(); + while ( $this->next_token() && $this->get_current_depth() >= $depth ) { + // This is inside the FIGCAPTION element. + } + + if ( null !== $this->get_last_error() || $this->paused_at_incomplete_token() ) { + return false; + } + + // Set position of the closing tag. + $this->set_bookmark( 'closing' ); + + $opening = $this->bookmarks['_opening']; + $closing = $this->bookmarks['_closing']; + $start = $opening->start + $opening->length; + + $this->lexical_updates[] = new WP_HTML_Text_Replacement( + $start, + $closing->start - $start, + wp_kses_post( $new_content ) + ); + + return true; + } + }; + + $block_reader = $bindings_processor_builder::create_fragment( $block_content ); + if ( $block_reader->next_tag( 'figcaption' ) ) { + $block_reader->set_figcaption_inner_html( $source_value ); + } + return $block_reader->get_updated_html(); + } + $block_reader = new WP_HTML_Tag_Processor( $block_content ); // TODO: Support for CSS selectors whenever they are ready in the HTML API. diff --git a/tests/phpunit/tests/block-bindings/render.php b/tests/phpunit/tests/block-bindings/render.php index 09c6fb50075fb..6b9139c001b93 100644 --- a/tests/phpunit/tests/block-bindings/render.php +++ b/tests/phpunit/tests/block-bindings/render.php @@ -298,4 +298,41 @@ public function test_default_binding_for_pattern_overrides() { 'The `__default` attribute should be replaced with the real attribute prior to the callback.' ); } + + /** + * Tests that replacing inner text for bound attributes works as expected. + * + * @ticket 61466 + * + * @covers WP_Block::process_block_bindings + */ + public function test_replacing_inner_text_with_block_bindings_value() { + $get_value_callback = function () { + return '$12.50'; + }; + + register_block_bindings_source( + self::SOURCE_NAME, + array( + 'label' => self::SOURCE_LABEL, + 'get_value_callback' => $get_value_callback, + ) + ); + + $block_content = << +
Default value
+ +HTML; + + $parsed_blocks = parse_blocks( $block_content ); + $block = new WP_Block( $parsed_blocks[0] ); + $result = $block->render(); + + $this->assertSame( + '
$12.50
', + trim( $result ), + 'The image caption should be updated with the value returned by the source.' + ); + } }