diff --git a/lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php b/lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php index a4dfba2e19dd71..95c6d1816ecc32 100644 --- a/lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php +++ b/lib/compat/wordpress-6.5/block-bindings/pattern-overrides.php @@ -15,11 +15,30 @@ * @return mixed The value computed for the source. */ function gutenberg_block_bindings_pattern_overrides_callback( $source_attrs, $block_instance, $attribute_name ) { - if ( empty( $block_instance->attributes['metadata']['name'] ) ) { + if ( ! isset( $block_instance->context[ 'pattern/overrides'] ) ) { return null; } - $metadata_name = $block_instance->attributes['metadata']['name']; - return _wp_array_get( $block_instance->context, array( 'pattern/overrides', $metadata_name, $attribute_name ), null ); + + $override_content = $block_instance->context[ 'pattern/overrides']; + + // Back compat. Pattern overrides previously used a metadata `id` instead of `name`. + // We check first for the name, and if it exists, use that value. + if ( isset( $block_instance->attributes['metadata']['name'] ) ) { + $metadata_name = $block_instance->attributes['metadata']['name']; + if ( array_key_exists( $metadata_name, $override_content ) ) { + return _wp_array_get( $override_content, array( $metadata_name, $attribute_name ), null ); + } + } + + // Next check for the `id`. + if ( isset( $block_instance->attributes['metadata']['id'] ) ) { + $metadata_id = $block_instance->attributes['metadata']['id']; + if ( array_key_exists( $metadata_id, $override_content ) ) { + return _wp_array_get( $override_content, array( $metadata_id, $attribute_name ), null ); + } + } + + return null; } /** diff --git a/packages/block-library/src/block/deprecated.js b/packages/block-library/src/block/deprecated.js index 70c548055fa3e2..f820867fff6271 100644 --- a/packages/block-library/src/block/deprecated.js +++ b/packages/block-library/src/block/deprecated.js @@ -7,7 +7,7 @@ const v2 = { ref: { type: 'number', }, - overrides: { + content: { type: 'object', }, }, @@ -61,7 +61,7 @@ const v2 = { return { ...retainedAttributes, - content, + content: updatedContent, }; } diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index ab9ec116d198de..77ad6cd1941691 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -110,18 +110,41 @@ function applyInitialContentValuesToInnerBlocks( defaultValues ); const metadataName = block.attributes.metadata?.name; - if ( ! metadataName || ! hasOverridableAttributes( block ) ) + const metadataId = block.attributes.metadata?.id; + + if ( + ( ! metadataName && ! metadataId ) || + ! hasOverridableAttributes( block ) + ) { return { ...block, innerBlocks }; + } + const attributes = getOverridableAttributes( block ); const newAttributes = { ...block.attributes }; for ( const attributeKey of attributes ) { - defaultValues[ metadataName ] ??= {}; - defaultValues[ metadataName ][ attributeKey ] = - block.attributes[ attributeKey ]; - - const contentValues = content[ metadataName ]; - if ( contentValues?.[ attributeKey ] !== undefined ) { - newAttributes[ attributeKey ] = contentValues[ attributeKey ]; + // Back compat. Pattern overrides used to use a metadata `id` + // before using a `name`. Check for the name first, but if it + // doesn't exist use the id. + if ( Object.hasOwn( content, metadataName ) ) { + defaultValues[ metadataName ] ??= {}; + defaultValues[ metadataName ][ attributeKey ] = + block.attributes[ attributeKey ]; + + const contentValues = content[ metadataName ]; + if ( contentValues?.[ attributeKey ] !== undefined ) { + newAttributes[ attributeKey ] = + contentValues[ attributeKey ]; + } + } else if ( Object.hasOwn( content, metadataId ) ) { + defaultValues[ metadataId ] ??= {}; + defaultValues[ metadataId ][ attributeKey ] = + block.attributes[ attributeKey ]; + + const contentValues = content[ metadataId ]; + if ( contentValues?.[ attributeKey ] !== undefined ) { + newAttributes[ attributeKey ] = + contentValues[ attributeKey ]; + } } } return { @@ -152,19 +175,39 @@ function getContentValuesFromInnerBlocks( blocks, defaultValues ) { getContentValuesFromInnerBlocks( block.innerBlocks, defaultValues ) ); const metadataName = block.attributes.metadata?.name; - if ( ! metadataName || ! hasOverridableAttributes( block ) ) continue; + const metadataId = block.attributes.metadata?.id; + if ( + ( ! metadataName && ! metadataId ) || + ! hasOverridableAttributes( block ) + ) { + continue; + } + const attributes = getOverridableAttributes( block ); + + // Back compat. Pattern overrides previously used an 'id' instead of a 'name'. + // Check first if the metadata name or id is in use in the existing content, + // and if so use the appropriate one. If not, prefer the name when defined. + let contentKey; + if ( Object.hasOwn( content, metadataName ) ) { + contentKey = metadataName; + } else if ( Object.hasOwn( content, metadataId ) ) { + contentKey = metadataId; + } else { + contentKey = metadataName ? metadataName : metadataId; + } + for ( const attributeKey of attributes ) { if ( ! isAttributeEqual( block.attributes[ attributeKey ], - defaultValues[ metadataName ][ attributeKey ] + defaultValues?.[ contentKey ]?.[ attributeKey ] ) ) { - content[ metadataName ] ??= {}; + content[ contentKey ] ??= {}; // TODO: We need a way to represent `undefined` in the serialized overrides. // Also see: https://github.com/WordPress/gutenberg/pull/57249#discussion_r1452987871 - content[ metadataName ][ attributeKey ] = + content[ contentKey ][ attributeKey ] = block.attributes[ attributeKey ] === undefined ? // TODO: We use an empty string to represent undefined for now until // we support a richer format for overrides and the block binding API.