diff --git a/lib/compat/wordpress-6.5/block-bindings/block-bindings.php b/lib/compat/wordpress-6.5/block-bindings/block-bindings.php index 3da4ecc185ab5c..87518d6afc5d34 100644 --- a/lib/compat/wordpress-6.5/block-bindings/block-bindings.php +++ b/lib/compat/wordpress-6.5/block-bindings/block-bindings.php @@ -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 { * The array of arguments that are used to register a source. * * @type string $label The label of the source. @@ -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 ); } } @@ -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 ); } } @@ -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 ); + } +} diff --git a/lib/compat/wordpress-6.5/block-bindings/class-wp-block-bindings-registry.php b/lib/compat/wordpress-6.5/block-bindings/class-wp-block-bindings-registry.php index b6f17dd53af810..5a5322bec44090 100644 --- a/lib/compat/wordpress-6.5/block-bindings/class-wp-block-bindings-registry.php +++ b/lib/compat/wordpress-6.5/block-bindings/class-wp-block-bindings-registry.php @@ -44,7 +44,9 @@ final class WP_Block_Bindings_Registry { * * @since 6.5.0 * - * @param string $source_name The name of the source. + * @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 { * The array of arguments that are used to register a source. * @@ -57,13 +59,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__, @@ -120,7 +122,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__, @@ -156,7 +158,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; } diff --git a/lib/compat/wordpress-6.5/block-bindings/sources/pattern.php b/lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php similarity index 61% rename from lib/compat/wordpress-6.5/block-bindings/sources/pattern.php rename to lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php index 9168cfc785b557..0e6d3fc94eeaa7 100644 --- a/lib/compat/wordpress-6.5/block-bindings/sources/pattern.php +++ b/lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php @@ -1,17 +1,30 @@ "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() ) ) { diff --git a/lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php b/lib/compat/wordpress-6.5/block-bindings/post-meta.php similarity index 64% rename from lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php rename to lib/compat/wordpress-6.5/block-bindings/post-meta.php index e9b84108e52a34..655abd982663b8 100644 --- a/lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php +++ b/lib/compat/wordpress-6.5/block-bindings/post-meta.php @@ -1,9 +1,17 @@ "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; @@ -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() ) ) { diff --git a/lib/compat/wordpress-6.5/blocks.php b/lib/compat/wordpress-6.5/blocks.php index 637ebaf6483be7..34d9a1f72cb1a4 100644 --- a/lib/compat/wordpress-6.5/blocks.php +++ b/lib/compat/wordpress-6.5/blocks.php @@ -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' ); @@ -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'], $source_value ); return $amended_content->get_updated_html(); break; @@ -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( @@ -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'] ) || + ! is_array( $parsed_block['attrs']['metadata']['bindings'] ) + ) { return $block_content; } @@ -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; } diff --git a/lib/load.php b/lib/load.php index 5fd9df15d987eb..3d9213ce6e93a9 100644 --- a/lib/load.php +++ b/lib/load.php @@ -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.