UNPKG

payload

Version:

Node, React, Headless CMS and Application Framework built on Next.js

335 lines (334 loc) • 12.6 kB
import { ensureUsernameOrEmail } from '../../../auth/ensureUsernameOrEmail.js'; import { generatePasswordSaltHash } from '../../../auth/strategies/local/generatePasswordSaltHash.js'; import { afterChange } from '../../../fields/hooks/afterChange/index.js'; import { afterRead } from '../../../fields/hooks/afterRead/index.js'; import { beforeChange } from '../../../fields/hooks/beforeChange/index.js'; import { beforeValidate } from '../../../fields/hooks/beforeValidate/index.js'; import { deepCopyObjectSimple, getLatestCollectionVersion, saveVersion } from '../../../index.js'; import { deleteAssociatedFiles } from '../../../uploads/deleteAssociatedFiles.js'; import { uploadFiles } from '../../../uploads/uploadFiles.js'; import { checkDocumentLockStatus } from '../../../utilities/checkDocumentLockStatus.js'; import { hasDraftsEnabled, hasDraftValidationEnabled, hasLocalizeStatusEnabled } from '../../../utilities/getVersionsConfig.js'; import { mergeLocalizedData } from '../../../utilities/mergeLocalizedData.js'; /** * This function is used to update a document in the DB and return the result. * * It runs the following hooks in order: * - beforeValidate - Fields * - beforeValidate - Collection * - beforeChange - Collection * - beforeChange - Fields * - afterRead - Fields * - afterRead - Collection * - afterChange - Fields * - afterChange - Collection */ export const updateDocument = async ({ id, autosave, collectionConfig, config, data, depth, docWithLocales, draftArg, fallbackLocale, filesToUpload, locale, overrideAccess, overrideLock, payload, populate, publishAllLocales: publishAllLocalesArg, publishSpecificLocale, req, select, showHiddenFields, unpublishAllLocales: unpublishAllLocalesArg })=>{ const password = data?.password; const publishAllLocales = !draftArg && (publishAllLocalesArg ?? (hasLocalizeStatusEnabled(collectionConfig) ? false : true)); const unpublishAllLocales = typeof unpublishAllLocalesArg === 'string' ? unpublishAllLocalesArg === 'true' : !!unpublishAllLocalesArg; const isSavingDraft = Boolean(draftArg && hasDraftsEnabled(collectionConfig)) && data._status !== 'published' && !publishAllLocales; const shouldSavePassword = Boolean(password && collectionConfig.auth && (!collectionConfig.auth.disableLocalStrategy || typeof collectionConfig.auth.disableLocalStrategy === 'object' && collectionConfig.auth.disableLocalStrategy.enableFields) && !isSavingDraft); if (isSavingDraft) { data._status = 'draft'; } // ///////////////////////////////////// // Handle potentially locked documents // ///////////////////////////////////// await checkDocumentLockStatus({ id, collectionSlug: collectionConfig.slug, lockErrorMessage: `Document with ID ${id} is currently locked by another user and cannot be updated.`, overrideLock, req }); const originalDoc = await afterRead({ collection: collectionConfig, context: req.context, depth: 0, doc: deepCopyObjectSimple(docWithLocales), draft: draftArg, fallbackLocale: id ? null : fallbackLocale, global: null, locale, overrideAccess: true, req, showHiddenFields: true }); const isRestoringDraftFromTrash = Boolean(originalDoc?.deletedAt) && data?._status !== 'published'; if (collectionConfig.auth) { ensureUsernameOrEmail({ authOptions: collectionConfig.auth, collectionSlug: collectionConfig.slug, data, operation: 'update', originalDoc, req }); } // ///////////////////////////////////// // Delete any associated files // ///////////////////////////////////// await deleteAssociatedFiles({ collectionConfig, config, doc: docWithLocales, files: filesToUpload, overrideDelete: false, req }); // ///////////////////////////////////// // beforeValidate - Fields // ///////////////////////////////////// data = await beforeValidate({ id, collection: collectionConfig, context: req.context, data, doc: originalDoc, global: null, operation: 'update', overrideAccess, req }); // ///////////////////////////////////// // beforeValidate - Collection // ///////////////////////////////////// if (collectionConfig.hooks?.beforeValidate?.length) { for (const hook of collectionConfig.hooks.beforeValidate){ data = await hook({ collection: collectionConfig, context: req.context, data, operation: 'update', originalDoc, req }) || data; } } // ///////////////////////////////////// // Write files to local storage // ///////////////////////////////////// if (!collectionConfig.upload.disableLocalStorage) { await uploadFiles(payload, filesToUpload, req); } // ///////////////////////////////////// // beforeChange - Collection // ///////////////////////////////////// if (collectionConfig.hooks?.beforeChange?.length) { for (const hook of collectionConfig.hooks.beforeChange){ data = await hook({ collection: collectionConfig, context: req.context, data, operation: 'update', originalDoc, req }) || data; } } // ///////////////////////////////////// // beforeChange - Fields // ///////////////////////////////////// const beforeChangeArgs = { id, collection: collectionConfig, context: req.context, data: { ...data, id }, doc: originalDoc, docWithLocales, global: null, operation: 'update', overrideAccess, req, skipValidation: // only skip validation for drafts when draft validation is false isSavingDraft && !hasDraftValidationEnabled(collectionConfig) || // Skip validation for trash operations since they're just metadata updates collectionConfig.trash && (Boolean(data?.deletedAt) || isRestoringDraftFromTrash) }; // ///////////////////////////////////// // Handle Localized Data Merging // ///////////////////////////////////// let result = await beforeChange(beforeChangeArgs); let snapshotToSave; if (config.localization && collectionConfig.versions) { let snapshotData; let currentDoc; if (collectionConfig.versions.drafts && collectionConfig.versions.drafts.localizeStatus) { if (publishAllLocales || unpublishAllLocales) { let accessibleLocaleCodes = config.localization.localeCodes; if (config.localization.filterAvailableLocales) { const filteredLocales = await config.localization.filterAvailableLocales({ locales: config.localization.locales, req }); accessibleLocaleCodes = filteredLocales.map((locale)=>typeof locale === 'string' ? locale : locale.code); } if (typeof result._status !== 'object' || result._status === null) { result._status = {}; } for (const localeCode of accessibleLocaleCodes){ result._status[localeCode] = unpublishAllLocales ? 'draft' : 'published'; } } else if (!isSavingDraft) { // publishing a single locale currentDoc = await payload.db.findOne({ collection: collectionConfig.slug, req, where: { id: { equals: id } } }); snapshotData = result; } } else if (publishSpecificLocale) { // previous way of publishing a single locale currentDoc = await getLatestCollectionVersion({ id, config: collectionConfig, payload, published: true, query: { collection: collectionConfig.slug, locale: 'all', req, where: { id: { equals: id } } }, req }); snapshotData = { ...result, _status: 'draft' }; } if (snapshotData) { snapshotToSave = deepCopyObjectSimple(snapshotData || {}); result = mergeLocalizedData({ configBlockReferences: config.blocks, dataWithLocales: result || {}, docWithLocales: currentDoc || {}, fields: collectionConfig.fields, selectedLocales: [ locale ] }); } } const dataToUpdate = { ...result }; // ///////////////////////////////////// // Handle potential password update // ///////////////////////////////////// if (shouldSavePassword && typeof password === 'string') { const { hash, salt } = await generatePasswordSaltHash({ collection: collectionConfig, password, req }); dataToUpdate.salt = salt; dataToUpdate.hash = hash; delete dataToUpdate.password; delete data.password; } // ///////////////////////////////////// // Update // ///////////////////////////////////// if (!isSavingDraft) { // Ensure updatedAt date is always updated dataToUpdate.updatedAt = new Date().toISOString(); result = await req.payload.db.updateOne({ id, collection: collectionConfig.slug, data: dataToUpdate, locale, req }); } // ///////////////////////////////////// // Create version // ///////////////////////////////////// if (collectionConfig.versions) { result = await saveVersion({ id, autosave, collection: collectionConfig, docWithLocales: result, draft: isSavingDraft, operation: 'update', payload, publishSpecificLocale, req, snapshot: snapshotToSave }); } // ///////////////////////////////////// // afterRead - Fields // ///////////////////////////////////// result = await afterRead({ collection: collectionConfig, context: req.context, depth, doc: result, draft: draftArg, fallbackLocale, global: null, locale, overrideAccess, populate, req, select, showHiddenFields }); // ///////////////////////////////////// // afterRead - Collection // ///////////////////////////////////// if (collectionConfig.hooks?.afterRead?.length) { for (const hook of collectionConfig.hooks.afterRead){ result = await hook({ collection: collectionConfig, context: req.context, doc: result, req }) || result; } } // ///////////////////////////////////// // afterChange - Fields // ///////////////////////////////////// result = await afterChange({ collection: collectionConfig, context: req.context, data, doc: result, global: null, operation: 'update', previousDoc: originalDoc, req }); // ///////////////////////////////////// // afterChange - Collection // ///////////////////////////////////// if (collectionConfig.hooks?.afterChange?.length) { for (const hook of collectionConfig.hooks.afterChange){ result = await hook({ collection: collectionConfig, context: req.context, data, doc: result, operation: 'update', previousDoc: originalDoc, req }) || result; } } return result; }; //# sourceMappingURL=update.js.map