mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-08-11 20:30:23 +00:00
Editor: Merge uses_context defined by block bindings sources with block types
Adds logic that fixes the limitation for souces by allowing merging the `uses_context` defined by block bindings sources into supported block types. Each source defines the context it needs and it is added to the block types that are using the block bindings API. Fixes #60525. Props santosguillamot, gziolo, czapla, thekt12. git-svn-id: https://develop.svn.wordpress.org/trunk@57641 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -75,18 +75,19 @@
|
||||
* @param array $source_properties {
|
||||
* The array of arguments that are used to register a source.
|
||||
*
|
||||
* @type string $label The label of the source.
|
||||
* @type callback $get_value_callback A callback executed when the source is processed during block rendering.
|
||||
* The callback should have the following signature:
|
||||
* @type string $label The label of the source.
|
||||
* @type callback $get_value_callback A callback executed when the source is processed during block rendering.
|
||||
* The callback should have the following signature:
|
||||
*
|
||||
* `function ($source_args, $block_instance,$attribute_name): mixed`
|
||||
* - @param array $source_args Array containing source arguments
|
||||
* used to look up the override value,
|
||||
* i.e. {"key": "foo"}.
|
||||
* - @param WP_Block $block_instance The block instance.
|
||||
* - @param string $attribute_name The name of an attribute .
|
||||
* The callback has a mixed return type; it may return a string to override
|
||||
* the block's original value, null, false to remove an attribute, etc.
|
||||
* `function ($source_args, $block_instance,$attribute_name): mixed`
|
||||
* - @param array $source_args Array containing source arguments
|
||||
* used to look up the override value,
|
||||
* i.e. {"key": "foo"}.
|
||||
* - @param WP_Block $block_instance The block instance.
|
||||
* - @param string $attribute_name The name of an attribute .
|
||||
* The callback has a mixed return type; it may return a string to override
|
||||
* the block's original value, null, false to remove an attribute, etc.
|
||||
* @type array $uses_context (optional) Array of values to add to block `uses_context` needed by the source.
|
||||
* }
|
||||
* @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure.
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,7 @@ function _register_block_bindings_pattern_overrides_source() {
|
||||
array(
|
||||
'label' => _x( 'Pattern Overrides', 'block bindings source' ),
|
||||
'get_value_callback' => '_block_bindings_pattern_overrides_get_value',
|
||||
'uses_context' => array( 'pattern/overrides' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,22 +13,20 @@
|
||||
* @since 6.5.0
|
||||
* @access private
|
||||
*
|
||||
* @param array $source_args Array containing source arguments used to look up the override value.
|
||||
* Example: array( "key" => "foo" ).
|
||||
* @param array $source_args Array containing source arguments used to look up the override value.
|
||||
* Example: array( "key" => "foo" ).
|
||||
* @param WP_Block $block_instance The block instance.
|
||||
* @return mixed The value computed for the source.
|
||||
*/
|
||||
function _block_bindings_post_meta_get_value( array $source_args ) {
|
||||
if ( ! isset( $source_args['key'] ) ) {
|
||||
function _block_bindings_post_meta_get_value( array $source_args, $block_instance ) {
|
||||
if ( empty( $source_args['key'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use the postId attribute if available.
|
||||
if ( isset( $source_args['postId'] ) ) {
|
||||
$post_id = $source_args['postId'];
|
||||
} else {
|
||||
// $block_instance->context['postId'] is not available in the Image block.
|
||||
$post_id = get_the_ID();
|
||||
if ( empty( $block_instance->context['postId'] ) ) {
|
||||
return null;
|
||||
}
|
||||
$post_id = $block_instance->context['postId'];
|
||||
|
||||
// If a post isn't public, we need to prevent unauthorized users from accessing the post meta.
|
||||
$post = get_post( $post_id );
|
||||
@@ -51,6 +49,7 @@ function _register_block_bindings_post_meta_source() {
|
||||
array(
|
||||
'label' => _x( 'Post Meta', 'block bindings source' ),
|
||||
'get_value_callback' => '_block_bindings_post_meta_get_value',
|
||||
'uses_context' => array( 'postId', 'postType' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,31 @@ final class WP_Block_Bindings_Registry {
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Supported source properties that can be passed to the registered source.
|
||||
*
|
||||
* @since 6.5.0
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_source_properties = array(
|
||||
'label',
|
||||
'get_value_callback',
|
||||
'uses_context',
|
||||
);
|
||||
|
||||
/**
|
||||
* Supported blocks that can use the block bindings API.
|
||||
*
|
||||
* @since 6.5.0
|
||||
* @var array
|
||||
*/
|
||||
private $supported_blocks = array(
|
||||
'core/paragraph',
|
||||
'core/heading',
|
||||
'core/image',
|
||||
'core/button',
|
||||
);
|
||||
|
||||
/**
|
||||
* Registers a new block bindings source.
|
||||
*
|
||||
@@ -53,18 +78,19 @@ final class WP_Block_Bindings_Registry {
|
||||
* @param array $source_properties {
|
||||
* The array of arguments that are used to register a source.
|
||||
*
|
||||
* @type string $label The label of the source.
|
||||
* @type callback $get_value_callback A callback executed when the source is processed during block rendering.
|
||||
* The callback should have the following signature:
|
||||
* @type string $label The label of the source.
|
||||
* @type callback $get_value_callback A callback executed when the source is processed during block rendering.
|
||||
* The callback should have the following signature:
|
||||
*
|
||||
* `function ($source_args, $block_instance,$attribute_name): mixed`
|
||||
* - @param array $source_args Array containing source arguments
|
||||
* used to look up the override value,
|
||||
* i.e. {"key": "foo"}.
|
||||
* - @param WP_Block $block_instance The block instance.
|
||||
* - @param string $attribute_name The name of the target attribute.
|
||||
* The callback has a mixed return type; it may return a string to override
|
||||
* the block's original value, null, false to remove an attribute, etc.
|
||||
* `function ($source_args, $block_instance,$attribute_name): mixed`
|
||||
* - @param array $source_args Array containing source arguments
|
||||
* used to look up the override value,
|
||||
* i.e. {"key": "foo"}.
|
||||
* - @param WP_Block $block_instance The block instance.
|
||||
* - @param string $attribute_name The name of the target attribute.
|
||||
* The callback has a mixed return type; it may return a string to override
|
||||
* the block's original value, null, false to remove an attribute, etc.
|
||||
* @type array $uses_context (optional) Array of values to add to block `uses_context` needed by the source.
|
||||
* }
|
||||
* @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure.
|
||||
*/
|
||||
@@ -107,7 +133,7 @@ final class WP_Block_Bindings_Registry {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate that the source properties contain the label */
|
||||
// Validates that the source properties contain the label.
|
||||
if ( ! isset( $source_properties['label'] ) ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
@@ -117,7 +143,7 @@ final class WP_Block_Bindings_Registry {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate that the source properties contain the get_value_callback */
|
||||
// Validates that the source properties contain the get_value_callback.
|
||||
if ( ! isset( $source_properties['get_value_callback'] ) ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
@@ -127,7 +153,7 @@ final class WP_Block_Bindings_Registry {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate that the get_value_callback is a valid callback */
|
||||
// Validates that the get_value_callback is a valid callback.
|
||||
if ( ! is_callable( $source_properties['get_value_callback'] ) ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
@@ -137,6 +163,25 @@ final class WP_Block_Bindings_Registry {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validates that the uses_context parameter is an array.
|
||||
if ( isset( $source_properties['uses_context'] ) && ! is_array( $source_properties['uses_context'] ) ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
__( 'The "uses_context" parameter must be an array.' ),
|
||||
'6.5.0'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! empty( array_diff( array_keys( $source_properties ), $this->allowed_source_properties ) ) ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
__( 'The $source_properties array contains invalid properties.' ),
|
||||
'6.5.0'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$source = new WP_Block_Bindings_Source(
|
||||
$source_name,
|
||||
$source_properties
|
||||
@@ -144,6 +189,20 @@ final class WP_Block_Bindings_Registry {
|
||||
|
||||
$this->sources[ $source_name ] = $source;
|
||||
|
||||
// Adds `uses_context` defined by block bindings sources.
|
||||
add_filter(
|
||||
'get_block_type_uses_context',
|
||||
function ( $uses_context, $block_type ) use ( $source ) {
|
||||
if ( ! in_array( $block_type->name, $this->supported_blocks, true ) || empty( $source->uses_context ) ) {
|
||||
return $uses_context;
|
||||
}
|
||||
// Use array_values to reset the array keys.
|
||||
return array_values( array_unique( array_merge( $uses_context, $source->uses_context ) ) );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,14 @@ final class WP_Block_Bindings_Source {
|
||||
*/
|
||||
private $get_value_callback;
|
||||
|
||||
/**
|
||||
* The context added to the blocks needed by the source.
|
||||
*
|
||||
* @since 6.5.0
|
||||
* @var array|null
|
||||
*/
|
||||
public $uses_context = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -57,9 +65,10 @@ final class WP_Block_Bindings_Source {
|
||||
* @param array $source_properties The properties of the source.
|
||||
*/
|
||||
public function __construct( string $name, array $source_properties ) {
|
||||
$this->name = $name;
|
||||
$this->label = $source_properties['label'];
|
||||
$this->get_value_callback = $source_properties['get_value_callback'];
|
||||
$this->name = $name;
|
||||
foreach ( $source_properties as $property_name => $property_value ) {
|
||||
$this->$property_name = $property_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,7 +180,7 @@ class WP_Block_Type {
|
||||
* @since 5.5.0
|
||||
* @var string[]
|
||||
*/
|
||||
public $uses_context = array();
|
||||
private $uses_context = array();
|
||||
|
||||
/**
|
||||
* Context provided by blocks of this type.
|
||||
@@ -366,6 +366,10 @@ class WP_Block_Type {
|
||||
return $this->get_variations();
|
||||
}
|
||||
|
||||
if ( 'uses_context' === $name ) {
|
||||
return $this->get_uses_context();
|
||||
}
|
||||
|
||||
if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
|
||||
return;
|
||||
}
|
||||
@@ -394,7 +398,7 @@ class WP_Block_Type {
|
||||
* or false otherwise.
|
||||
*/
|
||||
public function __isset( $name ) {
|
||||
if ( 'variations' === $name ) {
|
||||
if ( in_array( $name, array( 'variations', 'uses_context' ), true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -417,11 +421,6 @@ class WP_Block_Type {
|
||||
* @param mixed $value Property value.
|
||||
*/
|
||||
public function __set( $name, $value ) {
|
||||
if ( 'variations' === $name ) {
|
||||
$this->variations = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
|
||||
$this->{$name} = $value;
|
||||
return;
|
||||
@@ -616,4 +615,23 @@ class WP_Block_Type {
|
||||
*/
|
||||
return apply_filters( 'get_block_type_variations', $this->variations, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get block uses context.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function get_uses_context() {
|
||||
/**
|
||||
* Filters the registered uses context for a block type.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*
|
||||
* @param array $uses_context Array of registered uses context for a block type.
|
||||
* @param WP_Block_Type $block_type The full block type object.
|
||||
*/
|
||||
return apply_filters( 'get_block_type_uses_context', $this->uses_context, $this );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,23 +231,19 @@ class WP_Block {
|
||||
* @return array The computed block attributes for the provided block bindings.
|
||||
*/
|
||||
private function process_block_bindings() {
|
||||
$parsed_block = $this->parsed_block;
|
||||
|
||||
$computed_attributes = array();
|
||||
|
||||
// Allowed blocks that support block bindings.
|
||||
// TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes?
|
||||
$allowed_blocks = array(
|
||||
$parsed_block = $this->parsed_block;
|
||||
$computed_attributes = array();
|
||||
$supported_block_attributes = array(
|
||||
'core/paragraph' => array( 'content' ),
|
||||
'core/heading' => array( 'content' ),
|
||||
'core/image' => array( 'url', 'title', 'alt' ),
|
||||
'core/button' => array( 'url', 'text', 'linkTarget', 'rel' ),
|
||||
);
|
||||
|
||||
// If the block doesn't have the bindings property, isn't one of the allowed
|
||||
// If the block doesn't have the bindings property, isn't one of the supported
|
||||
// block types, or the bindings property is not an array, return the block content.
|
||||
if (
|
||||
! isset( $allowed_blocks[ $this->name ] ) ||
|
||||
! isset( $supported_block_attributes[ $this->name ] ) ||
|
||||
empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
|
||||
! is_array( $parsed_block['attrs']['metadata']['bindings'] )
|
||||
) {
|
||||
@@ -255,8 +251,8 @@ class WP_Block {
|
||||
}
|
||||
|
||||
foreach ( $parsed_block['attrs']['metadata']['bindings'] as $attribute_name => $block_binding ) {
|
||||
// If the attribute is not in the allowed list, process next attribute.
|
||||
if ( ! in_array( $attribute_name, $allowed_blocks[ $this->name ], true ) ) {
|
||||
// If the attribute is not in the supported list, process next attribute.
|
||||
if ( ! in_array( $attribute_name, $supported_block_attributes[ $this->name ], true ) ) {
|
||||
continue;
|
||||
}
|
||||
// If no source is provided, or that source is not registered, process next attribute.
|
||||
|
||||
@@ -114,6 +114,49 @@ HTML;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests passing `uses_context` as argument to the source.
|
||||
*
|
||||
* @ticket 60525
|
||||
*
|
||||
* @covers ::register_block_bindings_source
|
||||
*/
|
||||
public function test_passing_uses_context_to_source() {
|
||||
$get_value_callback = function ( $source_args, $block_instance, $attribute_name ) {
|
||||
$value = $block_instance->context['sourceContext'];
|
||||
return "Value: $value";
|
||||
};
|
||||
|
||||
register_block_bindings_source(
|
||||
self::SOURCE_NAME,
|
||||
array(
|
||||
'label' => self::SOURCE_LABEL,
|
||||
'get_value_callback' => $get_value_callback,
|
||||
'uses_context' => array( 'sourceContext' ),
|
||||
)
|
||||
);
|
||||
|
||||
$block_content = <<<HTML
|
||||
<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source", "args": {"key": "test"}}}}} -->
|
||||
<p>This should not appear</p>
|
||||
<!-- /wp:paragraph -->
|
||||
HTML;
|
||||
$parsed_blocks = parse_blocks( $block_content );
|
||||
$block = new WP_Block( $parsed_blocks[0], array( 'sourceContext' => 'source context value' ) );
|
||||
$result = $block->render();
|
||||
|
||||
$this->assertSame(
|
||||
'Value: source context value',
|
||||
$block->attributes['content'],
|
||||
"The 'content' should be updated with the value of the source context."
|
||||
);
|
||||
$this->assertSame(
|
||||
'<p>Value: source context value</p>',
|
||||
trim( $result ),
|
||||
'The block content should be updated with the value of the source context.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the block content is updated with the value returned by the source
|
||||
* for the Image block in the placeholder state.
|
||||
|
||||
@@ -39,6 +39,7 @@ class Tests_Blocks_wpBlockBindingsRegistry extends WP_UnitTestCase {
|
||||
'get_value_callback' => function () {
|
||||
return 'test-value';
|
||||
},
|
||||
'uses_context' => array( 'sourceContext' ),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -162,6 +163,23 @@ class Tests_Blocks_wpBlockBindingsRegistry extends WP_UnitTestCase {
|
||||
$this->assertFalse( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Should reject block bindings registration if `uses_context` is not an array.
|
||||
*
|
||||
* @ticket 60525
|
||||
*
|
||||
* @covers WP_Block_Bindings_Registry::register
|
||||
*
|
||||
* @expectedIncorrectUsage WP_Block_Bindings_Registry::register
|
||||
*/
|
||||
public function test_register_invalid_string_uses_context() {
|
||||
|
||||
self::$test_source_properties['uses_context'] = 'not-an-array';
|
||||
|
||||
$result = $this->registry->register( self::$test_source_name, self::$test_source_properties );
|
||||
$this->assertFalse( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Should accept valid block binding source.
|
||||
*
|
||||
@@ -179,6 +197,13 @@ class Tests_Blocks_wpBlockBindingsRegistry extends WP_UnitTestCase {
|
||||
),
|
||||
$result
|
||||
);
|
||||
$this->assertSame( 'test/source', $result->name );
|
||||
$this->assertSame( 'Test source', $result->label );
|
||||
$this->assertSame(
|
||||
'test-value',
|
||||
$result->get_value( array(), null, '' )
|
||||
);
|
||||
$this->assertEquals( array( 'sourceContext' ), $result->uses_context );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,4 +346,49 @@ class Tests_Blocks_wpBlockBindingsRegistry extends WP_UnitTestCase {
|
||||
$result = $this->registry->is_registered( self::$test_source_name );
|
||||
$this->assertTrue( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests merging `uses_context` from multiple sources.
|
||||
*
|
||||
* @ticket 60525
|
||||
*
|
||||
* @covers ::register_block_bindings_source
|
||||
* @covers WP_Block_Type::get_uses_context
|
||||
*/
|
||||
public function test_merging_uses_context_from_multiple_sources() {
|
||||
$get_value_callback = function () {
|
||||
return 'Anything';
|
||||
};
|
||||
|
||||
$block_registry = WP_Block_Type_Registry::get_instance();
|
||||
$original_uses_context = $block_registry->get_registered( 'core/paragraph' )->uses_context;
|
||||
|
||||
register_block_bindings_source(
|
||||
'test/source-one',
|
||||
array(
|
||||
'label' => 'Test Source One',
|
||||
'get_value_callback' => $get_value_callback,
|
||||
'uses_context' => array( 'commonContext', 'sourceOneContext' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_block_bindings_source(
|
||||
'test/source-two',
|
||||
array(
|
||||
'label' => 'Test Source Two',
|
||||
'get_value_callback' => $get_value_callback,
|
||||
'uses_context' => array( 'commonContext', 'sourceTwoContext' ),
|
||||
)
|
||||
);
|
||||
|
||||
$new_uses_context = $block_registry->get_registered( 'core/paragraph' )->uses_context;
|
||||
// Checks that the resulting `uses_context` contains the values from both sources.
|
||||
$this->assertContains( 'commonContext', $new_uses_context );
|
||||
$this->assertContains( 'sourceOneContext', $new_uses_context );
|
||||
$this->assertContains( 'sourceTwoContext', $new_uses_context );
|
||||
// Checks that the resulting `uses_context` added 3 unique items.
|
||||
$this->assertSame( count( $original_uses_context ) + 3, count( $new_uses_context ) );
|
||||
// Checks that the array isn't sparse to prevent issues in the editor.
|
||||
$this->assertSame( array_key_last( $new_uses_context ), count( $new_uses_context ) - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user