From 77047c3549f120904d7bccfb54f2f2fa28bac744 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Tue, 1 Aug 2023 11:29:26 +0200 Subject: [PATCH 01/33] Add custom fields experimental setting --- lib/experimental/editor-settings.php | 4 ++++ lib/experiments-page.php | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 9c7f66a587a3aa..987d1cfd87c22b 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -87,6 +87,10 @@ function gutenberg_enable_experiments() { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableGroupGridVariation = true', 'before' ); } + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-custom-fields', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalCustomFields = true', 'before' ); + } + if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' ); } diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 3f468d0cbd12db..70362aca7518fa 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -103,6 +103,18 @@ function gutenberg_initialize_experiments_settings() { ) ); + add_settings_field( + 'gutenberg-custom-fields', + __( 'Custom Fields', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Test Custom Fields', 'gutenberg' ), + 'id' => 'gutenberg-custom-fields', + ) + ); + register_setting( 'gutenberg-experiments', 'gutenberg-experiments' From ff1224810b17d296492377991d1fb9ad41fdc7a3 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Tue, 1 Aug 2023 18:12:43 +0200 Subject: [PATCH 02/33] Add connections experimental setting, block inspector control and block attributes --- docs/reference-guides/core-blocks.md | 2 +- lib/experimental/editor-settings.php | 4 +- lib/experiments-page.php | 6 +- .../block-editor/src/hooks/custom-fields.js | 133 ++++++++++++++++++ packages/block-editor/src/hooks/index.js | 1 + .../block-library/src/paragraph/block.json | 1 + 6 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 packages/block-editor/src/hooks/custom-fields.js diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 51813220e58240..347165330b10c4 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -479,7 +479,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Name:** core/paragraph - **Category:** text -- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ +- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), connections, spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ - **Attributes:** align, content, direction, dropCap, placeholder ## Pattern placeholder diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 987d1cfd87c22b..2059f3db5c5366 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -87,8 +87,8 @@ function gutenberg_enable_experiments() { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableGroupGridVariation = true', 'before' ); } - if ( $gutenberg_experiments && array_key_exists( 'gutenberg-custom-fields', $gutenberg_experiments ) ) { - wp_add_inline_script( 'wp-block-editor', 'window.__experimentalCustomFields = 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_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 70362aca7518fa..9405cb344010ec 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -105,13 +105,13 @@ function gutenberg_initialize_experiments_settings() { add_settings_field( 'gutenberg-custom-fields', - __( 'Custom Fields', 'gutenberg' ), + __( 'Connections', 'gutenberg' ), 'gutenberg_display_experiment_field', 'gutenberg-experiments', 'gutenberg_experiments_section', array( - 'label' => __( 'Test Custom Fields', 'gutenberg' ), - 'id' => 'gutenberg-custom-fields', + 'label' => __( 'Test Connections', 'gutenberg' ), + 'id' => 'gutenberg-connections', ) ); diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js new file mode 100644 index 00000000000000..5f7f96808146fc --- /dev/null +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -0,0 +1,133 @@ +/** + * WordPress dependencies + */ +import { addFilter } from '@wordpress/hooks'; +import { PanelBody, TextControl } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { hasBlockSupport } from '@wordpress/blocks'; +import { createHigherOrderComponent } from '@wordpress/compose'; +import { useRef } from '@wordpress/element'; + +/** + * 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. + */ +export function addAttribute( settings ) { + if ( hasBlockSupport( settings, 'connections', true ) ) { + // Gracefully handle if settings.attributes is undefined. + settings.attributes = { + ...settings.attributes, + connections: { + type: 'object', + }, + }; + } + + return settings; +} + +/** + * Override the default edit UI to include a new block inspector control for + * assigning a connection to blocks that has support for connections. + * Currently, only the `core/paragraph` block is supported and there is only a relation + * between paragraph content and a custom field. + * + * @param {WPComponent} BlockEdit Original component. + * + * @return {WPComponent} Wrapped component. + */ +export const withInspectorControl = createHigherOrderComponent( + ( BlockEdit ) => { + return ( props ) => { + const blockEditingMode = useBlockEditingMode(); + const hasCustomFieldsSupport = hasBlockSupport( + props.name, + 'connections', + false + ); + // We prevent that the content is lost when the user removes the custom field. + // Editing the content in the paragraph block with a placeholder is not the best solution. + const prevContent = useRef( props.attributes?.content ); + if ( ! prevContent.current ) { + prevContent.current = ''; + } + if ( hasCustomFieldsSupport && props.isSelected ) { + return ( + <> + + { blockEditingMode === 'default' && ( + + + { + if ( nextValue === '' ) { + props.setAttributes( { + connections: undefined, + content: + prevContent.current !== + '' + ? prevContent.current + : undefined, + } ); + } else { + props.setAttributes( { + connections: { + // Content will be variable, could be content, href, src, etc. + content: { + // 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, + }, + }, + content: sprintf( + 'This content will be replaced in the frontend by the custom field "%s" value.', + nextValue + ), + } ); + } + } } + /> + + + ) } + + ); + } + + return ; + }; + }, + 'withInspectorControl' +); +if ( window.__experimentalConnections ) { + addFilter( + 'blocks.registerBlockType', + 'core/connections/attribute', + addAttribute + ); + addFilter( + 'editor.BlockEdit', + 'core/connections/with-inspector-control', + withInspectorControl + ); +} diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index a66aa0a73ed411..6834d859d25453 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -21,6 +21,7 @@ import './content-lock-ui'; import './metadata'; import './metadata-name'; import './behaviors'; +import './custom-fields'; export { useCustomSides } from './dimensions'; export { useLayoutClasses, useLayoutStyles } from './layout'; diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 7e13b13dc4feb9..d8fa3ac19af83f 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -41,6 +41,7 @@ "text": true } }, + "connections": true, "spacing": { "margin": true, "padding": true, From 514a0683f185347d4d9f95b1cc7a4cfb9e23de6d Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Tue, 1 Aug 2023 18:21:00 +0100 Subject: [PATCH 03/33] Add "connections" block attribute and get the meta field --- docs/reference-guides/core-blocks.md | 2 +- lib/experimental/blocks.php | 95 +++++++++++++++++++ lib/experimental/custom-sources/meta.php | 33 +++++++ .../block-library/src/paragraph/block.json | 9 ++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 lib/experimental/custom-sources/meta.php diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 51813220e58240..ab4e84c2cce51d 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -480,7 +480,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Name:** core/paragraph - **Category:** text - **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ -- **Attributes:** align, content, direction, dropCap, placeholder +- **Attributes:** align, connections, content, direction, dropCap, placeholder ## Pattern placeholder diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index fafeba00e3b3f6..55460d0f5ec60f 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -121,3 +121,98 @@ function gutenberg_register_metadata_attribute( $args ) { return $args; } add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' ); + + + + +// TODO: Wrap this with a check for the experimental Custom Sources API. + +/** + * Renders the block meta attributes. + * + * @param string $block_content Block Content. + * @param array $block Block attributes. + * @param WP_Block $block_instance The block instance. + */ +function gutenberg_render_custom_sources( $block_content, $block, $block_instance ) { + $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; + $block_type = $block_instance->block_type; + + // Not sure if we need it, it was in Riad's PR. + if ( null === $block_type ) { + return $block_content; + } + + // Whitelist of the block types that support custom sources + // Currently, we only allow the Paragraph and Image blocks to use custom sources. + if ( ! in_array( $block_type->name, array( 'core/paragraph', 'core/image' ), true ) ) { + return $block_content; + } + + // Get all the attributes that have a connection. + $connected_attributes = _wp_array_get( $block_type->attributes, array( 'connections', 'attributes' ), false ); + if ( ! $connected_attributes ) { + return $block_content; + } + + foreach ( $connected_attributes as $attribute_name => $attribute_value ) { + // If the source value is not meta, skip it because we only support meta + // sources for now. + if ( 'meta' !== $attribute_value['source'] ) { + continue; + } + + // If the attribute does not have a source, skip it. + if ( ! isset( $block_type->attributes[ $attribute_name ]['source'] ) ) { + continue; + } + + // If the attribute does not specify the name of the custom field, skip it. + if ( ! isset( $attribute_value['value'] ) ) { + continue; + } + + $block_content = $meta_custom_source['apply_source']( + $block_content, + $block_instance, + $attribute_value['value'], + $block_type->attributes[ $attribute_name ] + ); + } + + return $block_content; +} + +add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 3 ); + + + + +// ----- This is just for testing, remove later ----- + +/** + * Registers a custom meta + */ +function init_test_summary_meta_field() { + register_meta( + 'post', + 'test_custom_field', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'default' => 'hello this is a custom field test', + ) + ); + register_meta( + 'post', + 'second_test_custom_field', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'default' => 'second custom field test', + ) + ); +} +add_action( 'init', 'init_test_summary_meta_field' ); diff --git a/lib/experimental/custom-sources/meta.php b/lib/experimental/custom-sources/meta.php new file mode 100644 index 00000000000000..038a20a32c332a --- /dev/null +++ b/lib/experimental/custom-sources/meta.php @@ -0,0 +1,33 @@ + 'meta', + 'apply_source' => function ( $block_content, $block_instance, $meta_field, $attribute_config ) { + $meta_value = get_post_meta( $block_instance->context['postId'], $meta_field, true ); + $p = new WP_HTML_Tag_Processor( $block_content ); + $found = $p->next_tag( + array( + // TODO: build the query from CSS selector. + 'tag_name' => $attribute_config['selector'], + ) + ); + if ( ! $found ) { + return $block_content; + } + $tag_name = $p->get_tag(); + $markup = "<$tag_name>$meta_value"; + $p2 = new WP_HTML_Tag_Processor( $markup ); + $p2->next_tag(); + $names = $p->get_attribute_names_with_prefix( '' ); + foreach ( $names as $name ) { + $p2->set_attribute( $name, $p->get_attribute( $name ) ); + } + + return $p2 . ''; + }, +); diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 7e13b13dc4feb9..b9db5eaae065ec 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -7,6 +7,7 @@ "description": "Start with the basic building block of all narrative.", "keywords": [ "text" ], "textdomain": "default", + "usesContext": [ "postId" ], "attributes": { "align": { "type": "string" @@ -28,6 +29,14 @@ "direction": { "type": "string", "enum": [ "ltr", "rtl" ] + }, + "connections": { + "type": "object", + "attributes": { + "content": { + "source": "meta" + } + } } }, "supports": { From 5fbf21cb0b82857ee64ae02f55f2516af4b308d8 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Tue, 1 Aug 2023 19:17:44 +0100 Subject: [PATCH 04/33] Get rid of a phpcs warning --- 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 55460d0f5ec60f..b75b0f25933c95 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -193,7 +193,7 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc /** * Registers a custom meta */ -function init_test_summary_meta_field() { +function gutenberg_init_test_summary_meta_field() { register_meta( 'post', 'test_custom_field', @@ -215,4 +215,4 @@ function init_test_summary_meta_field() { ) ); } -add_action( 'init', 'init_test_summary_meta_field' ); +add_action( 'init', 'gutenberg_init_test_summary_meta_field' ); From a493bb158bbad0f2ecdd89efa46dfe0443ef6bb5 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 14:12:59 +0100 Subject: [PATCH 05/33] Update attribute acces and source definition Changed the attribute access from `$block_type->attributes` to `$block['attrs']`. Also updated attribute 'source' value from 'meta' to 'meta_fields'. These changes have been applied in both `blocks.php` and block attributes declaration in `block.json`. --- lib/experimental/blocks.php | 4 ++-- packages/block-library/src/paragraph/block.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index b75b0f25933c95..bbed1c90aa349f 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -150,7 +150,7 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc } // Get all the attributes that have a connection. - $connected_attributes = _wp_array_get( $block_type->attributes, array( 'connections', 'attributes' ), false ); + $connected_attributes = _wp_array_get( $block['attrs'], array( 'connections', 'attributes' ), false ); if ( ! $connected_attributes ) { return $block_content; } @@ -158,7 +158,7 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc foreach ( $connected_attributes as $attribute_name => $attribute_value ) { // If the source value is not meta, skip it because we only support meta // sources for now. - if ( 'meta' !== $attribute_value['source'] ) { + if ( 'meta_fields' !== $attribute_value['source'] ) { continue; } diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index ec4563517efa7a..60983dd63ca235 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -34,7 +34,7 @@ "type": "object", "attributes": { "content": { - "source": "meta" + "source": "meta_fields" } } } From 7575b404a65d93044bc96b3100e529ea1490d7a1 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 14:16:20 +0100 Subject: [PATCH 06/33] Updated the 'connections' property to include 'attributes' --- packages/block-editor/src/hooks/custom-fields.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 5f7f96808146fc..47a75484c91540 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -91,12 +91,14 @@ export const withInspectorControl = createHigherOrderComponent( } else { props.setAttributes( { connections: { - // Content will be variable, could be content, href, src, etc. - content: { - // 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, + attributes: { + // Content will be variable, could be content, href, src, etc. + content: { + // 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, + }, }, }, content: sprintf( From 39cb9313bb5b429fba3852ad819e98903663b3ea Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 14:49:54 +0100 Subject: [PATCH 07/33] Add `attributes` to TextControl value --- packages/block-editor/src/hooks/custom-fields.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 47a75484c91540..89677d1bf556a8 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -76,7 +76,8 @@ export const withInspectorControl = createHigherOrderComponent( label={ __( 'Custom field meta_key' ) } value={ props.attributes?.connections - ?.content?.value || '' + ?.attributes?.content?.value || + '' } onChange={ ( nextValue ) => { if ( nextValue === '' ) { From fa042cabb86134d6d537dd63a0107205dcf98372 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 14:49:54 +0100 Subject: [PATCH 08/33] Add `attributes` to TextControl value --- packages/block-editor/src/hooks/custom-fields.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 47a75484c91540..89677d1bf556a8 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -76,7 +76,8 @@ export const withInspectorControl = createHigherOrderComponent( label={ __( 'Custom field meta_key' ) } value={ props.attributes?.connections - ?.content?.value || '' + ?.attributes?.content?.value || + '' } onChange={ ( nextValue ) => { if ( nextValue === '' ) { From 64a5f034e0faf60a7556ee81de5d51289af4cb7f Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 14:54:23 +0100 Subject: [PATCH 09/33] Wrap the PHP code in experimental flag --- lib/experimental/blocks.php | 111 ++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index bbed1c90aa349f..872b9bfe7fd859 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -123,76 +123,73 @@ function gutenberg_register_metadata_attribute( $args ) { add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' ); +$gutenberg_experiments = get_option( 'gutenberg-experiments' ); +if ( $gutenberg_experiments && array_key_exists( 'gutenberg-connections', $gutenberg_experiments ) ) { + /** + * Renders the block meta attributes. + * + * @param string $block_content Block Content. + * @param array $block Block attributes. + * @param WP_Block $block_instance The block instance. + */ + function gutenberg_render_custom_sources( $block_content, $block, $block_instance ) { + $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; + $block_type = $block_instance->block_type; + // Not sure if we need it, it was in Riad's PR. + if ( null === $block_type ) { + return $block_content; + } -// TODO: Wrap this with a check for the experimental Custom Sources API. - -/** - * Renders the block meta attributes. - * - * @param string $block_content Block Content. - * @param array $block Block attributes. - * @param WP_Block $block_instance The block instance. - */ -function gutenberg_render_custom_sources( $block_content, $block, $block_instance ) { - $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; - $block_type = $block_instance->block_type; - - // Not sure if we need it, it was in Riad's PR. - if ( null === $block_type ) { - return $block_content; - } + // Whitelist of the block types that support custom sources + // Currently, we only allow the Paragraph and Image blocks to use custom sources. + if ( ! in_array( $block_type->name, array( 'core/paragraph', 'core/image' ), true ) ) { + return $block_content; + } - // Whitelist of the block types that support custom sources - // Currently, we only allow the Paragraph and Image blocks to use custom sources. - if ( ! in_array( $block_type->name, array( 'core/paragraph', 'core/image' ), true ) ) { - return $block_content; - } + // Get all the attributes that have a connection. + $connected_attributes = _wp_array_get( $block['attrs'], array( 'connections', 'attributes' ), false ); + if ( ! $connected_attributes ) { + return $block_content; + } - // Get all the attributes that have a connection. - $connected_attributes = _wp_array_get( $block['attrs'], array( 'connections', 'attributes' ), false ); - if ( ! $connected_attributes ) { - return $block_content; - } + foreach ( $connected_attributes as $attribute_name => $attribute_value ) { + // If the source value is not meta, skip it because we only support meta + // sources for now. + if ( 'meta_fields' !== $attribute_value['source'] ) { + continue; + } - foreach ( $connected_attributes as $attribute_name => $attribute_value ) { - // If the source value is not meta, skip it because we only support meta - // sources for now. - if ( 'meta_fields' !== $attribute_value['source'] ) { - continue; - } + // If the attribute does not have a source, skip it. + if ( ! isset( $block_type->attributes[ $attribute_name ]['source'] ) ) { + continue; + } - // If the attribute does not have a source, skip it. - if ( ! isset( $block_type->attributes[ $attribute_name ]['source'] ) ) { - continue; - } + // If the attribute does not specify the name of the custom field, skip it. + if ( ! isset( $attribute_value['value'] ) ) { + continue; + } - // If the attribute does not specify the name of the custom field, skip it. - if ( ! isset( $attribute_value['value'] ) ) { - continue; + $block_content = $meta_custom_source['apply_source']( + $block_content, + $block_instance, + $attribute_value['value'], + $block_type->attributes[ $attribute_name ] + ); } - $block_content = $meta_custom_source['apply_source']( - $block_content, - $block_instance, - $attribute_value['value'], - $block_type->attributes[ $attribute_name ] - ); + return $block_content; } - - return $block_content; } -add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 3 ); + add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 3 ); + // ----- This is just for testing, remove later ----- - -// ----- This is just for testing, remove later ----- - -/** - * Registers a custom meta - */ + /** + * Registers a custom meta + */ function gutenberg_init_test_summary_meta_field() { register_meta( 'post', @@ -215,4 +212,6 @@ function gutenberg_init_test_summary_meta_field() { ) ); } -add_action( 'init', 'gutenberg_init_test_summary_meta_field' ); + add_action( 'init', 'gutenberg_init_test_summary_meta_field' ); + +} From 0b521596bacc2d7f000b3da9284015e23425ec09 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 15:40:06 +0100 Subject: [PATCH 10/33] Update parens --- lib/experimental/blocks.php | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 872b9bfe7fd859..b83fdd00099b5a 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -180,7 +180,7 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc return $block_content; } -} + add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 3 ); @@ -190,28 +190,28 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc /** * Registers a custom meta */ -function gutenberg_init_test_summary_meta_field() { - register_meta( - 'post', - 'test_custom_field', - array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - 'default' => 'hello this is a custom field test', - ) - ); - register_meta( - 'post', - 'second_test_custom_field', - array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - 'default' => 'second custom field test', - ) - ); -} + function gutenberg_init_test_summary_meta_field() { + register_meta( + 'post', + 'test_custom_field', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'default' => 'hello this is a custom field test', + ) + ); + register_meta( + 'post', + 'second_test_custom_field', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'default' => 'second custom field test', + ) + ); + } add_action( 'init', 'gutenberg_init_test_summary_meta_field' ); } From 2014934a5db51a913ce6107de48b1a0650575082 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 15:53:07 +0100 Subject: [PATCH 11/33] Return early if there is no support for connections --- lib/experimental/blocks.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index b83fdd00099b5a..78fbc88b10215e 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -136,11 +136,14 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; $block_type = $block_instance->block_type; - // Not sure if we need it, it was in Riad's PR. if ( null === $block_type ) { return $block_content; } + if ( ! block_has_support( $block_type, array( 'connections' ), false ) ) { + return $block_content; + } + // Whitelist of the block types that support custom sources // Currently, we only allow the Paragraph and Image blocks to use custom sources. if ( ! in_array( $block_type->name, array( 'core/paragraph', 'core/image' ), true ) ) { @@ -180,8 +183,6 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc return $block_content; } - - add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 3 ); From 52b4a58d323265d7f2fff25d4fee659ae382b92b Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 15:59:52 +0100 Subject: [PATCH 12/33] Updated block type checks Rearranged the block type checks. Now, the code first checks if a given block is in the accepted types list (Paragraph and Image) and then it checks if the block type is null. This is a slight revamp of the existing checks for block support and block type acceptance. --- lib/experimental/blocks.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 78fbc88b10215e..837c354a2b016d 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -136,17 +136,17 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; $block_type = $block_instance->block_type; - if ( null === $block_type ) { + // Whitelist of the block types that support custom sources + // Currently, we only allow the Paragraph and Image blocks to use custom sources. + if ( ! in_array( $block['blockName'], array( 'core/paragraph', 'core/image' ), true ) ) { return $block_content; } - if ( ! block_has_support( $block_type, array( 'connections' ), false ) ) { + if ( null === $block_type ) { return $block_content; } - // Whitelist of the block types that support custom sources - // Currently, we only allow the Paragraph and Image blocks to use custom sources. - if ( ! in_array( $block_type->name, array( 'core/paragraph', 'core/image' ), true ) ) { + if ( ! block_has_support( $block_type, array( 'connections' ), false ) ) { return $block_content; } From cbec419eedfc3b80407b5baf27bc7eb19fc7db81 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 16:21:15 +0100 Subject: [PATCH 13/33] Updated experimental blocks logic - Removed unused test functions. - Update the comment above the tag name query in the `custom-sources` to clarify future improvements for supporting more block types. --- lib/experimental/blocks.php | 31 ------------------------ lib/experimental/custom-sources/meta.php | 3 ++- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 837c354a2b016d..4f04f6cad50b2c 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -184,35 +184,4 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc return $block_content; } add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 3 ); - - - // ----- This is just for testing, remove later ----- - - /** - * Registers a custom meta - */ - function gutenberg_init_test_summary_meta_field() { - register_meta( - 'post', - 'test_custom_field', - array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - 'default' => 'hello this is a custom field test', - ) - ); - register_meta( - 'post', - 'second_test_custom_field', - array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - 'default' => 'second custom field test', - ) - ); - } - add_action( 'init', 'gutenberg_init_test_summary_meta_field' ); - } diff --git a/lib/experimental/custom-sources/meta.php b/lib/experimental/custom-sources/meta.php index 038a20a32c332a..d574353f9e34c3 100644 --- a/lib/experimental/custom-sources/meta.php +++ b/lib/experimental/custom-sources/meta.php @@ -12,7 +12,8 @@ $p = new WP_HTML_Tag_Processor( $block_content ); $found = $p->next_tag( array( - // TODO: build the query from CSS selector. + // TODO: In the future, when blocks other than Paragraph and Image are + // supported, we should build the full query from CSS selector. 'tag_name' => $attribute_config['selector'], ) ); From c29d885938510961d33756227f9427c45c0d1137 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 2 Aug 2023 16:53:31 +0100 Subject: [PATCH 14/33] Add a comment in meta.php --- lib/experimental/custom-sources/meta.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/experimental/custom-sources/meta.php b/lib/experimental/custom-sources/meta.php index d574353f9e34c3..f6886c23a31c5e 100644 --- a/lib/experimental/custom-sources/meta.php +++ b/lib/experimental/custom-sources/meta.php @@ -8,6 +8,8 @@ return array( 'name' => 'meta', 'apply_source' => function ( $block_content, $block_instance, $meta_field, $attribute_config ) { + // 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. $meta_value = get_post_meta( $block_instance->context['postId'], $meta_field, true ); $p = new WP_HTML_Tag_Processor( $block_content ); $found = $p->next_tag( From 9f0d8510374064b3189ef7e5fb0480da91b8f06e Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 2 Aug 2023 18:57:06 +0200 Subject: [PATCH 15/33] Update comment --- packages/block-editor/src/hooks/custom-fields.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 89677d1bf556a8..33fd3e5046cbb5 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -56,6 +56,7 @@ export const withInspectorControl = createHigherOrderComponent( ); // We prevent that the content is lost when the user removes the custom field. // Editing the content in the paragraph block with a placeholder is not the best solution. + // As this is a temporary solution, that will be removed soon, we can live with it. const prevContent = useRef( props.attributes?.content ); if ( ! prevContent.current ) { prevContent.current = ''; From 1c65e748ef7e39501009ef99706c79a80c6e7652 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 2 Aug 2023 19:16:41 +0200 Subject: [PATCH 16/33] Clear the paragraph if meta value is cleared --- packages/block-editor/src/hooks/custom-fields.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 33fd3e5046cbb5..c1b8037a1c0cdb 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -6,7 +6,6 @@ import { PanelBody, TextControl } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { hasBlockSupport } from '@wordpress/blocks'; import { createHigherOrderComponent } from '@wordpress/compose'; -import { useRef } from '@wordpress/element'; /** * Internal dependencies @@ -54,13 +53,6 @@ export const withInspectorControl = createHigherOrderComponent( 'connections', false ); - // We prevent that the content is lost when the user removes the custom field. - // Editing the content in the paragraph block with a placeholder is not the best solution. - // As this is a temporary solution, that will be removed soon, we can live with it. - const prevContent = useRef( props.attributes?.content ); - if ( ! prevContent.current ) { - prevContent.current = ''; - } if ( hasCustomFieldsSupport && props.isSelected ) { return ( <> @@ -84,11 +76,7 @@ export const withInspectorControl = createHigherOrderComponent( if ( nextValue === '' ) { props.setAttributes( { connections: undefined, - content: - prevContent.current !== - '' - ? prevContent.current - : undefined, + content: undefined, } ); } else { props.setAttributes( { From 8638dff966ebf4e049733e72da355e52ceca1d7b Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 2 Aug 2023 19:19:16 +0200 Subject: [PATCH 17/33] Use placeholders instead of content --- packages/block-editor/src/hooks/custom-fields.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index c1b8037a1c0cdb..f3d562e019d1ed 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -77,6 +77,7 @@ export const withInspectorControl = createHigherOrderComponent( props.setAttributes( { connections: undefined, content: undefined, + placeholder: undefined, } ); } else { props.setAttributes( { @@ -91,7 +92,8 @@ export const withInspectorControl = createHigherOrderComponent( }, }, }, - content: sprintf( + content: undefined, + placeholder: sprintf( 'This content will be replaced in the frontend by the custom field "%s" value.', nextValue ), From 88dd0b561294ad02b6ba6424f22445bec7bf9ad2 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 3 Aug 2023 12:29:52 +0100 Subject: [PATCH 18/33] Update names of HTML processor instances in meta.php --- lib/experimental/custom-sources/meta.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/experimental/custom-sources/meta.php b/lib/experimental/custom-sources/meta.php index f6886c23a31c5e..b0d65e51724c32 100644 --- a/lib/experimental/custom-sources/meta.php +++ b/lib/experimental/custom-sources/meta.php @@ -11,8 +11,8 @@ // 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. $meta_value = get_post_meta( $block_instance->context['postId'], $meta_field, true ); - $p = new WP_HTML_Tag_Processor( $block_content ); - $found = $p->next_tag( + $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. @@ -22,15 +22,15 @@ if ( ! $found ) { return $block_content; } - $tag_name = $p->get_tag(); + $tag_name = $tags->get_tag(); $markup = "<$tag_name>$meta_value"; - $p2 = new WP_HTML_Tag_Processor( $markup ); - $p2->next_tag(); - $names = $p->get_attribute_names_with_prefix( '' ); + $updated_tags = new WP_HTML_Tag_Processor( $markup ); + $updated_tags->next_tag(); + $names = $tags->get_attribute_names_with_prefix( '' ); foreach ( $names as $name ) { - $p2->set_attribute( $name, $p->get_attribute( $name ) ); + $updated_tags->set_attribute( $name, $tags->get_attribute( $name ) ); } - return $p2 . ''; + return $updated_tags->get_updated_html() . ''; }, ); From 81f9c678e5eec28e8ee2a43c588248b9670ca353 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 3 Aug 2023 13:15:54 +0100 Subject: [PATCH 19/33] Add extra comments --- lib/experimental/blocks.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 4f04f6cad50b2c..d92df6c1533727 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -142,10 +142,12 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc 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 connections, skip it. if ( ! block_has_support( $block_type, array( 'connections' ), false ) ) { return $block_content; } From 713c6df868d924475b36c092a6e49ca47b578a21 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 3 Aug 2023 17:09:54 +0100 Subject: [PATCH 20/33] Rephrase the placeholder comment --- packages/block-editor/src/hooks/custom-fields.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index f3d562e019d1ed..4325d33987f645 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -94,7 +94,7 @@ export const withInspectorControl = createHigherOrderComponent( }, content: undefined, placeholder: sprintf( - 'This content will be replaced in the frontend by the custom field "%s" value.', + 'This content will be replaced on the frontend by the value of "%s" custom field.', nextValue ), } ); From e80effea1bd585cbf1f44aec0f6a918df68a5cda Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 3 Aug 2023 17:13:39 +0100 Subject: [PATCH 21/33] Check if current block is a paragraph or image --- packages/block-editor/src/hooks/custom-fields.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 4325d33987f645..789f543c9de189 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -53,6 +53,12 @@ export const withInspectorControl = createHigherOrderComponent( 'connections', false ); + + // Check if the current block is a paragraph or image block. + if ( ! [ 'core/paragraph', 'core/image' ].includes( props.name ) ) { + return ; + } + if ( hasCustomFieldsSupport && props.isSelected ) { return ( <> From 7640a60d1aee41d46b47f8b9842ebeac0e9c03fd Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Thu, 3 Aug 2023 17:40:23 +0100 Subject: [PATCH 22/33] Abstract the attribute name to use for the connection --- .../block-editor/src/hooks/custom-fields.js | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 789f543c9de189..63821a15b2873e 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -55,10 +55,18 @@ export const withInspectorControl = createHigherOrderComponent( ); // Check if the current block is a paragraph or image block. + // Currently, only these two blocks are supported. if ( ! [ 'core/paragraph', 'core/image' ].includes( props.name ) ) { return ; } + // 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 ( props.name === 'core/paragraph' ) attributeName = 'content'; + if ( props.name === 'core/image' ) attributeName = 'url'; + if ( hasCustomFieldsSupport && props.isSelected ) { return ( <> @@ -75,22 +83,23 @@ export const withInspectorControl = createHigherOrderComponent( label={ __( 'Custom field meta_key' ) } value={ props.attributes?.connections - ?.attributes?.content?.value || - '' + ?.attributes?.[ attributeName ] + ?.value || '' } onChange={ ( nextValue ) => { if ( nextValue === '' ) { props.setAttributes( { connections: undefined, - content: undefined, + [ attributeName ]: + undefined, placeholder: undefined, } ); } else { props.setAttributes( { connections: { attributes: { - // Content will be variable, could be content, href, src, etc. - content: { + // 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', @@ -98,7 +107,8 @@ export const withInspectorControl = createHigherOrderComponent( }, }, }, - content: undefined, + [ attributeName ]: + undefined, placeholder: sprintf( 'This content will be replaced on the frontend by the value of "%s" custom field.', nextValue From f0bcb0fb04f9412fb67303f12c101c073a7bc0c6 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 12:26:35 +0100 Subject: [PATCH 23/33] rename `connections` to `__experimentalConnections` --- docs/reference-guides/core-blocks.md | 2 +- packages/block-editor/src/hooks/custom-fields.js | 8 ++++---- packages/block-library/src/paragraph/block.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 347165330b10c4..51813220e58240 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -479,7 +479,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Name:** core/paragraph - **Category:** text -- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), connections, spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ +- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ - **Attributes:** align, content, direction, dropCap, placeholder ## Pattern placeholder diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 63821a15b2873e..6df76f8a085a18 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -14,18 +14,18 @@ import { InspectorControls } from '../components'; import { useBlockEditingMode } from '../components/block-editing-mode'; /** - * Filters registered block settings, extending attributes to include `connections`. + * Filters registered block settings, extending attributes to include `__experimentalConnections`. * * @param {Object} settings Original block settings. * * @return {Object} Filtered block settings. */ export function addAttribute( settings ) { - if ( hasBlockSupport( settings, 'connections', true ) ) { + if ( hasBlockSupport( settings, '__experimentalConnections', true ) ) { // Gracefully handle if settings.attributes is undefined. settings.attributes = { ...settings.attributes, - connections: { + __experimentalConnections: { type: 'object', }, }; @@ -50,7 +50,7 @@ export const withInspectorControl = createHigherOrderComponent( const blockEditingMode = useBlockEditingMode(); const hasCustomFieldsSupport = hasBlockSupport( props.name, - 'connections', + '__experimentalConnections', false ); diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index d8fa3ac19af83f..db30d95db84756 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -41,7 +41,7 @@ "text": true } }, - "connections": true, + "__experimentalConnections": true, "spacing": { "margin": true, "padding": true, From 6a2785f6e51674a1ff0b9fe275ed00103cbc5321 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 14:58:18 +0100 Subject: [PATCH 24/33] Do not export `addAttribute()` or `withInspectorControl()` --- .../block-editor/src/hooks/custom-fields.js | 156 +++++++++--------- 1 file changed, 76 insertions(+), 80 deletions(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 6df76f8a085a18..03f286386d27a1 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. */ -export function addAttribute( settings ) { +function addAttribute( settings ) { if ( hasBlockSupport( settings, '__experimentalConnections', true ) ) { // Gracefully handle if settings.attributes is undefined. settings.attributes = { @@ -44,91 +44,87 @@ export function addAttribute( settings ) { * * @return {WPComponent} Wrapped component. */ -export const withInspectorControl = createHigherOrderComponent( - ( BlockEdit ) => { - return ( props ) => { - const blockEditingMode = useBlockEditingMode(); - const hasCustomFieldsSupport = hasBlockSupport( - props.name, - '__experimentalConnections', - false - ); +const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => { + return ( props ) => { + const blockEditingMode = useBlockEditingMode(); + const hasCustomFieldsSupport = hasBlockSupport( + props.name, + '__experimentalConnections', + false + ); - // Check if the current block is a paragraph or image block. - // Currently, only these two blocks are supported. - if ( ! [ 'core/paragraph', 'core/image' ].includes( props.name ) ) { - return ; - } + // Check if the current block is a paragraph or image block. + // Currently, only these two blocks are supported. + if ( ! [ 'core/paragraph', 'core/image' ].includes( props.name ) ) { + return ; + } - // 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 ( props.name === 'core/paragraph' ) attributeName = 'content'; - if ( props.name === 'core/image' ) attributeName = 'url'; + // 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 ( props.name === 'core/paragraph' ) attributeName = 'content'; + if ( props.name === 'core/image' ) attributeName = 'url'; - if ( hasCustomFieldsSupport && props.isSelected ) { - return ( - <> - - { blockEditingMode === 'default' && ( - - - { - if ( nextValue === '' ) { - props.setAttributes( { - connections: undefined, - [ attributeName ]: - undefined, - placeholder: undefined, - } ); - } else { - props.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, - }, + if ( hasCustomFieldsSupport && props.isSelected ) { + return ( + <> + + { blockEditingMode === 'default' && ( + + + { + if ( nextValue === '' ) { + props.setAttributes( { + connections: undefined, + [ attributeName ]: undefined, + placeholder: undefined, + } ); + } else { + props.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 - ), - } ); - } - } } - /> - - - ) } - - ); - } + }, + [ attributeName ]: undefined, + placeholder: sprintf( + 'This content will be replaced on the frontend by the value of "%s" custom field.', + nextValue + ), + } ); + } + } } + /> + + + ) } + + ); + } + + return ; + }; +}, 'withInspectorControl' ); - return ; - }; - }, - 'withInspectorControl' -); if ( window.__experimentalConnections ) { addFilter( 'blocks.registerBlockType', From 5cd3d2292deb3692f15bcc50e56f9eeaa715d41b Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 16:14:05 +0100 Subject: [PATCH 25/33] Renamed '__experimentalConnections' to 'connections' in block settings --- packages/block-editor/src/hooks/custom-fields.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 03f286386d27a1..dbc8c3ec2c089f 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -14,7 +14,7 @@ import { InspectorControls } from '../components'; import { useBlockEditingMode } from '../components/block-editing-mode'; /** - * Filters registered block settings, extending attributes to include `__experimentalConnections`. + * Filters registered block settings, extending attributes to include `connections`. * * @param {Object} settings Original block settings. * @@ -22,10 +22,10 @@ import { useBlockEditingMode } from '../components/block-editing-mode'; */ function addAttribute( settings ) { if ( hasBlockSupport( settings, '__experimentalConnections', true ) ) { - // Gracefully handle if settings.attributes is undefined. + // Gracefully handle if settings.attributes.connections is undefined. settings.attributes = { ...settings.attributes, - __experimentalConnections: { + connections: { type: 'object', }, }; From 6d00a9bb35388e59e6d10901ebaefac9c5f69d4e Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 16:14:05 +0100 Subject: [PATCH 26/33] Renamed '__experimentalConnections' to 'connections' in block settings --- packages/block-editor/src/hooks/custom-fields.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 03f286386d27a1..dbc8c3ec2c089f 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -14,7 +14,7 @@ import { InspectorControls } from '../components'; import { useBlockEditingMode } from '../components/block-editing-mode'; /** - * Filters registered block settings, extending attributes to include `__experimentalConnections`. + * Filters registered block settings, extending attributes to include `connections`. * * @param {Object} settings Original block settings. * @@ -22,10 +22,10 @@ import { useBlockEditingMode } from '../components/block-editing-mode'; */ function addAttribute( settings ) { if ( hasBlockSupport( settings, '__experimentalConnections', true ) ) { - // Gracefully handle if settings.attributes is undefined. + // Gracefully handle if settings.attributes.connections is undefined. settings.attributes = { ...settings.attributes, - __experimentalConnections: { + connections: { type: 'object', }, }; From 367ee0c34a482c1199031f3768746c5fdcd12304 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 18:18:08 +0100 Subject: [PATCH 27/33] Remove `connections` attribute from block.json --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/paragraph/block.json | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index ab4e84c2cce51d..51813220e58240 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -480,7 +480,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Name:** core/paragraph - **Category:** text - **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ -- **Attributes:** align, connections, content, direction, dropCap, placeholder +- **Attributes:** align, content, direction, dropCap, placeholder ## Pattern placeholder diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 122d826c810001..85f56f4a838f50 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -29,14 +29,6 @@ "direction": { "type": "string", "enum": [ "ltr", "rtl" ] - }, - "connections": { - "type": "object", - "attributes": { - "content": { - "source": "meta_fields" - } - } } }, "supports": { From a45e464d3fb179515b6d5273226034cc00fc3dbb Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 16:14:05 +0100 Subject: [PATCH 28/33] Renamed '__experimentalConnections' to 'connections' in block settings --- 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 d92df6c1533727..969d0a93558185 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -148,7 +148,7 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc } // If the block does not have support for connections, skip it. - if ( ! block_has_support( $block_type, array( 'connections' ), false ) ) { + if ( ! block_has_support( $block_type, array( '__experimentalConnections' ), false ) ) { return $block_content; } From 84f2a08f314efd505969e56379739b564b571fe9 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 18:30:11 +0100 Subject: [PATCH 29/33] Remove `get_updated_html() . ''` in meta.php --- lib/experimental/custom-sources/meta.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/custom-sources/meta.php b/lib/experimental/custom-sources/meta.php index b0d65e51724c32..0b6b0148c64cad 100644 --- a/lib/experimental/custom-sources/meta.php +++ b/lib/experimental/custom-sources/meta.php @@ -31,6 +31,6 @@ $updated_tags->set_attribute( $name, $tags->get_attribute( $name ) ); } - return $updated_tags->get_updated_html() . ''; + return $updated_tags->get_updated_html(); }, ); From 49d5cb6e3c6a167825af85cf00e40370cf767988 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 19:20:28 +0100 Subject: [PATCH 30/33] Add an allowlist of blocks/attributes --- lib/experimental/blocks.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 969d0a93558185..5f6f9234c14896 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -136,9 +136,18 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; $block_type = $block_instance->block_type; + // Allowlist of blocks that support custom sources + // 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 custom sources // Currently, we only allow the Paragraph and Image blocks to use custom sources. - if ( ! in_array( $block['blockName'], array( 'core/paragraph', 'core/image' ), true ) ) { + if ( ! in_array( $block['blockName'], array_keys( $blocks_attributes_allowlist ), true ) ) { return $block_content; } @@ -159,6 +168,12 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc } 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; + } + // If the source value is not meta, skip it because we only support meta // sources for now. if ( 'meta_fields' !== $attribute_value['source'] ) { From 414b322a5886f1e3280b3ec2719e237c09195f25 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Mon, 7 Aug 2023 19:59:35 +0100 Subject: [PATCH 31/33] Refactor blocks.php & custom sources - Separate the "connections" into own file. - Move the code that renders the HTML using the meta fields back into blocks.php --- lib/experimental/blocks.php | 37 +++++++++++++++---- lib/experimental/connection-sources/index.php | 15 ++++++++ lib/experimental/custom-sources/meta.php | 36 ------------------ 3 files changed, 44 insertions(+), 44 deletions(-) create mode 100644 lib/experimental/connection-sources/index.php delete mode 100644 lib/experimental/custom-sources/meta.php diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 5f6f9234c14896..2388304103b136 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -132,11 +132,11 @@ function gutenberg_register_metadata_attribute( $args ) { * @param array $block Block attributes. * @param WP_Block $block_instance The block instance. */ - function gutenberg_render_custom_sources( $block_content, $block, $block_instance ) { - $meta_custom_source = require __DIR__ . '/custom-sources/meta.php'; + function gutenberg_render_block_connections( $block_content, $block, $block_instance ) { + $connection_sources = require __DIR__ . '/connection-sources/index.php'; $block_type = $block_instance->block_type; - // Allowlist of blocks that support custom sources + // Allowlist of blocks that support custom connections // Currently, we only allow the following blocks and attributes: // - Paragraph: content. // - Image: url. @@ -145,7 +145,7 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc 'core/image' => array( 'url' ), ); - // Whitelist of the block types that support custom sources + // Whitelist of the block types that support custom connection sources // Currently, we only allow the Paragraph and Image blocks to use custom sources. if ( ! in_array( $block['blockName'], array_keys( $blocks_attributes_allowlist ), true ) ) { return $block_content; @@ -190,15 +190,36 @@ function gutenberg_render_custom_sources( $block_content, $block, $block_instanc continue; } - $block_content = $meta_custom_source['apply_source']( - $block_content, + // Get the content from the connection. + $custom_value = $connection_sources[ $attribute_value['source'] ]( $block_instance, $attribute_value['value'], - $block_type->attributes[ $attribute_name ] ); + + $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; + }; + $tag_name = $tags->get_tag(); + $markup = "<$tag_name>$custom_value"; + $updated_tags = new WP_HTML_Tag_Processor( $markup ); + $updated_tags->next_tag(); + $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 $block_content; } - add_filter( 'render_block', 'gutenberg_render_custom_sources', 10, 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 new file mode 100644 index 00000000000000..ed46c60b40ea79 --- /dev/null +++ b/lib/experimental/connection-sources/index.php @@ -0,0 +1,15 @@ + '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 ); + }, +); diff --git a/lib/experimental/custom-sources/meta.php b/lib/experimental/custom-sources/meta.php deleted file mode 100644 index 0b6b0148c64cad..00000000000000 --- a/lib/experimental/custom-sources/meta.php +++ /dev/null @@ -1,36 +0,0 @@ - 'meta', - 'apply_source' => function ( $block_content, $block_instance, $meta_field, $attribute_config ) { - // 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. - $meta_value = get_post_meta( $block_instance->context['postId'], $meta_field, true ); - $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' => $attribute_config['selector'], - ) - ); - if ( ! $found ) { - return $block_content; - } - $tag_name = $tags->get_tag(); - $markup = "<$tag_name>$meta_value"; - $updated_tags = new WP_HTML_Tag_Processor( $markup ); - $updated_tags->next_tag(); - $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(); - }, -); From 92c3cd9a6e36d0b43e3084a4c2f4b66e176c729e Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Tue, 8 Aug 2023 12:20:30 +0100 Subject: [PATCH 32/33] Remove trailing comma? --- 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 2388304103b136..77e1d18f942815 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -193,7 +193,7 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst // Get the content from the connection. $custom_value = $connection_sources[ $attribute_value['source'] ]( $block_instance, - $attribute_value['value'], + $attribute_value['value'] ); $tags = new WP_HTML_Tag_Processor( $block_content ); From ba713a4fd3c866c46d993dc19f0874a8aa3850a3 Mon Sep 17 00:00:00 2001 From: Michal Czaplinski Date: Wed, 9 Aug 2023 14:07:05 +0100 Subject: [PATCH 33/33] Update naming: - "custom sources" -> connection sources - use "block connections" consistently --- lib/experimental/blocks.php | 16 +++++++++------- lib/experimental/connection-sources/index.php | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 77e1d18f942815..febb404394a4f9 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -136,7 +136,7 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst $connection_sources = require __DIR__ . '/connection-sources/index.php'; $block_type = $block_instance->block_type; - // Allowlist of blocks that support custom connections + // Allowlist of blocks that support block connections. // Currently, we only allow the following blocks and attributes: // - Paragraph: content. // - Image: url. @@ -145,8 +145,8 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst 'core/image' => array( 'url' ), ); - // Whitelist of the block types that support custom connection sources - // Currently, we only allow the Paragraph and Image blocks to use custom sources. + // 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; } @@ -156,7 +156,7 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst return $block_content; } - // If the block does not have support for connections, skip it. + // If the block does not have support for block connections, skip it. if ( ! block_has_support( $block_type, array( '__experimentalConnections' ), false ) ) { return $block_content; } @@ -174,8 +174,8 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst continue; } - // If the source value is not meta, skip it because we only support meta - // sources for now. + // If the source value is not "meta_fields", skip it because the only supported + // connection source is meta (custom fields) for now. if ( 'meta_fields' !== $attribute_value['source'] ) { continue; } @@ -190,7 +190,7 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst continue; } - // Get the content from the connection. + // Get the content from the connection source. $custom_value = $connection_sources[ $attribute_value['source'] ]( $block_instance, $attribute_value['value'] @@ -211,6 +211,8 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst $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 ) ); diff --git a/lib/experimental/connection-sources/index.php b/lib/experimental/connection-sources/index.php index ed46c60b40ea79..b63abcad96f628 100644 --- a/lib/experimental/connection-sources/index.php +++ b/lib/experimental/connection-sources/index.php @@ -1,6 +1,6 @@