Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
63d381f
Read `uses_context` defined by sources
SantosGuillamot Feb 13, 2024
65e259a
Add `uses_context` to post meta source
SantosGuillamot Feb 13, 2024
68ce2a8
Change post meta `$post_id`
SantosGuillamot Feb 13, 2024
ade4119
Use `uses_context` in pattern overrides source
SantosGuillamot Feb 13, 2024
5011e16
Fix formatting
SantosGuillamot Feb 13, 2024
929afb6
Move logic to source registration
SantosGuillamot Feb 13, 2024
44125a4
Format latest changes
SantosGuillamot Feb 13, 2024
5a931f4
Remove empty line
SantosGuillamot Feb 13, 2024
dccef2e
Revert "Use `uses_context` in pattern overrides source"
SantosGuillamot Feb 13, 2024
016ec29
Add `uses_context` to pattern overrides source
SantosGuillamot Feb 13, 2024
e305f7c
Create `get_block_type_uses_context` filter
SantosGuillamot Feb 13, 2024
49a8b7a
Use `get_block_type_uses_context` in source registration
SantosGuillamot Feb 13, 2024
42575c4
Update docstrings
SantosGuillamot Feb 13, 2024
e41ba30
Move supported blocks to private variables
SantosGuillamot Feb 13, 2024
4ca8176
Remove unnecessary attributes from variable
SantosGuillamot Feb 13, 2024
46df776
Change `$support_blocks` comment
SantosGuillamot Feb 13, 2024
59746c0
Fix typo in comment
SantosGuillamot Feb 13, 2024
7bcc71e
Check the `uses_context` property is an array
SantosGuillamot Feb 13, 2024
5810180
Fix process bindings check
SantosGuillamot Feb 13, 2024
a578d5c
Use strict argument in `in_array`
SantosGuillamot Feb 13, 2024
9f01568
Use `empty` function in post meta source
SantosGuillamot Feb 13, 2024
ba7bf25
Update comments and descriptions
SantosGuillamot Feb 13, 2024
0c4a795
Remove variations and uses_context in `__set` function
SantosGuillamot Feb 14, 2024
7e80f5e
Add test to check if the value of the source context shows
SantosGuillamot Feb 15, 2024
d839f33
Test if the source result object properties are correct
SantosGuillamot Feb 15, 2024
e82879e
Test registration fails when `uses_context` is string
SantosGuillamot Feb 15, 2024
102c940
Test merging multiple sources `uses_context`
SantosGuillamot Feb 15, 2024
f4af4f7
Join variations and uses_context in the same conditional
SantosGuillamot Feb 15, 2024
f2b3b3a
Move multiple sources test to a different file
SantosGuillamot Feb 15, 2024
4f8668d
Provide error message in uses_context test
SantosGuillamot Feb 15, 2024
07a7317
Check allowed source properties in the registry
SantosGuillamot Feb 15, 2024
f14817f
Remove register_meta code
SantosGuillamot Feb 15, 2024
edaaffa
Remove spaces in $block_content in tests
SantosGuillamot Feb 15, 2024
03e66ee
Check that `uses_context` increase by three
SantosGuillamot Feb 15, 2024
0b49cc4
Update comment to use third person
SantosGuillamot Feb 15, 2024
9838fec
Don't use $merged_uses_context variable
SantosGuillamot Feb 15, 2024
59fde29
Change translators comment syntax to match standard
SantosGuillamot Feb 15, 2024
5f2ddc3
Add covers get_uses_context
SantosGuillamot Feb 15, 2024
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
25 changes: 13 additions & 12 deletions src/wp-includes/block-bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
* `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 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.
* @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.
*/
Expand Down
1 change: 1 addition & 0 deletions src/wp-includes/block-bindings/pattern-overrides.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ),
)
);
}
Expand Down
19 changes: 9 additions & 10 deletions src/wp-includes/block-bindings/post-meta.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand All @@ -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' ),
)
);
}
Expand Down
89 changes: 74 additions & 15 deletions src/wp-includes/class-wp-block-bindings-registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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:
*
* `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 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.
* @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.
*/
Expand Down Expand Up @@ -107,7 +133,7 @@ public function register( string $source_name, array $source_properties ) {
return false;
}

/* Validate that the source properties contain the label */
// Validate that the source properties contain the label.
if ( ! isset( $source_properties['label'] ) ) {
_doing_it_wrong(
__METHOD__,
Expand All @@ -117,7 +143,7 @@ public function register( string $source_name, array $source_properties ) {
return false;
}

/* Validate that the source properties contain the get_value_callback */
// Validate that the source properties contain the get_value_callback.
if ( ! isset( $source_properties['get_value_callback'] ) ) {
_doing_it_wrong(
__METHOD__,
Expand All @@ -127,7 +153,7 @@ public function register( string $source_name, array $source_properties ) {
return false;
}

/* Validate that the get_value_callback is a valid callback */
// Validate that the get_value_callback is a valid callback.
if ( ! is_callable( $source_properties['get_value_callback'] ) ) {
_doing_it_wrong(
__METHOD__,
Expand All @@ -137,13 +163,46 @@ public function register( string $source_name, array $source_properties ) {
return false;
}

// Validate 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
);

$this->sources[ $source_name ] = $source;

// Add `uses_context` defined by block bindings sources.
add_filter(
'get_block_type_uses_context',
function ( $uses_context, $block_type ) use ( $source ) {
Copy link
Copy Markdown
Member

@gziolo gziolo Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing here and not a blocker. I believe we should use a class method and unregister the filter when unregistering the source. It would also help with documenting, as we could put all details from inline comments in the PHPDoc.

I'm not entirely sure how that would look in practice, but happy to explore together options. It can be another patch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me. Although I don't know how it will work 🤔 I was thinking something like this:

	/**
	 * Source that is being processed.
	 *
	 * @since 6.5.0
	 * @var WP_Block_Bindings_Source|false
	 */
	private $source;

	/**
	 * Callback passed to the `get_block_type_uses_context` filter to add the source's `uses_context` to the block.
	 *
	 * @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 array Modified array of uses_context with the values provided by the source.
	 */
	public function _add_source_uses_context( $uses_context, $block_type ) {
		$source = $this->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 ) ) );
	}

But we probably need a different callback name per source.

Anyway, I'll add a task in the tracking issue to handle it 🙂

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;
}

Expand Down
15 changes: 12 additions & 3 deletions src/wp-includes/class-wp-block-bindings-source.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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;
}
}

/**
Expand Down
32 changes: 25 additions & 7 deletions src/wp-includes/class-wp-block-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -366,6 +366,10 @@ public function __get( $name ) {
return $this->get_variations();
}

if ( 'uses_context' === $name ) {
return $this->get_uses_context();
}

if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
return;
}
Expand Down Expand Up @@ -394,7 +398,7 @@ public function __get( $name ) {
* or false otherwise.
*/
public function __isset( $name ) {
if ( 'variations' === $name ) {
if ( in_array( $name, array( 'variations', 'uses_context' ), true ) ) {
return true;
}

Expand All @@ -417,11 +421,6 @@ public function __isset( $name ) {
* @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;
Expand Down Expand Up @@ -616,4 +615,23 @@ public function get_variations() {
*/
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 );
}
}
12 changes: 5 additions & 7 deletions src/wp-includes/class-wp-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,28 +235,26 @@ private function process_block_bindings() {

$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(
$supported_block_attrs = 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_attrs[ $this->name ] ) ||
empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
! is_array( $parsed_block['attrs']['metadata']['bindings'] )
) {
return $computed_attributes;
}

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_attrs[ $this->name ], true ) ) {
continue;
}
// If no source is provided, or that source is not registered, process next attribute.
Expand Down
43 changes: 43 additions & 0 deletions tests/phpunit/tests/block-bindings/render.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,49 @@ public function test_passing_arguments_to_source() {
);
}

/**
* Test 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.
Expand Down
Loading