diff --git a/packages/payload/src/fields/baseFields/baseBeforeDuplicateArrays.ts b/packages/payload/src/fields/baseFields/baseBeforeDuplicateArrays.ts new file mode 100644 index 000000000..0b1256443 --- /dev/null +++ b/packages/payload/src/fields/baseFields/baseBeforeDuplicateArrays.ts @@ -0,0 +1,18 @@ +import ObjectIdImport from 'bson-objectid' + +import type { FieldHook } from '../config/types.js' + +const ObjectId = (ObjectIdImport.default || + ObjectIdImport) as unknown as typeof ObjectIdImport.default +/** + * Arrays and Blocks need to clear ids beforeDuplicate + */ +export const baseBeforeDuplicateArrays: FieldHook = ({ value }) => { + if (value) { + value = value.map((item) => { + item.id = new ObjectId().toHexString() + return item + }) + return value + } +} diff --git a/packages/payload/src/fields/config/sanitize.ts b/packages/payload/src/fields/config/sanitize.ts index 134efb68d..4b87a8242 100644 --- a/packages/payload/src/fields/config/sanitize.ts +++ b/packages/payload/src/fields/config/sanitize.ts @@ -12,6 +12,7 @@ import { } from '../../errors/index.js' import { MissingEditorProp } from '../../errors/MissingEditorProp.js' import { formatLabels, toWords } from '../../utilities/formatLabels.js' +import { baseBeforeDuplicateArrays } from '../baseFields/baseBeforeDuplicateArrays.js' import { baseBlockFields } from '../baseFields/baseBlockFields.js' import { baseIDField } from '../baseFields/baseIDField.js' import { setDefaultBeforeDuplicate } from '../setDefaultBeforeDuplicate.js' @@ -130,6 +131,15 @@ export const sanitizeFields = async ({ if (field.type === 'array' && field.fields) { field.fields.push(baseIDField) + if (field.localized) { + if (!field.hooks) { + field.hooks = {} + } + if (!field.hooks.beforeDuplicate) { + field.hooks.beforeDuplicate = [] + } + field.hooks.beforeDuplicate.push(baseBeforeDuplicateArrays) + } } if ((field.type === 'blocks' || field.type === 'array') && field.label) { @@ -210,6 +220,15 @@ export const sanitizeFields = async ({ } if (field.type === 'blocks' && field.blocks) { + if (field.localized) { + if (!field.hooks) { + field.hooks = {} + } + if (!field.hooks.beforeDuplicate) { + field.hooks.beforeDuplicate = [] + } + field.hooks.beforeDuplicate.push(baseBeforeDuplicateArrays) + } for (const block of field.blocks) { if (block._sanitized === true) { continue diff --git a/test/localization/config.ts b/test/localization/config.ts index a9f2324bc..1727ec76b 100644 --- a/test/localization/config.ts +++ b/test/localization/config.ts @@ -24,9 +24,9 @@ import { portugueseLocale, relationEnglishTitle, relationEnglishTitle2, + relationshipLocalizedSlug, relationSpanishTitle, relationSpanishTitle2, - relationshipLocalizedSlug, spanishLocale, spanishTitle, withLocalizedRelSlug, @@ -348,7 +348,9 @@ export default buildConfigWithDefaults({ if (payload.db.name === 'mongoose') { await new Promise((resolve, reject) => { payload.db?.collections[localizedPostsSlug]?.ensureIndexes(function (err) { - if (err) reject(err) + if (err) { + reject(err) + } resolve(true) }) }) diff --git a/test/localization/int.spec.ts b/test/localization/int.spec.ts index 115974420..05f54336e 100644 --- a/test/localization/int.spec.ts +++ b/test/localization/int.spec.ts @@ -22,9 +22,9 @@ import { portugueseLocale, relationEnglishTitle, relationEnglishTitle2, + relationshipLocalizedSlug, relationSpanishTitle, relationSpanishTitle2, - relationshipLocalizedSlug, spanishLocale, spanishTitle, withLocalizedRelSlug, @@ -1118,6 +1118,57 @@ describe('Localization', () => { expect(allLocales.localizedCheckbox.en).toBeTruthy() expect(allLocales.localizedCheckbox.es).toBeFalsy() }) + + it('should duplicate with localized blocks', async () => { + const englishText = 'english' + const spanishText = 'spanish' + const doc = await payload.create({ + collection: withRequiredLocalizedFields, + data: { + layout: [ + { + blockType: 'text', + text: englishText, + }, + ], + title: 'hello', + }, + locale: defaultLocale, + }) + + await payload.update({ + id: doc.id, + collection: withRequiredLocalizedFields, + data: { + layout: [ + { + blockType: 'text', + text: spanishText, + }, + ], + title: 'hello', + }, + locale: spanishLocale, + }) + + const result = await payload.duplicate({ + id: doc.id, + collection: withRequiredLocalizedFields, + locale: defaultLocale, + }) + + const allLocales = await payload.findByID({ + id: result.id, + collection: withRequiredLocalizedFields, + locale: 'all', + }) + + // check fields + expect(result.layout[0].text).toStrictEqual(englishText) + + expect(allLocales.layout.en[0].text).toStrictEqual(englishText) + expect(allLocales.layout.es[0].text).toStrictEqual(spanishText) + }) }) describe('Localized group and tabs', () => {