Skip to content

Commit

Permalink
Fix: User capacities are not taken into account in the trash post act…
Browse files Browse the repository at this point in the history
…ion.
  • Loading branch information
jorgefilipecosta committed Jun 19, 2024
1 parent 9da6486 commit 09c79cc
Showing 1 changed file with 199 additions and 153 deletions.
352 changes: 199 additions & 153 deletions packages/editor/src/components/post-actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { external, trash, backup } from '@wordpress/icons';
import { addQueryArgs } from '@wordpress/url';
import { useDispatch, useSelect } from '@wordpress/data';
import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
import { decodeEntities } from '@wordpress/html-entities';
import { store as coreStore } from '@wordpress/core-data';
import { __, _n, sprintf, _x } from '@wordpress/i18n';
Expand Down Expand Up @@ -147,165 +147,207 @@ const deletePostAction = {
},
};

const trashPostAction = {
id: 'move-to-trash',
label: __( 'Move to Trash' ),
isPrimary: true,
icon: trash,
isEligible( item ) {
return ! [ 'auto-draft', 'trash' ].includes( item.status );
},
supportsBulk: true,
hideModalHeader: true,
RenderModal: ( { items, closeModal, onActionPerformed } ) => {
const [ isBusy, setIsBusy ] = useState( false );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const { deleteEntityRecord } = useDispatch( coreStore );
return (
<VStack spacing="5">
<Text>
{ items.length === 1
? sprintf(
// translators: %s: The item's title.
__(
'Are you sure you want to move to trash "%s"?'
),
getItemTitle( items[ 0 ] )
)
: sprintf(
// translators: %d: The number of items (2 or more).
_n(
'Are you sure you want to move to trash %d item?',
'Are you sure you want to move to trash %d items?',
items.length
),
items.length
) }
</Text>
<HStack justify="right">
<Button
variant="tertiary"
onClick={ closeModal }
disabled={ isBusy }
__experimentalIsFocusable
>
{ __( 'Cancel' ) }
</Button>
<Button
variant="primary"
onClick={ async () => {
setIsBusy( true );
const promiseResult = await Promise.allSettled(
items.map( ( item ) =>
deleteEntityRecord(
'postType',
item.type,
item.id,
{},
{ throwOnError: true }
)
)
);
// If all the promises were fulfilled with success.
if (
promiseResult.every(
( { status } ) => status === 'fulfilled'
)
) {
let successMessage;
if ( promiseResult.length === 1 ) {
successMessage = sprintf(
/* translators: The item's title. */
__( '"%s" moved to trash.' ),
function useTrashPostAction( postType ) {
const registry = useRegistry();
const { resource, canUserResolvers } = useSelect(
( select ) => {
const { getPostType, getCachedResolvers } = select( coreStore );
return {
resource: getPostType( postType )?.rest_base || '',
canUserResolvers: getCachedResolvers().canUser,
};
},
[ postType ]
);
return useMemo(
() => ( {
id: 'move-to-trash',
label: __( 'Move to Trash' ),
isPrimary: true,
icon: trash,
isEligible( item ) {
return (
registry
.select( coreStore )
.canUser( 'delete', resource, item.id ) &&
! [ 'auto-draft', 'trash' ].includes( item.status )
);
},
supportsBulk: true,
hideModalHeader: true,
RenderModal: ( { items, closeModal, onActionPerformed } ) => {
const [ isBusy, setIsBusy ] = useState( false );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const { deleteEntityRecord } = useDispatch( coreStore );
return (
<VStack spacing="5">
<Text>
{ items.length === 1
? sprintf(
// translators: %s: The item's title.
__(
'Are you sure you want to move to trash "%s"?'
),
getItemTitle( items[ 0 ] )
);
} else if ( items[ 0 ].type === 'page' ) {
successMessage = sprintf(
/* translators: The number of items. */
__( '%s items moved to trash.' ),
items.length
);
} else {
successMessage = sprintf(
/* translators: The number of posts. */
__( '%s items move to trash.' ),
)
: sprintf(
// translators: %d: The number of items (2 or more).
_n(
'Are you sure you want to move to trash %d item?',
'Are you sure you want to move to trash %d items?',
items.length
),
items.length
);
}
createSuccessNotice( successMessage, {
type: 'snackbar',
id: 'move-to-trash-action',
} );
} else {
// If there was at least one failure.
let errorMessage;
// If we were trying to delete a single item.
if ( promiseResult.length === 1 ) {
if ( promiseResult[ 0 ].reason?.message ) {
errorMessage =
promiseResult[ 0 ].reason.message;
} else {
errorMessage = __(
'An error occurred while moving to trash the item.'
) }
</Text>
<HStack justify="right">
<Button
variant="tertiary"
onClick={ closeModal }
disabled={ isBusy }
__experimentalIsFocusable
>
{ __( 'Cancel' ) }
</Button>
<Button
variant="primary"
onClick={ async () => {
setIsBusy( true );
const promiseResult =
await Promise.allSettled(
items.map( ( item ) =>
deleteEntityRecord(
'postType',
item.type,
item.id,
{},
{ throwOnError: true }
)
)
);
}
// If we were trying to delete multiple items.
} else {
const errorMessages = new Set();
const failedPromises = promiseResult.filter(
( { status } ) => status === 'rejected'
);
for ( const failedPromise of failedPromises ) {
if ( failedPromise.reason?.message ) {
errorMessages.add(
failedPromise.reason.message
// If all the promises were fulfilled with success.
if (
promiseResult.every(
( { status } ) =>
status === 'fulfilled'
)
) {
let successMessage;
if ( promiseResult.length === 1 ) {
successMessage = sprintf(
/* translators: The item's title. */
__( '"%s" moved to trash.' ),
getItemTitle( items[ 0 ] )
);
} else if (
items[ 0 ].type === 'page'
) {
successMessage = sprintf(
/* translators: The number of items. */
__(
'%s items moved to trash.'
),
items.length
);
} else {
successMessage = sprintf(
/* translators: The number of posts. */
__( '%s items move to trash.' ),
items.length
);
}
}
if ( errorMessages.size === 0 ) {
errorMessage = __(
'An error occurred while moving to trash the items.'
);
} else if ( errorMessages.size === 1 ) {
errorMessage = sprintf(
/* translators: %s: an error message */
__(
'An error occurred while moving to trash the item: %s'
),
[ ...errorMessages ][ 0 ]
);
createSuccessNotice( successMessage, {
type: 'snackbar',
id: 'move-to-trash-action',
} );
} else {
errorMessage = sprintf(
/* translators: %s: a list of comma separated error messages */
__(
'Some errors occurred while moving to trash the items: %s'
),
[ ...errorMessages ].join( ',' )
);
// If there was at least one failure.
let errorMessage;
// If we were trying to delete a single item.
if ( promiseResult.length === 1 ) {
if (
promiseResult[ 0 ].reason
?.message
) {
errorMessage =
promiseResult[ 0 ].reason
.message;
} else {
errorMessage = __(
'An error occurred while moving to trash the item.'
);
}
// If we were trying to delete multiple items.
} else {
const errorMessages = new Set();
const failedPromises =
promiseResult.filter(
( { status } ) =>
status === 'rejected'
);
for ( const failedPromise of failedPromises ) {
if (
failedPromise.reason
?.message
) {
errorMessages.add(
failedPromise.reason
.message
);
}
}
if ( errorMessages.size === 0 ) {
errorMessage = __(
'An error occurred while moving to trash the items.'
);
} else if (
errorMessages.size === 1
) {
errorMessage = sprintf(
/* translators: %s: an error message */
__(
'An error occurred while moving to trash the item: %s'
),
[ ...errorMessages ][ 0 ]
);
} else {
errorMessage = sprintf(
/* translators: %s: a list of comma separated error messages */
__(
'Some errors occurred while moving to trash the items: %s'
),
[ ...errorMessages ].join(
','
)
);
}
}
createErrorNotice( errorMessage, {
type: 'snackbar',
} );
}
}
createErrorNotice( errorMessage, {
type: 'snackbar',
} );
}
if ( onActionPerformed ) {
onActionPerformed( items );
}
setIsBusy( false );
closeModal();
} }
isBusy={ isBusy }
disabled={ isBusy }
__experimentalIsFocusable
>
{ __( 'Trash' ) }
</Button>
</HStack>
</VStack>
);
},
};
if ( onActionPerformed ) {
onActionPerformed( items );
}
setIsBusy( false );
closeModal();
} }
isBusy={ isBusy }
disabled={ isBusy }
__experimentalIsFocusable
>
{ __( 'Trash' ) }
</Button>
</HStack>
</VStack>
);
},
} ),
// eslint-disable-next-line react-hooks/exhaustive-deps
[ registry, resource, canUserResolvers ]
);
}

const permanentlyDeletePostAction = {
id: 'permanently-delete',
Expand Down Expand Up @@ -1020,6 +1062,7 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
);

const duplicatePostAction = useDuplicatePostAction( postType );
const trashPostAction = useTrashPostAction( postType );
const isTemplateOrTemplatePart = [
TEMPLATE_POST_TYPE,
TEMPLATE_PART_POST_TYPE,
Expand Down Expand Up @@ -1113,6 +1156,9 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
isPattern,
postTypeObject?.viewable,
duplicatePostAction,
permanentlyDeletePostAction,
trashPostAction,
restorePostAction,
onActionPerformed,
isLoaded,
supportsRevisions,
Expand Down

0 comments on commit 09c79cc

Please sign in to comment.