Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
226 lines
7.6 KiB
Plaintext
226 lines
7.6 KiB
Plaintext
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 |