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' } )