From 2fd22b98dcf4abf81d05847e149bda97bcf4fb3a Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 07:53:37 +0100 Subject: [PATCH 01/28] Change block bindings experiment name --- lib/block-supports/pattern.php | 2 +- lib/experimental/blocks.php | 4 ++-- lib/experimental/editor-settings.php | 4 ++-- lib/experiments-page.php | 6 +++--- packages/block-editor/src/hooks/custom-fields.js | 6 +++--- packages/block-editor/src/hooks/index.js | 2 +- packages/block-library/src/block/edit.js | 6 +++++- packages/block-library/src/paragraph/block.json | 2 +- packages/editor/src/hooks/pattern-partial-syncing.js | 2 +- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/block-supports/pattern.php b/lib/block-supports/pattern.php index a783135c793e3..f9dd1b4b44248 100644 --- a/lib/block-supports/pattern.php +++ b/lib/block-supports/pattern.php @@ -13,7 +13,7 @@ * @param WP_Block_Type $block_type Block Type. */ function gutenberg_register_pattern_support( $block_type ) { - $pattern_support = property_exists( $block_type, 'supports' ) ? _wp_array_get( $block_type->supports, array( '__experimentalConnections' ), false ) : false; + $pattern_support = property_exists( $block_type, 'supports' ) ? _wp_array_get( $block_type->supports, array( '__experimentalBlockBindings' ), false ) : false; if ( $pattern_support ) { if ( ! $block_type->uses_context ) { diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index d4bb6c9b4586e..6a05f1b266c02 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -83,7 +83,7 @@ function wp_enqueue_block_view_script( $block_name, $args ) { $gutenberg_experiments = get_option( 'gutenberg-experiments' ); if ( $gutenberg_experiments && ( - array_key_exists( 'gutenberg-connections', $gutenberg_experiments ) || + array_key_exists( 'gutenberg-block-bindings', $gutenberg_experiments ) || array_key_exists( 'gutenberg-pattern-partial-syncing', $gutenberg_experiments ) ) ) { /** @@ -118,7 +118,7 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst } // If the block does not have support for block connections, skip it. - if ( ! block_has_support( $block_type, array( '__experimentalConnections' ), false ) ) { + if ( ! block_has_support( $block_type, array( '__experimentalBlockBindings' ), false ) ) { return $block_content; } diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 5f61684e8b134..729376cf030dd 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -26,8 +26,8 @@ function gutenberg_enable_experiments() { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableGroupGridVariation = true', 'before' ); } - if ( $gutenberg_experiments && array_key_exists( 'gutenberg-connections', $gutenberg_experiments ) ) { - wp_add_inline_script( 'wp-block-editor', 'window.__experimentalConnections = true', 'before' ); + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-block-bindings', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalBlockBindings = true', 'before' ); } if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 2d2e76273d2d5..8af1eb82c6bed 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -128,13 +128,13 @@ function gutenberg_initialize_experiments_settings() { add_settings_field( 'gutenberg-custom-fields', - __( 'Connections', 'gutenberg' ), + __( 'Block Bindings & Custom Fields', 'gutenberg' ), 'gutenberg_display_experiment_field', 'gutenberg-experiments', 'gutenberg_experiments_section', array( - 'label' => __( 'Test connecting block attribute values to a custom field value', 'gutenberg' ), - 'id' => 'gutenberg-connections', + 'label' => __( 'Test connecting block attributes to different sources like custom fields', 'gutenberg' ), + 'id' => 'gutenberg-block-bindings', ) ); diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 9b677933adc13..5dc82473ef40e 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -20,7 +20,7 @@ import { useBlockEditingMode } from '../components/block-editing-mode'; * @return {Object} Filtered block settings. */ function addAttribute( settings ) { - if ( hasBlockSupport( settings, '__experimentalConnections', true ) ) { + if ( hasBlockSupport( settings, '__experimentalBlockBindings', true ) ) { // Gracefully handle if settings.attributes.connections is undefined. settings.attributes = { ...settings.attributes, @@ -95,7 +95,7 @@ export default { attributeKeys: [ 'connections' ], hasSupport( name ) { return ( - hasBlockSupport( name, '__experimentalConnections', false ) && + hasBlockSupport( name, '__experimentalBlockBindings', false ) && // Check if the current block is a paragraph or image block. // Currently, only these two blocks are supported. [ 'core/paragraph', 'core/image' ].includes( name ) @@ -104,7 +104,7 @@ export default { }; if ( - window.__experimentalConnections || + window.__experimentalBlockBindings || window.__experimentalPatternPartialSyncing ) { addFilter( diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 385b9fe6b1511..fa5ab255f8b6e 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -39,7 +39,7 @@ createBlockEditFilter( position, layout, contentLockUI, - window.__experimentalConnections ? customFields : null, + window.__experimentalBlockBindings ? customFields : null, blockHooks, blockRenaming, ].filter( Boolean ) diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 57db2d166f9f9..2adbd9908ba29 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -38,7 +38,11 @@ const { useLayoutClasses } = unlock( blockEditorPrivateApis ); function isPartiallySynced( block ) { return ( - !! getBlockSupport( block.name, '__experimentalConnections', false ) && + !! getBlockSupport( + block.name, + '__experimentalBlockBindings', + false + ) && !! block.attributes.connections?.attributes && Object.values( block.attributes.connections.attributes ).some( ( connection ) => connection.source === 'pattern_attributes' diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 3fe4fbb34e102..96ec9c778c096 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -41,7 +41,7 @@ "text": true } }, - "__experimentalConnections": true, + "__experimentalBlockBindings": true, "spacing": { "margin": true, "padding": true, diff --git a/packages/editor/src/hooks/pattern-partial-syncing.js b/packages/editor/src/hooks/pattern-partial-syncing.js index 40bd1e16dfc00..976efebb720f6 100644 --- a/packages/editor/src/hooks/pattern-partial-syncing.js +++ b/packages/editor/src/hooks/pattern-partial-syncing.js @@ -34,7 +34,7 @@ const withPartialSyncingControls = createHigherOrderComponent( const blockEditingMode = useBlockEditingMode(); const hasCustomFieldsSupport = hasBlockSupport( props.name, - '__experimentalConnections', + '__experimentalBlockBindings', false ); const isEditingPattern = useSelect( From d98b4e65b76cafd786cf7b1714d7a6945d146520 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 07:58:32 +0100 Subject: [PATCH 02/28] Remove old custom fields UI --- .../block-editor/src/hooks/custom-fields.js | 115 ------------------ packages/block-editor/src/hooks/index.js | 2 - .../block-library/src/paragraph/block.json | 1 - 3 files changed, 118 deletions(-) delete mode 100644 packages/block-editor/src/hooks/custom-fields.js diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js deleted file mode 100644 index 5dc82473ef40e..0000000000000 --- a/packages/block-editor/src/hooks/custom-fields.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * WordPress dependencies - */ -import { addFilter } from '@wordpress/hooks'; -import { PanelBody, TextControl } from '@wordpress/components'; -import { __, sprintf } from '@wordpress/i18n'; -import { hasBlockSupport } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import { InspectorControls } from '../components'; -import { useBlockEditingMode } from '../components/block-editing-mode'; - -/** - * Filters registered block settings, extending attributes to include `connections`. - * - * @param {Object} settings Original block settings. - * - * @return {Object} Filtered block settings. - */ -function addAttribute( settings ) { - if ( hasBlockSupport( settings, '__experimentalBlockBindings', true ) ) { - // Gracefully handle if settings.attributes.connections is undefined. - settings.attributes = { - ...settings.attributes, - connections: { - type: 'object', - }, - }; - } - - return settings; -} - -function CustomFieldsControlPure( { name, connections, setAttributes } ) { - const blockEditingMode = useBlockEditingMode(); - if ( blockEditingMode !== 'default' ) { - return null; - } - - // If the block is a paragraph or image block, we need to know which - // attribute to use for the connection. Only the `content` attribute - // of the paragraph block and the `url` attribute of the image block are supported. - let attributeName; - if ( name === 'core/paragraph' ) attributeName = 'content'; - if ( name === 'core/image' ) attributeName = 'url'; - - return ( - - - { - if ( nextValue === '' ) { - setAttributes( { - connections: undefined, - [ attributeName ]: undefined, - placeholder: undefined, - } ); - } else { - setAttributes( { - connections: { - attributes: { - // The attributeName will be either `content` or `url`. - [ attributeName ]: { - // Source will be variable, could be post_meta, user_meta, term_meta, etc. - // Could even be a custom source like a social media attribute. - source: 'meta_fields', - value: nextValue, - }, - }, - }, - [ attributeName ]: undefined, - placeholder: sprintf( - 'This content will be replaced on the frontend by the value of "%s" custom field.', - nextValue - ), - } ); - } - } } - /> - - - ); -} - -export default { - edit: CustomFieldsControlPure, - attributeKeys: [ 'connections' ], - hasSupport( name ) { - return ( - hasBlockSupport( name, '__experimentalBlockBindings', false ) && - // Check if the current block is a paragraph or image block. - // Currently, only these two blocks are supported. - [ 'core/paragraph', 'core/image' ].includes( name ) - ); - }, -}; - -if ( - window.__experimentalBlockBindings || - window.__experimentalPatternPartialSyncing -) { - addFilter( - 'blocks.registerBlockType', - 'core/editor/connections/attribute', - addAttribute - ); -} diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index fa5ab255f8b6e..f17c0a22166e4 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -25,7 +25,6 @@ import layout from './layout'; import childLayout from './layout-child'; import contentLockUI from './content-lock-ui'; import './metadata'; -import customFields from './custom-fields'; import blockHooks from './block-hooks'; import blockRenaming from './block-renaming'; @@ -39,7 +38,6 @@ createBlockEditFilter( position, layout, contentLockUI, - window.__experimentalBlockBindings ? customFields : null, blockHooks, blockRenaming, ].filter( Boolean ) diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 96ec9c778c096..a81d754d8ca1b 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -41,7 +41,6 @@ "text": true } }, - "__experimentalBlockBindings": true, "spacing": { "margin": true, "padding": true, From 4d085c50bd1d8d9eca2c605b556347c71956f71b Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 08:11:09 +0100 Subject: [PATCH 03/28] Add block bindings PHP logic --- .../block-bindings/html-processing.php | 112 ++++++++++++ lib/experimental/block-bindings/index.php | 20 +++ .../block-bindings/sources/index.php | 24 +++ lib/experimental/blocks.php | 164 +++++++----------- lib/experimental/connection-sources/index.php | 23 --- 5 files changed, 222 insertions(+), 121 deletions(-) create mode 100644 lib/experimental/block-bindings/html-processing.php create mode 100644 lib/experimental/block-bindings/index.php create mode 100644 lib/experimental/block-bindings/sources/index.php delete mode 100644 lib/experimental/connection-sources/index.php diff --git a/lib/experimental/block-bindings/html-processing.php b/lib/experimental/block-bindings/html-processing.php new file mode 100644 index 0000000000000..9fb629d5e2821 --- /dev/null +++ b/lib/experimental/block-bindings/html-processing.php @@ -0,0 +1,112 @@ +get_registered( $block_name ); + if ( null === $block_type ) { + return; + } + + // Depending on the attribute source, the processing will be different. + // TODO: Get the type from the block attribute definition and modify/validate the value returned by the source if needed. + switch ( $block_type->attributes[ $block_attr ]['source'] ) { + case 'html': + case 'rich-text': + $p = 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'] ); + // Add a bookmark to the first tag to be able to iterate over the selectors. + $p->next_tag(); + $p->set_bookmark( 'iterate-selectors' ); + + // TODO: This shouldn't be needed when the `set_inner_html` function is ready. + // Store the parent tag and its attributes to be able to restore them later in the button. + // The button block has a wrapper while the paragraph and heading blocks don't. + if ( 'core/button' === $block_name ) { + $parent_tag = $p->get_tag(); + $parent_tag_names = $p->get_attribute_names_with_prefix( '' ); + $parent_tag_attrs = array(); + foreach ( $parent_tag_names as $name ) { + $parent_tag_attrs[ $name ] = $p->get_attribute( $name ); + } + } + + foreach ( $selectors as $selector ) { + // If the parent tag, or any of its children, matches the selector, replace the HTML. + if ( strcasecmp( $p->get_tag( $selector ), $selector ) === 0 || $p->next_tag( + array( + 'tag_name' => $selector, + ) + ) ) { + $p->release_bookmark( 'iterate-selectors' ); + + // TODO: Use `set_inner_html` method whenever it's ready in the HTML API. + // Until then, it is hardcoded for the paragraph, heading, and button blocks. + // Store the tag and its attributes to be able to restore them later. + $selector_tag_names = $p->get_attribute_names_with_prefix( '' ); + $selector_tag_attrs = array(); + foreach ( $selector_tag_names as $name ) { + $selector_tag_attrs[ $name ] = $p->get_attribute( $name ); + } + $selector_markup = "<$selector>" . esc_html( $source_value ) . ""; + $p2 = new WP_HTML_Tag_Processor( $selector_markup ); + $p2->next_tag(); + foreach ( $selector_tag_attrs as $attribute_key => $attribute_value ) { + $p2->set_attribute( $attribute_key, $attribute_value ); + } + $selector_updated_html = $p2->get_updated_html(); + if ( 'core/paragraph' === $block_name || 'core/heading' === $block_name ) { + return $selector_updated_html; + } + if ( 'core/button' === $block_name ) { + $markup = "<$parent_tag>$selector_updated_html"; + $p3 = new WP_HTML_Tag_Processor( $markup ); + $p3->next_tag(); + foreach ( $parent_tag_attrs as $attribute_key => $attribute_value ) { + $p3->set_attribute( $attribute_key, $attribute_value ); + } + return $p3->get_updated_html(); + } + } else { + $p->seek( 'iterate-selectors' ); + } + } + $p->release_bookmark( 'iterate-selectors' ); + return $block_content; + + case 'attribute': + $p = new WP_HTML_Tag_Processor( $block_content ); + if ( ! $p->next_tag( + array( + // TODO: build the query from CSS selector. + 'tag_name' => $block_type->attributes[ $block_attr ]['selector'], + ) + ) ) { + return $block_content; + } + $p->set_attribute( $block_type->attributes[ $block_attr ]['attribute'], esc_attr( $source_value ) ); + return $p->get_updated_html(); + break; + + default: + return $block_content; + break; + } + return; + } +} diff --git a/lib/experimental/block-bindings/index.php b/lib/experimental/block-bindings/index.php new file mode 100644 index 0000000000000..cca857e93702f --- /dev/null +++ b/lib/experimental/block-bindings/index.php @@ -0,0 +1,20 @@ +block_type; - - // Allowlist of blocks that support block connections. - // Currently, we only allow the following blocks and attributes: - // - Paragraph: content. - // - Image: url. - $blocks_attributes_allowlist = array( - 'core/paragraph' => array( 'content' ), - 'core/image' => array( 'url' ), - ); - - // Whitelist of the block types that support block connections. - // Currently, we only allow the Paragraph and Image blocks to use block connections. - if ( ! in_array( $block['blockName'], array_keys( $blocks_attributes_allowlist ), true ) ) { - return $block_content; - } - - // If for some reason, the block type is not found, skip it. - if ( null === $block_type ) { - return $block_content; - } - - // If the block does not have support for block connections, skip it. - if ( ! block_has_support( $block_type, array( '__experimentalBlockBindings' ), false ) ) { - return $block_content; - } - - // Get all the attributes that have a connection. - $connected_attributes = $block['attrs']['connections']['attributes'] ?? false; - if ( ! $connected_attributes ) { - return $block_content; - } - - foreach ( $connected_attributes as $attribute_name => $attribute_value ) { - - // If the attribute is not in the allowlist, skip it. - if ( ! in_array( $attribute_name, $blocks_attributes_allowlist[ $block['blockName'] ], true ) ) { - continue; - } - - // Skip if the source value is not "meta_fields" or "pattern_attributes". - if ( 'meta_fields' !== $attribute_value['source'] && 'pattern_attributes' !== $attribute_value['source'] ) { - continue; - } - // If the attribute does not have a source, skip it. - if ( ! isset( $block_type->attributes[ $attribute_name ]['source'] ) ) { - continue; + require_once __DIR__ . '/block-bindings/index.php'; + // Whitelist of blocks that support block bindings. + // TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes? + global $block_bindings_whitelist; + $block_bindings_whitelist = array( + 'core/paragraph' => array( 'content' ), + 'core/heading' => array( 'content' ), + 'core/image' => array( 'url', 'title' ), + 'core/button' => array( 'url', 'text' ), + ); + if ( ! function_exists( 'process_block_bindings' ) ) { + /** + * Process the block bindings attribute. + * + * @param string $block_content Block Content. + * @param array $block Block attributes. + * @param WP_Block $block_instance The block instance. + */ + function process_block_bindings( $block_content, $block, $block_instance ) { + // If the block doesn't have the bindings property, return. + if ( ! isset( $block['attrs']['metadata']['bindings'] ) ) { + return $block_content; } - if ( 'pattern_attributes' === $attribute_value['source'] ) { - if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) { + // TODO: Review the bindings syntax. + // Assuming the following format for the bindings property of the "metadata" attribute: + // + // "bindings": { + // "title": { + // "source": { + // "name": "post_meta", + // "attributes": { "value": "text_custom_field" } + // } + // }, + // "url": { + // "source": { + // "name": "post_meta", + // "attributes": { "value": "text_custom_field" } + // } + // } + // }, + // . + global $block_bindings_whitelist; + global $block_bindings_sources; + $modified_block_content = $block_content; + foreach ( $block['attrs']['metadata']['bindings'] as $binding_attribute => $binding_source ) { + // If the block is not in the whitelist, stop processing. + if ( ! isset( $block_bindings_whitelist[ $block['blockName'] ] ) ) { + return $block_content; + } + // If the attribute is not in the whitelist, process next attribute. + if ( ! in_array( $binding_attribute, $block_bindings_whitelist[ $block['blockName'] ], true ) ) { continue; } - - $custom_value = $connection_sources[ $attribute_value['source'] ]( $block_instance, $attribute_name ); - } else { - // If the attribute does not specify the name of the custom field, skip it. - if ( ! isset( $attribute_value['value'] ) ) { + // If no source is provided, or that source is not registered, process next attribute. + if ( ! isset( $binding_source['source'] ) || ! isset( $binding_source['source']['name'] ) || ! isset( $block_bindings_sources[ $binding_source['source']['name'] ] ) ) { continue; } - // Get the content from the connection source. - $custom_value = $connection_sources[ $attribute_value['source'] ]( - $block_instance, - $attribute_value['value'] - ); - } - - if ( false === $custom_value ) { - continue; - } + $source_callback = $block_bindings_sources[ $binding_source['source']['name'] ]['apply']; + // Get the value based on the source. + $source_value = $source_callback( $binding_source['source']['attributes'], $block_content, $block, $block_instance ); + // If the value is null, process next attribute. + if ( is_null( $source_value ) ) { + continue; + } - $tags = new WP_HTML_Tag_Processor( $block_content ); - $found = $tags->next_tag( - array( - // TODO: In the future, when blocks other than Paragraph and Image are - // supported, we should build the full query from CSS selector. - 'tag_name' => $block_type->attributes[ $attribute_name ]['selector'], - ) - ); - if ( ! $found ) { - return $block_content; + // Process the HTML based on the block and the attribute. + $modified_block_content = block_bindings_replace_html( $modified_block_content, $block['blockName'], $binding_attribute, $source_value ); } - $tag_name = $tags->get_tag(); - $markup = "<$tag_name>$custom_value"; - $updated_tags = new WP_HTML_Tag_Processor( $markup ); - $updated_tags->next_tag(); - - // Get all the attributes from the original block and add them to the new markup. - $names = $tags->get_attribute_names_with_prefix( '' ); - foreach ( $names as $name ) { - $updated_tags->set_attribute( $name, $tags->get_attribute( $name ) ); - } - - return $updated_tags->get_updated_html(); + return $modified_block_content; } - return $block_content; + // Add filter only to the blocks in the whitelist. + foreach ( $block_bindings_whitelist as $block_name => $attributes ) { + add_filter( 'render_block_' . $block_name, 'process_block_bindings', 20, 3 ); + } } - - add_filter( 'render_block', 'gutenberg_render_block_connections', 10, 3 ); } diff --git a/lib/experimental/connection-sources/index.php b/lib/experimental/connection-sources/index.php deleted file mode 100644 index 4f9e06cb13b94..0000000000000 --- a/lib/experimental/connection-sources/index.php +++ /dev/null @@ -1,23 +0,0 @@ - 'meta', - 'meta_fields' => function ( $block_instance, $meta_field ) { - // We should probably also check if the meta field exists but for now it's okay because - // if it doesn't, `get_post_meta()` will just return an empty string. - return get_post_meta( $block_instance->context['postId'], $meta_field, true ); - }, - 'pattern_attributes' => function ( $block_instance, $attribute_name ) { - $block_id = $block_instance->attributes['metadata']['id']; - return _wp_array_get( - $block_instance->context, - array( 'pattern/overrides', $block_id, $attribute_name ), - false - ); - }, -); From a88e9dc211f5862bf42c5921915e0ee3910c657e Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 08:11:27 +0100 Subject: [PATCH 04/28] Add pattern source --- .../block-bindings/sources/pattern.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/experimental/block-bindings/sources/pattern.php diff --git a/lib/experimental/block-bindings/sources/pattern.php b/lib/experimental/block-bindings/sources/pattern.php new file mode 100644 index 0000000000000..4948e1d8fdd03 --- /dev/null +++ b/lib/experimental/block-bindings/sources/pattern.php @@ -0,0 +1,23 @@ +attributes, array( 'metadata', 'id' ), false ) ) { + return; + } + $block_id = $block_instance->attributes['metadata']['id']; + return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id ), false ); + }; + register_block_bindings_source( + 'pattern_attributes', + array( + 'label' => __( 'Pattern Attributes' ), + 'apply' => $pattern_source_callback, + ) + ); +} From 999784f9073f971f1e69b593f8b797b7ec563a71 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 08:11:34 +0100 Subject: [PATCH 05/28] Add post meta source --- .../block-bindings/sources/post-meta.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 lib/experimental/block-bindings/sources/post-meta.php diff --git a/lib/experimental/block-bindings/sources/post-meta.php b/lib/experimental/block-bindings/sources/post-meta.php new file mode 100644 index 0000000000000..3220b3c6defb2 --- /dev/null +++ b/lib/experimental/block-bindings/sources/post-meta.php @@ -0,0 +1,27 @@ +context['postId'] but it wasn't available in the image block. + $post_id = get_the_ID(); + } + + return get_post_meta( $post_id, $source_attrs['value'], true ); + }; + register_block_bindings_source( + 'post_meta', + array( + 'label' => __( 'Post Meta' ), + 'apply' => $post_meta_source_callback, + ) + ); +} From 9105898ee3dd95713203ae2acaee9772900192f7 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 08:40:57 +0100 Subject: [PATCH 06/28] Adapt partially synced patterns experiment --- lib/experimental/blocks.php | 7 ++- packages/block-library/src/block/edit.js | 10 ++--- .../block-library/src/paragraph/block.json | 1 + .../components/partial-syncing-controls.js | 44 +++++++++++-------- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 232829be487ba..ebc65f9dfbe89 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -148,7 +148,12 @@ function process_block_bindings( $block_content, $block, $block_instance ) { $source_callback = $block_bindings_sources[ $binding_source['source']['name'] ]['apply']; // Get the value based on the source. - $source_value = $source_callback( $binding_source['source']['attributes'], $block_content, $block, $block_instance ); + if ( ! isset( $binding_source['source']['attributes'] ) ) { + $source_args = array(); + } else { + $source_args = $binding_source['source']['attributes']; + } + $source_value = $source_callback( $source_args, $block_content, $block, $block_instance ); // If the value is null, process next attribute. if ( is_null( $source_value ) ) { continue; diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 2adbd9908ba29..44192f260fe3c 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -43,16 +43,16 @@ function isPartiallySynced( block ) { '__experimentalBlockBindings', false ) && - !! block.attributes.connections?.attributes && - Object.values( block.attributes.connections.attributes ).some( - ( connection ) => connection.source === 'pattern_attributes' + !! block.attributes.metadata?.bindings && + Object.values( block.attributes.metadata.bindings ).some( + ( binding ) => binding.source.name === 'pattern_attributes' ) ); } function getPartiallySyncedAttributes( block ) { - return Object.entries( block.attributes.connections.attributes ) + return Object.entries( block.attributes.metadata.bindings ) .filter( - ( [ , connection ] ) => connection.source === 'pattern_attributes' + ( [ , binding ] ) => binding.source.name === 'pattern_attributes' ) .map( ( [ attributeKey ] ) => attributeKey ); } diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index a81d754d8ca1b..70f43d3793bca 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -63,6 +63,7 @@ "fontSize": true } }, + "__experimentalBlockBindings": true, "__experimentalSelector": "p", "__unstablePasteTextInline": true }, diff --git a/packages/patterns/src/components/partial-syncing-controls.js b/packages/patterns/src/components/partial-syncing-controls.js index d20bd1d347012..f5ac19bc05f3d 100644 --- a/packages/patterns/src/components/partial-syncing-controls.js +++ b/packages/patterns/src/components/partial-syncing-controls.js @@ -19,7 +19,7 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ]; const attributeSources = Object.keys( syncedAttributes ).map( ( attributeName ) => - attributes.connections?.attributes?.[ attributeName ]?.source + attributes.metadata?.bindings?.[ attributeName ]?.source?.name ); const isConnectedToOtherSources = attributeSources.every( ( source ) => source && source !== 'pattern_attributes' @@ -30,52 +30,58 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { return null; } - function updateConnections( isChecked ) { - let updatedConnections = { - ...attributes.connections, - attributes: { ...attributes.connections?.attributes }, + function updateBindings( isChecked ) { + let updatedBindings = { + ...attributes?.metadata?.bindings, }; if ( ! isChecked ) { for ( const attributeName of Object.keys( syncedAttributes ) ) { if ( - updatedConnections.attributes[ attributeName ]?.source === + updatedBindings[ attributeName ]?.source?.name === 'pattern_attributes' ) { - delete updatedConnections.attributes[ attributeName ]; + delete updatedBindings[ attributeName ]; } } - if ( ! Object.keys( updatedConnections.attributes ).length ) { - delete updatedConnections.attributes; - } - if ( ! Object.keys( updatedConnections ).length ) { - updatedConnections = undefined; + if ( ! Object.keys( updatedBindings ).length ) { + updatedBindings = undefined; } setAttributes( { - connections: updatedConnections, + metadata: { + ...attributes.metadata, + bindings: updatedBindings, + }, } ); return; } for ( const attributeName of Object.keys( syncedAttributes ) ) { - if ( ! updatedConnections.attributes[ attributeName ] ) { - updatedConnections.attributes[ attributeName ] = { - source: 'pattern_attributes', + if ( ! updatedBindings[ attributeName ] ) { + updatedBindings[ attributeName ] = { + source: { + name: 'pattern_attributes', + }, }; } } if ( typeof attributes.metadata?.id === 'string' ) { - setAttributes( { connections: updatedConnections } ); + setAttributes( { + metadata: { + ...attributes.metadata, + bindings: updatedBindings, + }, + } ); return; } const id = nanoid( 6 ); setAttributes( { - connections: updatedConnections, metadata: { ...attributes.metadata, id, + bindings: updatedBindings, }, } ); } @@ -93,7 +99,7 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { ( source ) => source === 'pattern_attributes' ) } onChange={ ( isChecked ) => { - updateConnections( isChecked ); + updateBindings( isChecked ); } } /> From 35bd58dc26512d363a568e61f65f304dcb3bfa2c Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 09:47:07 +0100 Subject: [PATCH 07/28] Add domain to the translations in sources --- lib/experimental/block-bindings/sources/pattern.php | 2 +- lib/experimental/block-bindings/sources/post-meta.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/experimental/block-bindings/sources/pattern.php b/lib/experimental/block-bindings/sources/pattern.php index 4948e1d8fdd03..c2549b6048928 100644 --- a/lib/experimental/block-bindings/sources/pattern.php +++ b/lib/experimental/block-bindings/sources/pattern.php @@ -16,7 +16,7 @@ register_block_bindings_source( 'pattern_attributes', array( - 'label' => __( 'Pattern Attributes' ), + 'label' => __( 'Pattern Attributes', 'gutenberg' ), 'apply' => $pattern_source_callback, ) ); diff --git a/lib/experimental/block-bindings/sources/post-meta.php b/lib/experimental/block-bindings/sources/post-meta.php index 3220b3c6defb2..d8b091d97aade 100644 --- a/lib/experimental/block-bindings/sources/post-meta.php +++ b/lib/experimental/block-bindings/sources/post-meta.php @@ -20,7 +20,7 @@ register_block_bindings_source( 'post_meta', array( - 'label' => __( 'Post Meta' ), + 'label' => __( 'Post Meta', 'gutenberg' ), 'apply' => $post_meta_source_callback, ) ); From f57d5317517e53a4814fb1c436a0e137e19129a1 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 09:55:20 +0100 Subject: [PATCH 08/28] Remove unused post_meta attrs --- lib/experimental/block-bindings/sources/post-meta.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/block-bindings/sources/post-meta.php b/lib/experimental/block-bindings/sources/post-meta.php index d8b091d97aade..9e2bf22aa8b73 100644 --- a/lib/experimental/block-bindings/sources/post-meta.php +++ b/lib/experimental/block-bindings/sources/post-meta.php @@ -6,7 +6,7 @@ */ if ( function_exists( 'register_block_bindings_source' ) ) { - $post_meta_source_callback = function ( $source_attrs, $block_content, $block, $block_instance ) { + $post_meta_source_callback = function ( $source_attrs ) { // Use the postId attribute if available, otherwise use the context. if ( isset( $source_attrs['postId'] ) ) { $post_id = $source_attrs['postId']; From 766295e4b226ec26586ede30a57d5f9cb49716d4 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 20 Dec 2023 15:28:09 +0100 Subject: [PATCH 09/28] Change variable name --- lib/experimental/blocks.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index ebc65f9dfbe89..82676aae11231 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -88,10 +88,10 @@ function wp_enqueue_block_view_script( $block_name, $args ) { ) ) { require_once __DIR__ . '/block-bindings/index.php'; - // Whitelist of blocks that support block bindings. + // Allowed blocks that support block bindings. // TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes? - global $block_bindings_whitelist; - $block_bindings_whitelist = array( + global $block_bindings_allowed_blocks; + $block_bindings_allowed_blocks = array( 'core/paragraph' => array( 'content' ), 'core/heading' => array( 'content' ), 'core/image' => array( 'url', 'title' ), @@ -129,16 +129,16 @@ function process_block_bindings( $block_content, $block, $block_instance ) { // } // }, // . - global $block_bindings_whitelist; + global $block_bindings_allowed_blocks; global $block_bindings_sources; $modified_block_content = $block_content; foreach ( $block['attrs']['metadata']['bindings'] as $binding_attribute => $binding_source ) { - // If the block is not in the whitelist, stop processing. - if ( ! isset( $block_bindings_whitelist[ $block['blockName'] ] ) ) { + // If the block is not in the list, stop processing. + if ( ! isset( $block_bindings_allowed_blocks[ $block['blockName'] ] ) ) { return $block_content; } - // If the attribute is not in the whitelist, process next attribute. - if ( ! in_array( $binding_attribute, $block_bindings_whitelist[ $block['blockName'] ], true ) ) { + // If the attribute is not in the list, process next attribute. + if ( ! in_array( $binding_attribute, $block_bindings_allowed_blocks[ $block['blockName'] ], true ) ) { continue; } // If no source is provided, or that source is not registered, process next attribute. @@ -165,8 +165,8 @@ function process_block_bindings( $block_content, $block, $block_instance ) { return $modified_block_content; } - // Add filter only to the blocks in the whitelist. - foreach ( $block_bindings_whitelist as $block_name => $attributes ) { + // Add filter only to the blocks in the list. + foreach ( $block_bindings_allowed_blocks as $block_name => $attributes ) { add_filter( 'render_block_' . $block_name, 'process_block_bindings', 20, 3 ); } } From 34e2e40ec068097fa28b5ae8b961adffca7734f1 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Fri, 22 Dec 2023 17:14:13 +0100 Subject: [PATCH 10/28] Remove a couple of comments --- lib/experimental/block-bindings/html-processing.php | 1 - lib/experimental/blocks.php | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/experimental/block-bindings/html-processing.php b/lib/experimental/block-bindings/html-processing.php index 9fb629d5e2821..8fbe311ba0c9a 100644 --- a/lib/experimental/block-bindings/html-processing.php +++ b/lib/experimental/block-bindings/html-processing.php @@ -21,7 +21,6 @@ function block_bindings_replace_html( $block_content, $block_name, $block_attr, } // Depending on the attribute source, the processing will be different. - // TODO: Get the type from the block attribute definition and modify/validate the value returned by the source if needed. switch ( $block_type->attributes[ $block_attr ]['source'] ) { case 'html': case 'rich-text': diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 82676aae11231..87073ff14dfdd 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -111,7 +111,6 @@ function process_block_bindings( $block_content, $block, $block_instance ) { return $block_content; } - // TODO: Review the bindings syntax. // Assuming the following format for the bindings property of the "metadata" attribute: // // "bindings": { From bd099d28d7639aa80d4783cac2a1994dd2ba3f26 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Wed, 3 Jan 2024 07:35:29 -0500 Subject: [PATCH 11/28] Add image alt to permitted attributes --- lib/experimental/blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 87073ff14dfdd..39d5297eb0471 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -94,7 +94,7 @@ function wp_enqueue_block_view_script( $block_name, $args ) { $block_bindings_allowed_blocks = array( 'core/paragraph' => array( 'content' ), 'core/heading' => array( 'content' ), - 'core/image' => array( 'url', 'title' ), + 'core/image' => array( 'url', 'title', 'alt' ), 'core/button' => array( 'url', 'text' ), ); if ( ! function_exists( 'process_block_bindings' ) ) { From a1099d62d6abd25fc4a15c03ee013a5c126cefbc Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Wed, 3 Jan 2024 12:48:17 -0500 Subject: [PATCH 12/28] Rename variables and clean up code --- .../block-bindings/html-processing.php | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/lib/experimental/block-bindings/html-processing.php b/lib/experimental/block-bindings/html-processing.php index 8fbe311ba0c9a..f0488e34ffcaa 100644 --- a/lib/experimental/block-bindings/html-processing.php +++ b/lib/experimental/block-bindings/html-processing.php @@ -24,73 +24,72 @@ function block_bindings_replace_html( $block_content, $block_name, $block_attr, switch ( $block_type->attributes[ $block_attr ]['source'] ) { case 'html': case 'rich-text': - $p = new WP_HTML_Tag_Processor( $block_content ); + $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'] ); // Add a bookmark to the first tag to be able to iterate over the selectors. - $p->next_tag(); - $p->set_bookmark( 'iterate-selectors' ); + $block_reader->next_tag(); + $block_reader->set_bookmark( 'iterate-selectors' ); // TODO: This shouldn't be needed when the `set_inner_html` function is ready. // Store the parent tag and its attributes to be able to restore them later in the button. // The button block has a wrapper while the paragraph and heading blocks don't. if ( 'core/button' === $block_name ) { - $parent_tag = $p->get_tag(); - $parent_tag_names = $p->get_attribute_names_with_prefix( '' ); - $parent_tag_attrs = array(); - foreach ( $parent_tag_names as $name ) { - $parent_tag_attrs[ $name ] = $p->get_attribute( $name ); + $button_wrapper = $block_reader->get_tag(); + $button_wrapper_attribute_names = $block_reader->get_attribute_names_with_prefix( '' ); + $button_wrapper_attrs = array(); + foreach ( $button_wrapper_attribute_names as $name ) { + $button_wrapper_attrs[ $name ] = $block_reader->get_attribute( $name ); } } foreach ( $selectors as $selector ) { // If the parent tag, or any of its children, matches the selector, replace the HTML. - if ( strcasecmp( $p->get_tag( $selector ), $selector ) === 0 || $p->next_tag( + if ( strcasecmp( $block_reader->get_tag( $selector ), $selector ) === 0 || $block_reader->next_tag( array( 'tag_name' => $selector, ) ) ) { - $p->release_bookmark( 'iterate-selectors' ); + $block_reader->release_bookmark( 'iterate-selectors' ); // TODO: Use `set_inner_html` method whenever it's ready in the HTML API. // Until then, it is hardcoded for the paragraph, heading, and button blocks. // Store the tag and its attributes to be able to restore them later. - $selector_tag_names = $p->get_attribute_names_with_prefix( '' ); - $selector_tag_attrs = array(); - foreach ( $selector_tag_names as $name ) { - $selector_tag_attrs[ $name ] = $p->get_attribute( $name ); + $selector_attribute_names = $block_reader->get_attribute_names_with_prefix( '' ); + $selector_attrs = array(); + foreach ( $selector_attribute_names as $name ) { + $selector_attrs[ $name ] = $block_reader->get_attribute( $name ); } $selector_markup = "<$selector>" . esc_html( $source_value ) . ""; - $p2 = new WP_HTML_Tag_Processor( $selector_markup ); - $p2->next_tag(); - foreach ( $selector_tag_attrs as $attribute_key => $attribute_value ) { - $p2->set_attribute( $attribute_key, $attribute_value ); + $amended_content = new WP_HTML_Tag_Processor( $selector_markup ); + $amended_content->next_tag(); + foreach ( $selector_attrs as $attribute_key => $attribute_value ) { + $amended_content->set_attribute( $attribute_key, $attribute_value ); } - $selector_updated_html = $p2->get_updated_html(); if ( 'core/paragraph' === $block_name || 'core/heading' === $block_name ) { - return $selector_updated_html; + return $amended_content->get_updated_html(); } if ( 'core/button' === $block_name ) { - $markup = "<$parent_tag>$selector_updated_html"; - $p3 = new WP_HTML_Tag_Processor( $markup ); - $p3->next_tag(); - foreach ( $parent_tag_attrs as $attribute_key => $attribute_value ) { - $p3->set_attribute( $attribute_key, $attribute_value ); + $button_markup = "<$button_wrapper>{$amended_content->get_updated_html()}"; + $amended_button = new WP_HTML_Tag_Processor( $button_markup ); + $amended_button->next_tag(); + foreach ( $button_wrapper_attrs as $attribute_key => $attribute_value ) { + $amended_button->set_attribute( $attribute_key, $attribute_value ); } - return $p3->get_updated_html(); + return $amended_button->get_updated_html(); } } else { - $p->seek( 'iterate-selectors' ); + $block_reader->seek( 'iterate-selectors' ); } } - $p->release_bookmark( 'iterate-selectors' ); + $block_reader->release_bookmark( 'iterate-selectors' ); return $block_content; case 'attribute': - $p = new WP_HTML_Tag_Processor( $block_content ); - if ( ! $p->next_tag( + $amended_content = new WP_HTML_Tag_Processor( $block_content ); + if ( ! $amended_content->next_tag( array( // TODO: build the query from CSS selector. 'tag_name' => $block_type->attributes[ $block_attr ]['selector'], @@ -98,8 +97,8 @@ function block_bindings_replace_html( $block_content, $block_name, $block_attr, ) ) { return $block_content; } - $p->set_attribute( $block_type->attributes[ $block_attr ]['attribute'], esc_attr( $source_value ) ); - return $p->get_updated_html(); + $amended_content->set_attribute( $block_type->attributes[ $block_attr ]['attribute'], esc_attr( $source_value ) ); + return $amended_content->get_updated_html(); break; default: From 0d686c985d5a6518c7f49eacc3d555a9f464c46b Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Mon, 8 Jan 2024 07:54:28 -0500 Subject: [PATCH 13/28] Fix bug wherein original pattern values were not being displayed --- lib/experimental/block-bindings/sources/pattern.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/experimental/block-bindings/sources/pattern.php b/lib/experimental/block-bindings/sources/pattern.php index c2549b6048928..e521cab2d1828 100644 --- a/lib/experimental/block-bindings/sources/pattern.php +++ b/lib/experimental/block-bindings/sources/pattern.php @@ -8,10 +8,10 @@ if ( function_exists( 'register_block_bindings_source' ) ) { $pattern_source_callback = function ( $source_attrs, $block_content, $block, $block_instance ) { if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) { - return; + return null; } $block_id = $block_instance->attributes['metadata']['id']; - return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id ), false ); + return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id ), null ); }; register_block_bindings_source( 'pattern_attributes', From b3a21d4255ff361a74b929cef2cad83c53bea166 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Tue, 9 Jan 2024 16:48:16 +0000 Subject: [PATCH 14/28] fix typo --- lib/experimental/block-bindings/html-processing.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/block-bindings/html-processing.php b/lib/experimental/block-bindings/html-processing.php index f0488e34ffcaa..515749d0a8e75 100644 --- a/lib/experimental/block-bindings/html-processing.php +++ b/lib/experimental/block-bindings/html-processing.php @@ -1,6 +1,6 @@ Date: Tue, 9 Jan 2024 18:00:16 +0000 Subject: [PATCH 15/28] Format a comment --- .vscode/settings.json | 3 +++ lib/experimental/blocks.php | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 13 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000..d36838aba21ee --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "todo-tree.tree.scanMode": "open files" +} diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 39d5297eb0471..a19a41d612fe3 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -114,20 +114,20 @@ function process_block_bindings( $block_content, $block, $block_instance ) { // Assuming the following format for the bindings property of the "metadata" attribute: // // "bindings": { - // "title": { - // "source": { - // "name": "post_meta", - // "attributes": { "value": "text_custom_field" } + // "title": { + // "source": { + // "name": "post_meta", + // "attributes": { "value": "text_custom_field" } + // } + // }, + // "url": { + // "source": { + // "name": "post_meta", + // "attributes": { "value": "text_custom_field" } + // } + // } // } - // }, - // "url": { - // "source": { - // "name": "post_meta", - // "attributes": { "value": "text_custom_field" } - // } - // } - // }, - // . + // global $block_bindings_allowed_blocks; global $block_bindings_sources; $modified_block_content = $block_content; From 3704637f3039dd3c4576eea67c3409be6c815437 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Tue, 9 Jan 2024 18:40:47 +0000 Subject: [PATCH 16/28] fix comment indentation --- lib/experimental/blocks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index a19a41d612fe3..7f57b98754be7 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -121,8 +121,8 @@ function process_block_bindings( $block_content, $block, $block_instance ) { // } // }, // "url": { - // "source": { - // "name": "post_meta", + // "source": { + // "name": "post_meta", // "attributes": { "value": "text_custom_field" } // } // } From e0604df2801407ca3771fc870d3c697e528ec14f Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Tue, 9 Jan 2024 18:44:08 +0000 Subject: [PATCH 17/28] remove the .vscode/settings.json file that sneaked in --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d36838aba21ee..0000000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "todo-tree.tree.scanMode": "open files" -} From 25fa668b4aae1701125534c70b68b2e6310fcfce Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Tue, 9 Jan 2024 14:02:22 -0500 Subject: [PATCH 18/28] Sync with added support for multiple attributes in patterns --- lib/experimental/block-bindings/sources/pattern.php | 4 ++-- lib/experimental/blocks.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/experimental/block-bindings/sources/pattern.php b/lib/experimental/block-bindings/sources/pattern.php index e521cab2d1828..d015dad405dc2 100644 --- a/lib/experimental/block-bindings/sources/pattern.php +++ b/lib/experimental/block-bindings/sources/pattern.php @@ -6,12 +6,12 @@ */ if ( function_exists( 'register_block_bindings_source' ) ) { - $pattern_source_callback = function ( $source_attrs, $block_content, $block, $block_instance ) { + $pattern_source_callback = function ( $source_attrs, $block_content, $block, $block_instance, $attribute_name ) { if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) { return null; } $block_id = $block_instance->attributes['metadata']['id']; - return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id ), null ); + return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $block_id, $attribute_name ), null ); }; register_block_bindings_source( 'pattern_attributes', diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 7f57b98754be7..1241853142f00 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -152,7 +152,7 @@ function process_block_bindings( $block_content, $block, $block_instance ) { } else { $source_args = $binding_source['source']['attributes']; } - $source_value = $source_callback( $source_args, $block_content, $block, $block_instance ); + $source_value = $source_callback( $source_args, $block_content, $block, $block_instance, $binding_attribute ); // If the value is null, process next attribute. if ( is_null( $source_value ) ) { continue; From 7fb646cae16f1703cfdee25a7f8629e6b020fec3 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Tue, 9 Jan 2024 14:29:09 -0500 Subject: [PATCH 19/28] Remove block supports requirement for partial syncing --- packages/editor/src/hooks/pattern-partial-syncing.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/editor/src/hooks/pattern-partial-syncing.js b/packages/editor/src/hooks/pattern-partial-syncing.js index 976efebb720f6..a940890dfa693 100644 --- a/packages/editor/src/hooks/pattern-partial-syncing.js +++ b/packages/editor/src/hooks/pattern-partial-syncing.js @@ -5,7 +5,6 @@ import { addFilter } from '@wordpress/hooks'; import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; import { createHigherOrderComponent } from '@wordpress/compose'; import { useBlockEditingMode } from '@wordpress/block-editor'; -import { hasBlockSupport } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; /** @@ -32,11 +31,6 @@ const { const withPartialSyncingControls = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { const blockEditingMode = useBlockEditingMode(); - const hasCustomFieldsSupport = hasBlockSupport( - props.name, - '__experimentalBlockBindings', - false - ); const isEditingPattern = useSelect( ( select ) => select( editorStore ).getCurrentPostType() === @@ -45,7 +39,6 @@ const withPartialSyncingControls = createHigherOrderComponent( ); const shouldShowPartialSyncingControls = - hasCustomFieldsSupport && props.isSelected && isEditingPattern && blockEditingMode === 'default' && From 34e3f29078e9f9196befda3ded01a200509266d5 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Tue, 9 Jan 2024 14:31:10 -0500 Subject: [PATCH 20/28] Remove __experimentalBlockBindings supports from paragraph --- packages/block-library/src/paragraph/block.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 70f43d3793bca..a81d754d8ca1b 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -63,7 +63,6 @@ "fontSize": true } }, - "__experimentalBlockBindings": true, "__experimentalSelector": "p", "__unstablePasteTextInline": true }, From 8ff5352cc5795e5bd6b574705a7ab8a87f9ca6e5 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Tue, 9 Jan 2024 15:52:00 -0500 Subject: [PATCH 21/28] Simplify source callback and update comments --- lib/experimental/block-bindings/sources/index.php | 10 ++++++++-- lib/experimental/block-bindings/sources/pattern.php | 2 +- lib/experimental/blocks.php | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/experimental/block-bindings/sources/index.php b/lib/experimental/block-bindings/sources/index.php index 5e99f8038976c..11cc05dfec0ba 100644 --- a/lib/experimental/block-bindings/sources/index.php +++ b/lib/experimental/block-bindings/sources/index.php @@ -12,9 +12,15 @@ * Function to register a new source. * * @param string $source_name The name of the source. - * @param function $source_args List of arguments for the block bindings source: + * @param array $source_args List of arguments for the block bindings source: * - label: The label of the source. - * - apply: The callback executed when the source is processed in the server. + * - apply: The callback executed when the source is processed, which happens when a block is being rendered. + * @param {object} $source_attrs {"value": "{ID}"} Object containing source ID used to look up value. + * @param {object} $block_instance The block instance. + * @param {string} $attribute_name The name of an attribute used to retrieve override value from block context. + * @return {string} A value that will be used to override the block's original value. + * + * * @return void */ function register_block_bindings_source( $source_name, $source_args ) { diff --git a/lib/experimental/block-bindings/sources/pattern.php b/lib/experimental/block-bindings/sources/pattern.php index d015dad405dc2..5ea1b1fc8b779 100644 --- a/lib/experimental/block-bindings/sources/pattern.php +++ b/lib/experimental/block-bindings/sources/pattern.php @@ -6,7 +6,7 @@ */ if ( function_exists( 'register_block_bindings_source' ) ) { - $pattern_source_callback = function ( $source_attrs, $block_content, $block, $block_instance, $attribute_name ) { + $pattern_source_callback = function ( $source_attrs, $block_instance, $attribute_name ) { if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) { return null; } diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 1241853142f00..42663e127870c 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -152,7 +152,7 @@ function process_block_bindings( $block_content, $block, $block_instance ) { } else { $source_args = $binding_source['source']['attributes']; } - $source_value = $source_callback( $source_args, $block_content, $block, $block_instance, $binding_attribute ); + $source_value = $source_callback( $source_args, $block_instance, $binding_attribute ); // If the value is null, process next attribute. if ( is_null( $source_value ) ) { continue; From 6a7074d5951f3dc11b948c6b9f14a21a3aff94b2 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Tue, 9 Jan 2024 16:08:20 -0500 Subject: [PATCH 22/28] Remove erroneous comment --- lib/experimental/block-bindings/sources/post-meta.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/block-bindings/sources/post-meta.php b/lib/experimental/block-bindings/sources/post-meta.php index 9e2bf22aa8b73..528dcf9aa199b 100644 --- a/lib/experimental/block-bindings/sources/post-meta.php +++ b/lib/experimental/block-bindings/sources/post-meta.php @@ -7,7 +7,7 @@ if ( function_exists( 'register_block_bindings_source' ) ) { $post_meta_source_callback = function ( $source_attrs ) { - // Use the postId attribute if available, otherwise use the context. + // Use the postId attribute if available if ( isset( $source_attrs['postId'] ) ) { $post_id = $source_attrs['postId']; } else { From 7e8576c95cad89653109ece149edd404153d6465 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Wed, 10 Jan 2024 09:10:37 -0500 Subject: [PATCH 23/28] Revert "Remove block supports requirement for partial syncing" This reverts commit 7fb646cae16f1703cfdee25a7f8629e6b020fec3. --- packages/editor/src/hooks/pattern-partial-syncing.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/editor/src/hooks/pattern-partial-syncing.js b/packages/editor/src/hooks/pattern-partial-syncing.js index a940890dfa693..976efebb720f6 100644 --- a/packages/editor/src/hooks/pattern-partial-syncing.js +++ b/packages/editor/src/hooks/pattern-partial-syncing.js @@ -5,6 +5,7 @@ import { addFilter } from '@wordpress/hooks'; import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; import { createHigherOrderComponent } from '@wordpress/compose'; import { useBlockEditingMode } from '@wordpress/block-editor'; +import { hasBlockSupport } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; /** @@ -31,6 +32,11 @@ const { const withPartialSyncingControls = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { const blockEditingMode = useBlockEditingMode(); + const hasCustomFieldsSupport = hasBlockSupport( + props.name, + '__experimentalBlockBindings', + false + ); const isEditingPattern = useSelect( ( select ) => select( editorStore ).getCurrentPostType() === @@ -39,6 +45,7 @@ const withPartialSyncingControls = createHigherOrderComponent( ); const shouldShowPartialSyncingControls = + hasCustomFieldsSupport && props.isSelected && isEditingPattern && blockEditingMode === 'default' && From 62adeb8ba9660a1348e5bcf3274b08c09ab4f427 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Wed, 10 Jan 2024 09:11:20 -0500 Subject: [PATCH 24/28] Revert "Remove __experimentalBlockBindings supports from paragraph" This reverts commit 34e3f29078e9f9196befda3ded01a200509266d5. --- packages/block-library/src/paragraph/block.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index a81d754d8ca1b..70f43d3793bca 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -63,6 +63,7 @@ "fontSize": true } }, + "__experimentalBlockBindings": true, "__experimentalSelector": "p", "__unstablePasteTextInline": true }, From 5231b1cf0dedcd21f3792a4844613e5f94be6b95 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Wed, 10 Jan 2024 12:34:22 -0500 Subject: [PATCH 25/28] Remove block supports dependency --- lib/block-supports/pattern.php | 2 +- packages/block-library/src/block/edit.js | 8 ++------ packages/block-library/src/paragraph/block.json | 1 - packages/editor/src/hooks/pattern-partial-syncing.js | 7 ------- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/block-supports/pattern.php b/lib/block-supports/pattern.php index f9dd1b4b44248..2f7717091ff50 100644 --- a/lib/block-supports/pattern.php +++ b/lib/block-supports/pattern.php @@ -13,7 +13,7 @@ * @param WP_Block_Type $block_type Block Type. */ function gutenberg_register_pattern_support( $block_type ) { - $pattern_support = property_exists( $block_type, 'supports' ) ? _wp_array_get( $block_type->supports, array( '__experimentalBlockBindings' ), false ) : false; + $pattern_support = 'core/paragraph' === $block_type->name ? true : false; if ( $pattern_support ) { if ( ! $block_type->uses_context ) { diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 44192f260fe3c..6331d33c27a7b 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -27,7 +27,7 @@ import { store as blockEditorStore, BlockControls, } from '@wordpress/block-editor'; -import { getBlockSupport, parse, cloneBlock } from '@wordpress/blocks'; +import { parse, cloneBlock } from '@wordpress/blocks'; /** * Internal dependencies @@ -38,11 +38,7 @@ const { useLayoutClasses } = unlock( blockEditorPrivateApis ); function isPartiallySynced( block ) { return ( - !! getBlockSupport( - block.name, - '__experimentalBlockBindings', - false - ) && + 'core/paragraph' === block.name && !! block.attributes.metadata?.bindings && Object.values( block.attributes.metadata.bindings ).some( ( binding ) => binding.source.name === 'pattern_attributes' diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 70f43d3793bca..a81d754d8ca1b 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -63,7 +63,6 @@ "fontSize": true } }, - "__experimentalBlockBindings": true, "__experimentalSelector": "p", "__unstablePasteTextInline": true }, diff --git a/packages/editor/src/hooks/pattern-partial-syncing.js b/packages/editor/src/hooks/pattern-partial-syncing.js index 976efebb720f6..a940890dfa693 100644 --- a/packages/editor/src/hooks/pattern-partial-syncing.js +++ b/packages/editor/src/hooks/pattern-partial-syncing.js @@ -5,7 +5,6 @@ import { addFilter } from '@wordpress/hooks'; import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; import { createHigherOrderComponent } from '@wordpress/compose'; import { useBlockEditingMode } from '@wordpress/block-editor'; -import { hasBlockSupport } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; /** @@ -32,11 +31,6 @@ const { const withPartialSyncingControls = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { const blockEditingMode = useBlockEditingMode(); - const hasCustomFieldsSupport = hasBlockSupport( - props.name, - '__experimentalBlockBindings', - false - ); const isEditingPattern = useSelect( ( select ) => select( editorStore ).getCurrentPostType() === @@ -45,7 +39,6 @@ const withPartialSyncingControls = createHigherOrderComponent( ); const shouldShowPartialSyncingControls = - hasCustomFieldsSupport && props.isSelected && isEditingPattern && blockEditingMode === 'default' && From 5a212e6f5ec74a5b2f2feabd2f7cf24f8799b324 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Wed, 10 Jan 2024 13:57:11 -0500 Subject: [PATCH 26/28] Update signature and docblock of register function --- .../block-bindings/sources/index.php | 21 +++++++++++-------- .../block-bindings/sources/pattern.php | 6 ++---- .../block-bindings/sources/post-meta.php | 6 ++---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/experimental/block-bindings/sources/index.php b/lib/experimental/block-bindings/sources/index.php index 11cc05dfec0ba..6571085989d7f 100644 --- a/lib/experimental/block-bindings/sources/index.php +++ b/lib/experimental/block-bindings/sources/index.php @@ -12,19 +12,22 @@ * Function to register a new source. * * @param string $source_name The name of the source. - * @param array $source_args List of arguments for the block bindings source: - * - label: The label of the source. - * - apply: The callback executed when the source is processed, which happens when a block is being rendered. - * @param {object} $source_attrs {"value": "{ID}"} Object containing source ID used to look up value. - * @param {object} $block_instance The block instance. - * @param {string} $attribute_name The name of an attribute used to retrieve override value from block context. - * @return {string} A value that will be used to override the block's original value. + * @param string $label The label of the source. + * @param callable $apply The callback executed when the source is processed during block rendering. The callable should have the following signature: + * function (object $source_attrs, object $block_instance, string $attribute_name): string + * - object $source_attrs: Object containing source ID used to look up the override value, i.e. {"value": "{ID}"}. + * - object $block_instance: The block instance. + * - string $attribute_name: The name of an attribute used to retrieve an override value from the block context. + * The callable should return a string that will be used to override the block's original value. * * * @return void */ - function register_block_bindings_source( $source_name, $source_args ) { + function register_block_bindings_source( $source_name, $label, $apply ) { global $block_bindings_sources; - $block_bindings_sources[ $source_name ] = $source_args; + $block_bindings_sources[ $source_name ] = array( + 'label' => $label, + 'apply' => $apply, + ); } } diff --git a/lib/experimental/block-bindings/sources/pattern.php b/lib/experimental/block-bindings/sources/pattern.php index 5ea1b1fc8b779..e3456aa468d3e 100644 --- a/lib/experimental/block-bindings/sources/pattern.php +++ b/lib/experimental/block-bindings/sources/pattern.php @@ -15,9 +15,7 @@ }; register_block_bindings_source( 'pattern_attributes', - array( - 'label' => __( 'Pattern Attributes', 'gutenberg' ), - 'apply' => $pattern_source_callback, - ) + __( 'Pattern Attributes', 'gutenberg' ), + $pattern_source_callback ); } diff --git a/lib/experimental/block-bindings/sources/post-meta.php b/lib/experimental/block-bindings/sources/post-meta.php index 528dcf9aa199b..99b6afc03c0d4 100644 --- a/lib/experimental/block-bindings/sources/post-meta.php +++ b/lib/experimental/block-bindings/sources/post-meta.php @@ -19,9 +19,7 @@ }; register_block_bindings_source( 'post_meta', - array( - 'label' => __( 'Post Meta', 'gutenberg' ), - 'apply' => $post_meta_source_callback, - ) + __( 'Post Meta', 'gutenberg' ), + $post_meta_source_callback ); } From b051f172e3ac0d19477b2b8225bacf24bbcedc4d Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 11 Jan 2024 13:21:34 +0000 Subject: [PATCH 27/28] Empty-Commit From 23629de78de1769902aa6dff1fc508193fd8774f Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 11 Jan 2024 13:46:13 +0000 Subject: [PATCH 28/28] Update the PHPDoc string --- lib/experimental/block-bindings/sources/index.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/experimental/block-bindings/sources/index.php b/lib/experimental/block-bindings/sources/index.php index 6571085989d7f..6d14bc96d86ba 100644 --- a/lib/experimental/block-bindings/sources/index.php +++ b/lib/experimental/block-bindings/sources/index.php @@ -13,8 +13,7 @@ * * @param string $source_name The name of the source. * @param string $label The label of the source. - * @param callable $apply The callback executed when the source is processed during block rendering. The callable should have the following signature: - * function (object $source_attrs, object $block_instance, string $attribute_name): string + * @param callable(object, object, string): string $apply - The callback executed when the source is processed during block rendering. * - object $source_attrs: Object containing source ID used to look up the override value, i.e. {"value": "{ID}"}. * - object $block_instance: The block instance. * - string $attribute_name: The name of an attribute used to retrieve an override value from the block context.