Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
aba3b41
adds tests and config properties for localizing metadata
JarrodMFlesch Nov 18, 2025
1185032
adjust where the properties are set
JarrodMFlesch Nov 19, 2025
35f8cbb
chore: add failing localized status test
jessrynkar Nov 19, 2025
a49a46f
fixes data loss test
JarrodMFlesch Nov 19, 2025
b3a0841
adjust experimental placement
JarrodMFlesch Nov 19, 2025
d050348
update property usage
JarrodMFlesch Nov 19, 2025
7d64254
only allow localizing status if localization is enabled
JarrodMFlesch Nov 19, 2025
5c37391
Merge branch 'main' into feat/experimental-localize-metadata
jessrynkar Nov 20, 2025
4d49f77
adjust conditional in update function
JarrodMFlesch Nov 20, 2025
19fb173
update test to fail
JarrodMFlesch Nov 20, 2025
5546935
implements publish/unpublish all locale functionality
JarrodMFlesch Nov 20, 2025
8fa923f
Merge branch 'main' into feat/experimental-localize-metadata
JarrodMFlesch Nov 20, 2025
c51dfbd
Merge branch 'main' into feat/experimental-localize-metadata
jessrynkar Nov 21, 2025
a7cef11
chore: update int test
jessrynkar Nov 21, 2025
405c563
integrate publishAllLocales in create operation
JarrodMFlesch Nov 21, 2025
48ab45c
don't publish all if draft arg is true
JarrodMFlesch Nov 21, 2025
dc311c5
Merge branch 'main' into feat/experimental-localize-metadata
jessrynkar Nov 26, 2025
47c5f41
chore: move global functionality from UI branch
jessrynkar Nov 26, 2025
78dc90a
chore: revert changes to saveSnapshot and saveVersion
jessrynkar Dec 3, 2025
8d31fc1
chore: add publishAll and unpublishAll to global operations
jessrynkar Dec 3, 2025
2d23b3e
Merge branch 'main' into feat/experimental-localize-metadata
jessrynkar Dec 3, 2025
4acec1c
chore: fix test
jessrynkar Dec 3, 2025
944a10a
chore: revert save snapshot/version changes and update
jessrynkar Dec 3, 2025
17019d7
fix: globals
JarrodMFlesch Dec 3, 2025
a6639f5
chore: fix global update error
jessrynkar Dec 4, 2025
8cb4a7d
Merge branch 'main' into feat/experimental-localize-metadata
JarrodMFlesch Dec 4, 2025
00c0a2a
adds hasLocalizeStatusEnabled helper
JarrodMFlesch Dec 4, 2025
299bd32
chore: add postgres update
jessrynkar Dec 4, 2025
f54223f
fix build
JarrodMFlesch Dec 4, 2025
cda67ec
Merge remote-tracking branch 'refs/remotes/origin/feat/experimental-l…
JarrodMFlesch Dec 4, 2025
bf0a45c
remove ui changes from this PR
JarrodMFlesch Dec 4, 2025
3740803
Merge branch 'main' into feat/experimental-localize-metadata
JarrodMFlesch Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/drizzle/src/queries/getTableColumnFromPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const getTableColumnFromPath = ({
}
}

let localizedPathQuery = false
if (field) {
const pathSegments = [...incomingSegments]

Expand All @@ -131,6 +132,7 @@ export const getTableColumnFromPath = ({

if (matchedLocale) {
locale = matchedLocale
localizedPathQuery = true
pathSegments.splice(1, 1)
}
}
Expand Down Expand Up @@ -967,7 +969,13 @@ export const getTableColumnFromPath = ({
const parentTable = aliasTable || adapter.tables[tableName]
newTableName = `${tableName}${adapter.localesSuffix}`

newTable = adapter.tables[newTableName]
// use an alias because the same query may contain constraints with different locale value
if (localizedPathQuery) {
const { newAliasTable } = getTableAlias({ adapter, tableName: newTableName })
newTable = newAliasTable
} else {
newTable = adapter.tables[newTableName]
}

let condition = eq(parentTable.id, newTable._parentID)

Expand Down
27 changes: 22 additions & 5 deletions packages/payload/src/collections/config/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { uploadCollectionEndpoints } from '../../uploads/endpoints/index.js'
import { getBaseUploadFields } from '../../uploads/getBaseFields.js'
import { flattenAllFields } from '../../utilities/flattenAllFields.js'
import { formatLabels } from '../../utilities/formatLabels.js'
import { traverseForLocalizedFields } from '../../utilities/traverseForLocalizedFields.js'
import { baseVersionFields } from '../../versions/baseFields.js'
import { versionDefaults } from '../../versions/defaults.js'
import { defaultCollectionEndpoints } from '../endpoints/index.js'
Expand Down Expand Up @@ -166,14 +167,17 @@ export const sanitizeCollection = async (
}

if (sanitized.versions) {
if (sanitized.versions === true) {
sanitized.versions = { drafts: false, maxPerDoc: 100 }
}

if (sanitized.timestamps === false) {
throw new TimestampsRequired(collection)
}

if (sanitized.versions === true) {
sanitized.versions = {
drafts: false,
maxPerDoc: 100,
}
}

sanitized.versions.maxPerDoc =
typeof sanitized.versions.maxPerDoc === 'number' ? sanitized.versions.maxPerDoc : 100

Expand All @@ -185,6 +189,14 @@ export const sanitizeCollection = async (
}
}

const hasLocalizedFields = traverseForLocalizedFields(sanitized.fields)

if (config.localization && hasLocalizedFields) {
sanitized.versions.drafts.localizeStatus ??= Boolean(config.experimental?.localizeStatus)
} else {
sanitized.versions.drafts.localizeStatus = false
}

if (sanitized.versions.drafts.autosave === true) {
sanitized.versions.drafts.autosave = {
interval: versionDefaults.autosaveInterval,
Expand All @@ -195,7 +207,12 @@ export const sanitizeCollection = async (
sanitized.versions.drafts.validate = false
}

sanitized.fields = mergeBaseFields(sanitized.fields, baseVersionFields)
sanitized.fields = mergeBaseFields(
sanitized.fields,
baseVersionFields({
localized: sanitized.versions.drafts.localizeStatus,
}),
)
}
} else {
delete sanitized.versions
Expand Down
3 changes: 2 additions & 1 deletion packages/payload/src/collections/endpoints/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { createOperation } from '../operations/create.js'
export const createHandler: PayloadHandler = async (req) => {
const collection = getRequestCollection(req)

const { autosave, depth, draft, populate, select } = parseParams(req.query)
const { autosave, depth, draft, populate, publishAllLocales, select } = parseParams(req.query)

const publishSpecificLocale = req.query.publishSpecificLocale as string | undefined

Expand All @@ -22,6 +22,7 @@ export const createHandler: PayloadHandler = async (req) => {
depth,
draft,
populate,
publishAllLocales,
publishSpecificLocale,
req,
select,
Expand Down
18 changes: 15 additions & 3 deletions packages/payload/src/collections/endpoints/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,19 @@ import { updateOperation } from '../operations/update.js'
export const updateHandler: PayloadHandler = async (req) => {
const collection = getRequestCollection(req)

const { depth, draft, limit, overrideLock, populate, select, sort, trash, where } = parseParams(
req.query,
)
const {
depth,
draft,
limit,
overrideLock,
populate,
publishAllLocales,
select,
sort,
trash,
unpublishAllLocales,
where,
} = parseParams(req.query)

const result = await updateOperation({
collection,
Expand All @@ -23,10 +33,12 @@ export const updateHandler: PayloadHandler = async (req) => {
limit,
overrideLock: overrideLock ?? false,
populate,
publishAllLocales,
req,
select,
sort,
trash,
unpublishAllLocales,
where: where!,
})

Expand Down
16 changes: 14 additions & 2 deletions packages/payload/src/collections/endpoints/updateByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,18 @@ import { updateByIDOperation } from '../operations/updateByID.js'
export const updateByIDHandler: PayloadHandler = async (req) => {
const { id, collection } = getRequestCollectionWithID(req)

const { autosave, depth, draft, overrideLock, populate, publishSpecificLocale, select, trash } =
parseParams(req.query)
const {
autosave,
depth,
draft,
overrideLock,
populate,
publishAllLocales,
publishSpecificLocale,
select,
trash,
unpublishAllLocales,
} = parseParams(req.query)

const doc = await updateByIDOperation({
id,
Expand All @@ -22,10 +32,12 @@ export const updateByIDHandler: PayloadHandler = async (req) => {
draft,
overrideLock: overrideLock ?? false,
populate,
publishAllLocales,
publishSpecificLocale,
req,
select,
trash,
unpublishAllLocales,
})

let message = req.t('general:updatedSuccessfully')
Expand Down
45 changes: 43 additions & 2 deletions packages/payload/src/collections/operations/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import { generateFileData } from '../../uploads/generateFileData.js'
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles.js'
import { uploadFiles } from '../../uploads/uploadFiles.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { hasDraftsEnabled, hasDraftValidationEnabled } from '../../utilities/getVersionsConfig.js'
import {
hasDraftsEnabled,
hasDraftValidationEnabled,
hasLocalizeStatusEnabled,
} from '../../utilities/getVersionsConfig.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { sanitizeInternalFields } from '../../utilities/sanitizeInternalFields.js'
Expand All @@ -49,6 +53,7 @@ export type Arguments<TSlug extends CollectionSlug> = {
overrideAccess?: boolean
overwriteExistingFiles?: boolean
populate?: PopulateType
publishAllLocales?: boolean
publishSpecificLocale?: string
req: PayloadRequest
select?: SelectType
Expand Down Expand Up @@ -100,6 +105,7 @@ export const createOperation = async <
overrideAccess,
overwriteExistingFiles = false,
populate,
publishAllLocales: publishAllLocalesArg,
publishSpecificLocale,
req: {
fallbackLocale,
Expand All @@ -115,7 +121,14 @@ export const createOperation = async <

let { data } = args

const isSavingDraft = Boolean(draft && hasDraftsEnabled(collectionConfig))
const publishAllLocales =
!draft &&
(publishAllLocalesArg ?? (hasLocalizeStatusEnabled(collectionConfig) ? false : true))
const isSavingDraft = Boolean(draft && hasDraftsEnabled(collectionConfig) && !publishAllLocales)

if (isSavingDraft) {
data._status = 'draft'
}

let duplicatedFromDocWithLocales: JsonObject = {}
let duplicatedFromDoc: JsonObject = {}
Expand Down Expand Up @@ -229,6 +242,34 @@ export const createOperation = async <
skipValidation: isSavingDraft && !hasDraftValidationEnabled(collectionConfig),
})

if (
config.localization &&
collectionConfig.versions &&
collectionConfig.versions.drafts &&
collectionConfig.versions.drafts.localizeStatus &&
publishAllLocales
) {
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 resultWithLocales._status !== 'object' || resultWithLocales._status === null) {
resultWithLocales._status = {}
}

for (const localeCode of accessibleLocaleCodes) {
resultWithLocales._status[localeCode] = 'published'
}
}

// /////////////////////////////////////
// Write files to local storage
// /////////////////////////////////////
Expand Down
6 changes: 6 additions & 0 deletions packages/payload/src/collections/operations/local/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType> = {
* Specify [populate](https://payloadcms.com/docs/queries/select#populate) to control which fields to include to the result from populated documents.
*/
populate?: PopulateType
/**
* Publish to all locales
*/
publishAllLocales?: boolean
/**
* The `PayloadRequest` object. You can pass it to thread the current [transaction](https://payloadcms.com/docs/database/transactions), user and locale to the operation.
* Recommended to pass when using the Local API from hooks, as usually you want to execute the operation within the current transaction.
Expand Down Expand Up @@ -151,6 +155,7 @@ export async function createLocal<
overrideAccess = true,
overwriteExistingFiles = false,
populate,
publishAllLocales,
select,
showHiddenFields,
} = options
Expand Down Expand Up @@ -178,6 +183,7 @@ export async function createLocal<
overrideAccess,
overwriteExistingFiles,
populate,
publishAllLocales,
req,
select,
showHiddenFields,
Expand Down
16 changes: 16 additions & 0 deletions packages/payload/src/collections/operations/local/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,16 @@ export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType
* Specify [populate](https://payloadcms.com/docs/queries/select#populate) to control which fields to include to the result from populated documents.
*/
populate?: PopulateType
/**
* Publish the document / documents in all locales. Requires `versions.drafts.localizeStatus` to be enabled.
*
* @default undefined
*/
publishAllLocales?: boolean
/**
* Publish the document / documents with a specific locale.
*
* @default undefined
*/
publishSpecificLocale?: string
/**
Expand All @@ -120,6 +128,10 @@ export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType
* @default false
*/
trash?: boolean
/**
* Unpublish the document / documents in all locales. Requires `versions.drafts.localizeStatus` to be enabled.
*/
unpublishAllLocales?: boolean
/**
* If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks.
*/
Expand Down Expand Up @@ -222,11 +234,13 @@ async function updateLocal<
overrideLock,
overwriteExistingFiles = false,
populate,
publishAllLocales,
publishSpecificLocale,
select,
showHiddenFields,
sort,
trash = false,
unpublishAllLocales,
where,
} = options

Expand Down Expand Up @@ -255,12 +269,14 @@ async function updateLocal<
overwriteExistingFiles,
payload,
populate,
publishAllLocales,
publishSpecificLocale,
req,
select,
showHiddenFields,
sort,
trash,
unpublishAllLocales,
where,
}

Expand Down
7 changes: 6 additions & 1 deletion packages/payload/src/collections/operations/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type Arguments<TSlug extends CollectionSlug> = {
overrideLock?: boolean
overwriteExistingFiles?: boolean
populate?: PopulateType
publishAllLocales?: boolean
publishSpecificLocale?: string
req: PayloadRequest
select?: SelectType
Expand All @@ -59,6 +60,7 @@ export type Arguments<TSlug extends CollectionSlug> = {
*/
sort?: Sort
trash?: boolean
unpublishAllLocales?: boolean
where: Where
}

Expand Down Expand Up @@ -98,6 +100,7 @@ export const updateOperation = async <
overrideLock,
overwriteExistingFiles = false,
populate,
publishAllLocales,
publishSpecificLocale,
req: {
fallbackLocale,
Expand All @@ -110,6 +113,7 @@ export const updateOperation = async <
showHiddenFields,
sort: incomingSort,
trash = false,
unpublishAllLocales,
where,
} = args

Expand Down Expand Up @@ -238,7 +242,6 @@ export const updateOperation = async <
// ///////////////////////////////////////////////
const updatedDoc = await updateDocument({
id,
accessResults: accessResult,
autosave,
collectionConfig,
config,
Expand All @@ -253,10 +256,12 @@ export const updateOperation = async <
overrideLock: overrideLock!,
payload,
populate,
publishAllLocales,
publishSpecificLocale,
req,
select: select!,
showHiddenFields: showHiddenFields!,
unpublishAllLocales,
})

return updatedDoc
Expand Down
Loading
Loading