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 authored and getdave committed Feb 20, 2024
1 parent a2f73e6 commit 3a6fad4
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

0 comments on commit 3a6fad4

Please sign in to comment.