import ObjectIdImport from 'bson-objectid'; import { canAccessAdmin, traverseFields } from 'payload'; import { fieldAffectsData, fieldShouldBeLocalized, tabHasName } from 'payload/shared'; const ObjectId = 'default' in ObjectIdImport ? ObjectIdImport.default : ObjectIdImport; function iterateFields(fields, fromLocaleData, toLocaleData, req, parentIsLocalized) { fields.map(field => { if (fieldAffectsData(field)) { switch (field.type) { case 'array': // if the field has no value, take the source value if (field.name in toLocaleData && // only replace if the target value is null or undefined [null, undefined].includes(toLocaleData[field.name]) && field.name in fromLocaleData) { toLocaleData[field.name] = fromLocaleData[field.name]; break; } // if the field has a value - loop over the data from target if (field.name in toLocaleData) { toLocaleData[field.name].map((item, index) => { if (fromLocaleData[field.name]?.[index]) { // Generate new IDs if the field is localized to prevent errors with relational DBs. if (fieldShouldBeLocalized({ field, parentIsLocalized })) { toLocaleData[field.name][index].id = new ObjectId().toHexString(); } iterateFields(field.fields, fromLocaleData[field.name][index], item, req, parentIsLocalized || field.localized); } }); } break; case 'blocks': // if the field has no value, take the source value if (field.name in toLocaleData && ( // only replace if the target value is null, undefined, or empty array [null, undefined].includes(toLocaleData[field.name]) || Array.isArray(toLocaleData[field.name]) && toLocaleData[field.name].length === 0) && field.name in fromLocaleData) { toLocaleData[field.name] = fromLocaleData[field.name]; break; } // if the field has a value - loop over the data from target if (field.name in toLocaleData) { toLocaleData[field.name].map((blockData, index) => { const block = req.payload.blocks[blockData.blockType] ?? (field.blockReferences ?? field.blocks).find(block => typeof block !== 'string' && block.slug === blockData.blockType); // Generate new IDs if the field is localized to prevent errors with relational DBs. if (fieldShouldBeLocalized({ field, parentIsLocalized })) { toLocaleData[field.name][index].id = new ObjectId().toHexString(); } if (block?.fields?.length) { iterateFields(block?.fields, fromLocaleData[field.name][index], blockData, req, parentIsLocalized || field.localized); } }); } break; case 'checkbox': case 'code': case 'date': case 'email': case 'json': case 'number': case 'point': case 'radio': case 'relationship': case 'richText': case 'select': case 'text': case 'textarea': case 'upload': if (field.name in toLocaleData && // only replace if the target value is null or undefined [null, undefined].includes(toLocaleData[field.name]) && field.name in fromLocaleData) { toLocaleData[field.name] = fromLocaleData[field.name]; } break; case 'group': { if (fieldAffectsData(field) && field.name in toLocaleData && fromLocaleData?.[field.name] !== undefined) { iterateFields(field.fields, fromLocaleData[field.name], toLocaleData[field.name], req, parentIsLocalized || field.localized); } else { iterateFields(field.fields, fromLocaleData, toLocaleData, req, parentIsLocalized); } break; } } } else { switch (field.type) { case 'collapsible': case 'row': iterateFields(field.fields, fromLocaleData, toLocaleData, req, parentIsLocalized); break; case 'tabs': field.tabs.map(tab => { if (tabHasName(tab)) { if (tab.name in toLocaleData && fromLocaleData?.[tab.name] !== undefined) { iterateFields(tab.fields, fromLocaleData[tab.name], toLocaleData[tab.name], req, parentIsLocalized); } } else { iterateFields(tab.fields, fromLocaleData, toLocaleData, req, parentIsLocalized); } }); break; } } }); } function mergeData(fromLocaleData, toLocaleData, fields, req, parentIsLocalized) { iterateFields(fields, fromLocaleData, toLocaleData, req, parentIsLocalized); return toLocaleData; } /** * We don't have to recursively remove all ids, * just the ones from the fields inside a localized array or block. */ function removeIdIfParentIsLocalized(data, fields) { traverseFields({ callback: ({ parentIsLocalized, ref }) => { if (parentIsLocalized) { delete ref.id; } }, fields, fillEmpty: false, ref: data }); return data; } export const copyDataFromLocaleHandler = async args => { const { req } = args; try { return await copyDataFromLocale(args); } catch (err) { req.payload.logger.error({ err, msg: `There was an error copying data from "${args.fromLocale}" to "${args.toLocale}"` }); // Re-throw the error so it can be caught by the client throw err; } }; export const copyDataFromLocale = async args => { const { collectionSlug, docID, fromLocale, globalSlug, overrideData = false, req: { payload, payload: { collections, globals }, user }, req, toLocale } = args; await canAccessAdmin({ req }); const [fromLocaleData, toLocaleData] = await Promise.allSettled([globalSlug ? payload.findGlobal({ slug: globalSlug, depth: 0, locale: fromLocale, overrideAccess: false, user }) : payload.findByID({ id: docID, collection: collectionSlug, depth: 0, joins: false, locale: fromLocale, overrideAccess: false, user }), globalSlug ? payload.findGlobal({ slug: globalSlug, depth: 0, locale: toLocale, overrideAccess: false, user }) : payload.findByID({ id: docID, collection: collectionSlug, depth: 0, joins: false, locale: toLocale, overrideAccess: false, user })]); if (fromLocaleData.status === 'rejected') { throw new Error(`Error fetching data from locale "${fromLocale}"`); } if (toLocaleData.status === 'rejected') { throw new Error(`Error fetching data from locale "${toLocale}"`); } const fields = globalSlug ? globals[globalSlug].config.fields : collections[collectionSlug].config.fields; const fromLocaleDataWithoutID = fromLocaleData.value; const toLocaleDataWithoutID = toLocaleData.value; const dataWithID = overrideData ? fromLocaleDataWithoutID : mergeData(fromLocaleDataWithoutID, toLocaleDataWithoutID, fields, req, false); const data = removeIdIfParentIsLocalized(dataWithID, fields); return globalSlug ? await payload.updateGlobal({ slug: globalSlug, data, locale: toLocale, overrideAccess: false, req, user }) : await payload.update({ id: docID, collection: collectionSlug, data, locale: toLocale, overrideAccess: false, req, user }); }; //# sourceMappingURL=copyDataFromLocale.js.map