Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
24 changes: 20 additions & 4 deletions lib/compat/wordpress-6.5/block-bindings/block-bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
*
* @since 6.5.0
*
* @param string $source_name The name of the source.
* @param array $source_properties {
* @param string $source_name The name of the source. It must be a string containing a namespace prefix, i.e.
* `my-plugin/my-custom-source`. It must only contain lowercase alphanumeric
* characters, the forward slash `/` and dashes.
* @param array $source_properties {
Comment on lines +22 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

I would also add this on the WP_Block_Bindings_Registry::register() method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in this commit: link.

* The array of arguments that are used to register a source.
*
* @type string $label The label of the source.
Expand All @@ -39,7 +41,7 @@
* @return array|false Source when the registration was successful, or `false` on failure.
*/
if ( ! function_exists( 'register_block_bindings_source' ) ) {
function register_block_bindings_source( $source_name, array $source_properties ) {
function register_block_bindings_source( string $source_name, array $source_properties ) {
return WP_Block_Bindings_Registry::get_instance()->register( $source_name, $source_properties );
}
}
Expand All @@ -53,7 +55,7 @@ function register_block_bindings_source( $source_name, array $source_properties
* @return array|false The unregistred block bindings source on success and `false` otherwise.
*/
if ( ! function_exists( 'unregister_block_bindings_source' ) ) {
function unregister_block_bindings_source( $source_name ) {
function unregister_block_bindings_source( string $source_name ) {
return WP_Block_Bindings_Registry::get_instance()->unregister( $source_name );
}
}
Expand All @@ -70,3 +72,17 @@ function get_all_registered_block_bindings_sources() {
return WP_Block_Bindings_Registry::get_instance()->get_all_registered();
}
}

/**
* Retrieves a registered block bindings source.
*
* @since 6.5.0
*
* @param string $source_name The name of the source.
* @return array|null The registered block bindings source, or `null` if it is not registered.
*/
if ( ! function_exists( 'get_block_bindings_source' ) ) {
function get_block_bindings_source( string $source_name ) {
return WP_Block_Bindings_Registry::get_instance()->get_registered( $source_name );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ final class WP_Block_Bindings_Registry {
* 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 .
* - @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.
* }
* @return array|false Source when the registration was successful, or `false` on failure.
*/
public function register( $source_name, array $source_properties ) {
public function register( string $source_name, array $source_properties ) {
if ( ! is_string( $source_name ) ) {
_doing_it_wrong(
__METHOD__,
Expand Down Expand Up @@ -120,7 +120,7 @@ public function register( $source_name, array $source_properties ) {
* @param string $source_name Block bindings source name including namespace.
* @return array|false The unregistred block bindings source on success and `false` otherwise.
*/
public function unregister( $source_name ) {
public function unregister( string $source_name ) {
if ( ! $this->is_registered( $source_name ) ) {
_doing_it_wrong(
__METHOD__,
Expand Down Expand Up @@ -156,7 +156,7 @@ public function get_all_registered() {
* @param string $source_name The name of the source.
* @return array|null The registered block bindings source, or `null` if it is not registered.
*/
public function get_registered( $source_name ) {
public function get_registered( string $source_name ) {
if ( ! $this->is_registered( $source_name ) ) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
<?php
/**
* Add the metadata source to the block bindings API.
* Pattern Overrides source for the Block Bindings.
*
* @package gutenberg
*/

/**
* Gets value for the Pattern Overrides source.
*
* @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.
* @param string $attribute_name The name of the target attribute.
* @return mixed The value computed for the source.
*/
function gutenberg_block_bindings_pattern_overrides_callback( $source_attrs, $block_instance, $attribute_name ) {
if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) {
if ( empty( $block_instance->attributes['metadata']['id'] ) ) {
return null;
}
$block_id = $block_instance->attributes['metadata']['id'];
return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id, 'values', $attribute_name ), null );
}

/**
* Registers Pattern Overrides source in the Block Bindings registry.
*/
function gutenberg_register_block_bindings_pattern_overrides_source() {
// Override the "core/pattern-overrides" source from core.
if ( array_key_exists( 'core/pattern-overrides', get_all_registered_block_bindings_sources() ) ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<?php
/**
* Add the post_meta source to the block bindings API.
* Post Meta source for the block bindings.
*
* @package gutenberg
*/

/**
* 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" ).
* @return mixed The value computed for the source.
*/
function gutenberg_block_bindings_post_meta_callback( $source_attrs ) {
if ( ! isset( $source_attrs['key'] ) ) {
return null;
Expand All @@ -17,16 +25,18 @@ function gutenberg_block_bindings_post_meta_callback( $source_attrs ) {
$post_id = get_the_ID();
}

// If a post isn't public, we need to prevent
// unauthorized users from accessing the post meta.
// If a post isn't public, we need to prevent unauthorized users from accessing the post meta.
$post = get_post( $post_id );
if ( ( $post && 'publish' !== $post->post_status && ! current_user_can( 'read_post', $post_id ) ) || post_password_required( $post_id ) ) {
if ( ( ! is_post_publicly_viewable( $post ) && ! current_user_can( 'read_post', $post_id ) ) || post_password_required( $post ) ) {
return null;
}

return get_post_meta( $post_id, $source_attrs['key'], true );
}

/**
* Registers Post Meta source in the block bindings registry.
*/
function gutenberg_register_block_bindings_post_meta_source() {
// Override the "core/post-meta" source from core.
if ( array_key_exists( 'core/post-meta', get_all_registered_block_bindings_sources() ) ) {
Expand Down
66 changes: 34 additions & 32 deletions lib/compat/wordpress-6.5/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,29 @@ function gutenberg_register_metadata_attribute( $args ) {
add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' );

/**
* Replaces the HTML content of a block based on the provided source value.
* Depending on the block attribute name, replace its value in the HTML based on the value provided.
*
* @param string $block_content Block Content.
* @param string $block_name The name of the block to process.
* @param string $block_attr The attribute of the block we want to process.
* @param string $source_value The value used to replace the HTML.
* @param string $block_content Block Content.
* @param string $block_name The name of the block to process.
* @param string $attribute_name The attribute name to replace.
* @param mixed $source_value The value used to replace in the HTML.
* @return string The modified block content.
*/
function gutenberg_block_bindings_replace_html( $block_content, $block_name, $block_attr, $source_value ) {
function gutenberg_block_bindings_replace_html( $block_content, $block_name, string $attribute_name, $source_value ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
if ( null === $block_type ) {
return;
if ( ! isset( $block_type->attributes[ $attribute_name ] ) ) {
return $block_content;
}

// Depending on the attribute source, the processing will be different.
switch ( $block_type->attributes[ $block_attr ]['source'] ) {
switch ( $block_type->attributes[ $attribute_name ]['source'] ) {
case 'html':
case 'rich-text':
$block_reader = new WP_HTML_Tag_Processor( $block_content );

// TODO: Support for CSS selectors whenever they are ready in the HTML API.
// In the meantime, support comma-separated selectors by exploding them into an array.
$selectors = explode( ',', $block_type->attributes[ $block_attr ]['selector'] );
$selectors = explode( ',', $block_type->attributes[ $attribute_name ]['selector'] );
// Add a bookmark to the first tag to be able to iterate over the selectors.
$block_reader->next_tag();
$block_reader->set_bookmark( 'iterate-selectors' );
Expand Down Expand Up @@ -133,12 +133,12 @@ function gutenberg_block_bindings_replace_html( $block_content, $block_name, $bl
if ( ! $amended_content->next_tag(
array(
// TODO: build the query from CSS selector.
'tag_name' => $block_type->attributes[ $block_attr ]['selector'],
'tag_name' => $block_type->attributes[ $attribute_name ]['selector'],
)
) ) {
return $block_content;
}
$amended_content->set_attribute( $block_type->attributes[ $block_attr ]['attribute'], esc_attr( $source_value ) );
$amended_content->set_attribute( $block_type->attributes[ $attribute_name ]['attribute'], esc_attr( $source_value ) );
return $amended_content->get_updated_html();
break;

Expand All @@ -153,10 +153,10 @@ function gutenberg_block_bindings_replace_html( $block_content, $block_name, $bl
* Process the block bindings attribute.
*
* @param string $block_content Block Content.
* @param array $block Block attributes.
* @param array $parsed_block The full block, including name and attributes.
* @param WP_Block $block_instance The block instance.
*/
function gutenberg_process_block_bindings( $block_content, $block, $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(
Expand All @@ -167,7 +167,11 @@ function gutenberg_process_block_bindings( $block_content, $block, $block_instan
);

// If the block doesn't have the bindings property or isn't one of the allowed block types, return.
if ( ! isset( $block['attrs']['metadata']['bindings'] ) || ! isset( $allowed_blocks[ $block_instance->name ] ) ) {
if (
! isset( $allowed_blocks[ $block_instance->name ] ) ||
empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
Copy link
Contributor

Choose a reason for hiding this comment

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

Here
we added a check with isset() but I think empty() is even more appropriate
(like you did). Let's remember to update it in Core

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was included in this backport: link. From what I can see, it seems to be already in core: link.

! is_array( $parsed_block['attrs']['metadata']['bindings'] )
) {
return $block_content;
}

Expand All @@ -186,34 +190,32 @@ function gutenberg_process_block_bindings( $block_content, $block, $block_instan
* }
*/

$block_bindings_sources = get_all_registered_block_bindings_sources();
$modified_block_content = $block_content;
foreach ( $block['attrs']['metadata']['bindings'] as $binding_attribute => $binding_source ) {
// If the attribute is not in the list, process next attribute.
if ( ! in_array( $binding_attribute, $allowed_blocks[ $block_instance->name ], true ) ) {
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 ) ) {
continue;
}
// If no source is provided, or that source is not registered, process next attribute.
if ( ! isset( $binding_source['source'] ) || ! is_string( $binding_source['source'] ) || ! isset( $block_bindings_sources[ $binding_source['source'] ] ) ) {
if ( ! isset( $block_binding['source'] ) || ! is_string( $block_binding['source'] ) ) {
continue;
}

$source_callback = $block_bindings_sources[ $binding_source['source'] ]['get_value_callback'];
// Get the value based on the source.
if ( ! isset( $binding_source['args'] ) ) {
$source_args = array();
} else {
$source_args = $binding_source['args'];
}
$source_value = $source_callback( $source_args, $block_instance, $binding_attribute );
// If the value is null, process next attribute.
if ( is_null( $source_value ) ) {
$block_binding_source = get_block_bindings_source( $block_binding['source'] );
if ( null === $block_binding_source ) {
continue;
}

// Process the HTML based on the block and the attribute.
$modified_block_content = gutenberg_block_bindings_replace_html( $modified_block_content, $block_instance->name, $binding_attribute, $source_value );
$source_callback = $block_binding_source['get_value_callback'];
$source_args = ! empty( $block_binding['args'] ) && is_array( $block_binding['args'] ) ? $block_binding['args'] : array();
$source_value = call_user_func_array( $source_callback, array( $source_args, $block_instance, $attribute_name ) );

// If the value is not null, process the HTML based on the block and the attribute.
if ( ! is_null( $source_value ) ) {
$modified_block_content = gutenberg_block_bindings_replace_html( $modified_block_content, $block_instance->name, $attribute_name, $source_value );
}
}

return $modified_block_content;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-6.5/block-bindings/class-wp-block-bindings-registry.php';
}
require __DIR__ . '/compat/wordpress-6.5/block-bindings/block-bindings.php';
require __DIR__ . '/compat/wordpress-6.5/block-bindings/sources/post-meta.php';
require __DIR__ . '/compat/wordpress-6.5/block-bindings/sources/pattern.php';
require __DIR__ . '/compat/wordpress-6.5/block-bindings/post-meta.php';
require __DIR__ . '/compat/wordpress-6.5/block-bindings/pattern-overrides.php';
require __DIR__ . '/compat/wordpress-6.5/script-loader.php';

// Experimental features.
Expand Down