diff --git a/client/components/Deposit/Deposit.tsx b/client/components/Deposit/Deposit.tsx index 5056a637dc..37107b991e 100644 --- a/client/components/Deposit/Deposit.tsx +++ b/client/components/Deposit/Deposit.tsx @@ -47,7 +47,9 @@ function extractDoiSuffix(doi: string, depositTarget?: DepositTarget) { } export default function Deposit(props: Props) { - const { depositTarget } = props; + const { depositTarget, communityData } = props; + const isCommunityUnapproved = + communityData.spamTag != null && communityData.spamTag.status !== 'confirmed-not-spam'; const [resource, setResource] = useState(); const [doiSuffix, setDoiSuffix] = useState(''); const [persistingDoiSuffix, setPersistingDoiSuffix] = useState(false); @@ -150,6 +152,13 @@ export default function Deposit(props: Props) { let children: React.ReactNode; + const unapprovedWarning = isCommunityUnapproved ? ( + + DOI deposit is not available until your community has been approved. You may still + preview deposits. + + ) : null; + if (depositTarget?.service === 'datacite') { if (!exists(doiPrefix)) { children = ( @@ -161,6 +170,7 @@ export default function Deposit(props: Props) { } else { children = ( <> + {unapprovedWarning} {'pub' in props && resource && firstIntraWorkRelationship && (

This Pub will be cited as a member of the{' '} @@ -198,7 +208,11 @@ export default function Deposit(props: Props) { /> )} {!disabledDueToNoReleases && !persistingDoiSuffix && ( - + )} ); @@ -206,13 +220,17 @@ export default function Deposit(props: Props) { } else { assert('pub' in props); children = ( - + <> + {unapprovedWarning} + + ); } diff --git a/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx b/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx index e982495b42..cb057e9b6f 100644 --- a/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx +++ b/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx @@ -54,6 +54,9 @@ const CollectionSettings = (props: Props) => { } }, [collection.slug, hasChanges]); + const isCommunityApproved = + !communityData.spamTag || communityData.spamTag.status === 'confirmed-not-spam'; + const tabs: Subtab[] = pruneFalsyValues([ { id: 'details', @@ -83,7 +86,9 @@ const CollectionSettings = (props: Props) => { collection={collection} communityData={communityData} onUpdateCollection={updateCollection} - allowUpdateDoi={depositTarget?.service !== 'datacite'} + allowUpdateDoi={ + depositTarget?.service !== 'datacite' && isCommunityApproved + } /> , ], diff --git a/client/containers/DashboardSettings/PubSettings/Doi.tsx b/client/containers/DashboardSettings/PubSettings/Doi.tsx index adc9c2066b..6a228e2ab5 100644 --- a/client/containers/DashboardSettings/PubSettings/Doi.tsx +++ b/client/containers/DashboardSettings/PubSettings/Doi.tsx @@ -44,6 +44,7 @@ type Props = { pubData: any; updatePubData: (...args: any[]) => any; depositTarget?: DepositTarget; + depositDisabled?: boolean; }; const extractDoiSuffix = (doi: string, depositTarget?: DepositTarget) => { @@ -516,6 +517,7 @@ class Doi extends Component { pubData={this.props.pubData} target="pub" disabled={ + this.props.depositDisabled || this.disabledDueToParentWithoutDoi() || this.disabledDueToNoReleases() || this.disabledDueToUnmanagedPrefix() || diff --git a/server/collection/api.ts b/server/collection/api.ts index 49cf3e9587..3a4180545b 100644 --- a/server/collection/api.ts +++ b/server/collection/api.ts @@ -4,6 +4,7 @@ import { z } from 'zod'; import { prepareResource, submitResource } from 'deposit/datacite/deposit'; import { transformCollectionToResource } from 'deposit/transform/collection'; +import { assertCommunityApprovedForDoi } from 'server/doi/permissions'; import { generateDoi } from 'server/doi/queries'; import { ForbiddenError, NotFoundError } from 'server/utils/errors'; import { contract } from 'utils/api/contract'; @@ -87,6 +88,7 @@ export const collectionServer = s.router(contract.collection, { if (!permissions.update) { throw new ForbiddenError(); } + await assertCommunityApprovedForDoi(collection.communityId); const collectionDoi = collection.doi ?? ( diff --git a/server/doi/api.ts b/server/doi/api.ts index 720f556308..8afee8b0a6 100644 --- a/server/doi/api.ts +++ b/server/doi/api.ts @@ -6,7 +6,7 @@ import { ForbiddenError } from 'server/utils/errors'; import { wrap } from 'server/wrap'; import { parentToSupplementNeedsDoiError } from 'utils/crossref/createDeposit'; -import { getPermissions } from './permissions'; +import { assertCommunityApprovedForDoi, getPermissions } from './permissions'; import { generateDoi, getDoiData, setDoiData } from './queries'; import { validatePubRelationshipsForDeposit } from './validate'; @@ -51,6 +51,10 @@ const previewOrDepositDoi = async (user, body, options = { deposit: false }) => await assertUserAuthorized(target, requestIds); + if (deposit) { + await assertCommunityApprovedForDoi(communityId); + } + if (pubId && (await pubExistsAndIsMissingReleases(pubId))) { throw new ForbiddenError(); } @@ -137,6 +141,7 @@ router.get( }; await assertUserAuthorized(target, requestIds); + await assertCommunityApprovedForDoi(communityId as string); return res.status(200).json({ dois: await generateDoi({ communityId, collectionId, pubId }, target), diff --git a/server/doi/permissions.ts b/server/doi/permissions.ts index 28c9ff71c4..230e37f359 100644 --- a/server/doi/permissions.ts +++ b/server/doi/permissions.ts @@ -1,3 +1,5 @@ +import { Community, SpamTag } from 'server/models'; +import { ForbiddenError } from 'server/utils/errors'; import { getScope } from 'server/utils/queryHelpers'; export const getPermissions = async ({ pubId, collectionId, userId, communityId }) => { @@ -19,3 +21,14 @@ export const getPermissions = async ({ pubId, collectionId, userId, communityId collection: canAdminCommunity, }; }; + +export const assertCommunityApprovedForDoi = async (communityId: string) => { + const community = await Community.findByPk(communityId, { + include: [{ model: SpamTag, as: 'spamTag' }], + }); + if (community?.spamTag && community.spamTag.status !== 'confirmed-not-spam') { + throw new ForbiddenError( + new Error('DOI minting is not available until your community has been approved.'), + ); + } +}; diff --git a/server/pub/api.ts b/server/pub/api.ts index d8375885cc..635bac925b 100644 --- a/server/pub/api.ts +++ b/server/pub/api.ts @@ -9,6 +9,7 @@ import { z } from 'zod'; import { prepareResource, submitResource } from 'deposit/datacite/deposit'; import { transformPubToResource } from 'deposit/transform/pub'; import { assertValidResource } from 'deposit/validate'; +import { assertCommunityApprovedForDoi } from 'server/doi/permissions'; import { generateDoi } from 'server/doi/queries'; import { verifyCaptchaPayload } from 'server/utils/captcha'; import { BadRequestError, ForbiddenError, NotFoundError } from 'server/utils/errors'; @@ -203,6 +204,7 @@ export const pubServer = s.router(contract.pub, { throw new ForbiddenError(); } const pub = expect(await findPub(pubId)); + await assertCommunityApprovedForDoi(pub.communityId); const pubDoi = pub.doi ?? (