Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement partially synced patterns behind an experimental flag #56235

Merged
merged 9 commits into from
Dec 1, 2023
36 changes: 36 additions & 0 deletions lib/block-supports/pattern.php
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this could be called pattern-partial-syncing.php or similar. While it does a slightly different thing to the js file of the same name, it is a little more descriptive.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/**
* Pattern block support flag.
*
* @package gutenberg
*/

$gutenberg_experiments = get_option( 'gutenberg-experiments' );
if ( $gutenberg_experiments && array_key_exists( 'gutenberg-connections', $gutenberg_experiments ) ) {
/**
* Registers the dynamicContent context for block types that support it.
*
* @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;

if ( $pattern_support ) {
if ( ! $block_type->uses_context ) {
$block_type->uses_context = array();
}

if ( ! in_array( 'dynamicContent', $block_type->uses_context, true ) ) {
$block_type->uses_context[] = 'dynamicContent';
}
}
}

// Register the block support.
WP_Block_Supports::get_instance()->register(
'pattern',
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this might be a bit confusing as we don't add pattern as a support option to the block.json of paragraph. We could probably add a comment to the file about why it works this way.

Similar to the other comment, the name pattern might also not be descriptive enough.

Copy link
Member

@gziolo gziolo Nov 30, 2023

Choose a reason for hiding this comment

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

This looks more specific to block bindings (previously connections), but it is indeed necessary for the Pattern block.

array(
'register_attribute' => 'gutenberg_register_pattern_support',
)
);
}
10 changes: 7 additions & 3 deletions lib/experimental/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,8 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst
continue;
}

// 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'] ) {
// Skip if the source value is not "meta_fields" or "pattern_attributes".
if ( 'meta_fields' !== $attribute_value['source'] && 'pattern_attributes' !== $attribute_value['source'] ) {
continue;
}

Expand All @@ -154,6 +153,10 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst
$attribute_value['value']
);

if ( false === $custom_value ) {
continue;
}

$tags = new WP_HTML_Tag_Processor( $block_content );
$found = $tags->next_tag(
array(
Expand Down Expand Up @@ -181,5 +184,6 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst

return $block_content;
}

add_filter( 'render_block', 'gutenberg_render_block_connections', 10, 3 );
}
7 changes: 5 additions & 2 deletions lib/experimental/connection-sources/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
*/

return array(
'name' => 'meta',
'meta_fields' => function ( $block_instance, $meta_field ) {
'name' => '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, $meta_field ) {
return _wp_array_get( $block_instance->context, array( 'dynamicContent', $meta_field ), false );
},
);
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ function () {
require __DIR__ . '/block-supports/shadow.php';
require __DIR__ . '/block-supports/background.php';
require __DIR__ . '/block-supports/behaviors.php';
require __DIR__ . '/block-supports/pattern.php';

// Data views.
require_once __DIR__ . '/experimental/data-views.php';
90 changes: 58 additions & 32 deletions packages/block-editor/src/hooks/custom-fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { PanelBody, TextControl } from '@wordpress/components';
import { PanelBody, TextControl, SelectControl } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { hasBlockSupport } from '@wordpress/blocks';
import { createHigherOrderComponent } from '@wordpress/compose';
Expand Down Expand Up @@ -47,45 +47,71 @@ function CustomFieldsControl( props ) {
if ( props.name === 'core/paragraph' ) attributeName = 'content';
if ( props.name === 'core/image' ) attributeName = 'url';

const connectionSource =
props.attributes?.connections?.attributes?.[ attributeName ]?.source ||
'';
const connectionValue =
props.attributes?.connections?.attributes?.[ attributeName ]?.value ||
'';

function updateConnections( source, value ) {
if ( value === '' ) {
props.setAttributes( {
connections: 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,
value,
},
},
},
placeholder: sprintf(
'This content will be replaced on the frontend by the value of "%s" custom field.',
value
),
} );
}
}

return (
<InspectorControls>
<PanelBody title={ __( 'Connections' ) } initialOpen={ true }>
<SelectControl
label={ __( 'Source' ) }
value={ connectionSource }
options={ [
{
label: __( 'None' ),
value: '',
},
{
label: __( 'Meta fields' ),
value: 'meta_fields',
},
{
label: __( 'Pattern attributes' ),
value: 'pattern_attributes',
},
] }
onChange={ ( nextSource ) => {
updateConnections( nextSource, connectionValue );
} }
/>
<TextControl
__nextHasNoMarginBottom
autoComplete="off"
label={ __( 'Custom field meta_key' ) }
value={
props.attributes?.connections?.attributes?.[
attributeName
]?.value || ''
}
value={ connectionValue }
onChange={ ( nextValue ) => {
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
),
} );
}
updateConnections( connectionSource, nextValue );
} }
/>
</PanelBody>
Expand Down
8 changes: 8 additions & 0 deletions packages/block-editor/src/store/private-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,11 @@ export function deleteStyleOverride( id ) {
id,
};
}

export function syncDerivedBlockAttributes( clientId, attributes ) {
return {
type: 'SYNC_DERIVED_BLOCK_ATTRIBUTES',
clientIds: [ clientId ],
attributes,
};
}
8 changes: 8 additions & 0 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ const withBlockTree =
false
);
break;
case 'SYNC_DERIVED_BLOCK_ATTRIBUTES':
case 'UPDATE_BLOCK_ATTRIBUTES': {
newState.tree = new Map( newState.tree );
action.clientIds.forEach( ( clientId ) => {
Expand Down Expand Up @@ -456,6 +457,12 @@ function withPersistentBlockChange( reducer ) {
return ( state, action ) => {
let nextState = reducer( state, action );

if ( action.type === 'SYNC_DERIVED_BLOCK_ATTRIBUTES' ) {
return nextState.isPersistentChange
? { ...nextState, isPersistentChange: false }
: nextState;
}

const isExplicitPersistentChange =
action.type === 'MARK_LAST_CHANGE_AS_PERSISTENT' ||
markNextChangeAsNotPersistent;
Expand Down Expand Up @@ -860,6 +867,7 @@ export const blocks = pipe(
return newState;
}

case 'SYNC_DERIVED_BLOCK_ATTRIBUTES':
case 'UPDATE_BLOCK_ATTRIBUTES': {
// Avoid a state change if none of the block IDs are known.
if ( action.clientIds.every( ( id ) => ! state.get( id ) ) ) {
Expand Down
Loading
Loading