Skip to content

Commit

Permalink
DataViews: Use items with permissions and avoid hooks to register act…
Browse files Browse the repository at this point in the history
…ions (#63923)

Co-authored-by: youknowriad <[email protected]>
Co-authored-by: scruffian <[email protected]>
Co-authored-by: Mamaduka <[email protected]>
  • Loading branch information
4 people authored Jul 29, 2024
1 parent d803b3f commit 14816d2
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 90 deletions.
19 changes: 19 additions & 0 deletions packages/core-data/src/private-selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,22 @@ export const getEntityRecordsPermissions = createRegistrySelector( ( select ) =>
( state ) => [ state.userPermissions ]
)
);

/**
* Returns the entity record permissions for the given entity record id.
*
* @param state Data state.
* @param kind Entity kind.
* @param name Entity name.
* @param id Entity record id.
*
* @return The entity record permissions.
*/
export function getEntityRecordPermissions(
state: State,
kind: string,
name: string,
id: string
) {
return getEntityRecordsPermissions( state, kind, name, [ id ] )[ 0 ];
}
32 changes: 31 additions & 1 deletion packages/edit-site/src/components/page-patterns/use-patterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { parse } from '@wordpress/blocks';
import { useSelect, createSelector } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
Expand Down Expand Up @@ -260,7 +261,7 @@ export const usePatterns = (
categoryId,
{ search = '', syncStatus } = {}
) => {
return useSelect(
const { patterns, ...rest } = useSelect(
( select ) => {
if ( postType === TEMPLATE_PART_POST_TYPE ) {
return selectTemplateParts( select, categoryId, search );
Expand All @@ -283,6 +284,35 @@ export const usePatterns = (
},
[ categoryId, postType, search, syncStatus ]
);

const ids = useMemo(
() => patterns?.map( ( record ) => record.id ) ?? [],
[ patterns ]
);

const permissions = useSelect(
( select ) => {
const { getEntityRecordsPermissions } = unlock(
select( coreStore )
);
return getEntityRecordsPermissions( 'postType', postType, ids );
},
[ ids, postType ]
);

const patternsWithPermissions = useMemo(
() =>
patterns?.map( ( record, index ) => ( {
...record,
permissions: permissions[ index ],
} ) ) ?? [],
[ patterns, permissions ]
);

return {
...rest,
patterns: patternsWithPermissions,
};
};

export default usePatterns;
12 changes: 5 additions & 7 deletions packages/edit-site/src/components/page-templates/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { useState, useMemo, useCallback, useEffect } from '@wordpress/element';
import { useEntityRecords } from '@wordpress/core-data';
import { privateApis as corePrivateApis } from '@wordpress/core-data';
import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews';
import { privateApis as routerPrivateApis } from '@wordpress/router';
import { privateApis as editorPrivateApis } from '@wordpress/editor';
Expand Down Expand Up @@ -31,6 +31,7 @@ import {

const { usePostActions } = unlock( editorPrivateApis );
const { useHistory, useLocation } = unlock( routerPrivateApis );
const { useEntityRecordsWithPermissions } = unlock( corePrivateApis );

const EMPTY_ARRAY = [];

Expand Down Expand Up @@ -134,13 +135,10 @@ export default function PageTemplates() {
} ) );
}, [ activeView ] );

const { records, isResolving: isLoadingData } = useEntityRecords(
'postType',
TEMPLATE_POST_TYPE,
{
const { records, isResolving: isLoadingData } =
useEntityRecordsWithPermissions( 'postType', TEMPLATE_POST_TYPE, {
per_page: -1,
}
);
} );
const history = useHistory();
const onChangeSelection = useCallback(
( items ) => {
Expand Down
95 changes: 20 additions & 75 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, useRegistry } from '@wordpress/data';
import { useDispatch, useSelect } 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 @@ -88,7 +88,10 @@ const trashPostAction = {
isPrimary: true,
icon: trash,
isEligible( item ) {
return ! [ 'auto-draft', 'trash' ].includes( item.status );
return (
! [ 'auto-draft', 'trash' ].includes( item.status ) &&
item.permissions?.delete
);
},
supportsBulk: true,
hideModalHeader: true,
Expand Down Expand Up @@ -240,40 +243,12 @@ const trashPostAction = {
},
};

function useCanUserEligibilityCheckPostType( capability, postType, action ) {
const registry = useRegistry();
return useMemo(
() => ( {
...action,
isEligible( item ) {
return (
action.isEligible( item ) &&
registry.select( coreStore ).canUser( capability, {
kind: 'postType',
name: postType,
id: item.id,
} )
);
},
} ),
[ action, registry, capability, postType ]
);
}

function useTrashPostAction( postType ) {
return useCanUserEligibilityCheckPostType(
'delete',
postType,
trashPostAction
);
}

const permanentlyDeletePostAction = {
id: 'permanently-delete',
label: __( 'Permanently delete' ),
supportsBulk: true,
isEligible( { status } ) {
return status === 'trash';
isEligible( { status, permissions } ) {
return status === 'trash' && permissions?.delete;
},
async callback( posts, { registry, onActionPerformed } ) {
const { createSuccessNotice, createErrorNotice } =
Expand Down Expand Up @@ -359,22 +334,14 @@ const permanentlyDeletePostAction = {
},
};

function usePermanentlyDeletePostAction( postType ) {
return useCanUserEligibilityCheckPostType(
'delete',
postType,
permanentlyDeletePostAction
);
}

const restorePostAction = {
id: 'restore',
label: __( 'Restore' ),
isPrimary: true,
icon: backup,
supportsBulk: true,
isEligible( { status } ) {
return status === 'trash';
isEligible( { status, permissions } ) {
return status === 'trash' && permissions?.update;
},
async callback( posts, { registry, onActionPerformed } ) {
const { createSuccessNotice, createErrorNotice } =
Expand Down Expand Up @@ -474,14 +441,6 @@ const restorePostAction = {
},
};

function useRestorePostAction( postType ) {
return useCanUserEligibilityCheckPostType(
'update',
postType,
restorePostAction
);
}

const viewPostAction = {
id: 'view-post',
label: __( 'View' ),
Expand Down Expand Up @@ -548,11 +507,15 @@ const renamePostAction = {
...Object.values( PATTERN_TYPES ),
].includes( post.type )
) {
return true;
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;
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;
Expand All @@ -564,7 +527,7 @@ const renamePostAction = {
isUserPattern ||
( isTemplatePart && post.source === TEMPLATE_ORIGINS.custom );
const hasThemeFile = post?.has_theme_file;
return isCustomPattern && ! hasThemeFile;
return isCustomPattern && ! hasThemeFile && post.permissions?.update;
},
RenderModal: ( { items, closeModal, onActionPerformed } ) => {
const [ item ] = items;
Expand Down Expand Up @@ -635,14 +598,6 @@ const renamePostAction = {
},
};

function useRenamePostAction( postType ) {
return useCanUserEligibilityCheckPostType(
'update',
postType,
renamePostAction
);
}

function ReorderModal( { items, closeModal, onActionPerformed } ) {
const [ item, setItem ] = useState( items[ 0 ] );
const orderInput = item.menu_order;
Expand Down Expand Up @@ -1004,11 +959,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
);

const duplicatePostAction = useDuplicatePostAction( postType );
const trashPostActionForPostType = useTrashPostAction( postType );
const permanentlyDeletePostActionForPostType =
usePermanentlyDeletePostAction( postType );
const renamePostActionForPostType = useRenamePostAction( postType );
const restorePostActionForPostType = useRestorePostAction( postType );
const reorderPagesAction = useReorderPagesAction( postType );
const isTemplateOrTemplatePart = [
TEMPLATE_POST_TYPE,
Expand All @@ -1035,14 +985,13 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
userCanCreatePostType &&
duplicateTemplatePartAction,
isPattern && userCanCreatePostType && duplicatePatternAction,
supportsTitle && renamePostActionForPostType,
supportsTitle && renamePostAction,
reorderPagesAction,
! isTemplateOrTemplatePart && restorePostActionForPostType,
! isTemplateOrTemplatePart && ! isPattern && restorePostAction,
! isTemplateOrTemplatePart && ! isPattern && trashPostAction,
! isTemplateOrTemplatePart &&
! isPattern &&
trashPostActionForPostType,
! isTemplateOrTemplatePart &&
permanentlyDeletePostActionForPostType,
permanentlyDeletePostAction,
...defaultActions,
].filter( Boolean );
// Filter actions based on provided context. If not provided
Expand Down Expand Up @@ -1117,10 +1066,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
postTypeObject?.viewable,
duplicatePostAction,
reorderPagesAction,
trashPostActionForPostType,
restorePostActionForPostType,
renamePostActionForPostType,
permanentlyDeletePostActionForPostType,
onActionPerformed,
isLoaded,
supportsRevisions,
Expand Down
26 changes: 19 additions & 7 deletions packages/editor/src/components/post-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,38 @@ const {

export default function PostActions( { onActionPerformed, buttonProps } ) {
const [ isActionsMenuOpen, setIsActionsMenuOpen ] = useState( false );
const { item, postType } = useSelect( ( select ) => {
const { item, permissions, postType } = useSelect( ( select ) => {
const { getCurrentPostType, getCurrentPostId } = select( editorStore );
const { getEditedEntityRecord } = select( coreStore );
const { getEditedEntityRecord, getEntityRecordPermissions } = unlock(
select( coreStore )
);
const _postType = getCurrentPostType();
const _id = getCurrentPostId();
return {
item: getEditedEntityRecord(
item: getEditedEntityRecord( 'postType', _postType, _id ),
permissions: getEntityRecordPermissions(
'postType',
_postType,
getCurrentPostId()
_id
),
postType: _postType,
};
}, [] );
const itemWithPermissions = useMemo( () => {
return {
...item,
permissions,
};
}, [ item, permissions ] );
const allActions = usePostActions( { postType, onActionPerformed } );

const actions = useMemo( () => {
return allActions.filter( ( action ) => {
return ! action.isEligible || action.isEligible( item );
return (
! action.isEligible || action.isEligible( itemWithPermissions )
);
} );
}, [ allActions, item ] );
}, [ allActions, itemWithPermissions ] );

return (
<DropdownMenu
Expand All @@ -72,7 +84,7 @@ export default function PostActions( { onActionPerformed, buttonProps } ) {
>
<ActionsDropdownMenuGroup
actions={ actions }
item={ item }
item={ itemWithPermissions }
onClose={ () => {
setIsActionsMenuOpen( false );
} }
Expand Down

0 comments on commit 14816d2

Please sign in to comment.