From 6df958fe2c6cd59db2409a1a4f769bf365022946 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 28 Oct 2024 11:02:43 +0530 Subject: [PATCH 1/3] Update class-wp-block.php --- src/wp-includes/class-wp-block.php | 60 ++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/class-wp-block.php b/src/wp-includes/class-wp-block.php index 8cfa996028da2..5fa1adaa31ed8 100644 --- a/src/wp-includes/class-wp-block.php +++ b/src/wp-includes/class-wp-block.php @@ -56,7 +56,7 @@ class WP_Block { * @var array * @access protected */ - protected $available_context; + protected $available_context = array(); /** * Block type registry. @@ -140,6 +140,26 @@ public function __construct( $block, $available_context = array(), $registry = n $this->available_context = $available_context; + $this->refresh_context_dependents(); + $this->refresh_parsed_block_dependents(); + } + + /** + * Updates the context for the current block and its inner blocks. + * + * This method updates the block's `context` property by merging the available context from ancestor blocks + * with the block's own context values. It removes specific context keys (`postId` and `postType`) that should + * not be carried over. The block only consumes the context keys specified in its registered block type (`uses_context`). + * + * After updating its own context, the method also updates the context of inner blocks, if any, by passing down + * any context values the block provides (`provides_context`). + * + * If the block has inner blocks, the method recursively processes them by creating new instances of `WP_Block` + * for each inner block and updating their context based on the block's `provides_context` property. + * + * @since 6.7.0 + */ + public function refresh_context_dependents() { if ( ! empty( $this->block_type->uses_context ) ) { foreach ( $this->block_type->uses_context as $context_name ) { if ( array_key_exists( $context_name, $this->available_context ) ) { @@ -147,8 +167,22 @@ public function __construct( $block, $available_context = array(), $registry = n } } } + } - if ( ! empty( $block['innerBlocks'] ) ) { + /** + * Updates the parsed block content for the current block and its inner blocks. + * + * This method sets the `inner_html` and `inner_content` properties of the block based on the parsed + * block content provided during initialization. It ensures that the block instance reflects the + * most up-to-date content for both the inner HTML and any string fragments around inner blocks. + * + * If the block has inner blocks, this method initializes a new `WP_Block_List` for them, ensuring the + * correct content and context are updated for each nested block. + * + * @since 6.7.0 + */ + public function refresh_parsed_block_dependents() { + if ( ! empty( $this->parsed_block['innerBlocks'] ) ) { $child_context = $this->available_context; if ( ! empty( $this->block_type->provides_context ) ) { @@ -159,15 +193,15 @@ public function __construct( $block, $available_context = array(), $registry = n } } - $this->inner_blocks = new WP_Block_List( $block['innerBlocks'], $child_context, $registry ); + $this->inner_blocks = new WP_Block_List( $this->parsed_block['innerBlocks'], $child_context, $this->registry ); } - if ( ! empty( $block['innerHTML'] ) ) { - $this->inner_html = $block['innerHTML']; + if ( ! empty( $this->parsed_block['innerHTML'] ) ) { + $this->inner_html = $this->parsed_block['innerHTML']; } - if ( ! empty( $block['innerContent'] ) ) { - $this->inner_content = $block['innerContent']; + if ( ! empty( $this->parsed_block['innerContent'] ) ) { + $this->inner_content = $this->parsed_block['innerContent']; } } @@ -506,7 +540,8 @@ public function render( $options = array() ) { if ( ! is_null( $pre_render ) ) { $block_content .= $pre_render; } else { - $source_block = $inner_block->parsed_block; + $source_block = $inner_block->parsed_block; + $inner_block_context = $inner_block->context; /** This filter is documented in wp-includes/blocks.php */ $inner_block->parsed_block = apply_filters( 'render_block_data', $inner_block->parsed_block, $source_block, $parent_block ); @@ -514,6 +549,15 @@ public function render( $options = array() ) { /** This filter is documented in wp-includes/blocks.php */ $inner_block->context = apply_filters( 'render_block_context', $inner_block->context, $inner_block->parsed_block, $parent_block ); + if ( $inner_block->context !== $inner_block_context ) { + $inner_block->available_context = array_merge( $this->available_context, $inner_block->context ); + $inner_block->refresh_context_dependents(); + } + + if ( $inner_block->parsed_block !== $source_block ) { + $inner_block->refresh_parsed_block_dependents(); + } + $block_content .= $inner_block->render(); } From 2519a649b43acae67a33569f882c7540f34757cf Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 28 Oct 2024 11:44:35 +0530 Subject: [PATCH 2/3] Update renderBlock.php --- tests/phpunit/tests/blocks/renderBlock.php | 127 +++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/tests/phpunit/tests/blocks/renderBlock.php b/tests/phpunit/tests/blocks/renderBlock.php index 0c1c4ce10a1ee..d99c50b4006fc 100644 --- a/tests/phpunit/tests/blocks/renderBlock.php +++ b/tests/phpunit/tests/blocks/renderBlock.php @@ -192,4 +192,131 @@ public function test_default_context_is_filterable() { $this->assertSame( array( 'example' => 'ok' ), $provided_context[0] ); } + + /** + * Tests the behavior of the 'render_block_context' filter based on the location of the filtered block. + * + * @ticket 62046 + */ + public function test_render_block_context_inner_blocks() { + $provided_context = array(); + + register_block_type( + 'tests/context-provider', + array( + 'provides_context' => array( 'example' ), + ) + ); + + register_block_type( + 'tests/context-consumer', + array( + 'uses_context' => array( 'example' ), + 'render_callback' => static function ( $attributes, $content, $block ) use ( &$provided_context ) { + $provided_context = $block->context; + + return ''; + }, + ) + ); + + // Filter the context provided by the test block. + add_filter( + 'render_block_context', + function ( $context, $parsed_block ) { + if ( isset( $parsed_block['blockName'] ) && 'tests/context-provider' === $parsed_block['blockName'] ) { + $context['example'] = 'ok'; + } + + return $context; + }, + 10, + 2 + ); + + // Test inner block context when the provider block is a top-level block. + do_blocks( + << + + +HTML + ); + $this->assertTrue( isset( $provided_context['example'] ), 'Test block is top-level block: Context should include "example"' ); + $this->assertSame( 'ok', $provided_context['example'], 'Test block is top-level block: "example" in context should be "ok"' ); + + // Test inner block context when the provider block is an inner block. + do_blocks( + << + + + + +HTML + ); + $this->assertTrue( isset( $provided_context['example'] ), 'Test block is inner block: Block context should include "example"' ); + $this->assertSame( 'ok', $provided_context['example'], 'Test block is inner block: "example" in context should be "ok"' ); + } + + /** + * Tests that the 'render_block_context' filter arbitrary context. + * + * @ticket 62046 + */ + public function test_render_block_context_allowed_context() { + $provided_context = array(); + + register_block_type( + 'tests/context-consumer', + array( + 'uses_context' => array( 'example' ), + 'render_callback' => static function ( $attributes, $content, $block ) use ( &$provided_context ) { + // Assuming $block is your WP_Block object + $reflection = new ReflectionClass($block); + $property = $reflection->getProperty('available_context'); + $property->setAccessible(true); // Make the protected property accessible + + // Get the value of available_context + $available_context = $property->getValue($block); + + $provided_context = $available_context; + + return ''; + }, + ) + ); + + // Filter the context provided to the test block. + add_filter( + 'render_block_context', + function ( $context, $parsed_block ) { + if ( isset( $parsed_block['blockName'] ) && 'tests/context-consumer' === $parsed_block['blockName'] ) { + $context['arbitrary'] = 'ok'; + } + + return $context; + }, + 10, + 2 + ); + + do_blocks( + << +HTML + ); + $this->assertTrue( isset( $provided_context['arbitrary'] ), 'Test block is top-level block: Block context should include "arbitrary"' ); + $this->assertSame( 'ok', $provided_context['arbitrary'], 'Test block is top-level block: "arbitrary" in context should be "ok"' ); + + do_blocks( + << + + +HTML + ); + $this->assertTrue( isset( $provided_context['arbitrary'] ), 'Test block is inner block: Block context should include "arbitrary"' ); + $this->assertSame( 'ok', $provided_context['arbitrary'], 'Test block is inner block: "arbitrary" in context should be "ok"' ); + } } From 28c93634468e738cd98e246b71df078f3c06541e Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 17 Jan 2025 09:32:20 +0530 Subject: [PATCH 3/3] Update renderBlock.php --- tests/phpunit/tests/blocks/renderBlock.php | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/phpunit/tests/blocks/renderBlock.php b/tests/phpunit/tests/blocks/renderBlock.php index d99c50b4006fc..3e1b88d5d4d0e 100644 --- a/tests/phpunit/tests/blocks/renderBlock.php +++ b/tests/phpunit/tests/blocks/renderBlock.php @@ -272,15 +272,7 @@ public function test_render_block_context_allowed_context() { array( 'uses_context' => array( 'example' ), 'render_callback' => static function ( $attributes, $content, $block ) use ( &$provided_context ) { - // Assuming $block is your WP_Block object - $reflection = new ReflectionClass($block); - $property = $reflection->getProperty('available_context'); - $property->setAccessible(true); // Make the protected property accessible - - // Get the value of available_context - $available_context = $property->getValue($block); - - $provided_context = $available_context; + $provided_context = $block->context; return ''; }, @@ -306,8 +298,7 @@ function ( $context, $parsed_block ) { HTML ); - $this->assertTrue( isset( $provided_context['arbitrary'] ), 'Test block is top-level block: Block context should include "arbitrary"' ); - $this->assertSame( 'ok', $provided_context['arbitrary'], 'Test block is top-level block: "arbitrary" in context should be "ok"' ); + $this->assertFalse( isset( $provided_context['arbitrary'] ), 'Test block is top-level block: Block context should not include "arbitrary"' ); do_blocks( << HTML ); + + /* + * These assertions assert something that ideally should not be the case: Inner blocks should respect the + * `uses_context` value just like top-level blocks do. However, due to logic in `WP_Block::render()`, the + * `context` property value itself is filterable when it should rather only apply to the `available_context` + * property. + * However, changing this behavior now would be a backward compatibility break, hence the assertion here. + * Potentially it can be reconsidered in the future, so that these two assertions could be replaced with an + * `assertFalse( isset( $provided_context['arbitrary'] ) )`. + */ $this->assertTrue( isset( $provided_context['arbitrary'] ), 'Test block is inner block: Block context should include "arbitrary"' ); $this->assertSame( 'ok', $provided_context['arbitrary'], 'Test block is inner block: "arbitrary" in context should be "ok"' ); }