diff --git a/.wp-env.json b/.wp-env.json
index 20d5597e54bbc9..bd5399ed0c1ff7 100644
--- a/.wp-env.json
+++ b/.wp-env.json
@@ -1,7 +1,7 @@
{
"core": "WordPress/WordPress",
"plugins": [ "." ],
- "themes": [ "./test/emptytheme" ],
+ "themes": [ "./test/emptytheme", "./test/emptytheme-child" ],
"env": {
"tests": {
"mappings": {
diff --git a/lib/block-supports/background.php b/lib/block-supports/background.php
index 10169f062039a3..edffecdfbc902c 100644
--- a/lib/block-supports/background.php
+++ b/lib/block-supports/background.php
@@ -45,9 +45,6 @@ function gutenberg_get_background_support_styles( $background_styles = array() )
* "theme" source implies relative path to the theme directory
*/
if ( ! empty( $background_styles['backgroundImage']['url'] ) && is_string( $background_styles['backgroundImage']['url'] ) && 'theme' === $background_image_source ) {
- if ( ! str_starts_with( $background_styles['backgroundImage']['url'], '/' ) ) {
- $background_styles['backgroundImage']['url'] = '/' . $background_styles['backgroundImage']['url'];
- }
$background_styles['backgroundImage']['url'] = esc_url( get_theme_file_uri( $background_styles['backgroundImage']['url'] ) );
}
return gutenberg_style_engine_get_styles( array( 'background' => $background_styles ) );
diff --git a/packages/block-editor/src/components/global-styles/background-panel.js b/packages/block-editor/src/components/global-styles/background-panel.js
index ef1f9673d601f9..a3637308ae260e 100644
--- a/packages/block-editor/src/components/global-styles/background-panel.js
+++ b/packages/block-editor/src/components/global-styles/background-panel.js
@@ -38,6 +38,7 @@ import { TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
import { setImmutably } from '../../utils/object';
import MediaReplaceFlow from '../media-replace-flow';
import { store as blockEditorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
const IMAGE_BACKGROUND_TYPE = 'image';
const DEFAULT_CONTROLS = {
@@ -201,6 +202,23 @@ function BackgroundImageToolsPanelItem( {
...inheritedValue?.background?.backgroundImage,
};
+ const { backgroundImageURL } = useSelect(
+ ( select ) => {
+ const { getThemeFileURI } = unlock( select( blockEditorStore ) );
+ let file = url;
+ if (
+ !! style?.background?.backgroundImage?.url &&
+ style?.background?.backgroundImage?.source === 'theme'
+ ) {
+ file = getThemeFileURI( style.background.backgroundImage.url );
+ }
+ return {
+ backgroundImageURL: file,
+ };
+ },
+ [ url ]
+ );
+
const replaceContainerRef = useRef();
const { createErrorNotice } = useDispatch( noticesStore );
@@ -295,7 +313,7 @@ function BackgroundImageToolsPanelItem( {
>
}
variant="secondary"
diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js
index a0d971ca27183b..cb5fd8c301cca6 100644
--- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js
+++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js
@@ -323,8 +323,7 @@ export function getStylesDeclarations(
selector = '',
useRootPaddingAlign,
tree = {},
- isTemplate = true,
- editorSettings
+ isTemplate = true
) {
const { kebabCase } = unlock( componentsPrivateApis );
const isRoot = ROOT_BLOCK_SELECTOR === selector;
@@ -401,17 +400,13 @@ export function getStylesDeclarations(
*/
if ( !! blockStyles?.background ) {
blockStyles = setBackgroundStyleDefaults( blockStyles, {
- ...editorSettings,
selector,
} );
}
// The goal is to move everything to server side generated engine styles
// This is temporary as we absorb more and more styles into the engine.
- const extraRules = getCSSRules( blockStyles, {
- ...editorSettings,
- isRoot,
- } );
+ const extraRules = getCSSRules( blockStyles );
extraRules.forEach( ( rule ) => {
// Don't output padding properties if padding variables are set or if we're not editing a full template.
if (
@@ -794,8 +789,7 @@ export const toStyles = (
hasBlockGapSupport,
hasFallbackGapSupport,
disableLayoutStyles = false,
- isTemplate = true,
- editorSettings = {}
+ isTemplate = true
) => {
const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
@@ -905,8 +899,7 @@ export const toStyles = (
styleVariations,
styleVariationSelector,
useRootPaddingAlign,
- tree,
- editorSettings
+ tree
);
if ( styleVariationDeclarations.length ) {
ruleset += `${ styleVariationSelector }{${ styleVariationDeclarations.join(
@@ -954,8 +947,7 @@ export const toStyles = (
selector,
useRootPaddingAlign,
tree,
- isTemplate,
- editorSettings
+ isTemplate
);
if ( declarations?.length ) {
ruleset += `:where(${ selector }){${ declarations.join(
@@ -1217,17 +1209,12 @@ export function useGlobalStylesOutputWithConfig( mergedConfig = {} ) {
const hasBlockGapSupport = blockGap !== null;
const hasFallbackGapSupport = ! hasBlockGapSupport; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback styles support.
- const { stylesheetURI, templateURI, disableLayoutStyles } = useSelect(
- ( select ) => {
- const _settings = select( blockEditorStore ).getSettings();
- return {
- stylesheetURI:
- _settings?.__experimentalCurrentTheme?.stylesheetURI,
- templateURI: _settings?.__experimentalCurrentTheme?.templateURI,
- disableLayoutStyles: !! _settings.disableLayoutStyles,
- };
- }
- );
+ const { disableLayoutStyles } = useSelect( ( select ) => {
+ const _settings = select( blockEditorStore ).getSettings();
+ return {
+ disableLayoutStyles: !! _settings.disableLayoutStyles,
+ };
+ } );
const blockContext = useContext( BlockContext );
@@ -1257,12 +1244,9 @@ export function useGlobalStylesOutputWithConfig( mergedConfig = {} ) {
hasBlockGapSupport,
hasFallbackGapSupport,
disableLayoutStyles,
- isTemplate,
- {
- stylesheetURI,
- templateURI,
- }
+ isTemplate
);
+ console.log( 'globalStyles', globalStyles );
const svgs = toSvgFilters( updatedConfig, blockSelectors );
const styles = [
@@ -1310,8 +1294,6 @@ export function useGlobalStylesOutputWithConfig( mergedConfig = {} ) {
disableLayoutStyles,
isTemplate,
getBlockStyles,
- stylesheetURI,
- templateURI,
] );
}
diff --git a/packages/block-editor/src/hooks/background.js b/packages/block-editor/src/hooks/background.js
index 894f621643c574..badaaa4b8a025d 100644
--- a/packages/block-editor/src/hooks/background.js
+++ b/packages/block-editor/src/hooks/background.js
@@ -18,6 +18,7 @@ import {
hasBackgroundImageValue,
} from '../components/global-styles/background-panel';
import { ROOT_BLOCK_SELECTOR } from '../components/global-styles/utils';
+import { unlock } from '../lock-unlock';
export const BACKGROUND_SUPPORT_KEY = 'background';
@@ -72,32 +73,6 @@ export function setBackgroundStyleDefaults( blockStyles, options ) {
const backgroundImage = blockStyles?.background?.backgroundImage;
const newBackgroundStyles = {};
-/* if (
- backgroundImage?.source === 'theme' &&
- !! backgroundImage?.url &&
- !! options?.stylesheetURI &&
- !! options?.templateURI
- ) {
- const activeThemeImageResource = `${ options.stylesheetURI }${
- backgroundImage.url.startsWith( '/' )
- ? backgroundImage.url
- : `/${ backgroundImage.url }`
- }`;
-
- const parentThemeImageResource = `${ options.templateURI }${
- backgroundImage.url.startsWith( '/' )
- ? backgroundImage.url
- : `/${ backgroundImage.url }`
- }`;
-
- newBackgroundStyles.backgroundImage = {
- ...backgroundImage,
- url: `${ encodeURI(
- safeDecodeURI( activeThemeImageResource )
- ) }, ${ encodeURI( safeDecodeURI( parentThemeImageResource ) ) }`,
- };
- }*/
-
// Set block background defaults.
if ( options?.selector !== ROOT_BLOCK_SELECTOR && !! backgroundImage ) {
if ( ! blockStyles?.background?.backgroundSize ) {
@@ -202,7 +177,44 @@ export function BackgroundImagePanel( {
);
}
+function useBlockProps( { name, style } ) {
+ const { backgroundImageURL } = useSelect(
+ ( select ) => {
+ const { getThemeFileURI } = unlock( select( blockEditorStore ) );
+ let file;
+ if (
+ !! style?.background?.backgroundImage?.url &&
+ style?.background?.backgroundImage?.source === 'theme'
+ ) {
+ file = getThemeFileURI( style.background.backgroundImage.url );
+ }
+ return {
+ backgroundImageURL: file,
+ };
+ },
+ [ style?.background?.backgroundImage ]
+ );
+
+ if (
+ ! hasBackgroundSupport( name, 'backgroundImage' ) ||
+ ! backgroundImageURL
+ ) {
+ return;
+ }
+
+ return {
+ style: {
+ // @TODO this should be backgroundImage. How to do that?
+ // Also, maybe consider reinstating https://github.com/WordPress/gutenberg/blob/fc98542a7dbba194bb4096d49cd0bd093b63f43e/packages/block-editor/src/hooks/background.js#L82
+ background: `url( '${ encodeURI(
+ safeDecodeURI( backgroundImageURL )
+ ) }' )`,
+ },
+ };
+}
+
export default {
attributeKeys: [ 'style' ],
hasSupport: hasBackgroundSupport,
+ useBlockProps,
};
diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js
index c008a8efa0970b..17190f39868a32 100644
--- a/packages/block-editor/src/hooks/index.js
+++ b/packages/block-editor/src/hooks/index.js
@@ -8,6 +8,7 @@ import {
} from './utils';
import './compat';
import align from './align';
+import background from './background';
import './lock';
import anchor from './anchor';
import ariaLabel from './aria-label';
@@ -48,6 +49,7 @@ createBlockEditFilter(
);
createBlockListBlockFilter( [
align,
+ background,
textAlign,
style,
color,
diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js
index 120e55bf36dcc5..863e25ff8717e5 100644
--- a/packages/block-editor/src/hooks/style.js
+++ b/packages/block-editor/src/hooks/style.js
@@ -61,11 +61,11 @@ const hasStyleSupport = ( nameOrType ) =>
*
* @return {Object} Flattened CSS variables declaration.
*/
-export function getInlineStyles( styles = {}, options ) {
+export function getInlineStyles( styles = {} ) {
const output = {};
// The goal is to move everything to server side generated engine styles
// This is temporary as we absorb more and more styles into the engine.
- getCSSRules( styles, options ).forEach( ( rule ) => {
+ getCSSRules( styles ).forEach( ( rule ) => {
output[ rule.key ] = rule.value;
} );
@@ -287,7 +287,6 @@ export function omitStyle( style, paths, preserveReference = false ) {
* @param {Object|string} blockNameOrType Block type.
* @param {Object} attributes Block attributes.
* @param {?Record} skipPaths An object of keys and paths to skip serialization.
- * @param {Object} editorSettings Current editor settings.
* @return {Object} Filtered props applied to save element.
*/
export function addSaveProps(
@@ -295,7 +294,6 @@ export function addSaveProps(
blockNameOrType,
attributes,
skipPaths = skipSerializationPathsSave,
- editorSettings
) {
if ( ! hasStyleSupport( blockNameOrType ) ) {
return props;
@@ -325,13 +323,11 @@ export function addSaveProps(
* Only applies to background styles for now.
*/
if ( !! style.background ) {
- style = setBackgroundStyleDefaults( style, editorSettings );
+ style = setBackgroundStyleDefaults( style );
}
props.style = {
- ...getInlineStyles( style, {
- ...editorSettings,
- } ),
+ ...getInlineStyles( style ),
...props.style,
};
@@ -481,10 +477,6 @@ function useBlockProps( { name, style } ) {
name,
{ style },
skipSerializationPathsEdit,
- {
- stylesheetURI,
- templateURI,
- }
);
}
diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js
index f10fcc4df2c726..d3c3e35c1f69dd 100644
--- a/packages/block-editor/src/private-apis.js
+++ b/packages/block-editor/src/private-apis.js
@@ -35,6 +35,7 @@ import { useFlashEditableBlocks } from './components/use-flash-editable-blocks';
import {
selectBlockPatternsKey,
reusableBlocksSelectKey,
+ getThemeFileURIKey,
} from './store/private-keys';
import { requiresWrapperOnCopy } from './components/writing-flow/utils';
import { PrivateRichText } from './components/rich-text/';
@@ -76,4 +77,5 @@ lock( privateApis, {
requiresWrapperOnCopy,
PrivateRichText,
reusableBlocksSelectKey,
+ getThemeFileURIKey,
} );
diff --git a/packages/block-editor/src/store/private-keys.js b/packages/block-editor/src/store/private-keys.js
index f48612e7491c9c..ec29e20a5c91d8 100644
--- a/packages/block-editor/src/store/private-keys.js
+++ b/packages/block-editor/src/store/private-keys.js
@@ -1,2 +1,3 @@
export const selectBlockPatternsKey = Symbol( 'selectBlockPatternsKey' );
export const reusableBlocksSelectKey = Symbol( 'reusableBlocksSelect' );
+export const getThemeFileURIKey = Symbol( 'getThemeFileURI' );
diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js
index 94de85cbabe70e..13d96a99018a8c 100644
--- a/packages/block-editor/src/store/private-selectors.js
+++ b/packages/block-editor/src/store/private-selectors.js
@@ -26,6 +26,7 @@ import { unlock } from '../lock-unlock';
import {
selectBlockPatternsKey,
reusableBlocksSelectKey,
+ getThemeFileURIKey,
} from './private-keys';
export { getBlockSettings } from './get-block-settings';
@@ -388,6 +389,15 @@ export const getReusableBlocks = createRegistrySelector(
}
);
+export const getThemeFileURI = createRegistrySelector(
+ ( select ) => ( state, file ) => {
+ const getThemeFileURISelect = state.settings[ getThemeFileURIKey ];
+ return getThemeFileURISelect
+ ? getThemeFileURISelect( select, file )
+ : '';
+ }
+);
+
/**
* Returns the element of the last element that had focus when focus left the editor canvas.
*
diff --git a/packages/core-data/src/private-selectors.ts b/packages/core-data/src/private-selectors.ts
index 6280bb96319634..b5b6b14ebf2a38 100644
--- a/packages/core-data/src/private-selectors.ts
+++ b/packages/core-data/src/private-selectors.ts
@@ -50,3 +50,7 @@ export const getBlockPatternsForPostType = createRegistrySelector(
() => [ select( STORE_NAME ).getBlockPatterns() ]
)
);
+
+export function getThemeFileURI( state: State, file: string ) {
+ return state.themeFileURIs?.[ file ];
+}
diff --git a/packages/core-data/src/reducer.js b/packages/core-data/src/reducer.js
index 8e6be425244687..e2dd830b092475 100644
--- a/packages/core-data/src/reducer.js
+++ b/packages/core-data/src/reducer.js
@@ -623,6 +623,18 @@ export function defaultTemplates( state = {}, action ) {
return state;
}
+export function themeFileURIs( state = {}, action ) {
+ switch ( action.type ) {
+ case 'RECEIVE_THEME_FILE_URI':
+ return {
+ ...state,
+ [ action.file ]: action.url,
+ };
+ }
+
+ return state;
+}
+
export default combineReducers( {
terms,
users,
@@ -644,4 +656,5 @@ export default combineReducers( {
userPatternCategories,
navigationFallbackId,
defaultTemplates,
+ themeFileURIs,
} );
diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js
index 7de2c354ba2bf8..ef953a1cfc981c 100644
--- a/packages/core-data/src/resolvers.js
+++ b/packages/core-data/src/resolvers.js
@@ -896,3 +896,34 @@ export const getRevision =
dispatch.receiveRevisions( kind, name, recordKey, record, query );
}
};
+
+export const getThemeFileURI =
+ ( file ) =>
+ async ( { resolveSelect, dispatch } ) => {
+ if ( typeof file !== 'string' ) {
+ return;
+ }
+
+ let trimmedFile = file.trim();
+ trimmedFile = trimmedFile.startsWith( '/' ) ? trimmedFile : `/${ trimmedFile }`;
+
+ const { stylesheet_uri, template_uri } =
+ await resolveSelect.getCurrentTheme();
+ const url = `${ stylesheet_uri }${ trimmedFile }`;
+
+ apiFetch( { url, method: 'HEAD', parse: false } )
+ .then( () => {
+ dispatch( {
+ type: 'RECEIVE_THEME_FILE_URI',
+ file,
+ url,
+ } );
+ } )
+ .catch( () => {
+ dispatch( {
+ type: 'RECEIVE_THEME_FILE_URI',
+ file,
+ url: `${ template_uri }${ trimmedFile }`,
+ } );
+ } );
+ };
diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts
index d238438e10eb13..055be0505eac1d 100644
--- a/packages/core-data/src/selectors.ts
+++ b/packages/core-data/src/selectors.ts
@@ -46,6 +46,7 @@ export interface State {
navigationFallbackId: EntityRecordKey;
userPatternCategories: Array< UserPatternCategory >;
defaultTemplates: Record< string, string >;
+ themeFileURIs: Record< string, string >;
}
type EntityRecordKey = string | number;
diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js
index 9a7ea71f75b9ec..34cb647fbe45bc 100644
--- a/packages/editor/src/components/provider/use-block-editor-settings.js
+++ b/packages/editor/src/components/provider/use-block-editor-settings.js
@@ -277,6 +277,8 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) {
),
[ unlock( privateApis ).reusableBlocksSelectKey ]:
__experimentalReusableBlocksSelect,
+ [ unlock( privateApis ).getThemeFileURIKey ]: ( select, file ) =>
+ unlock( select( coreStore ) ).getThemeFileURI( file ),
__experimentalBlockPatternCategories: blockPatternCategories,
__experimentalUserPatternCategories: userPatternCategories,
__experimentalFetchLinkSuggestions: ( search, searchOptions ) =>
diff --git a/packages/style-engine/src/styles/background/index.ts b/packages/style-engine/src/styles/background/index.ts
index 2b11af78bffd59..a8c8679888e153 100644
--- a/packages/style-engine/src/styles/background/index.ts
+++ b/packages/style-engine/src/styles/background/index.ts
@@ -8,38 +8,15 @@ const backgroundImage = {
name: 'backgroundImage',
generate: ( style: Style, options: StyleOptions ) => {
const _backgroundImage = style?.background?.backgroundImage;
-
if ( typeof _backgroundImage === 'object' && _backgroundImage?.url ) {
- let url = _backgroundImage.url;
- if (
- _backgroundImage?.source === 'theme' &&
- !! options?.stylesheetURI &&
- !! options?.templateURI
- ) {
- const activeThemeImageResource = `${ options.stylesheetURI }${
- url.startsWith( '/' ) ? url : `/${ url }`
- }`;
-
- const parentThemeImageResource = `${ options.templateURI }${
- url.startsWith( '/' ) ? url : `/${ url }`
- }`;
-
- url = `url('${ encodeURI(
- safeDecodeURI( activeThemeImageResource )
- ) }'), url('${ encodeURI(
- safeDecodeURI( parentThemeImageResource )
- ) }')`;
- } else {
- url = `url( '${ encodeURI(
- safeDecodeURI( _backgroundImage.url )
- ) }' )`;
- }
return [
{
selector: options.selector,
key: 'backgroundImage',
// Passed `url` may already be encoded. To prevent double encoding, decodeURI is executed to revert to the original string.
- value: url,
+ value: `url( '${ encodeURI(
+ safeDecodeURI( _backgroundImage.url )
+ ) }' )`,
},
];
}