Skip to content

Commit 5a37909

Browse files
fix: filteredLocales in the client config are stale (#14326)
Fixes #14299 Using `filterAvailableLocales` means they could change when the req changes. When we get the client config from the cache, we need to re-apply `filterAvailableLocales` so the config has the correct values. This was manually being done in the Root layout, but every other place that called getClientConfig was not i.e. the Root view. So this PR moves that logic from the Root layout into the getClientConfig function.
1 parent c96d50d commit 5a37909

File tree

9 files changed

+52
-19
lines changed

9 files changed

+52
-19
lines changed

packages/next/src/layouts/Root/index.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { rtlLanguages } from '@payloadcms/translations'
55
import { ProgressBar, RootProvider } from '@payloadcms/ui'
66
import { getClientConfig } from '@payloadcms/ui/utilities/getClientConfig'
77
import { cookies as nextCookies } from 'next/headers.js'
8+
import { applyLocaleFiltering } from 'payload/shared'
89
import React from 'react'
910

1011
import { getNavPrefs } from '../../elements/Nav/getNavPrefs.js'
@@ -87,20 +88,7 @@ export const RootLayout = async ({
8788
importMap,
8889
user: req.user,
8990
})
90-
91-
if (
92-
clientConfig.localization &&
93-
config.localization &&
94-
typeof config.localization.filterAvailableLocales === 'function'
95-
) {
96-
clientConfig.localization.locales = (
97-
await config.localization.filterAvailableLocales({
98-
locales: config.localization.locales,
99-
req,
100-
})
101-
).map(({ toString, ...rest }) => rest)
102-
clientConfig.localization.localeCodes = config.localization.locales.map(({ code }) => code)
103-
}
91+
await applyLocaleFiltering({ clientConfig, config, req })
10492

10593
return (
10694
<html

packages/next/src/views/Document/handleServerFunction.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { DocumentPreferences, VisibleEntities } from 'payload'
44
import { getClientConfig } from '@payloadcms/ui/utilities/getClientConfig'
55
import { headers as getHeaders } from 'next/headers.js'
66
import { canAccessAdmin, getAccessResults, isEntityHidden, parseCookies } from 'payload'
7+
import { applyLocaleFiltering } from 'payload/shared'
78

89
import { renderDocument } from './index.js'
910

@@ -43,6 +44,7 @@ export const renderDocumentHandler: RenderDocumentServerFunction = async (args)
4344
importMap: req.payload.importMap,
4445
user,
4546
})
47+
await applyLocaleFiltering({ clientConfig, config, req })
4648

4749
let preferences: DocumentPreferences
4850

packages/next/src/views/List/handleServerFunction.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { CollectionPreferences, ListQuery, ServerFunction, VisibleEntities
33
import { getClientConfig } from '@payloadcms/ui/utilities/getClientConfig'
44
import { headers as getHeaders } from 'next/headers.js'
55
import { canAccessAdmin, getAccessResults, isEntityHidden, parseCookies } from 'payload'
6+
import { applyLocaleFiltering } from 'payload/shared'
67

78
import { renderListView } from './index.js'
89

@@ -61,6 +62,7 @@ export const renderListHandler: ServerFunction<
6162
importMap: payload.importMap,
6263
user,
6364
})
65+
await applyLocaleFiltering({ clientConfig, config, req })
6466

6567
const preferencesKey = `collection-${collectionSlug}`
6668

packages/next/src/views/Root/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { PageConfigProvider } from '@payloadcms/ui'
1414
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
1515
import { getClientConfig } from '@payloadcms/ui/utilities/getClientConfig'
1616
import { notFound, redirect } from 'next/navigation.js'
17-
import { formatAdminURL } from 'payload/shared'
17+
import { applyLocaleFiltering, formatAdminURL } from 'payload/shared'
1818
import * as qs from 'qs-esm'
1919
import React from 'react'
2020

@@ -245,6 +245,7 @@ export const RootPage = async ({
245245
importMap,
246246
user: viewType === 'createFirstUser' ? true : req.user,
247247
})
248+
await applyLocaleFiltering({ clientConfig, config, req })
248249

249250
const visibleEntities = getVisibleEntities({ req })
250251

packages/next/src/views/Version/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,12 @@ export async function VersionView(props: DocumentViewServerProps) {
213213

214214
const clientSchemaMap = getClientSchemaMap({
215215
collectionSlug,
216-
config: getClientConfig({ config: payload.config, i18n, importMap: payload.importMap, user }),
216+
config: getClientConfig({
217+
config: payload.config,
218+
i18n,
219+
importMap: payload.importMap,
220+
user,
221+
}),
217222
globalSlug,
218223
i18n,
219224
payload,

packages/payload/src/config/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { DeepPartial } from 'ts-essentials'
44
import type { ImportMap } from '../bin/generateImportMap/index.js'
55
import type { ClientBlock } from '../fields/config/types.js'
66
import type { BlockSlug, TypedUser } from '../index.js'
7+
import type { PayloadRequest } from '../types/index.js'
78
import type {
89
RootLivePreviewConfig,
910
SanitizedConfig,

packages/payload/src/exports/shared.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export {
3939
} from '../fields/config/types.js'
4040

4141
export { getFieldPaths } from '../fields/getFieldPaths.js'
42-
export * from '../fields/validations.js'
4342

43+
export * from '../fields/validations.js'
4444
export type {
4545
FolderBreadcrumb,
4646
FolderDocumentItemKey,
@@ -52,13 +52,15 @@ export type {
5252
} from '../folders/types.js'
5353

5454
export { buildFolderWhereConstraints } from '../folders/utils/buildFolderWhereConstraints.js'
55+
5556
export { formatFolderOrDocumentItem } from '../folders/utils/formatFolderOrDocumentItem.js'
5657
export { validOperators, validOperatorSet } from '../types/constants.js'
57-
5858
export { formatFilesize } from '../uploads/formatFilesize.js'
5959

6060
export { isImage } from '../uploads/isImage.js'
61+
6162
export { appendUploadSelectFields } from '../utilities/appendUploadSelectFields.js'
63+
export { applyLocaleFiltering } from '../utilities/applyLocaleFiltering.js'
6264
export { combineWhereConstraints } from '../utilities/combineWhereConstraints.js'
6365

6466
export {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ClientConfig } from '../config/client.js'
2+
import type { SanitizedConfig } from '../config/types.js'
3+
import type { PayloadRequest } from '../types/index.js'
4+
5+
export async function applyLocaleFiltering({
6+
clientConfig,
7+
config,
8+
req,
9+
}: {
10+
clientConfig: ClientConfig
11+
config: SanitizedConfig
12+
req: PayloadRequest
13+
}): Promise<void> {
14+
if (
15+
!clientConfig.localization ||
16+
!config.localization ||
17+
typeof config.localization.filterAvailableLocales !== 'function'
18+
) {
19+
return
20+
}
21+
22+
const filteredLocales = (
23+
await config.localization.filterAvailableLocales({
24+
locales: config.localization.locales,
25+
req,
26+
})
27+
).map(({ toString, ...rest }) => rest)
28+
29+
clientConfig.localization.localeCodes = filteredLocales.map(({ code }) => code)
30+
clientConfig.localization.locales = filteredLocales
31+
}

packages/ui/src/utilities/buildTableState.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
} from 'payload'
1313

1414
import { APIError, canAccessAdmin, formatErrors, getAccessResults } from 'payload'
15-
import { isNumber } from 'payload/shared'
15+
import { applyLocaleFiltering, isNumber } from 'payload/shared'
1616

1717
import { getClientConfig } from './getClientConfig.js'
1818
import { getColumns } from './getColumns.js'
@@ -99,6 +99,7 @@ const buildTableState = async (
9999
importMap: payload.importMap,
100100
user,
101101
})
102+
await applyLocaleFiltering({ clientConfig, config, req })
102103

103104
const permissions = await getAccessResults({ req })
104105

0 commit comments

Comments
 (0)