diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js
index 2b3c883dfc367a..b34f8eea936155 100644
--- a/packages/block-editor/src/components/block-actions/index.js
+++ b/packages/block-editor/src/components/block-actions/index.js
@@ -31,10 +31,12 @@ export default function BlockActions( {
getDirectInsertBlock,
canMoveBlocks,
canRemoveBlocks,
+ getBlockEditingMode,
} = select( blockEditorStore );
const blocks = getBlocksByClientId( clientIds );
const rootClientId = getBlockRootClientId( clientIds[ 0 ] );
+ const rootBlockEditingMode = getBlockEditingMode( rootClientId );
const canInsertDefaultBlock = canInsertBlockType(
getDefaultBlockName(),
rootClientId
@@ -46,7 +48,9 @@ export default function BlockActions( {
return {
canMove: canMoveBlocks( clientIds, rootClientId ),
canRemove: canRemoveBlocks( clientIds, rootClientId ),
- canInsertBlock: canInsertDefaultBlock || !! directInsertBlock,
+ canInsertBlock:
+ ( canInsertDefaultBlock || !! directInsertBlock ) &&
+ rootBlockEditingMode === 'default',
canCopyStyles: blocks.every( ( block ) => {
return (
!! block &&
diff --git a/packages/block-editor/src/components/block-settings-menu-controls/index.js b/packages/block-editor/src/components/block-settings-menu-controls/index.js
index 8b43827605c53e..1edf02eb368945 100644
--- a/packages/block-editor/src/components/block-settings-menu-controls/index.js
+++ b/packages/block-editor/src/components/block-settings-menu-controls/index.js
@@ -27,15 +27,20 @@ import { BlockRenameControl, useBlockRename } from '../block-rename';
const { Fill, Slot } = createSlotFill( 'BlockSettingsMenuControls' );
const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
- const { selectedBlocks, selectedClientIds } = useSelect(
+ const { selectedBlocks, selectedClientIds, isContentOnly } = useSelect(
( select ) => {
- const { getBlockNamesByClientId, getSelectedBlockClientIds } =
- select( blockEditorStore );
+ const {
+ getBlockNamesByClientId,
+ getSelectedBlockClientIds,
+ getBlockEditingMode,
+ } = select( blockEditorStore );
const ids =
clientIds !== null ? clientIds : getSelectedBlockClientIds();
return {
selectedBlocks: getBlockNamesByClientId( ids ),
selectedClientIds: ids,
+ isContentOnly:
+ getBlockEditingMode( ids[ 0 ] ) === 'contentOnly',
};
},
[ clientIds ]
@@ -43,8 +48,10 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
const { canLock } = useBlockLock( selectedClientIds[ 0 ] );
const { canRename } = useBlockRename( selectedBlocks[ 0 ] );
- const showLockButton = selectedClientIds.length === 1 && canLock;
- const showRenameButton = selectedClientIds.length === 1 && canRename;
+ const showLockButton =
+ selectedClientIds.length === 1 && canLock && ! isContentOnly;
+ const showRenameButton =
+ selectedClientIds.length === 1 && canRename && ! isContentOnly;
// Check if current selection of blocks is Groupable or Ungroupable
// and pass this props down to ConvertToGroupButton.
@@ -89,17 +96,19 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
/>
) }
{ fills }
- { fillProps?.canMove && ! fillProps?.onlyBlock && (
-
- ) }
- { fillProps?.count === 1 && (
+ { fillProps?.canMove &&
+ ! fillProps?.onlyBlock &&
+ ! isContentOnly && (
+
+ ) }
+ { fillProps?.count === 1 && ! isContentOnly && (
{
const {
@@ -73,6 +74,7 @@ export function BlockSettingsDropdown( {
getSelectedBlockClientIds,
getBlockAttributes,
getOpenedBlockSettingsMenu,
+ getBlockEditingMode,
} = unlock( select( blockEditorStore ) );
const { getActiveBlockVariation } = select( blocksStore );
@@ -96,6 +98,8 @@ export function BlockSettingsDropdown( {
getPreviousBlockClientId( firstBlockClientId ),
selectedBlockClientIds: getSelectedBlockClientIds(),
openedBlockSettingsMenu: getOpenedBlockSettingsMenu(),
+ isContentOnly:
+ getBlockEditingMode( firstBlockClientId ) === 'contentOnly',
};
},
[ firstBlockClientId ]
@@ -231,11 +235,15 @@ export function BlockSettingsDropdown( {
clientId={ firstBlockClientId }
/>
) }
-
+ { ! isContentOnly && (
+
+ ) }
{ canDuplicate && (
) }
- { canCopyStyles && (
+ { canCopyStyles && ! isContentOnly && (
( {
+ isContentOnly:
+ select( blockEditorStore ).getBlockEditingMode( clientId ) ===
+ 'contentOnly',
+ } ),
+ [ clientId ]
+ );
+ const shouldShowLockIcon = isLocked && ! isContentOnly;
const isSticky = blockInformation?.positionType === 'sticky';
const images = useListViewImages( { clientId, isExpanded } );
@@ -147,7 +158,7 @@ function ListViewBlockSelectButton(
) ) }
) : null }
- { isLocked && (
+ { shouldShowLockIcon && (
diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js
index cdbc5939e6a2ab..c3ba9afe8cc165 100644
--- a/packages/block-editor/src/components/list-view/block.js
+++ b/packages/block-editor/src/components/list-view/block.js
@@ -104,34 +104,26 @@ function ListViewBlock( {
const blockInformation = useBlockDisplayInformation( clientId );
- const { block, blockName, blockEditingMode, allowRightClickOverrides } =
- useSelect(
- ( select ) => {
- const {
- getBlock,
- getBlockName,
- getBlockEditingMode,
- getSettings,
- } = select( blockEditorStore );
-
- return {
- block: getBlock( clientId ),
- blockName: getBlockName( clientId ),
- blockEditingMode: getBlockEditingMode( clientId ),
- allowRightClickOverrides:
- getSettings().allowRightClickOverrides,
- };
- },
- [ clientId ]
- );
+ const { block, blockName, allowRightClickOverrides } = useSelect(
+ ( select ) => {
+ const { getBlock, getBlockName, getSettings } =
+ select( blockEditorStore );
+
+ return {
+ block: getBlock( clientId ),
+ blockName: getBlockName( clientId ),
+ allowRightClickOverrides:
+ getSettings().allowRightClickOverrides,
+ };
+ },
+ [ clientId ]
+ );
const showBlockActions =
// When a block hides its toolbar it also hides the block settings menu,
// since that menu is part of the toolbar in the editor canvas.
// List View respects this by also hiding the block settings menu.
- hasBlockSupport( blockName, '__experimentalToolbar', true ) &&
- // Don't show the settings menu if block is disabled or content only.
- blockEditingMode === 'default';
+ hasBlockSupport( blockName, '__experimentalToolbar', true );
const instanceId = useInstanceId( ListViewBlock );
const descriptionId = `list-view-block-select-button__description-${ instanceId }`;
diff --git a/packages/block-editor/src/hooks/content-lock-ui.js b/packages/block-editor/src/hooks/content-lock-ui.js
index eff6cc9a2d8af3..7cca4b325b09d7 100644
--- a/packages/block-editor/src/hooks/content-lock-ui.js
+++ b/packages/block-editor/src/hooks/content-lock-ui.js
@@ -20,7 +20,6 @@ import { unlock } from '../lock-unlock';
// also includes artifacts on the store (actions, reducers, and selector).
function ContentLockControlsPure( { clientId, isSelected } ) {
- const { getBlockListSettings, getSettings } = useSelect( blockEditorStore );
const { templateLock, isLockedByParent, isEditingAsBlocks } = useSelect(
( select ) => {
const {
@@ -37,16 +36,11 @@ function ContentLockControlsPure( { clientId, isSelected } ) {
[ clientId ]
);
- const {
- updateSettings,
- updateBlockListSettings,
- __unstableSetTemporarilyEditingAsBlocks,
- } = useDispatch( blockEditorStore );
- const { stopEditingAsBlocks } = unlock( useDispatch( blockEditorStore ) );
+ const { stopEditingAsBlocks, modifyContentLockBlock } = unlock(
+ useDispatch( blockEditorStore )
+ );
const isContentLocked =
! isLockedByParent && templateLock === 'contentOnly';
- const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } =
- useDispatch( blockEditorStore );
const stopEditingAsBlockCallback = useCallback( () => {
stopEditingAsBlocks( clientId );
@@ -73,30 +67,19 @@ function ContentLockControlsPure( { clientId, isSelected } ) {
) }
{ showStartEditingAsBlocks && (
- { ( { onClose } ) => (
-
- ) }
+ { ( { selectedClientIds, onClose } ) =>
+ selectedClientIds.length === 1 &&
+ selectedClientIds[ 0 ] === clientId && (
+
+ )
+ }
) }
>
diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js
index 6cfccb17287ff9..28a7b1da98f73f 100644
--- a/packages/block-editor/src/store/private-actions.js
+++ b/packages/block-editor/src/store/private-actions.js
@@ -391,3 +391,27 @@ export function expandBlock( clientId ) {
clientId,
};
}
+
+/**
+ * Temporarily modify/unlock the content-only block for editions.
+ *
+ * @param {string} clientId The client id of the block.
+ */
+export const modifyContentLockBlock =
+ ( clientId ) =>
+ ( { select, dispatch } ) => {
+ dispatch.__unstableMarkNextChangeAsNotPersistent();
+ dispatch.updateBlockAttributes( clientId, {
+ templateLock: undefined,
+ } );
+ dispatch.updateBlockListSettings( clientId, {
+ ...select.getBlockListSettings( clientId ),
+ templateLock: false,
+ } );
+ const focusModeToRevert = select.getSettings().focusMode;
+ dispatch.updateSettings( { focusMode: true } );
+ dispatch.__unstableSetTemporarilyEditingAsBlocks(
+ clientId,
+ focusModeToRevert
+ );
+ };
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index 4ce85afb8cfd1e..176c15557c8386 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -1759,14 +1759,16 @@ export function canMoveBlock( state, clientId, rootClientId = null ) {
if ( attributes === null ) {
return true;
}
+ if ( getBlockEditingMode( state, rootClientId ) !== 'default' ) {
+ return false;
+ }
if ( attributes.lock?.move !== undefined ) {
return ! attributes.lock.move;
}
if ( getTemplateLock( state, rootClientId ) === 'all' ) {
return false;
}
-
- return getBlockEditingMode( state, rootClientId ) !== 'disabled';
+ return true;
}
/**
diff --git a/packages/edit-site/src/components/template-part-converter/index.js b/packages/edit-site/src/components/template-part-converter/index.js
index 7694735cbb3025..de47eb6ae26f4d 100644
--- a/packages/edit-site/src/components/template-part-converter/index.js
+++ b/packages/edit-site/src/components/template-part-converter/index.js
@@ -27,12 +27,25 @@ export default function TemplatePartConverter() {
}
function TemplatePartConverterMenuItem( { clientIds, onClose } ) {
- const blocks = useSelect(
- ( select ) =>
- select( blockEditorStore ).getBlocksByClientId( clientIds ),
+ const { isContentOnly, blocks } = useSelect(
+ ( select ) => {
+ const { getBlocksByClientId, getBlockEditingMode } =
+ select( blockEditorStore );
+ return {
+ blocks: getBlocksByClientId( clientIds ),
+ isContentOnly:
+ clientIds.length === 1 &&
+ getBlockEditingMode( clientIds[ 0 ] ) === 'contentOnly',
+ };
+ },
[ clientIds ]
);
+ // Do not show the convert button if the block is in content-only mode.
+ if ( isContentOnly ) {
+ return null;
+ }
+
// Allow converting a single template part to standard blocks.
if ( blocks.length === 1 && blocks[ 0 ]?.name === 'core/template-part' ) {
return (
diff --git a/packages/editor/src/components/block-settings-menu/content-only-settings-menu.js b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.js
new file mode 100644
index 00000000000000..4683dd38593a59
--- /dev/null
+++ b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.js
@@ -0,0 +1,175 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ BlockSettingsMenuControls,
+ __unstableBlockSettingsMenuFirstItem as BlockSettingsMenuFirstItem,
+ store as blockEditorStore,
+ useBlockDisplayInformation,
+} from '@wordpress/block-editor';
+import { store as coreStore } from '@wordpress/core-data';
+import { __experimentalText as Text, MenuItem } from '@wordpress/components';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { store as editorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
+
+function ContentOnlySettingsMenuItems( { clientId, onClose } ) {
+ const { entity, onNavigateToEntityRecord } = useSelect(
+ ( select ) => {
+ const {
+ getBlockEditingMode,
+ getBlockParentsByBlockName,
+ getSettings,
+ getBlockAttributes,
+ } = select( blockEditorStore );
+ const contentOnly =
+ getBlockEditingMode( clientId ) === 'contentOnly';
+ if ( ! contentOnly ) {
+ return {};
+ }
+ const patternParent = getBlockParentsByBlockName(
+ clientId,
+ 'core/block',
+ true
+ )[ 0 ];
+
+ let record;
+ if ( patternParent ) {
+ record = select( coreStore ).getEntityRecord(
+ 'postType',
+ 'wp_block',
+ getBlockAttributes( patternParent ).ref
+ );
+ } else {
+ const { getCurrentPostType, getCurrentTemplateId } =
+ select( editorStore );
+ const currentPostType = getCurrentPostType();
+ const templateId = getCurrentTemplateId();
+ if ( currentPostType === 'page' && templateId ) {
+ record = select( coreStore ).getEntityRecord(
+ 'postType',
+ 'wp_template',
+ templateId
+ );
+ }
+ }
+ return {
+ entity: record,
+ onNavigateToEntityRecord:
+ getSettings().onNavigateToEntityRecord,
+ };
+ },
+ [ clientId ]
+ );
+
+ if ( ! entity ) {
+ return (
+
+ );
+ }
+
+ const isPattern = entity.type === 'wp_block';
+
+ return (
+ <>
+
+
+
+
+ { isPattern
+ ? __(
+ 'Edit the pattern to move, delete, or make further changes to this block.'
+ )
+ : __(
+ 'Edit the template to move, delete, or make further changes to this block.'
+ ) }
+
+ >
+ );
+}
+
+function TemplateLockContentOnlyMenuItems( { clientId, onClose } ) {
+ const { contentLockingParent } = useSelect(
+ ( select ) => {
+ const { getContentLockingParent } = unlock(
+ select( blockEditorStore )
+ );
+ return {
+ contentLockingParent: getContentLockingParent( clientId ),
+ };
+ },
+ [ clientId ]
+ );
+ const blockDisplayInformation =
+ useBlockDisplayInformation( contentLockingParent );
+ // Disable reason: We're using a hook here so it has to be on top-level.
+ // eslint-disable-next-line @wordpress/no-unused-vars-before-return
+ const { modifyContentLockBlock, selectBlock } = unlock(
+ useDispatch( blockEditorStore )
+ );
+
+ if ( ! blockDisplayInformation?.title ) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+
+ { __(
+ 'Temporarily unlock the parent block to edit, delete or make further changes to this block.'
+ ) }
+
+ >
+ );
+}
+
+export default function ContentOnlySettingsMenu() {
+ return (
+
+ { ( { selectedClientIds, onClose } ) =>
+ selectedClientIds.length === 1 && (
+
+ )
+ }
+
+ );
+}
diff --git a/packages/editor/src/components/block-settings-menu/content-only-settings-menu.native.js b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.native.js
new file mode 100644
index 00000000000000..11cebce87bbba5
--- /dev/null
+++ b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.native.js
@@ -0,0 +1,4 @@
+// Render nothing in native for now.
+export default function ContentOnlySettingsMenu() {
+ return null;
+}
diff --git a/packages/editor/src/components/block-settings-menu/style.scss b/packages/editor/src/components/block-settings-menu/style.scss
new file mode 100644
index 00000000000000..53fa391d28ef04
--- /dev/null
+++ b/packages/editor/src/components/block-settings-menu/style.scss
@@ -0,0 +1,4 @@
+.editor-content-only-settings-menu__description {
+ padding: $grid-unit;
+ min-width: 235px;
+}
diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js
index df0fb488c69dc1..4f359104ea02da 100644
--- a/packages/editor/src/components/provider/index.js
+++ b/packages/editor/src/components/provider/index.js
@@ -28,6 +28,7 @@ import useCommands from '../commands';
import BlockRemovalWarnings from '../block-removal-warnings';
import StartPageOptions from '../start-page-options';
import KeyboardShortcutHelpModal from '../keyboard-shortcut-help-modal';
+import ContentOnlySettingsMenu from '../block-settings-menu/content-only-settings-menu';
const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis );
const { PatternsMenuItems } = unlock( editPatternsPrivateApis );
@@ -264,6 +265,7 @@ export const ExperimentalEditorProvider = withRegistryProvider(
{ ! settings.__unstableIsPreviewMode && (
<>
+
{ mode === 'template-locked' && (
) }
diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss
index b382fd82c583ae..dcc693c08b80a7 100644
--- a/packages/editor/src/style.scss
+++ b/packages/editor/src/style.scss
@@ -3,6 +3,7 @@
@import "./components/autocompleters/style.scss";
@import "./components/block-manager/style.scss";
@import "./components/collapsible-block-toolbar/style.scss";
+@import "./components/block-settings-menu/style.scss";
@import "./components/document-bar/style.scss";
@import "./components/document-outline/style.scss";
@import "./components/document-tools/style.scss";