diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js
index a2324c06e4dbc..bc1fe29759950 100644
--- a/packages/core-data/src/entity-provider.js
+++ b/packages/core-data/src/entity-provider.js
@@ -16,8 +16,6 @@ import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
import { STORE_NAME } from './name';
import { updateFootnotesFromMeta } from './footnotes';
-/** @typedef {import('@wordpress/blocks').WPBlock} WPBlock */
-
const EMPTY_ARRAY = [];
const EntityContext = createContext( {} );
@@ -133,7 +131,7 @@ const parsedBlocksCache = new WeakMap();
* @param {Object} options
* @param {string} [options.id] An entity ID to use instead of the context-provided one.
*
- * @return {[WPBlock[], Function, Function]} The block array and setters.
+ * @return {[unknown[], Function, Function]} The block array and setters.
*/
export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
const providerId = useEntityId( kind, name );
diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js
index 152afc294f428..0b4cf883d0780 100644
--- a/packages/edit-post/src/index.js
+++ b/packages/edit-post/src/index.js
@@ -25,8 +25,10 @@ import {
import Layout from './components/layout';
import { unlock } from './lock-unlock';
-const { BackButton: __experimentalMainDashboardButton } =
- unlock( editorPrivateApis );
+const {
+ BackButton: __experimentalMainDashboardButton,
+ registerDefaultActions,
+} = unlock( editorPrivateApis );
/**
* Initializes and returns an instance of Editor.
@@ -91,6 +93,7 @@ export function initializeEditor(
enableFSEBlocks: settings.__unstableEnableFullSiteEditingBlocks,
} );
}
+ registerDefaultActions();
// Show a console log warning if the browser is not in Standards rendering mode.
const documentMode =
diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js
index f9888be6b27ca..01c056cdca33d 100644
--- a/packages/edit-site/src/components/page-pages/index.js
+++ b/packages/edit-site/src/components/page-pages/index.js
@@ -482,7 +482,6 @@ export default function PagePages() {
],
[ authors, view.type, frontPageId, postsPageId ]
);
-
const postTypeActions = usePostActions( {
postType: 'page',
context: 'list',
diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js
index 3e8c637a493de..14f5bb6be650c 100644
--- a/packages/edit-site/src/index.js
+++ b/packages/edit-site/src/index.js
@@ -10,7 +10,10 @@ import {
import { dispatch } from '@wordpress/data';
import deprecated from '@wordpress/deprecated';
import { createRoot, StrictMode } from '@wordpress/element';
-import { store as editorStore } from '@wordpress/editor';
+import {
+ store as editorStore,
+ privateApis as editorPrivateApis,
+} from '@wordpress/editor';
import { store as preferencesStore } from '@wordpress/preferences';
import {
registerLegacyWidgetBlock,
@@ -22,8 +25,11 @@ import {
*/
import './hooks';
import { store as editSiteStore } from './store';
+import { unlock } from './lock-unlock';
import App from './components/app';
+const { registerDefaultActions } = unlock( editorPrivateApis );
+
/**
* Initializes the site editor screen.
*
@@ -47,6 +53,7 @@ export function initializeEditor( id, settings ) {
enableFSEBlocks: true,
} );
}
+ registerDefaultActions();
// We dispatch actions and update the store synchronously before rendering
// so that we won't trigger unnecessary re-renders with useEffect.
diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js
index 89ac77a6945c4..60fc718279afb 100644
--- a/packages/editor/src/components/post-actions/actions.js
+++ b/packages/editor/src/components/post-actions/actions.js
@@ -73,81 +73,6 @@ function getItemTitle( item ) {
return decodeEntities( item.title?.rendered || '' );
}
-// This action is used for templates, patterns and template parts.
-// Every other post type uses the similar `trashPostAction` which
-// moves the post to trash.
-const deletePostAction = {
- id: 'delete-post',
- label: __( 'Delete' ),
- isPrimary: true,
- icon: trash,
- isEligible( post ) {
- if (
- [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes(
- post.type
- )
- ) {
- return isTemplateRemovable( post );
- }
- // We can only remove user patterns.
- return post.type === PATTERN_TYPES.user;
- },
- supportsBulk: true,
- hideModalHeader: true,
- RenderModal: ( { items, closeModal, onActionPerformed } ) => {
- const [ isBusy, setIsBusy ] = useState( false );
- const { removeTemplates } = unlock( useDispatch( editorStore ) );
- return (
-
-
- { items.length > 1
- ? sprintf(
- // translators: %d: number of items to delete.
- _n(
- 'Delete %d item?',
- 'Delete %d items?',
- items.length
- ),
- items.length
- )
- : sprintf(
- // translators: %s: The template or template part's titles
- __( 'Delete "%s"?' ),
- getItemTitle( items[ 0 ] )
- ) }
-
-
-
-
-
-
- );
- },
-};
-
const trashPostAction = {
id: 'move-to-trash',
label: __( 'Move to Trash' ),
@@ -1124,9 +1049,9 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
isTemplateOrTemplatePart
? resetTemplateAction
: restorePostActionForPostType,
- isTemplateOrTemplatePart || isPattern
- ? deletePostAction
- : trashPostActionForPostType,
+ ! isTemplateOrTemplatePart &&
+ ! isPattern &&
+ trashPostActionForPostType,
! isTemplateOrTemplatePart &&
permanentlyDeletePostActionForPostType,
...defaultActions,
diff --git a/packages/editor/src/dataviews/actions/delete-post.tsx b/packages/editor/src/dataviews/actions/delete-post.tsx
new file mode 100644
index 0000000000000..e5810e32fde69
--- /dev/null
+++ b/packages/editor/src/dataviews/actions/delete-post.tsx
@@ -0,0 +1,107 @@
+/**
+ * WordPress dependencies
+ */
+import { trash } from '@wordpress/icons';
+import { useDispatch } from '@wordpress/data';
+import { __, _n, sprintf, _x } from '@wordpress/i18n';
+import { useState } from '@wordpress/element';
+import {
+ Button,
+ __experimentalText as Text,
+ __experimentalHStack as HStack,
+ __experimentalVStack as VStack,
+} from '@wordpress/components';
+// @ts-ignore
+import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
+import type { Action } from '@wordpress/dataviews';
+import type { StoreDescriptor } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import {
+ isTemplateRemovable,
+ getItemTitle,
+ isTemplateOrTemplatePart,
+} from './utils';
+// @ts-ignore
+import { store as editorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
+import type { Post } from '../types';
+
+const { PATTERN_TYPES } = unlock( patternsPrivateApis );
+
+// This action is used for templates, patterns and template parts.
+// Every other post type uses the similar `trashPostAction` which
+// moves the post to trash.
+const deletePostAction: Action< Post > = {
+ id: 'delete-post',
+ label: __( 'Delete' ),
+ isPrimary: true,
+ icon: trash,
+ isEligible( post ) {
+ if ( isTemplateOrTemplatePart( post ) ) {
+ return isTemplateRemovable( post );
+ }
+ // We can only remove user patterns.
+ return post.type === PATTERN_TYPES.user;
+ },
+ supportsBulk: true,
+ hideModalHeader: true,
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const [ isBusy, setIsBusy ] = useState( false );
+ const { removeTemplates } = unlock(
+ useDispatch( editorStore as StoreDescriptor )
+ );
+ return (
+
+
+ { items.length > 1
+ ? sprintf(
+ // translators: %d: number of items to delete.
+ _n(
+ 'Delete %d item?',
+ 'Delete %d items?',
+ items.length
+ ),
+ items.length
+ )
+ : sprintf(
+ // translators: %s: The template or template part's titles
+ __( 'Delete "%s"?' ),
+ getItemTitle( items[ 0 ] )
+ ) }
+
+
+
+
+
+
+ );
+ },
+};
+
+export default deletePostAction;
diff --git a/packages/editor/src/dataviews/actions/index.ts b/packages/editor/src/dataviews/actions/index.ts
new file mode 100644
index 0000000000000..87589d68262f3
--- /dev/null
+++ b/packages/editor/src/dataviews/actions/index.ts
@@ -0,0 +1,20 @@
+/**
+ * WordPress dependencies
+ */
+import { type StoreDescriptor, dispatch } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import deletePost from './delete-post';
+// @ts-ignore
+import { store as editorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
+
+export default function registerDefaultActions() {
+ const { registerEntityAction } = unlock(
+ dispatch( editorStore as StoreDescriptor )
+ );
+
+ registerEntityAction( 'postType', '*', deletePost );
+}
diff --git a/packages/editor/src/dataviews/actions/utils.ts b/packages/editor/src/dataviews/actions/utils.ts
new file mode 100644
index 0000000000000..14da130789b08
--- /dev/null
+++ b/packages/editor/src/dataviews/actions/utils.ts
@@ -0,0 +1,50 @@
+/**
+ * WordPress dependencies
+ */
+import { decodeEntities } from '@wordpress/html-entities';
+
+/**
+ * Internal dependencies
+ */
+import {
+ TEMPLATE_ORIGINS,
+ TEMPLATE_PART_POST_TYPE,
+ TEMPLATE_POST_TYPE,
+} from '../../store/constants';
+
+import type { Post, TemplateOrTemplatePart } from '../types';
+
+export function isTemplateOrTemplatePart(
+ p: Post
+): p is TemplateOrTemplatePart {
+ return p.type === TEMPLATE_POST_TYPE || p.type === TEMPLATE_PART_POST_TYPE;
+}
+
+export function getItemTitle( item: Post ) {
+ if ( typeof item.title === 'string' ) {
+ return decodeEntities( item.title );
+ }
+ return decodeEntities( item.title?.rendered || '' );
+}
+
+/**
+ * Check if a template is removable.
+ *
+ * @param template The template entity to check.
+ * @return Whether the template is removable.
+ */
+export function isTemplateRemovable( template: TemplateOrTemplatePart ) {
+ if ( ! template ) {
+ return false;
+ }
+ // In patterns list page we map the templates parts to a different object
+ // than the one returned from the endpoint. This is why we need to check for
+ // two props whether is custom or has a theme file.
+ return (
+ [ template.source, template.source ].includes(
+ TEMPLATE_ORIGINS.custom
+ ) &&
+ ! template.has_theme_file &&
+ ! template?.has_theme_file
+ );
+}
diff --git a/packages/editor/src/dataviews/store/private-selectors.ts b/packages/editor/src/dataviews/store/private-selectors.ts
index b2f60d7b0f33e..bbe3d7ca95c7c 100644
--- a/packages/editor/src/dataviews/store/private-selectors.ts
+++ b/packages/editor/src/dataviews/store/private-selectors.ts
@@ -1,15 +1,22 @@
/**
* WordPress dependencies
*/
-import type { Action } from '@wordpress/dataviews';
+import { createSelector } from '@wordpress/data';
/**
* Internal dependencies
*/
import type { State } from './reducer';
-const EMPTY_ARRAY: Action< any >[] = [];
-
-export function getEntityActions( state: State, kind: string, name: string ) {
- return state.actions[ kind ]?.[ name ] ?? EMPTY_ARRAY;
-}
+export const getEntityActions = createSelector(
+ ( state: State, kind: string, name: string ) => {
+ return [
+ ...( state.actions[ kind ]?.[ name ] ?? [] ),
+ ...( state.actions[ kind ]?.[ '*' ] ?? [] ),
+ ];
+ },
+ ( state: State, kind: string, name: string ) => [
+ state.actions[ kind ]?.[ name ],
+ state.actions[ kind ]?.[ '*' ],
+ ]
+);
diff --git a/packages/editor/src/dataviews/types.ts b/packages/editor/src/dataviews/types.ts
new file mode 100644
index 0000000000000..c1771801d8ee3
--- /dev/null
+++ b/packages/editor/src/dataviews/types.ts
@@ -0,0 +1,21 @@
+type PostStatus =
+ | 'published'
+ | 'draft'
+ | 'pending'
+ | 'private'
+ | 'future'
+ | 'auto-draft'
+ | 'trash';
+
+export interface BasePost {
+ status?: PostStatus;
+ title: string | { rendered: string };
+ type: string;
+}
+export interface TemplateOrTemplatePart extends BasePost {
+ type: 'template' | 'template-part';
+ source: string;
+ has_theme_file: boolean;
+}
+
+export type Post = TemplateOrTemplatePart | BasePost;
diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js
index 48e43e7737fec..8eeb97375268c 100644
--- a/packages/editor/src/private-apis.js
+++ b/packages/editor/src/private-apis.js
@@ -23,6 +23,7 @@ import {
mergeBaseAndUserConfigs,
GlobalStylesProvider,
} from './components/global-styles-provider';
+import registerDefaultActions from './dataviews/actions';
const { store: interfaceStore, ...remainingInterfaceApis } = interfaceApis;
@@ -41,6 +42,7 @@ lock( privateApis, {
ToolsMoreMenuGroup,
ViewMoreMenuGroup,
ResizableEditor,
+ registerDefaultActions,
// This is a temporary private API while we're updating the site editor to use EditorProvider.
useBlockEditorSettings,
diff --git a/packages/editor/src/store/constants.js b/packages/editor/src/store/constants.ts
similarity index 100%
rename from packages/editor/src/store/constants.js
rename to packages/editor/src/store/constants.ts
diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json
index 1177a1040b117..8dfdf2ba7f2d5 100644
--- a/packages/editor/tsconfig.json
+++ b/packages/editor/tsconfig.json
@@ -3,7 +3,8 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
- "declarationDir": "build-types"
+ "declarationDir": "build-types",
+ "checkJs": false
},
"references": [
{ "path": "../a11y" },
@@ -32,5 +33,5 @@
{ "path": "../warning" },
{ "path": "../wordcount" }
],
- "include": [ "src/**/*.ts", "src/**/*.tsx" ]
+ "include": [ "src" ]
}
diff --git a/patches/postcss-urlrebase+1.3.0.patch b/patches/postcss-urlrebase+1.3.0.patch
new file mode 100644
index 0000000000000..3ba02c0e8f218
--- /dev/null
+++ b/patches/postcss-urlrebase+1.3.0.patch
@@ -0,0 +1,11 @@
+diff --git a/node_modules/postcss-urlrebase/index.d.ts b/node_modules/postcss-urlrebase/index.d.ts
+index 88b25d0..441ace4 100644
+--- a/node_modules/postcss-urlrebase/index.d.ts
++++ b/node_modules/postcss-urlrebase/index.d.ts
+@@ -1,6 +1,5 @@
+ declare module 'rebaseUrl' {
+ import { PluginCreator } from 'postcss';
+- import { URL } from 'url';
+
+ interface RebaseUrlOptions {
+ /**