Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataViews Extensibility: Allow unregistering the rename post action #64366

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 1 addition & 131 deletions packages/editor/src/components/post-actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { parse } from '@wordpress/blocks';
import { DataForm } from '@wordpress/dataviews';
import {
Button,
TextControl,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
} from '@wordpress/components';
Expand All @@ -23,7 +22,6 @@ import {
* Internal dependencies
*/
import {
TEMPLATE_ORIGINS,
TEMPLATE_PART_POST_TYPE,
TEMPLATE_POST_TYPE,
PATTERN_POST_TYPE,
Expand All @@ -34,7 +32,7 @@ import { CreateTemplatePartModalContents } from '../create-template-part-modal';
import { getItemTitle } from '../../dataviews/actions/utils';

// Patterns.
const { PATTERN_TYPES, CreatePatternModalContents, useDuplicatePatternProps } =
const { CreatePatternModalContents, useDuplicatePatternProps } =
unlock( patternsPrivateApis );

// TODO: this should be shared with other components (see post-fields in edit-site).
Expand All @@ -58,25 +56,6 @@ const formDuplicateAction = {
fields: [ 'title' ],
};

/**
* Check if a template is removable.
*
* @param {Object} template The template entity to check.
* @return {boolean} Whether the template is removable.
*/
function isTemplateRemovable( template ) {
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_ORIGINS.custom &&
! template?.has_theme_file
);
}

const viewPostAction = {
id: 'view-post',
label: __( 'View' ),
Expand Down Expand Up @@ -128,112 +107,6 @@ const postRevisionsAction = {
},
};

const renamePostAction = {
id: 'rename-post',
label: __( 'Rename' ),
isEligible( post ) {
if ( post.status === 'trash' ) {
return false;
}
// Templates, template parts and patterns have special checks for renaming.
if (
! [
TEMPLATE_POST_TYPE,
TEMPLATE_PART_POST_TYPE,
...Object.values( PATTERN_TYPES ),
].includes( post.type )
) {
return post.permissions?.update;
}
// In the case of templates, we can only rename custom templates.
if ( post.type === TEMPLATE_POST_TYPE ) {
return (
isTemplateRemovable( post ) &&
post.is_custom &&
post.permissions?.update
);
}
// Make necessary checks for template parts and patterns.
const isTemplatePart = post.type === TEMPLATE_PART_POST_TYPE;
const isUserPattern = post.type === PATTERN_TYPES.user;
// 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.
const isCustomPattern =
isUserPattern ||
( isTemplatePart && post.source === TEMPLATE_ORIGINS.custom );
const hasThemeFile = post?.has_theme_file;
return isCustomPattern && ! hasThemeFile && post.permissions?.update;
},
RenderModal: ( { items, closeModal, onActionPerformed } ) => {
const [ item ] = items;
const [ title, setTitle ] = useState( () => getItemTitle( item ) );
const { editEntityRecord, saveEditedEntityRecord } =
useDispatch( coreStore );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

async function onRename( event ) {
event.preventDefault();
try {
await editEntityRecord( 'postType', item.type, item.id, {
title,
} );
// Update state before saving rerenders the list.
setTitle( '' );
closeModal();
// Persist edited entity.
await saveEditedEntityRecord( 'postType', item.type, item.id, {
throwOnError: true,
} );
createSuccessNotice( __( 'Name updated' ), {
type: 'snackbar',
} );
onActionPerformed?.( items );
} catch ( error ) {
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: __( 'An error occurred while updating the name' );
createErrorNotice( errorMessage, { type: 'snackbar' } );
}
}

return (
<form onSubmit={ onRename }>
<VStack spacing="5">
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
label={ __( 'Name' ) }
value={ title }
onChange={ setTitle }
required
/>
<HStack justify="right">
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => {
closeModal();
} }
>
{ __( 'Cancel' ) }
</Button>
<Button
__next40pxDefaultSize
variant="primary"
type="submit"
>
{ __( 'Save' ) }
</Button>
</HStack>
</VStack>
</form>
);
},
};

const useDuplicatePostAction = ( postType ) => {
const userCanCreatePost = useSelect(
( select ) => {
Expand Down Expand Up @@ -494,7 +367,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
const isPattern = postType === PATTERN_POST_TYPE;
const isLoaded = !! postTypeObject;
const supportsRevisions = !! postTypeObject?.supports?.revisions;
const supportsTitle = !! postTypeObject?.supports?.title;
return useMemo( () => {
if ( ! isLoaded ) {
return [];
Expand All @@ -512,7 +384,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
userCanCreatePostType &&
duplicateTemplatePartAction,
isPattern && userCanCreatePostType && duplicatePatternAction,
supportsTitle && renamePostAction,
...defaultActions,
].filter( Boolean );
// Filter actions based on provided context. If not provided
Expand Down Expand Up @@ -586,7 +457,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
onActionPerformed,
isLoaded,
supportsRevisions,
supportsTitle,
context,
] );
}
146 changes: 146 additions & 0 deletions packages/editor/src/dataviews/actions/rename-post.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* WordPress dependencies
*/
import { useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
// @ts-ignore
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
import {
Button,
TextControl,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
} from '@wordpress/components';
import type { Action } from '@wordpress/dataviews';
import { store as noticesStore } from '@wordpress/notices';

/**
* Internal dependencies
*/
import {
TEMPLATE_ORIGINS,
TEMPLATE_PART_POST_TYPE,
TEMPLATE_POST_TYPE,
} from '../../store/constants';
import { unlock } from '../../lock-unlock';
import {
getItemTitle,
isTemplateRemovable,
isTemplate,
isTemplatePart,
} from './utils';
import type { CoreDataError, PostWithPermissions } from '../types';

// Patterns.
const { PATTERN_TYPES } = unlock( patternsPrivateApis );

const renamePost: Action< PostWithPermissions > = {
id: 'rename-post',
label: __( 'Rename' ),
isEligible( post ) {
if ( post.status === 'trash' ) {
return false;
}
// Templates, template parts and patterns have special checks for renaming.
if (
! [
TEMPLATE_POST_TYPE,
TEMPLATE_PART_POST_TYPE,
...Object.values( PATTERN_TYPES ),
].includes( post.type )
) {
return post.permissions?.update;
}

// In the case of templates, we can only rename custom templates.
if ( isTemplate( post ) ) {
return (
isTemplateRemovable( post ) &&
post.is_custom &&
post.permissions?.update
);
}

if ( isTemplatePart( post ) ) {
return (
post.source === TEMPLATE_ORIGINS.custom &&
! post?.has_theme_file &&
post.permissions?.update
);
}

return post.type === PATTERN_TYPES.user && post.permissions?.update;
},
RenderModal: ( { items, closeModal, onActionPerformed } ) => {
const [ item ] = items;
const [ title, setTitle ] = useState( () => getItemTitle( item ) );
const { editEntityRecord, saveEditedEntityRecord } =
useDispatch( coreStore );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

async function onRename( event: React.FormEvent ) {
event.preventDefault();
try {
await editEntityRecord( 'postType', item.type, item.id, {
title,
} );
// Update state before saving rerenders the list.
setTitle( '' );
closeModal?.();
// Persist edited entity.
await saveEditedEntityRecord( 'postType', item.type, item.id, {
throwOnError: true,
} );
createSuccessNotice( __( 'Name updated' ), {
type: 'snackbar',
} );
onActionPerformed?.( items );
} catch ( error ) {
const typedError = error as CoreDataError;
const errorMessage =
typedError.message && typedError.code !== 'unknown_error'
? typedError.message
: __( 'An error occurred while updating the name' );
createErrorNotice( errorMessage, { type: 'snackbar' } );
}
}

return (
<form onSubmit={ onRename }>
<VStack spacing="5">
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
label={ __( 'Name' ) }
value={ title }
onChange={ setTitle }
required
/>
<HStack justify="right">
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => {
closeModal?.();
} }
>
{ __( 'Cancel' ) }
</Button>
<Button
__next40pxDefaultSize
variant="primary"
type="submit"
>
{ __( 'Save' ) }
</Button>
</HStack>
</VStack>
</form>
);
},
};

export default renamePost;
14 changes: 11 additions & 3 deletions packages/editor/src/dataviews/actions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ import {
TEMPLATE_POST_TYPE,
} from '../../store/constants';

import type { Post, TemplateOrTemplatePart } from '../types';
import type { Post, TemplatePart, Template } from '../types';

export function isTemplate( post: Post ): post is Template {
return post.type === TEMPLATE_POST_TYPE;
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
}

export function isTemplatePart( post: Post ): post is TemplatePart {
return post.type === TEMPLATE_PART_POST_TYPE;
}

export function isTemplateOrTemplatePart(
p: Post
): p is TemplateOrTemplatePart {
): p is Template | TemplatePart {
return p.type === TEMPLATE_POST_TYPE || p.type === TEMPLATE_PART_POST_TYPE;
}

Expand All @@ -39,7 +47,7 @@ export function getItemTitle( item: Post ) {
* @param template The template entity to check.
* @return Whether the template is removable.
*/
export function isTemplateRemovable( template: TemplateOrTemplatePart ) {
export function isTemplateRemovable( template: Template | TemplatePart ) {
if ( ! template ) {
return false;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/editor/src/dataviews/store/private-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import exportPattern from '../actions/export-pattern';
import resetPost from '../actions/reset-post';
import trashPost from '../actions/trash-post';
import permanentlyDeletePost from '../actions/permanently-delete-post';
import restorePost from '../actions/restore-post';
import renamePost from '../actions/rename-post';
import reorderPage from '../actions/reorder-page';
import restorePost from '../actions/restore-post';
import type { PostType } from '../types';
import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
Expand Down Expand Up @@ -74,6 +75,7 @@ export const registerPostTypeActions =
.getPostType( postType ) ) as PostType;

const actions = [
postTypeConfig.supports?.title ? renamePost : undefined,
postTypeConfig?.supports?.[ 'page-attributes' ]
? reorderPage
: undefined,
Expand Down
Loading
Loading