Skip to content

Commit

Permalink
Block Bindings: Add block context needed for bindings in PHP (#58554)
Browse files Browse the repository at this point in the history
* Modify `uses_context` for allowed blocks in PHP

* Update post meta source to use block context

* Remove old hook used for the editor

* Remove possibility of passing post id from source

* Set correct post id

* Adapt post meta source

* Adapt pattern overrides source

* Remove old usesContext from block.json

* Remove old filter

* Add backport to `uses_context` in source registration

* Update source class to include `uses_context`

* Use `get_block_type_uses_context` when available

* Use `empty` function in post meta source

* Update descriptions

* Update $supported_blocks variable name

* Use `in_array` properly

* Update bindings registry comments

* Update allowed_blocks variable name

* Check allowed properties in the registry

* Use only compatibility filter
  • Loading branch information
SantosGuillamot committed Feb 16, 2024
1 parent 237865f commit 76d11ea
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 77 deletions.
23 changes: 12 additions & 11 deletions lib/compat/wordpress-6.5/block-bindings/block-bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,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.
*/
Expand Down
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 @@ -48,18 +73,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 @@ -102,7 +128,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 @@ -112,7 +138,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 @@ -122,7 +148,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 @@ -132,13 +158,49 @@ 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;
}

// Validate that the source properties contain only allowed properties.
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(
'register_block_type_args',
function ( $args, $block_name ) use ( $source ) {
if ( ! in_array( $block_name, $this->supported_blocks, true ) || empty( $source->uses_context ) ) {
return $args;
}
$original_use_context = isset( $args['uses_context'] ) ? $args['uses_context'] : array();
// Use array_values to reset the array keys.
$args['uses_context'] = array_values( array_unique( array_merge( $original_use_context, $source->uses_context ) ) );

return $args;
},
10,
2
);
return $source;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,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 @@ -58,9 +66,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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function gutenberg_register_block_bindings_pattern_overrides_source() {
array(
'label' => _x( 'Pattern Overrides', 'block bindings source' ),
'get_value_callback' => 'gutenberg_block_bindings_pattern_overrides_callback',
'uses_context' => array( 'pattern/overrides' ),
)
);
}
Expand Down
20 changes: 9 additions & 11 deletions lib/compat/wordpress-6.5/block-bindings/post-meta.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,20 @@
/**
* Gets value for Post Meta source.
*
* @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 gutenberg_block_bindings_post_meta_callback( $source_attrs ) {
if ( ! isset( $source_attrs['key'] ) ) {
function gutenberg_block_bindings_post_meta_callback( $source_attrs, $block_instance ) {
if ( empty( $source_attrs['key'] ) ) {
return null;
}

// Use the postId attribute if available
if ( isset( $source_attrs['postId'] ) ) {
$post_id = $source_attrs['postId'];
} else {
// I tried using $block_instance->context['postId'] but it wasn't 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 );
if ( ( ! is_post_publicly_viewable( $post ) && ! current_user_can( 'read_post', $post_id ) ) || post_password_required( $post ) ) {
Expand All @@ -47,6 +44,7 @@ function gutenberg_register_block_bindings_post_meta_source() {
array(
'label' => _x( 'Post Meta', 'block bindings source' ),
'get_value_callback' => 'gutenberg_block_bindings_post_meta_callback',
'uses_context' => array( 'postId', 'postType' ),
)
);
}
Expand Down
12 changes: 5 additions & 7 deletions lib/compat/wordpress-6.5/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,16 @@ function gutenberg_block_bindings_replace_html( $block_content, $block_name, str
* @param WP_Block $block_instance The block instance.
*/
function gutenberg_process_block_bindings( $block_content, $parsed_block, $block_instance ) {
// 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 or isn't one of the allowed block types, return.
// If the block doesn't have the bindings property or isn't one of the supported block types, return.
if (
! isset( $allowed_blocks[ $block_instance->name ] ) ||
! isset( $supported_block_attrs[ $block_instance->name ] ) ||
empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
! is_array( $parsed_block['attrs']['metadata']['bindings'] )
) {
Expand All @@ -192,8 +190,8 @@ function gutenberg_process_block_bindings( $block_content, $parsed_block, $block

$modified_block_content = $block_content;
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[ $block_instance->name ], true ) ) {
// If the attribute is not in the supported list, process next attribute.
if ( ! in_array( $attribute_name, $supported_block_attrs[ $block_instance->name ], true ) ) {
continue;
}
// If no source is provided, or that source is not registered, process next attribute.
Expand Down
21 changes: 0 additions & 21 deletions packages/block-editor/src/hooks/use-bindings-attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,3 @@ addFilter(
'core/editor/custom-sources-backwards-compatibility/shim-attribute-source',
shimAttributeSource
);

// Add the context to all blocks.
addFilter(
'blocks.registerBlockType',
'core/block-bindings-ui',
( settings, name ) => {
if ( ! ( name in BLOCK_BINDINGS_ALLOWED_BLOCKS ) ) {
return settings;
}
const contextItems = [ 'postId', 'postType', 'queryId' ];
const usesContextArray = settings.usesContext;
const oldUsesContextArray = new Set( usesContextArray );
contextItems.forEach( ( item ) => {
if ( ! oldUsesContextArray.has( item ) ) {
usesContextArray.push( item );
}
} );
settings.usesContext = usesContextArray;
return settings;
}
);
1 change: 0 additions & 1 deletion packages/block-library/src/button/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"description": "Prompt visitors to take action with a button-style link.",
"keywords": [ "link" ],
"textdomain": "default",
"usesContext": [ "pattern/overrides" ],
"attributes": {
"tagName": {
"type": "string",
Expand Down
1 change: 0 additions & 1 deletion packages/block-library/src/heading/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"description": "Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.",
"keywords": [ "title", "subtitle" ],
"textdomain": "default",
"usesContext": [ "pattern/overrides" ],
"attributes": {
"textAlign": {
"type": "string"
Expand Down
7 changes: 1 addition & 6 deletions packages/block-library/src/image/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
"name": "core/image",
"title": "Image",
"category": "media",
"usesContext": [
"allowResize",
"imageCrop",
"fixedHeight",
"pattern/overrides"
],
"usesContext": [ "allowResize", "imageCrop", "fixedHeight" ],
"description": "Insert an image to make a visual statement.",
"keywords": [ "img", "photo", "picture" ],
"textdomain": "default",
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/paragraph/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"description": "Start with the basic building block of all narrative.",
"keywords": [ "text" ],
"textdomain": "default",
"usesContext": [ "postId", "pattern/overrides" ],
"usesContext": [ "postId" ],
"attributes": {
"align": {
"type": "string"
Expand Down

1 comment on commit 76d11ea

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 76d11ea.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7931412549
📝 Reported issues:

Please sign in to comment.