diff --git a/lib/compat/wordpress-6.6/compat.php b/lib/compat/wordpress-6.6/compat.php
new file mode 100644
index 00000000000000..e1a72342db9578
--- /dev/null
+++ b/lib/compat/wordpress-6.6/compat.php
@@ -0,0 +1,27 @@
+= 6.6.0.
+ *
+ * @global array $submenu
+ */
+function gutenberg_change_patterns_link_and_remove_template_parts_submenu_item() {
+ if ( ! wp_is_block_theme() ) {
+ global $submenu;
+ foreach ( $submenu['themes.php'] as $key => $item ) {
+ if ( 'edit.php?post_type=wp_block' === $item[2] ) {
+ $submenu['themes.php'][ $key ][2] = 'site-editor.php?path=/patterns';
+ } elseif ( 'site-editor.php?path=/wp_template_part/all' === $item[2] ) {
+ unset( $submenu['themes.php'][ $key ] );
+ }
+ }
+ }
+}
+add_action( 'admin_init', 'gutenberg_change_patterns_link_and_remove_template_parts_submenu_item' );
diff --git a/lib/load.php b/lib/load.php
index 2ae1be2c9011ed..d556dd7f21b435 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -125,6 +125,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-6.5/script-loader.php';
// WordPress 6.6 compat.
+require __DIR__ . '/compat/wordpress-6.6/compat.php';
require __DIR__ . '/compat/wordpress-6.6/resolve-patterns.php';
require __DIR__ . '/compat/wordpress-6.6/block-bindings/pattern-overrides.php';
require __DIR__ . '/compat/wordpress-6.6/block-template-utils.php';
diff --git a/packages/core-commands/src/admin-navigation-commands.js b/packages/core-commands/src/admin-navigation-commands.js
index 0ba4df82920d9a..4de403761f4cc4 100644
--- a/packages/core-commands/src/admin-navigation-commands.js
+++ b/packages/core-commands/src/admin-navigation-commands.js
@@ -10,7 +10,7 @@ import { privateApis as routerPrivateApis } from '@wordpress/router';
/**
* Internal dependencies
*/
-import { useIsTemplatesAccessible, useIsBlockBasedTheme } from './hooks';
+import { useIsTemplatesAccessible } from './hooks';
import { unlock } from './lock-unlock';
const { useHistory } = unlock( routerPrivateApis );
@@ -18,7 +18,6 @@ const { useHistory } = unlock( routerPrivateApis );
export function useAdminNavigationCommands() {
const history = useHistory();
const isTemplatesAccessible = useIsTemplatesAccessible();
- const isBlockBasedTheme = useIsBlockBasedTheme();
const isSiteEditor = getPath( window.location.href )?.includes(
'site-editor.php'
@@ -45,7 +44,11 @@ export function useAdminNavigationCommands() {
label: __( 'Patterns' ),
icon: symbol,
callback: ( { close } ) => {
- if ( isTemplatesAccessible && isBlockBasedTheme ) {
+ // The site editor and templates both check whether the user
+ // can read templates. We can leverage that here and this
+ // command links to the classic dashboard manage patterns page
+ // if the user can't access it.
+ if ( isTemplatesAccessible ) {
const args = {
path: '/patterns',
};
diff --git a/packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js b/packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js
index 9c528214699d6f..d12ffb88ae557d 100644
--- a/packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js
+++ b/packages/edit-post/src/components/header/more-menu/manage-patterns-menu-item.js
@@ -3,7 +3,6 @@
*/
import { MenuItem } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
-import { store as editorStore } from '@wordpress/editor';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';
@@ -11,9 +10,6 @@ import { addQueryArgs } from '@wordpress/url';
function ManagePatternsMenuItem() {
const url = useSelect( ( select ) => {
const { canUser } = select( coreStore );
- const { getEditorSettings } = select( editorStore );
-
- const isBlockTheme = getEditorSettings().__unstableIsBlockBasedTheme;
const defaultUrl = addQueryArgs( 'edit.php', {
post_type: 'wp_block',
} );
@@ -24,9 +20,7 @@ function ManagePatternsMenuItem() {
// The site editor and templates both check whether the user has
// edit_theme_options capabilities. We can leverage that here and not
// display the manage patterns link if the user can't access it.
- return canUser( 'read', 'templates' ) && isBlockTheme
- ? patternsUrl
- : defaultUrl;
+ return canUser( 'read', 'templates' ) ? patternsUrl : defaultUrl;
}, [] );
return (
diff --git a/packages/edit-site/src/components/add-new-pattern/index.js b/packages/edit-site/src/components/add-new-pattern/index.js
index 437e4a531b055f..ce0ad6bbbccf48 100644
--- a/packages/edit-site/src/components/add-new-pattern/index.js
+++ b/packages/edit-site/src/components/add-new-pattern/index.js
@@ -5,13 +5,14 @@ import { DropdownMenu } from '@wordpress/components';
import { useState, useRef } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { plus, symbol, symbolFilled, upload } from '@wordpress/icons';
-import { useDispatch } from '@wordpress/data';
+import { useSelect, useDispatch } from '@wordpress/data';
import { privateApis as routerPrivateApis } from '@wordpress/router';
import {
privateApis as editPatternsPrivateApis,
store as patternsStore,
} from '@wordpress/patterns';
import { store as noticesStore } from '@wordpress/notices';
+import { store as coreStore } from '@wordpress/core-data';
/**
* Internal dependencies
@@ -30,7 +31,7 @@ const { CreatePatternModal, useAddPatternCategory } = unlock(
editPatternsPrivateApis
);
-export default function AddNewPattern( { canCreateParts, canCreatePatterns } ) {
+export default function AddNewPattern() {
const history = useHistory();
const { params } = useLocation();
const [ showPatternModal, setShowPatternModal ] = useState( false );
@@ -40,6 +41,10 @@ export default function AddNewPattern( { canCreateParts, canCreatePatterns } ) {
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const patternUploadInputRef = useRef();
+ const isBlockBasedTheme = useSelect(
+ ( select ) => select( coreStore ).getCurrentTheme()?.is_block_theme,
+ []
+ );
function handleCreatePattern( { pattern, categoryId } ) {
setShowPatternModal( false );
@@ -71,15 +76,13 @@ export default function AddNewPattern( { canCreateParts, canCreatePatterns } ) {
const controls = [];
- if ( canCreatePatterns ) {
- controls.push( {
- icon: symbol,
- onClick: () => setShowPatternModal( true ),
- title: __( 'Create pattern' ),
- } );
- }
+ controls.push( {
+ icon: symbol,
+ onClick: () => setShowPatternModal( true ),
+ title: __( 'Create pattern' ),
+ } );
- if ( canCreateParts ) {
+ if ( isBlockBasedTheme ) {
controls.push( {
icon: symbolFilled,
onClick: () => setShowTemplatePartModal( true ),
@@ -87,15 +90,13 @@ export default function AddNewPattern( { canCreateParts, canCreatePatterns } ) {
} );
}
- if ( canCreatePatterns ) {
- controls.push( {
- icon: upload,
- onClick: () => {
- patternUploadInputRef.current.click();
- },
- title: __( 'Import pattern from JSON' ),
- } );
- }
+ controls.push( {
+ icon: upload,
+ onClick: () => {
+ patternUploadInputRef.current.click();
+ },
+ title: __( 'Import pattern from JSON' ),
+ } );
const { categoryMap, findOrCreateTerm } = useAddPatternCategory();
return (
diff --git a/packages/edit-site/src/components/layout/router.js b/packages/edit-site/src/components/layout/router.js
index bce9043a77e1b0..0c1e0d1d103a31 100644
--- a/packages/edit-site/src/components/layout/router.js
+++ b/packages/edit-site/src/components/layout/router.js
@@ -117,26 +117,11 @@ export default function useLayoutAreas() {
};
}
- // Template parts
- /*
- * This is for legacy reasons, as the template parts are now part of the patterns screen.
- * However, hybrid themes (classic themes that support template parts) still access this URL.
- * While there are plans to make them use the patterns screen instead, we cannot do it for now.
- * See discussion at https://github.com/WordPress/gutenberg/pull/60689
+ /* Patterns and Template Parts
+ * `/wp_template_part/all` path is no longer used, but uses Patterns page screens for
+ * backwards compatibility.
*/
- if ( path === '/wp_template_part/all' ) {
- return {
- key: 'template-parts',
- areas: {
- sidebar: ,
- content: ,
- mobile: ,
- },
- };
- }
-
- // Patterns
- if ( path === '/patterns' ) {
+ if ( path === '/patterns' || path === '/wp_template_part/all' ) {
return {
key: 'patterns',
areas: {
diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js
index ea313e7e1dede0..2b45bdc3ebc54a 100644
--- a/packages/edit-site/src/components/page-patterns/index.js
+++ b/packages/edit-site/src/components/page-patterns/index.js
@@ -42,7 +42,6 @@ import {
LAYOUT_LIST,
PATTERN_TYPES,
TEMPLATE_PART_POST_TYPE,
- TEMPLATE_PART_ALL_AREAS_CATEGORY,
PATTERN_SYNC_TYPES,
PATTERN_DEFAULT_CATEGORY,
OPERATOR_IS,
@@ -252,18 +251,10 @@ function Title( { item, categoryId } ) {
export default function DataviewsPatterns() {
const {
- params: { categoryType, categoryId: categoryIdFromURL, path },
+ params: { categoryType, categoryId: categoryIdFromURL },
} = useLocation();
- const type =
- categoryType ||
- ( path === '/wp_template_part/all'
- ? TEMPLATE_PART_POST_TYPE
- : PATTERN_TYPES.theme );
- const categoryId =
- categoryIdFromURL ||
- ( path === '/wp_template_part/all'
- ? TEMPLATE_PART_ALL_AREAS_CATEGORY
- : PATTERN_DEFAULT_CATEGORY );
+ const type = categoryType || PATTERN_TYPES.theme;
+ const categoryId = categoryIdFromURL || PATTERN_DEFAULT_CATEGORY;
const [ view, setView ] = useState( DEFAULT_VIEW );
const isUncategorizedThemePatterns =
type === PATTERN_TYPES.theme && categoryId === 'uncategorized';
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js
index ec80b7054c6db4..6058b7d907d821 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { useDispatch, useSelect } from '@wordpress/data';
+import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { pencil } from '@wordpress/icons';
import { privateApis as routerPrivateApis } from '@wordpress/router';
@@ -30,31 +30,11 @@ export default function SidebarNavigationScreenPattern() {
useInitEditedEntityFromURL();
const patternDetails = usePatternDetails( postType, postId );
- const isTemplatePartsMode = useSelect( ( select ) => {
- return !! select( editSiteStore ).getSettings()
- .supportsTemplatePartsMode;
- }, [] );
- /**
- * This sidebar needs to temporarily accomodate two different "URLs" backpaths:
- *
- * 1. path = /patterns
- * Block based themes. Also classic themes can access this URL, though it's not linked anywhere.
- *
- * 2. path = /wp_template_part/all
- * Classic themes with support for block-template-parts. We need to list only Template Parts in this case.
- * The URL is accessible from the Appearance > Template Parts menu.
- *
- * Depending on whether the theme supports block-template-parts, we go back to Patterns or Template screens.
- * This is temporary. We aim to consolidate to /patterns.
- */
const backPath = {
categoryId,
categoryType,
- path:
- isTemplatePartsMode && postType === 'wp_template_part'
- ? '/wp_template_part/all'
- : '/patterns',
+ path: '/patterns',
};
return (
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js
index 5952c53e2ba54c..352c0c4c455ec3 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js
@@ -110,20 +110,12 @@ function CategoriesGroup( {
);
}
-const EMPTY_ARRAY = [];
export default function SidebarNavigationScreenPatterns() {
const {
params: { categoryType, categoryId, path },
} = useLocation();
- const isTemplatePartsPath = path === '/wp_template_part/all';
- const currentCategory =
- categoryId ||
- ( isTemplatePartsPath
- ? TEMPLATE_PART_ALL_AREAS_CATEGORY
- : PATTERN_DEFAULT_CATEGORY );
- const currentType =
- categoryType ||
- ( isTemplatePartsPath ? TEMPLATE_PART_POST_TYPE : PATTERN_TYPES.theme );
+ const currentCategory = categoryId || PATTERN_DEFAULT_CATEGORY;
+ const currentType = categoryType || PATTERN_TYPES.theme;
const { templatePartAreas, hasTemplateParts, isLoading } =
useTemplatePartAreas();
@@ -148,28 +140,11 @@ export default function SidebarNavigationScreenPatterns() {
return (
- )
- }
+ title={ __( 'Patterns' ) }
+ description={ __(
+ 'Manage what patterns are available when editing the site.'
+ ) }
+ actions={ }
content={
<>
{ isLoading && __( 'Loading items…' ) }
@@ -183,11 +158,7 @@ export default function SidebarNavigationScreenPatterns() {
diff --git a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js
index 8cfb0bca716f2f..1f9d71945f76cf 100644
--- a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js
+++ b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js
@@ -226,10 +226,6 @@ function useResolveEditedEntityAndContext( { path, postId, postType } ) {
return { isReady: true, postType: 'wp_template', postId, context };
}
- if ( path === '/wp_template_part/all' && postId ) {
- return { isReady: true, postType: 'wp_template_part', postId, context };
- }
-
if ( postTypesWithoutParentTemplate.includes( postType ) ) {
return { isReady: true, postType, postId, context };
}
diff --git a/packages/patterns/src/components/patterns-manage-button.js b/packages/patterns/src/components/patterns-manage-button.js
index 45ad96d8d8aae6..f2bd798a7b6fad 100644
--- a/packages/patterns/src/components/patterns-manage-button.js
+++ b/packages/patterns/src/components/patterns-manage-button.js
@@ -18,11 +18,10 @@ import { unlock } from '../lock-unlock';
function PatternsManageButton( { clientId } ) {
const { canRemove, isVisible, managePatternsUrl } = useSelect(
( select ) => {
- const { getBlock, canRemoveBlock, getBlockCount, getSettings } =
+ const { getBlock, canRemoveBlock, getBlockCount } =
select( blockEditorStore );
const { canUser } = select( coreStore );
const reusableBlock = getBlock( clientId );
- const isBlockTheme = getSettings().__unstableIsBlockBasedTheme;
return {
canRemove: canRemoveBlock( clientId ),
@@ -38,14 +37,13 @@ function PatternsManageButton( { clientId } ) {
// The site editor and templates both check whether the user
// has edit_theme_options capabilities. We can leverage that here
// and omit the manage patterns link if the user can't access it.
- managePatternsUrl:
- isBlockTheme && canUser( 'read', 'templates' )
- ? addQueryArgs( 'site-editor.php', {
- path: '/patterns',
- } )
- : addQueryArgs( 'edit.php', {
- post_type: 'wp_block',
- } ),
+ managePatternsUrl: canUser( 'read', 'templates' )
+ ? addQueryArgs( 'site-editor.php', {
+ path: '/patterns',
+ } )
+ : addQueryArgs( 'edit.php', {
+ post_type: 'wp_block',
+ } ),
};
},
[ clientId ]
diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js
index cf03c2a07b7a0c..a535eade291a09 100644
--- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js
+++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js
@@ -17,11 +17,10 @@ import { store as reusableBlocksStore } from '../../store';
function ReusableBlocksManageButton( { clientId } ) {
const { canRemove, isVisible, managePatternsUrl } = useSelect(
( select ) => {
- const { getBlock, canRemoveBlock, getBlockCount, getSettings } =
+ const { getBlock, canRemoveBlock, getBlockCount } =
select( blockEditorStore );
const { canUser } = select( coreStore );
const reusableBlock = getBlock( clientId );
- const isBlockTheme = getSettings().__unstableIsBlockBasedTheme;
return {
canRemove: canRemoveBlock( clientId ),
@@ -37,14 +36,13 @@ function ReusableBlocksManageButton( { clientId } ) {
// The site editor and templates both check whether the user
// has edit_theme_options capabilities. We can leverage that here
// and omit the manage patterns link if the user can't access it.
- managePatternsUrl:
- isBlockTheme && canUser( 'read', 'templates' )
- ? addQueryArgs( 'site-editor.php', {
- path: '/patterns',
- } )
- : addQueryArgs( 'edit.php', {
- post_type: 'wp_block',
- } ),
+ managePatternsUrl: canUser( 'read', 'templates' )
+ ? addQueryArgs( 'site-editor.php', {
+ path: '/patterns',
+ } )
+ : addQueryArgs( 'edit.php', {
+ post_type: 'wp_block',
+ } ),
};
},
[ clientId ]
diff --git a/test/e2e/specs/site-editor/hybrid-theme.spec.js b/test/e2e/specs/site-editor/hybrid-theme.spec.js
index bbef68f72eed64..ea22349f5b7eb8 100644
--- a/test/e2e/specs/site-editor/hybrid-theme.spec.js
+++ b/test/e2e/specs/site-editor/hybrid-theme.spec.js
@@ -12,27 +12,45 @@ test.describe( 'Hybrid theme', () => {
await requestUtils.activateTheme( 'twentytwentyone' );
} );
- test( 'can access template parts list page', async ( { admin, page } ) => {
- await admin.visitAdminPage(
- 'site-editor.php',
- 'postType=wp_template_part&path=/wp_template_part/all'
- );
+ test( 'can access Patterns page', async ( { admin, page } ) => {
+ await admin.visitAdminPage( 'site-editor.php', 'path=/patterns' );
await expect(
- page.getByText( 'header', { exact: true } )
+ page.getByRole( 'heading', { level: 1, text: 'Patterns' } )
+ ).toBeVisible();
+ await expect(
+ page.getByRole( 'heading', { level: 2, text: 'All patterns' } )
).toBeVisible();
} );
- test( 'can view a template part', async ( { admin, editor, page } ) => {
+ test( 'should show Patterns page when accessing template parts list page', async ( {
+ admin,
+ page,
+ } ) => {
await admin.visitAdminPage(
'site-editor.php',
- 'postType=wp_template_part&path=/wp_template_part/all'
+ 'path=/wp_template_part/all'
);
- const templatePart = page.getByText( 'header', { exact: true } );
+ await expect(
+ page.getByRole( 'heading', { level: 1, text: 'Patterns' } )
+ ).toBeVisible();
+ await expect(
+ page.getByRole( 'heading', { level: 2, text: 'All patterns' } )
+ ).toBeVisible();
+ } );
- await expect( templatePart ).toBeVisible();
- await templatePart.click();
+ test( 'can view a template part list', async ( {
+ admin,
+ editor,
+ page,
+ } ) => {
+ await admin.visitAdminPage( 'site-editor.php', 'path=/patterns' );
+
+ await page
+ .getByRole( 'button', { name: 'All template parts' } )
+ .click();
+ await page.getByText( 'header', { exact: true } ).click();
await expect(
page.getByRole( 'region', { name: 'Editor content' } )