Skip to content

Commit 337f618

Browse files
feat: optionally exclude collection documents from appearing in browse-by-folder (#12654)
Adds configurations for browse-by-folder document results. This PR **does NOT** allow for filtering out folders on a per collection basis. That will be addressed in a future PR 👍 ### Disable browse-by-folder all together ```ts type RootFoldersConfiguration = { /** * If true, the browse by folder view will be enabled * * @default true */ browseByFolder?: boolean // ...rest of type } ``` ### Remove document types from appearing in the browse by folder view ```ts type CollectionFoldersConfiguration = | boolean | { /** * If true, the collection documents will be included in the browse by folder view * * @default true */ browseByFolder?: boolean } ``` ### Misc Fixes #12631 where adding folders.collectionOverrides was being set on the client config - it should be omitted. Fixes an issue where `baseListFilters` were not being respected.
1 parent 48218bc commit 337f618

File tree

59 files changed

+1549
-367
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1549
-367
lines changed

docs/folders/overview.mdx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ On the payload config, you can configure the following settings under the `folde
2323
// Type definition
2424

2525
type RootFoldersConfiguration = {
26+
/**
27+
* If true, the browse by folder view will be enabled
28+
*
29+
* @default true
30+
*/
31+
browseByFolder?: boolean
2632
/**
2733
* An array of functions to be ran when the folder collection is initialized
2834
* This allows plugins to modify the collection configuration
@@ -82,7 +88,16 @@ To enable folders on a collection, you need to set the `admin.folders` property
8288
```ts
8389
// Type definition
8490

85-
type CollectionFoldersConfiguration = boolean
91+
type CollectionFoldersConfiguration =
92+
| boolean
93+
| {
94+
/**
95+
* If true, the collection will be included in the browse by folder view
96+
*
97+
* @default true
98+
*/
99+
browseByFolder?: boolean
100+
}
86101
```
87102
88103
```ts

packages/next/src/elements/Nav/index.client.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,11 @@ export const DefaultNavClient: React.FC<{
2323
admin: {
2424
routes: { browseByFolder: foldersRoute },
2525
},
26-
collections,
26+
folders,
2727
routes: { admin: adminRoute },
2828
},
2929
} = useConfig()
3030

31-
const [folderCollectionSlugs] = React.useState<string[]>(() => {
32-
return collections.reduce<string[]>((acc, collection) => {
33-
if (collection.folders) {
34-
acc.push(collection.slug)
35-
}
36-
return acc
37-
}, [])
38-
})
39-
4031
const { i18n } = useTranslation()
4132

4233
const folderURL = formatAdminURL({
@@ -48,7 +39,7 @@ export const DefaultNavClient: React.FC<{
4839

4940
return (
5041
<Fragment>
51-
{folderCollectionSlugs.length > 0 && <BrowseByFolderButton active={viewingRootFolderView} />}
42+
{folders && folders.browseByFolder && <BrowseByFolderButton active={viewingRootFolderView} />}
5243
{groups.map(({ entities, label }, key) => {
5344
return (
5445
<NavGroup isOpen={navPreferences?.groups?.[label]?.open} key={key} label={label}>

packages/next/src/views/BrowseByFolder/buildView.tsx

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import type {
33
BuildCollectionFolderViewResult,
44
FolderListViewServerPropsOnly,
55
ListQuery,
6+
Where,
67
} from 'payload'
78

89
import { DefaultBrowseByFolderView, FolderProvider, HydrateAuthProvider } from '@payloadcms/ui'
910
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
1011
import { formatAdminURL } from '@payloadcms/ui/shared'
1112
import { redirect } from 'next/navigation.js'
1213
import { getFolderData } from 'payload'
14+
import { buildFolderWhereConstraints } from 'payload/shared'
1315
import React from 'react'
1416

1517
import { getPreferences } from '../../utilities/getPreferences.js'
@@ -29,10 +31,10 @@ export const buildBrowseByFolderView = async (
2931
args: BuildFolderViewArgs,
3032
): Promise<BuildCollectionFolderViewResult> => {
3133
const {
34+
browseByFolderSlugs: browseByFolderSlugsFromArgs = [],
3235
disableBulkDelete,
3336
disableBulkEdit,
3437
enableRowSelections,
35-
folderCollectionSlugs,
3638
folderID,
3739
initPageResult,
3840
isInDrawer,
@@ -54,30 +56,83 @@ export const buildBrowseByFolderView = async (
5456
visibleEntities,
5557
} = initPageResult
5658

57-
const collections = folderCollectionSlugs.filter(
59+
const browseByFolderSlugs = browseByFolderSlugsFromArgs.filter(
5860
(collectionSlug) =>
5961
permissions?.collections?.[collectionSlug]?.read &&
6062
visibleEntities.collections.includes(collectionSlug),
6163
)
6264

63-
if (!collections.length) {
65+
if (config.folders === false || config.folders.browseByFolder === false) {
6466
throw new Error('not-found')
6567
}
6668

6769
const query = queryFromArgs || queryFromReq
6870
const selectedCollectionSlugs: string[] =
6971
Array.isArray(query?.relationTo) && query.relationTo.length
70-
? query.relationTo
71-
: [...folderCollectionSlugs, config.folders.slug]
72+
? query.relationTo.filter(
73+
(slug) =>
74+
browseByFolderSlugs.includes(slug) || (config.folders && slug === config.folders.slug),
75+
)
76+
: [...browseByFolderSlugs, config.folders.slug]
7277

7378
const {
7479
routes: { admin: adminRoute },
7580
} = config
7681

82+
const folderCollectionConfig = payload.collections[config.folders.slug].config
83+
84+
const browseByFolderPreferences = await getPreferences<{ viewPreference: string }>(
85+
'browse-by-folder',
86+
payload,
87+
user.id,
88+
user.collection,
89+
)
90+
91+
let documentWhere: undefined | Where = undefined
92+
let folderWhere: undefined | Where = undefined
93+
// if folderID, dont make a documentWhere since it only queries root folders
94+
for (const collectionSlug of selectedCollectionSlugs) {
95+
if (collectionSlug === config.folders.slug) {
96+
const folderCollectionConstraints = await buildFolderWhereConstraints({
97+
collectionConfig: folderCollectionConfig,
98+
folderID,
99+
localeCode: fullLocale?.code,
100+
req: initPageResult.req,
101+
search: typeof query?.search === 'string' ? query.search : undefined,
102+
})
103+
104+
if (folderCollectionConstraints) {
105+
folderWhere = folderCollectionConstraints
106+
}
107+
} else if (folderID) {
108+
if (!documentWhere) {
109+
documentWhere = {
110+
or: [],
111+
}
112+
}
113+
114+
const collectionConfig = payload.collections[collectionSlug].config
115+
if (collectionConfig.folders && collectionConfig.folders.browseByFolder === true) {
116+
const collectionConstraints = await buildFolderWhereConstraints({
117+
collectionConfig,
118+
folderID,
119+
localeCode: fullLocale?.code,
120+
req: initPageResult.req,
121+
search: typeof query?.search === 'string' ? query.search : undefined,
122+
})
123+
124+
if (collectionConstraints) {
125+
documentWhere.or.push(collectionConstraints)
126+
}
127+
}
128+
}
129+
}
130+
77131
const { breadcrumbs, documents, subfolders } = await getFolderData({
132+
documentWhere,
78133
folderID,
134+
folderWhere,
79135
req: initPageResult.req,
80-
search: query?.search as string,
81136
})
82137

83138
const resolvedFolderID = breadcrumbs[breadcrumbs.length - 1]?.id
@@ -96,13 +151,6 @@ export const buildBrowseByFolderView = async (
96151
)
97152
}
98153

99-
const browseByFolderPreferences = await getPreferences<{ viewPreference: string }>(
100-
'browse-by-folder',
101-
payload,
102-
user.id,
103-
user.collection,
104-
)
105-
106154
const serverProps: Omit<FolderListViewServerPropsOnly, 'collectionConfig' | 'listPreferences'> = {
107155
documents,
108156
i18n,
@@ -125,7 +173,7 @@ export const buildBrowseByFolderView = async (
125173

126174
// documents cannot be created without a parent folder in this view
127175
const hasCreatePermissionCollectionSlugs = folderID
128-
? [config.folders.slug, ...folderCollectionSlugs]
176+
? [config.folders.slug, ...browseByFolderSlugs]
129177
: [config.folders.slug]
130178

131179
return {
@@ -134,7 +182,8 @@ export const buildBrowseByFolderView = async (
134182
breadcrumbs={breadcrumbs}
135183
documents={documents}
136184
filteredCollectionSlugs={selectedCollectionSlugs}
137-
folderCollectionSlugs={folderCollectionSlugs}
185+
folderCollectionSlugs={browseByFolderSlugs}
186+
folderFieldName={config.folders.fieldName}
138187
folderID={folderID}
139188
subfolders={subfolders}
140189
>

packages/next/src/views/CollectionFolders/buildView.tsx

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import type {
88

99
import { DefaultCollectionFolderView, FolderProvider, HydrateAuthProvider } from '@payloadcms/ui'
1010
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
11-
import { formatAdminURL, mergeListSearchAndWhere } from '@payloadcms/ui/shared'
11+
import { formatAdminURL } from '@payloadcms/ui/shared'
1212
import { redirect } from 'next/navigation.js'
13-
import { getFolderData, parseDocumentID } from 'payload'
13+
import { getFolderData } from 'payload'
14+
import { buildFolderWhereConstraints } from 'payload/shared'
1415
import React from 'react'
1516

1617
import { getPreferences } from '../../utilities/getPreferences.js'
@@ -37,7 +38,6 @@ export const buildCollectionFolderView = async (
3738
disableBulkDelete,
3839
disableBulkEdit,
3940
enableRowSelections,
40-
folderCollectionSlugs,
4141
folderID,
4242
initPageResult,
4343
isInDrawer,
@@ -69,51 +69,58 @@ export const buildCollectionFolderView = async (
6969
if (collectionConfig) {
7070
const query = queryFromArgs || queryFromReq
7171

72-
const collectionFolderPreferences = await getPreferences<{ viewPreference: string }>(
73-
`${collectionSlug}-collection-folder`,
74-
payload,
75-
user.id,
76-
user.collection,
77-
)
72+
const collectionFolderPreferences = await getPreferences<{
73+
sort?: string
74+
viewPreference: string
75+
}>(`${collectionSlug}-collection-folder`, payload, user.id, user.collection)
76+
77+
const sortPreference = collectionFolderPreferences?.value.sort
7878

7979
const {
8080
routes: { admin: adminRoute },
8181
} = config
8282

8383
if (
8484
(!visibleEntities.collections.includes(collectionSlug) && !overrideEntityVisibility) ||
85-
!folderCollectionSlugs.includes(collectionSlug)
85+
!config.folders
8686
) {
8787
throw new Error('not-found')
8888
}
8989

90-
const whereConstraints = [
91-
mergeListSearchAndWhere({
92-
collectionConfig,
93-
search: typeof query?.search === 'string' ? query.search : undefined,
94-
where: (query?.where as Where) || undefined,
95-
}),
96-
]
97-
98-
if (folderID) {
99-
whereConstraints.push({
100-
[config.folders.fieldName]: {
101-
equals: parseDocumentID({ id: folderID, collectionSlug, payload }),
102-
},
103-
})
104-
} else {
105-
whereConstraints.push({
106-
[config.folders.fieldName]: {
107-
exists: false,
108-
},
109-
})
90+
let folderWhere: undefined | Where
91+
const folderCollectionConfig = payload.collections[config.folders.slug].config
92+
const folderCollectionConstraints = await buildFolderWhereConstraints({
93+
collectionConfig: folderCollectionConfig,
94+
folderID,
95+
localeCode: fullLocale?.code,
96+
req: initPageResult.req,
97+
search: typeof query?.search === 'string' ? query.search : undefined,
98+
sort: sortPreference,
99+
})
100+
101+
if (folderCollectionConstraints) {
102+
folderWhere = folderCollectionConstraints
103+
}
104+
105+
let documentWhere: undefined | Where
106+
const collectionConstraints = await buildFolderWhereConstraints({
107+
collectionConfig,
108+
folderID,
109+
localeCode: fullLocale?.code,
110+
req: initPageResult.req,
111+
search: typeof query?.search === 'string' ? query.search : undefined,
112+
sort: sortPreference,
113+
})
114+
if (collectionConstraints) {
115+
documentWhere = collectionConstraints
110116
}
111117

112118
const { breadcrumbs, documents, subfolders } = await getFolderData({
113119
collectionSlug,
120+
documentWhere,
114121
folderID,
122+
folderWhere,
115123
req: initPageResult.req,
116-
search: query?.search as string,
117124
})
118125

119126
const resolvedFolderID = breadcrumbs[breadcrumbs.length - 1]?.id
@@ -175,7 +182,8 @@ export const buildCollectionFolderView = async (
175182
breadcrumbs={breadcrumbs}
176183
collectionSlug={collectionSlug}
177184
documents={documents}
178-
folderCollectionSlugs={folderCollectionSlugs}
185+
folderCollectionSlugs={[collectionSlug]}
186+
folderFieldName={config.folders.fieldName}
179187
folderID={folderID}
180188
search={search}
181189
subfolders={subfolders}

0 commit comments

Comments
 (0)