Merge pull request #2551 from payloadcms/fix/doc-drawer-access
fix: document drawer access control
This commit is contained in:
@@ -12,20 +12,20 @@ import Button from '../Button';
|
||||
import { useConfig } from '../../utilities/Config';
|
||||
import { useLocale } from '../../utilities/Locale';
|
||||
import { useAuth } from '../../utilities/Auth';
|
||||
import { DocumentInfoProvider } from '../../utilities/DocumentInfo';
|
||||
import { DocumentInfoProvider, useDocumentInfo } from '../../utilities/DocumentInfo';
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
||||
import usePayloadAPI from '../../../hooks/usePayloadAPI';
|
||||
import formatFields from '../../views/collections/Edit/formatFields';
|
||||
import { useRelatedCollections } from '../../forms/field-types/Relationship/AddNew/useRelatedCollections';
|
||||
import IDLabel from '../IDLabel';
|
||||
import { baseClass } from '.';
|
||||
import { CollectionPermission } from '../../../../auth';
|
||||
|
||||
export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
|
||||
const Content: React.FC<DocumentDrawerProps> = ({
|
||||
collectionSlug,
|
||||
id,
|
||||
drawerSlug,
|
||||
onSave: onSaveFromProps,
|
||||
customHeader,
|
||||
onSave,
|
||||
}) => {
|
||||
const { serverURL, routes: { api } } = useConfig();
|
||||
const { toggleModal, modalState, closeModal } = useModal();
|
||||
@@ -36,20 +36,23 @@ export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
|
||||
const hasInitializedState = useRef(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [collectionConfig] = useRelatedCollections(collectionSlug);
|
||||
const { docPermissions, id } = useDocumentInfo();
|
||||
|
||||
const [fields, setFields] = useState(() => formatFields(collectionConfig, true));
|
||||
|
||||
// no need to an additional requests when creating new documents
|
||||
const initialID = useRef(id);
|
||||
const [{ data, isLoading: isLoadingDocument, isError }] = usePayloadAPI(
|
||||
(initialID.current ? `${serverURL}${api}/${collectionSlug}/${initialID.current}` : null),
|
||||
{ initialParams: { 'fallback-locale': 'null', depth: 0, draft: 'true' } },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setFields(formatFields(collectionConfig, true));
|
||||
}, [collectionSlug, collectionConfig]);
|
||||
|
||||
const [{ data, isLoading: isLoadingDocument, isError }] = usePayloadAPI(
|
||||
(id ? `${serverURL}${api}/${collectionSlug}/${id}` : null),
|
||||
{ initialParams: { 'fallback-locale': 'null', depth: 0, draft: 'true' } },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoadingDocument) {
|
||||
if (isLoadingDocument || hasInitializedState.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,62 +84,87 @@ export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
|
||||
}
|
||||
}, [isError, t, isOpen, data, drawerSlug, closeModal, isLoadingDocument]);
|
||||
|
||||
if (isError) return null;
|
||||
|
||||
const isEditing = Boolean(id);
|
||||
const apiURL = id ? `${serverURL}${api}/${collectionSlug}/${id}` : null;
|
||||
const action = `${serverURL}${api}/${collectionSlug}${id ? `/${id}` : ''}?locale=${locale}&depth=0&fallback-locale=null`;
|
||||
const hasSavePermission = (isEditing && docPermissions?.update?.permission) || (!isEditing && (docPermissions as CollectionPermission)?.create?.permission);
|
||||
const isLoading = !internalState || !docPermissions || isLoadingDocument;
|
||||
|
||||
return (
|
||||
<RenderCustomComponent
|
||||
DefaultComponent={DefaultEdit}
|
||||
CustomComponent={collectionConfig.admin?.components?.views?.Edit}
|
||||
componentProps={{
|
||||
isLoading,
|
||||
data,
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
permissions: permissions.collections[collectionConfig.slug],
|
||||
isEditing,
|
||||
apiURL,
|
||||
onSave,
|
||||
internalState,
|
||||
hasSavePermission,
|
||||
action,
|
||||
disableEyebrow: true,
|
||||
disableActions: true,
|
||||
me: true,
|
||||
disableLeaveWithoutSaving: true,
|
||||
customHeader: (
|
||||
<div className={`${baseClass}__header`}>
|
||||
<div className={`${baseClass}__header-content`}>
|
||||
<h2 className={`${baseClass}__header-text`}>
|
||||
{!customHeader ? t(!id ? 'fields:addNewLabel' : 'general:editLabel', { label: getTranslation(collectionConfig.labels.singular, i18n) }) : customHeader}
|
||||
</h2>
|
||||
<Button
|
||||
buttonStyle="none"
|
||||
className={`${baseClass}__header-close`}
|
||||
onClick={() => toggleModal(drawerSlug)}
|
||||
aria-label={t('general:close')}
|
||||
>
|
||||
<X />
|
||||
</Button>
|
||||
</div>
|
||||
{id && (
|
||||
<IDLabel id={id.toString()} />
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// First provide the document context using `DocumentInfoProvider`
|
||||
// this is so we can utilize the `useDocumentInfo` hook in the `Content` component
|
||||
// this drawer is used for both creating and editing documents
|
||||
// this means that the `id` may be unknown until the document is created
|
||||
export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = (props) => {
|
||||
const { collectionSlug, id: idFromProps, onSave: onSaveFromProps } = props;
|
||||
const [collectionConfig] = useRelatedCollections(collectionSlug);
|
||||
const [id, setId] = useState<string | null>(idFromProps);
|
||||
|
||||
const onSave = useCallback<DocumentDrawerProps['onSave']>((args) => {
|
||||
setId(args.doc.id);
|
||||
|
||||
if (typeof onSaveFromProps === 'function') {
|
||||
onSaveFromProps({
|
||||
...args,
|
||||
collectionConfig,
|
||||
});
|
||||
}
|
||||
}, [collectionConfig, onSaveFromProps]);
|
||||
|
||||
if (isError) return null;
|
||||
}, [onSaveFromProps, collectionConfig]);
|
||||
|
||||
return (
|
||||
<DocumentInfoProvider
|
||||
collection={collectionConfig}
|
||||
id={id}
|
||||
>
|
||||
<RenderCustomComponent
|
||||
DefaultComponent={DefaultEdit}
|
||||
CustomComponent={collectionConfig.admin?.components?.views?.Edit}
|
||||
componentProps={{
|
||||
isLoading: !internalState,
|
||||
data,
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
permissions: permissions.collections[collectionConfig.slug],
|
||||
isEditing: Boolean(id),
|
||||
apiURL: id ? `${serverURL}${api}/${collectionSlug}/${id}` : null,
|
||||
onSave,
|
||||
internalState,
|
||||
hasSavePermission: true,
|
||||
action: `${serverURL}${api}/${collectionSlug}${id ? `/${id}` : ''}?locale=${locale}&depth=0&fallback-locale=null`,
|
||||
disableEyebrow: true,
|
||||
disableActions: true,
|
||||
me: true,
|
||||
disableLeaveWithoutSaving: true,
|
||||
customHeader: (
|
||||
<div className={`${baseClass}__header`}>
|
||||
<div className={`${baseClass}__header-content`}>
|
||||
<h2 className={`${baseClass}__header-text`}>
|
||||
{!customHeader ? t(!id ? 'fields:addNewLabel' : 'general:editLabel', { label: getTranslation(collectionConfig.labels.singular, i18n) }) : customHeader}
|
||||
</h2>
|
||||
<Button
|
||||
buttonStyle="none"
|
||||
className={`${baseClass}__header-close`}
|
||||
onClick={() => toggleModal(drawerSlug)}
|
||||
aria-label={t('general:close')}
|
||||
>
|
||||
<X />
|
||||
</Button>
|
||||
</div>
|
||||
{id && (
|
||||
<IDLabel id={id} />
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
<Content
|
||||
{...props}
|
||||
onSave={onSave}
|
||||
/>
|
||||
</DocumentInfoProvider>
|
||||
);
|
||||
|
||||
@@ -92,7 +92,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
const json: Permissions = await request.json();
|
||||
setPermissions(json);
|
||||
} else {
|
||||
throw new Error("Fetching permissions failed with status code " + request.status);
|
||||
throw new Error(`Fetching permissions failed with status code ${request.status}`);
|
||||
}
|
||||
}, [serverURL, api, i18n]);
|
||||
|
||||
@@ -135,7 +135,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
if (id) {
|
||||
refreshPermissions();
|
||||
}
|
||||
}, [i18n, id, api, serverURL]);
|
||||
}, [i18n, id, api, serverURL, refreshPermissions]);
|
||||
|
||||
useEffect(() => {
|
||||
let reminder: ReturnType<typeof setTimeout>;
|
||||
|
||||
Reference in New Issue
Block a user