Compare commits

..

154 Commits

Author SHA1 Message Date
Patrik Kozak
58f841cee4 Merge branch 'feat/folders' of github.com:payloadcms/payload into fix/findValueInPath 2025-05-22 08:04:45 -04:00
Jarrod Flesch
f0a157f939 fix failing join test 2025-05-21 22:43:29 -04:00
Jarrod Flesch
19d6b1aac8 lint fix 2025-05-21 22:25:54 -04:00
Patrik Kozak
9b382b6adf fix: prevent top-level fields from shadowing nested group fields in list view 2025-05-21 22:17:40 -04:00
Jarrod Flesch
bf5d31960b fix column docs 2025-05-21 21:47:09 -04:00
Jarrod Flesch
3ad29d251f Merge branch 'main' into feat/folders 2025-05-21 21:27:25 -04:00
Jarrod Flesch
07e9444c09 fix build 2025-05-21 14:45:29 -04:00
Jarrod Flesch
5dd13f2873 Merge branch 'feat/folders' into test/folders/e2e 2025-05-21 13:44:25 -04:00
Jarrod Flesch
878be913fd adjust test selectors 2025-05-21 13:43:28 -04:00
Jarrod Flesch
0524f198a6 require onCreateSuccess fn in ListCreateNewDocInFolderButton 2025-05-21 12:47:53 -04:00
Jarrod Flesch
5f2e846350 fixes cell not updating fromFolderID and missing onCreateSuccess fn 2025-05-21 12:47:00 -04:00
Jarrod Flesch
d3265b9931 add docs 2025-05-21 12:35:52 -04:00
Jessica Chowdhury
33261b36bf test: add e2e tests folder folder view - WIP 2025-05-21 17:28:01 +01:00
Jessica Chowdhury
73d4201df8 fix(ui): add prefix to folder drawer ids 2025-05-21 17:23:55 +01:00
Jarrod Flesch
50d3da5824 remove enabled property from folders root config 2025-05-21 11:23:51 -04:00
Jarrod Flesch
41aac41df4 refactor how to enable a folder collection 2025-05-21 10:55:26 -04:00
Jarrod Flesch
6a5b95af7c fixes folderID with postgres 2025-05-21 09:12:49 -04:00
Jarrod Flesch
2e7bfcbd63 card grid sizing pt 3 2025-05-20 15:23:56 -04:00
Jarrod Flesch
3ee9a32a38 adjust card grid sizes 2025-05-20 15:22:48 -04:00
Jarrod Flesch
c2d38b4109 adjusts card width at larger screens 2025-05-20 15:20:52 -04:00
Jarrod Flesch
5d9c537145 update int tests 2025-05-20 15:08:53 -04:00
Jarrod Flesch
904b6a6dbe fix build 2025-05-20 14:14:52 -04:00
Jarrod Flesch
cc6de7ef42 rm console log 2025-05-20 14:10:40 -04:00
Jarrod Flesch
a3ef4fbfac fix filter bug 2025-05-20 14:10:08 -04:00
Jarrod Flesch
e9ff611879 more folder file name alignment 2025-05-20 14:09:43 -04:00
Jarrod Flesch
5825d0cfc7 align folder names with feature 2025-05-20 13:38:33 -04:00
Jarrod Flesch
103b476c82 Merge branch 'main' into feat/folders 2025-05-20 13:35:23 -04:00
Jarrod Flesch
a3279b319e update route structures to respect config routes 2025-05-20 13:29:58 -04:00
Jarrod Flesch
bbb0ab784c fix auotsave enabled collections 2025-05-20 13:04:17 -04:00
Jarrod Flesch
32eac5b0c2 rename test folder folder-view to folders 2025-05-20 12:28:14 -04:00
Jarrod Flesch
86098c9140 bug fixes 2025-05-20 10:55:45 -04:00
Jarrod Flesch
7fd2cdf04c allow for folders name to be configurable 2025-05-19 21:00:44 -04:00
Jarrod Flesch
8d507996c8 Merge branch 'main' into feat/folders 2025-05-19 13:24:19 -04:00
Jarrod Flesch
aa82763cb8 fix list menu items test 2025-05-19 13:20:17 -04:00
Jarrod Flesch
e7e6a7dd97 remove multiple listItem toggle clicks in preset query tests 2025-05-19 12:16:21 -04:00
Jarrod Flesch
e14c670a51 query preset passing tests 2025-05-19 11:01:09 -04:00
Jarrod Flesch
21f5d3473c fixes versions count test selectors 2025-05-19 09:14:32 -04:00
Jarrod Flesch
36597110e9 fix missing styles in admin-root 2025-05-16 15:59:25 -04:00
Jarrod Flesch
4f2b237858 fix: column selector bug 2025-05-16 14:26:04 -04:00
Jarrod Flesch
d90afba70d fixes more tests 2025-05-16 13:37:25 -04:00
Jarrod Flesch
decd512daa fix versions test selectors 2025-05-16 13:19:04 -04:00
Jarrod Flesch
b2bf95b17b allow using wait for ssr test 2025-05-16 11:46:40 -04:00
Jarrod Flesch
10e29dd5e2 fix outdated selectors in failing tests 2025-05-16 11:30:05 -04:00
Jarrod Flesch
4c4ae1295e fix css drawer header title selector in test 2025-05-16 10:27:31 -04:00
Jarrod Flesch
adb805dadb revert renderList type change 2025-05-15 17:00:19 -04:00
Jarrod Flesch
e5f0ca3d45 revert unused generics on ServerFunctionClient 2025-05-15 16:58:02 -04:00
Jarrod Flesch
fe2b7693cc successful build 2025-05-15 16:37:05 -04:00
Jarrod Flesch
30d4a098b1 adds missing translations 2025-05-15 16:34:39 -04:00
Jarrod Flesch
aa7918fe6e chore: extract tab style into button element 2025-05-15 16:30:13 -04:00
Jarrod Flesch
f23f87243c fixes issue with add folders in drawers 2025-05-15 16:29:51 -04:00
Jarrod Flesch
41b75882a1 Merge branch 'main' into HEAD 2025-05-15 16:28:06 -04:00
Jarrod Flesch
cfd3c34aba fix lint issues 2025-05-08 09:50:07 -07:00
Jarrod Flesch
270137e92e chore: add better preferences generic default 2025-05-07 15:07:49 -07:00
Jarrod Flesch
cab817d2aa build 2025-05-05 17:03:27 -04:00
Jarrod Flesch
db622e2b79 remove old folder int tests 2025-05-05 16:50:46 -04:00
Jarrod Flesch
c52204317a lint fixes 2025-05-05 16:35:08 -04:00
Jarrod Flesch
a85af1a6d3 import fix 2025-05-05 16:02:22 -04:00
Jarrod Flesch
bccadd5101 fix bad imports 2025-05-05 15:49:26 -04:00
Jarrod Flesch
2fa723743e chore: more test fixes 2025-05-05 14:53:25 -04:00
Jarrod Flesch
2904de778d fix unpublish and locked-docs suite 2025-05-05 12:53:48 -04:00
Jarrod Flesch
d37dfb1376 fix form-state tests 2025-05-05 12:02:42 -04:00
Jarrod Flesch
b203f617af fixes some failing test suites 2025-05-05 11:12:42 -04:00
Jarrod Flesch
ee46f27881 Merge branch 'main' into feat/folders 2025-05-05 09:01:32 -04:00
Jarrod Flesch
2426784726 passing versions suite 2025-05-05 08:31:48 -04:00
Jarrod Flesch
95b78a5951 fix admin locale picker test 2025-05-02 16:55:59 -04:00
Jarrod Flesch
d1c1ad2a1d fix build 2025-05-02 15:26:34 -04:00
Jarrod Flesch
2980bdb799 Merge branch 'main' into feat/folders 2025-05-02 14:37:46 -04:00
Jarrod Flesch
fe6923d0a7 bulk edit folder selection 2025-05-02 12:33:40 -04:00
Jarrod Flesch
7eec63ae69 fix toast missing title for media docs 2025-05-02 10:00:58 -04:00
Jarrod Flesch
77a7bc5e9c fix move to drawers layering and usage 2025-05-02 09:21:44 -04:00
Jarrod Flesch
780becc88a fix global views throwing errors on render 2025-05-02 08:23:37 -04:00
Jarrod Flesch
a2d394ec82 fix breadcrumb click issue 2025-05-02 08:20:49 -04:00
Jarrod Flesch
b1c8c96e97 chore: move doc to folder not updating label 2025-05-02 08:16:52 -04:00
Jarrod Flesch
6b6b596489 feat: allow searching all docs at root on collection-folder views 2025-05-01 16:52:36 -04:00
Jarrod Flesch
a89bc1479f better list selection organization 2025-05-01 14:13:48 -04:00
Jarrod Flesch
bd9f3b5bd2 chore: adds custom cell component for managing doc folder placement in default list view 2025-05-01 14:03:44 -04:00
Jarrod Flesch
2584ff42eb unifies move-to-drawer rather than needing many drawers 2025-05-01 00:34:37 -04:00
Jarrod Flesch
cc1f5fb70c add relationTo cell on browse-by-folder table 2025-04-30 16:46:33 -04:00
Jarrod Flesch
4f7f378b84 rename _parentFolder to _folder 2025-04-30 16:20:13 -04:00
Jarrod Flesch
b279fa7bde language edits for moving folders and documents 2025-04-30 16:13:10 -04:00
Jarrod Flesch
93230a5915 Merge branch 'main' into feat/folders 2025-04-23 06:46:18 -07:00
Jarrod Flesch
9f31daf8e7 prevent views from loading if folders are not enabled 2025-04-23 06:45:12 -07:00
Jarrod Flesch
ded7164dca pill sizing, fix e2e rendering issue 2025-04-21 16:24:09 -04:00
Jarrod Flesch
cc4526844a feat: allow add to folder from move drawer 2025-04-21 15:02:35 -04:00
Jarrod Flesch
362f25f593 commit updated types 2025-04-21 14:53:14 -04:00
Jarrod Flesch
7567d2358a fix: mismatch icon and overall size from small button and small pill 2025-04-21 14:52:44 -04:00
Jarrod Flesch
b418c3cade fix: folder not opening to current selection in doc view 2025-04-21 14:52:15 -04:00
Jarrod Flesch
db0d07d9a7 fix build 2025-04-21 12:41:02 -04:00
Jarrod Flesch
b56e2faad2 fixes folder specific build errors 2025-04-21 12:02:58 -04:00
Jarrod Flesch
56982f9811 Merge branch 'main' into feat/folders 2025-04-21 11:41:43 -04:00
Jarrod Flesch
9e1258811a finishes translations, adjusts create perms 2025-04-21 11:39:09 -04:00
Jarrod Flesch
1d2accfcbb chore: client side functional sorting 2025-04-18 15:50:56 -04:00
Jarrod Flesch
4a8bea2dde chore: adjust search placeholder and table column label 2025-04-18 11:22:36 -04:00
Jarrod Flesch
0149e42276 revert button styles with new chevron icon 2025-04-18 11:05:54 -04:00
Jarrod Flesch
36d9900774 Merge branch 'main' into feat/folders 2025-04-18 10:56:46 -04:00
Jarrod Flesch
f4d624a0c5 rm stale file 2025-04-18 09:46:51 -04:00
Jarrod Flesch
57bcfcc8be cleanup 2025-04-18 09:45:43 -04:00
Jarrod Flesch
d73ddbde0c browse by folder translation 2025-04-18 08:16:00 -04:00
Jarrod Flesch
189dd64799 debug false in test config 2025-04-17 15:21:01 -04:00
Jarrod Flesch
572ce2955a about done 2025-04-17 15:20:18 -04:00
Jarrod Flesch
87ca312a54 Merge branch 'main' into feat/folders 2025-04-14 14:24:12 -04:00
Jarrod Flesch
2b2fa67031 Merge remote-tracking branch 'origin/main' into feat/folders 2025-04-14 14:16:52 -04:00
Jarrod Flesch
2f45749634 rendering of root folder view 2025-04-14 14:16:01 -04:00
Jarrod Flesch
0534dd9506 add folder route to config 2025-04-14 08:05:25 -04:00
Jarrod Flesch
aee1ea1346 rename functions/files 2025-04-11 12:57:32 -04:00
Jarrod Flesch
82ba8cf8f4 feat: completes move-to folder drawer 2025-04-11 12:42:35 -04:00
Jarrod Flesch
1dfff2b0b0 adds no results and adjusts data functions 2025-04-08 10:43:20 -04:00
Jarrod Flesch
6b695355c3 wires up create new polymorphic list view button 2025-04-07 14:14:51 -04:00
Jarrod Flesch
29c32d3141 fix bad imports 2025-04-07 09:05:50 -04:00
Jarrod Flesch
f6afbed5d2 buildable 2025-04-07 08:56:56 -04:00
Jarrod Flesch
5dcf96ca10 chore: adjust translations function 2025-04-07 08:56:21 -04:00
Jarrod Flesch
91c22bd88c Merge branch 'main' into feat/folders 2025-04-04 16:22:57 -04:00
Jarrod Flesch
0d8b5677d9 feat: working collection-folder list view 2025-04-04 15:43:12 -04:00
Jarrod Flesch
e55c89c4b7 refactor: consolidates buildTableColumnState 2025-03-25 14:25:10 -04:00
Jarrod Flesch
f762d683c6 chore: refactoring, routing structure 2025-03-25 14:09:32 -04:00
Jarrod Flesch
953c538af0 Merge branch 'main' into feat/folders 2025-03-24 10:05:53 -04:00
Jarrod Flesch
65d0272950 refactor(folders): folder configuration 2025-03-13 10:05:30 -04:00
Jarrod Flesch
c570d6178c Merge branch 'main' into feat/folders 2025-02-27 15:30:14 -05:00
Jarrod Flesch
c935420937 lockfile 2025-02-26 16:00:10 -05:00
Jarrod Flesch
72dd527a15 Merge branch 'main' into feat/folders 2025-02-26 15:58:03 -05:00
Jarrod Flesch
1d6e0941e7 feat: search 2025-02-26 15:26:55 -05:00
Jarrod Flesch
91f7deb278 try/catch moveTo, fix button hover colors 2025-02-21 16:23:37 -05:00
Jarrod Flesch
d3986cfaf0 folder popup fix 2025-02-21 15:25:51 -05:00
Jarrod Flesch
4a9de40098 feat: wire up delete interations with confirm modal 2025-02-21 14:00:35 -05:00
Jarrod Flesch
f4679a4088 chore: adds folder delete confirmation translation 2025-02-21 13:59:55 -05:00
Jarrod Flesch
cb8ee7d2b0 fixes for moveTo drawer doc handling 2025-02-21 12:33:25 -05:00
Jarrod Flesch
06ef8da836 adds confirm translation for move drawer 2025-02-21 12:31:19 -05:00
Jarrod Flesch
91dc98978d chore: disabled instead of hide folders inside move drawer 2025-02-20 17:15:04 -05:00
Jarrod Flesch
07ff1ee7be misc fixes 2025-02-20 16:34:41 -05:00
Jarrod Flesch
72d393d24c more translations 2025-02-20 16:32:26 -05:00
Jarrod Flesch
95d4324af3 remove old files 2025-02-20 16:08:00 -05:00
Jarrod Flesch
a617d3166c use translations 2025-02-20 16:07:41 -05:00
Jarrod Flesch
6770e7a1b3 adds translation strings 2025-02-20 16:03:11 -05:00
Jarrod Flesch
f5e535dacf confirm modal for move 2025-02-20 15:21:43 -05:00
Jarrod Flesch
3d9c25e278 Merge branch 'main' into feat/folders 2025-02-20 13:37:13 -05:00
Jarrod Flesch
3bdb127ad4 start of modal tidy up 2025-02-20 13:31:46 -05:00
Jarrod Flesch
de4be1eb78 ui/ux fixes 2025-02-20 12:49:30 -05:00
Jarrod Flesch
d26529282f feat: move docs to folders within edit view 2025-02-19 16:52:33 -05:00
Jarrod Flesch
2694603353 feat: wire in pref for view type 2025-02-19 14:50:35 -05:00
Jarrod Flesch
9630de1bac feat: droppable crumbs, image cards, reusable item grid/table display 2025-02-19 13:59:38 -05:00
Jarrod Flesch
91e867ecb1 chore: regenerates lockfile 2025-02-14 14:54:54 -05:00
Jarrod Flesch
25e196bdd7 chore: successful build 2025-02-14 14:05:35 -05:00
Jarrod Flesch
b631eebdb5 Merge branch 'main' into feat/folders 2025-02-14 12:48:21 -05:00
Jarrod Flesch
a66f134cf6 feat: adds base folder functionality, list and grid views 2025-02-14 12:22:52 -05:00
Jarrod Flesch
b3aec9a23f chore: updates css imports 2025-01-16 15:10:49 -05:00
Jarrod Flesch
31911d87c1 chore: more merge resolutions 2025-01-14 16:05:19 -05:00
Jarrod Flesch
2d37ac41a2 merges main 2025-01-14 15:57:23 -05:00
Jarrod Flesch
b68af8ba31 Merge branch 'main' into feat/folders 2025-01-14 15:48:39 -05:00
Jarrod Flesch
4c775d1ced Merge branch 'main' into feat/folders 2025-01-02 12:25:43 -05:00
Jarrod Flesch
4706019a22 feat: brings back subfolder viewing, folder assigning etc 2024-12-18 14:40:26 -05:00
Jarrod Flesch
9c33a48192 Merge branch 'main' into feat/folders 2024-12-17 14:17:40 -05:00
Jarrod Flesch
8ec6784645 Merge branch 'main' into feat/beta/folders 2024-12-13 12:58:07 -05:00
Jarrod Flesch
8d9bf835a0 Merge branch 'main' into feat/beta/folders 2024-11-19 13:54:57 -05:00
Jarrod Flesch
2eef7ee388 moves base folder work into beta 2024-10-01 09:50:50 -04:00
287 changed files with 3241 additions and 5347 deletions

View File

@@ -43,7 +43,6 @@ body:
- 'plugin: cloud'
- 'plugin: cloud-storage'
- 'plugin: form-builder'
- 'plugin: multi-tenant'
- 'plugin: nested-docs'
- 'plugin: richtext-lexical'
- 'plugin: richtext-slate'
@@ -60,7 +59,10 @@ body:
label: Environment Info
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions. Please avoid using "latest"—specific version numbers help us accurately diagnose and resolve issues.
render: text
placeholder: Run `pnpm payload info` in your terminal and paste the output here.
placeholder: |
Payload:
Node.js:
Next.js:
validations:
required: true

View File

@@ -39,7 +39,7 @@ const GlobalWithAccessControl: GlobalConfig = {
update: ({ req: { user } }) => {...},
// Version-enabled Globals only
readVersions: () => {...},
readVersion: () => {...},
},
// highlight-end
}
@@ -64,7 +64,7 @@ If a Global supports [Versions](../versions/overview), the following additional
Returns a boolean result or optionally a [query constraint](../queries/overview) which limits who can read this global based on its current properties.
To add read Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../configuration/globals):
To add read Access Control to a [Global](../configuration/globals), use the `read` property in the [Global Config](../configuration/globals):
```ts
import { GlobalConfig } from 'payload'
@@ -72,7 +72,7 @@ import { GlobalConfig } from 'payload'
const Header: GlobalConfig = {
// ...
// highlight-start
access: {
read: {
read: ({ req: { user } }) => {
return Boolean(user)
},

View File

@@ -65,7 +65,7 @@ preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlig
The Preview feature can be used to achieve "Draft Preview". After clicking the preview button from the Admin Panel, you can enter into "draft mode" within your front-end application. This will allow you to adjust your page queries to include the `draft: true` param. When this param is present on the request, Payload will send back a draft document as opposed to a published one based on the document's `_status` field.
To enter draft mode, the URL provided to the `preview` function can point to a custom endpoint in your front-end application that sets a cookie or session variable to indicate that draft mode is enabled. This is framework specific, so the mechanisms here vary from framework to framework although the underlying concept is the same.
To enter draft mode, the URL provided to the `preview` function can point to a custom endpoint in your front-end application that sets a cookie or session variable to indicate that draft mode is enabled. This is framework specific, so the mechanisms here very from framework to framework although the underlying concept is the same.
### Next.js

View File

@@ -605,7 +605,7 @@ return (
textField: {
initialValue: 'Updated text',
valid: true,
value: 'Updated text',
value: 'Upddated text',
},
},
// blockType: "yourBlockSlug",
@@ -875,7 +875,7 @@ Useful to retrieve info about the currently logged in user as well as methods fo
| **`refreshCookie`** | A method to trigger the silent refreshing of a user's auth token |
| **`setToken`** | Set the token of the user, to be decoded and used to reset the user and token in memory |
| **`token`** | The logged in user's token (useful for creating preview links, etc.) |
| **`refreshPermissions`** | Load new permissions (useful when content that affects permissions has been changed) |
| **`refreshPermissions`** | Load new permissions (useful when content that effects permissions has been changed) |
| **`permissions`** | The permissions of the current user |
```tsx
@@ -1143,7 +1143,7 @@ This is useful for scenarios where you need to trigger another fetch regardless
Route transitions are useful in showing immediate visual feedback to the user when navigating between pages. This is especially useful on slow networks when navigating to data heavy or process intensive pages.
By default, any instances of `Link` from `@payloadcms/ui` will trigger route transitions by default.
By default, any instances of `Link` from `@payloadcms/ui` will trigger route transitions dy default.
```tsx
import { Link } from '@payloadcms/ui'

View File

@@ -62,7 +62,7 @@ In this scenario, if your cookie was still valid, malicious-intent.com would be
### CSRF Prevention
Define domains that you trust and are willing to accept Payload HTTP-only cookie based requests from. Use the `csrf` option on the base Payload Config to do this:
Define domains that your trust and are willing to accept Payload HTTP-only cookie based requests from. Use the `csrf` option on the base Payload Config to do this:
```ts
// payload.config.ts
@@ -102,8 +102,8 @@ If option 1 isn't possible, then you can get around this limitation by [configur
```
SameSite: None // allows the cookie to cross domains
Secure: true // ensures it's sent over HTTPS only
HttpOnly: true // ensures it's not accessible via client side JavaScript
Secure: true // ensures its sent over HTTPS only
HttpOnly: true // ensures its not accessible via client side JavaScript
```
Configuration example:

View File

@@ -25,12 +25,11 @@ A strategy is made up of the following:
The `authenticate` function is passed the following arguments:
| Argument | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **`canSetHeaders`** \* | Whether or not the strategy is being executed from a context where response headers can be set. Default is `false`. |
| **`headers`** \* | The headers on the incoming request. Useful for retrieving identifiable information on a request. |
| **`payload`** \* | The Payload class. Useful for authenticating the identifiable information against Payload. |
| **`isGraphQL`** | Whether or not the strategy is being executed within the GraphQL endpoint. Default is `false`. |
| Argument | Description |
| ---------------- | ------------------------------------------------------------------------------------------------- |
| **`headers`** \* | The headers on the incoming request. Useful for retrieving identifiable information on a request. |
| **`payload`** \* | The Payload class. Useful for authenticating the identifiable information against Payload. |
| **`isGraphQL`** | Whether or not the request was made from a GraphQL endpoint. Default is `false`. |
### Example Strategy

View File

@@ -71,7 +71,7 @@ export const Customers: CollectionConfig = {
#### generateEmailSubject
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function arguments are the same but you can only return a string - not HTML.
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import type { CollectionConfig } from 'payload'
@@ -178,7 +178,7 @@ The following arguments are passed to the `generateEmailHTML` function:
#### generateEmailSubject
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function arguments are the same but you can only return a string - not HTML.
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import type { CollectionConfig } from 'payload'

View File

@@ -38,7 +38,7 @@ const request = await fetch('http://localhost:3000', {
### Omitting The Token
In some cases you may want to prevent the token from being returned from the auth operations. You can do that by setting `removeTokenFromResponses` to `true` like so:
In some cases you may want to prevent the token from being returned from the auth operations. You can do that by setting `removeTokenFromResponse` to `true` like so:
```ts
import type { CollectionConfig } from 'payload'
@@ -46,7 +46,7 @@ import type { CollectionConfig } from 'payload'
export const UsersWithoutJWTs: CollectionConfig = {
slug: 'users-without-jwts',
auth: {
removeTokenFromResponses: true, // highlight-line
removeTokenFromResponse: true, // highlight-line
},
}
```

View File

@@ -67,7 +67,7 @@ query {
}
```
Document access can also be queried on a collection/global basis. Access on a global can be queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
Document access can also be queried on a collection/global basis. Access on a global can queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
## Me

View File

@@ -71,7 +71,7 @@ export const Admins: CollectionConfig = {
</Banner>
<Banner type="warning">
**Note:** Auth-enabled Collections will be automatically injected with the
**Note:** Auth-enabled Collections with be automatically injected with the
`hash`, `salt`, and `email` fields. [More
details](../fields/overview#field-names).
</Banner>
@@ -142,17 +142,14 @@ import { buildConfig } from 'payload'
export default buildConfig({
// ...
// highlight-start
admin: {
autoLogin:
process.env.NODE_ENV === 'development'
? {
email: 'test@example.com',
password: 'test',
prefillOnly: true,
}
: false,
}
autoLogin:
process.env.NEXT_PUBLIC_ENABLE_AUTOLOGIN === 'true'
? {
email: 'test@example.com',
password: 'test',
prefillOnly: true,
}
: false,
// highlight-end
})
```

View File

@@ -8,7 +8,7 @@ keywords: authentication, config, configuration, documentation, Content Manageme
During the lifecycle of a request you will be able to access the data you have configured to be stored in the JWT by accessing `req.user`. The user object is automatically appended to the request for you.
### Defining Token Data
### Definining Token Data
You can specify what data gets encoded to the Cookie/JWT-Token by setting `saveToJWT` property on fields within your auth collection.

View File

@@ -177,7 +177,7 @@ The following options are available:
#### Edit View Options
```ts
import type { CollectionConfig } from 'payload'
import type { CollectionCOnfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
@@ -275,7 +275,7 @@ You can also pass an object to the collection's `graphQL` property, which allows
## TypeScript
You can import types from Payload to help make writing your Collection configs easier and type-safe. There are two main types that represent the Collection Config, `CollectionConfig` and `SanitizedCollectionConfig`.
You can import types from Payload to help make writing your Collection configs easier and type-safe. There are two main types that represent the Collection Config, `CollectionConfig` and `SanitizeCollectionConfig`.
The `CollectionConfig` type represents a raw Collection Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedCollectionConfig` type represents a Collection Config after it has been fully sanitized. Generally, this is only used internally by Payload.

View File

@@ -205,7 +205,7 @@ You can also pass an object to the global's `graphQL` property, which allows you
## TypeScript
You can import types from Payload to help make writing your Global configs easier and type-safe. There are two main types that represent the Global Config, `GlobalConfig` and `SanitizedGlobalConfig`.
You can import types from Payload to help make writing your Global configs easier and type-safe. There are two main types that represent the Global Config, `GlobalConfig` and `SanitizeGlobalConfig`.
The `GlobalConfig` type represents a raw Global Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedGlobalConfig` type represents a Global Config after it has been fully sanitized. Generally, this is only used internally by Payload.

View File

@@ -213,7 +213,7 @@ For more information about what we track, take a look at our [privacy policy](/p
## Cross-origin resource sharing (CORS)#cors
Cross-origin resource sharing (CORS) can be configured with either a whitelist array of URLS to allow CORS requests from, a wildcard string (`*`) to accept incoming requests from any domain, or an object with the following properties:
Cross-origin resource sharing (CORS) can be configured with either a whitelist array of URLS to allow CORS requests from, a wildcard string (`*`) to accept incoming requests from any domain, or a object with the following properties:
| Option | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
@@ -292,7 +292,7 @@ export const script = async (config: SanitizedConfig) => {
collection: 'pages',
data: { title: 'my title' },
})
payload.logger.info('Successfully seeded!')
payload.logger.info('Succesffully seeded!')
process.exit(0)
}
```

View File

@@ -59,7 +59,7 @@ _For details on how to build Custom Views, including all available props, see [B
### Document Root
The Document Root is mounted on the top-level route for a Document. Setting this property will completely take over the entire Document View layout, including the title, [Document Tabs](#document-tabs), _and all other nested Document Views_ including the [Edit View](./edit-view), API View, etc.
The Document Root is mounted on the top-level route for a Document. Setting this property will completely take over the entire Document View layout, including the title, [Document Tabs](#ocument-tabs), _and all other nested Document Views_ including the [Edit View](./edit-view), API View, etc.
When setting a Document Root, you are responsible for rendering all necessary components and controls, as no document controls or tabs would be rendered. To replace only the Edit View precisely, use the `edit.default` key instead.

View File

@@ -40,7 +40,7 @@ The following options are available:
| `beforeDashboard` | An array of Custom Components to inject into the built-in Dashboard, _before_ the default dashboard contents. [More details](#beforedashboard). |
| `beforeLogin` | An array of Custom Components to inject into the built-in Login, _before_ the default login form. [More details](#beforelogin). |
| `beforeNavLinks` | An array of Custom Components to inject into the built-in Nav, _before_ the links themselves. [More details](#beforenavlinks). |
| `graphics.Icon` | The simplified logo used in contexts like the `Nav` component. [More details](#graphicsicon). |
| `graphics.Icon` | The simplified logo used in contexts like the the `Nav` component. [More details](#graphicsicon). |
| `graphics.Logo` | The full logo used in contexts like the `Login` view. [More details](#graphicslogo). |
| `header` | An array of Custom Components to be injected above the Payload header. [More details](#header). |
| `logout.Button` | The button displayed in the sidebar that logs the user out. [More details](#logoutbutton). |
@@ -345,7 +345,7 @@ export default function MyCustomIcon() {
The `Logo` property is the full logo used in contexts like the `Login` view. This is typically a larger, more detailed representation of your brand.
To add a custom logo, use the `admin.components.graphics.Logo` property in your Payload Config:
To add a custom logo, use the `admin.components.graphic.Logo` property in your Payload Config:
```ts
import { buildConfig } from 'payload'

View File

@@ -183,13 +183,13 @@ Depending on which Database Adapter you use, your migration workflow might diffe
In relational databases, migrations will be **required** for non-development database environments. But with MongoDB, you might only need to run migrations once in a while (or never even need them).
#### MongoDB#mongodb-migrations
#### MongoDB
In MongoDB, you'll only ever really need to run migrations for times where you change your database shape, and you have lots of existing data that you'd like to transform from Shape A to Shape B.
In this case, you can create a migration by running `pnpm payload migrate:create`, and then write the logic that you need to perform to migrate your documents to their new shape. You can then either run your migrations in CI before you build / deploy, or you can run them locally, against your production database, by using your production database connection string on your local computer and running the `pnpm payload migrate` command.
#### Postgres#postgres-migrations
#### Postgres
In relational databases like Postgres, migrations are a bit more important, because each time you add a new field or a new collection, you'll need to update the shape of your database to match your Payload Config (otherwise you'll see errors upon trying to read / write your data).

View File

@@ -39,7 +39,7 @@ export default buildConfig({
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
export default buildConfig({
// Automatically uses process.env.POSTGRES_URL if no options are provided.
// Automatically uses proces.env.POSTGRES_URL if no options are provided.
db: vercelPostgresAdapter(),
// Optionally, can accept the same options as the @vercel/postgres package.
db: vercelPostgresAdapter({
@@ -224,7 +224,7 @@ Make sure Payload doesn't overlap table names with its collections. For example,
### afterSchemaInit
Runs after the Drizzle schema is built. You can use this hook to modify the schema with features that aren't supported by Payload, or if you want to add a column that you don't want to be in the Payload config.
To extend a table, Payload exposes `extendTable` utility to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).
To extend a table, Payload exposes `extendTable` utillity to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).
The following example adds the `extra_integer_column` column and a composite index on `country` and `city` columns.
```ts

View File

@@ -189,7 +189,7 @@ Make sure Payload doesn't overlap table names with its collections. For example,
### afterSchemaInit
Runs after the Drizzle schema is built. You can use this hook to modify the schema with features that aren't supported by Payload, or if you want to add a column that you don't want to be in the Payload config.
To extend a table, Payload exposes `extendTable` utility to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).
To extend a table, Payload exposes `extendTable` utillity to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).
The following example adds the `extra_integer_column` column and a composite index on `country` and `city` columns.
```ts

View File

@@ -80,7 +80,7 @@ export const MyArrayField: Field = {
}
```
The Array Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Array Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------------- | ----------------------------------------------------------------------------------- |

View File

@@ -78,7 +78,7 @@ export const MyBlocksField: Field = {
}
```
The Blocks Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Blocks Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------- |

View File

@@ -68,7 +68,7 @@ export const MyCodeField: Field = {
}
```
The Code Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Code Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -58,7 +58,7 @@ export const MyCollapsibleField: Field = {
}
```
The Collapsible Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Collapsible Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------- |

View File

@@ -65,7 +65,7 @@ export const MyDateField: Field = {
}
```
The Date Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Date Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -65,7 +65,7 @@ export const MyEmailField: Field = {
}
```
The Email Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Email Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------ | ------------------------------------------------------------------------- |

View File

@@ -69,7 +69,7 @@ export const MyGroupField: Field = {
}
```
The Group Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Group Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

View File

@@ -67,7 +67,7 @@ export const MyJSONField: Field = {
}
```
The JSON Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The JSON Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -70,7 +70,7 @@ export const MyNumberField: Field = {
}
```
The Number Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Number Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------ | --------------------------------------------------------------------------------- |

View File

@@ -36,7 +36,7 @@ export const MyRadioField: Field = {
| Option | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing a `label` string and a `value` string. |
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing an `label` string and a `value` string. |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
@@ -82,7 +82,7 @@ export const MyRadioField: Field = {
}
```
The Radio Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Radio Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -86,7 +86,7 @@ export const MyRelationshipField: Field = {
}
```
The Relationship Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Relationship Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -54,7 +54,6 @@ export const MySelectField: Field = {
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
| **`filterOptions`** | Dynamically filter which options are available based on the user, data, etc. [More details](#filterOptions) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
@@ -68,61 +67,6 @@ _\* An asterisk denotes that a property is required._
used as a GraphQL enum.
</Banner>
### filterOptions
Used to dynamically filter which options are available based on the current user, document data, or other criteria.
Some examples of this might include:
- Restricting options based on a user's role, e.g. admin-only options
- Displaying different options based on the value of another field, e.g. a city/state selector
The result of `filterOptions` will determine:
- Which options are displayed in the Admin Panel
- Which options can be saved to the database
To do this, use the `filterOptions` property in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
export const MySelectField: Field = {
// ...
// highlight-start
type: 'select',
options: [
{
label: 'One',
value: 'one',
},
{
label: 'Two',
value: 'two',
},
{
label: 'Three',
value: 'three',
},
],
filterOptions: ({ options, data }) =>
data.disallowOption1
? options.filter(
(option) =>
(typeof option === 'string' ? options : option.value) !== 'one',
)
: options,
// highlight-end
}
```
<Banner type="warning">
**Note:** This property is similar to `filterOptions` in
[Relationship](./relationship) or [Upload](./upload) fields, except that the
return value of this function is simply an array of options, not a query
constraint.
</Banner>
## Admin Options
To customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
@@ -139,7 +83,7 @@ export const MySelectField: Field = {
}
```
The Select Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Select Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -70,7 +70,7 @@ export const MyTextField: Field = {
}
```
The Text Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Text Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -67,7 +67,7 @@ export const MyTextareaField: Field = {
}
```
The Textarea Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
The Textarea Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -1,6 +1,6 @@
---
title: Folders
label: Overview
label: Folders
order: 10
desc: Folders allow you to group documents across collections, and are a great way to organize your content.
keywords: folders, folder, content organization
@@ -10,11 +10,6 @@ Folders allow you to group documents across collections, and are a great way to
The configuration for folders is done in two places, the collection config and the Payload config. The collection config is where you enable folders, and the Payload config is where you configure the global folder settings.
<Banner type="warning">
**Note:** The Folders feature is currently in beta and may be subject to
change in minor versions updates prior to being stable.
</Banner>
## Folder Configuration
On the payload config, you can configure the following settings under the `folders` property:
@@ -95,7 +90,9 @@ const config = buildConfig({
{
slug: 'pages',
// highlight-start
folders: true, // defaults to false
admin: {
folders: true, // defaults to false
},
// highlight-end
},
],

View File

@@ -23,7 +23,7 @@ Let's see examples on how context can be used in the first two scenarios mention
### Passing Data Between Hooks
To pass data between hooks, you can assign values to context in an earlier hook in the lifecycle of a request and expect it in the context of a later hook.
To pass data between hooks, you can assign values to context in an earlier hook in the lifecycle of a request and expect it the context in a later hook.
For example:

View File

@@ -33,7 +33,7 @@ Simply add a task to the `jobs.tasks` array in your Payload config. A task consi
| `onSuccess` | Function to be executed if the task succeeds. |
| `retries` | Specify the number of times that this step should be retried if it fails. If this is undefined, the task will either inherit the retries from the workflow or have no retries. If this is 0, the task will not be retried. By default, this is undefined. |
The logic for the Task is defined in the `handler` - which can be defined as a function, or a path to a function. The `handler` will run once a worker picks up a Job that includes this task.
The logic for the Task is defined in the `handler` - which can be defined as a function, or a path to a function. The `handler` will run once a worker picks picks up a Job that includes this task.
It should return an object with an `output` key, which should contain the output of the task as you've defined.
@@ -213,7 +213,7 @@ export default buildConfig({
## Nested tasks
You can run sub-tasks within an existing task, by using the `tasks` or `inlineTask` arguments passed to the task `handler` function:
You can run sub-tasks within an existing task, by using the `tasks` or `ìnlineTask` arguments passed to the task `handler` function:
```ts
export default buildConfig({

View File

@@ -260,7 +260,7 @@ If you are using relationships or uploads in your front-end application, and you
{
// ...
// If your site is running on a different domain than your Payload server,
// This will allow requests to be made between the two domains
// This will allows requests to be made between the two domains
cors: [
'http://localhost:3001' // Your front-end application
],

View File

@@ -329,7 +329,7 @@ available:
// responseHeaders: { ... } // returned headers from the response
// }
const result = await payload.auth({ headers, canSetHeaders: false })
const result = await payload.auth({ headers })
```
### Login

View File

@@ -16,8 +16,8 @@ This plugin sets up multi-tenancy for your application from within your [Admin P
If you need help, check out our [Community
Help](https://payloadcms.com/community-help). If you think you've found a bug,
please [open a new
issue](https://github.com/payloadcms/payload/issues/new/choose) with as much
detail as possible.
issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%multi-tenant&template=bug_report.md&title=plugin-multi-tenant%3A)
with as much detail as possible.
</Banner>
## Core features
@@ -35,7 +35,7 @@ This plugin sets up multi-tenancy for your application from within your [Admin P
By default this plugin cleans up documents when a tenant is deleted. You should ensure you have
strong access control on your tenants collection to prevent deletions by unauthorized users.
You can disable this behavior by setting `cleanupAfterTenantDelete` to `false` in the plugin options.
You can disabled this behavior by setting `cleanupAfterTenantDelete` to `false` in the plugin options.
</Banner>

View File

@@ -46,12 +46,11 @@ const config = buildConfig({
The following options are available for Query Presets:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `access` | Used to define custom collection-level access control that applies to all presets. [More details](#access-control). |
| `filterConstraints` | Used to define which constraints are available to users when managing presets. [More details](#constraint-access-control). |
| `constraints` | Used to define custom document-level access control that apply to individual presets. [More details](#document-access-control). |
| `labels` | Custom labels to use for the Query Presets collection. |
| Option | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `access` | Used to define custom collection-level access control that applies to all presets. [More details](#access-control). |
| `constraints` | Used to define custom document-level access control that apply to individual presets. [More details](#document-access-control). |
| `labels` | Custom labels to use for the Query Presets collection. |
## Access Control
@@ -60,7 +59,7 @@ Query Presets are subject to the same [Access Control](../access-control/overvie
Access Control for Query Presets can be customized in two ways:
1. [Collection Access Control](#collection-access-control): Applies to all presets. These rules are not controllable by the user and are statically defined in the config.
2. [Document Access Control](#document-access-control): Applies to each individual preset. These rules are controllable by the user and are dynamically defined on each record in the database.
2. [Document Access Control](#document-access-control): Applies to each individual preset. These rules are controllable by the user and are saved to the document.
### Collection Access Control
@@ -98,7 +97,7 @@ This example restricts all Query Presets to users with the role of `admin`.
### Document Access Control
You can also define access control rules that apply to each specific preset. Users have the ability to define and modify these rules on the fly as they manage presets. These are saved dynamically in the database on each record.
You can also define access control rules that apply to each specific preset. Users have the ability to define and modify these rules on the fly as they manage presets. These are saved dynamically in the database on each document.
When a user manages a preset, document-level access control options will be available to them in the Admin Panel for each operation.
@@ -151,8 +150,8 @@ const config = buildConfig({
}),
},
],
// highlight-end
},
// highlight-end
},
})
```
@@ -172,39 +171,3 @@ The following options are available for each constraint:
| `value` | The value to store in the database when this constraint is selected. |
| `fields` | An array of fields to render when this constraint is selected. |
| `access` | A function that determines the access control rules for this constraint. |
### Constraint Access Control
Used to dynamically filter which constraints are available based on the current user, document data, or other criteria.
Some examples of this might include:
- Ensuring that only "admins" are allowed to make a preset available to "everyone"
- Preventing the "onlyMe" option from being selected based on a hypothetical "disablePrivatePresets" checkbox
When a user lacks the permission to set a constraint, the option will either be hidden from them, or disabled if it is already saved to that preset.
To do this, you can use the `filterConstraints` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
queryPresets: {
// ...
// highlight-start
filterConstraints: ({ req, options }) =>
!req.user?.roles?.includes('admin')
? options.filter(
(option) =>
(typeof option === 'string' ? option : option.value) !==
'everyone',
)
: options,
// highlight-end
},
})
```
The `filterConstraints` function receives the same arguments as [`filterOptions`](../fields/select#filterOptions) in the [Select field](../fields/select).

View File

@@ -738,7 +738,7 @@ Payload supports a method override feature that allows you to send GET requests
### How to Use
To use this feature, include the `X-Payload-HTTP-Method-Override` header set to `GET` in your POST request. The parameters should be sent in the body of the request with the `Content-Type` set to `application/x-www-form-urlencoded`.
To use this feature, include the `X-HTTP-Method-Override` header set to `GET` in your POST request. The parameters should be sent in the body of the request with the `Content-Type` set to `application/x-www-form-urlencoded`.
### Example
@@ -753,7 +753,7 @@ const res = await fetch(`${api}/${collectionSlug}`, {
headers: {
'Accept-Language': i18n.language,
'Content-Type': 'application/x-www-form-urlencoded',
'X-Payload-HTTP-Method-Override': 'GET',
'X-HTTP-Method-Override': 'GET',
},
body: qs.stringify({
depth: 1,

View File

@@ -135,7 +135,7 @@ export const MyComponent: React.FC<{
### Overriding Converters
You can override any of the default JSX converters by passing your custom converter, keyed to the node type, to the `converters` prop / the converters function.
You can override any of the default JSX converters by passing passing your custom converter, keyed to the node type, to the `converters` prop / the converters function.
Example - overriding the upload node converter to use next/image:

View File

@@ -3,7 +3,7 @@ title: Autosave
label: Autosave
order: 30
desc: Using Payload's Draft functionality, you can configure your collections and globals to autosave changes as drafts, and publish only you're ready.
keywords: version history, revisions, audit log, draft, publish, autosave, Content Management System, cms, headless, javascript, node, react, nextjs
keywords: version history, revisions, audit log, draft, publish, autosave, Content Management System, cms, headless, javascript, node, react, nextjss
---
Extending on Payload's [Draft](/docs/versions/drafts) functionality, you can configure your collections and globals to autosave changes as drafts, and publish only you're ready. The Admin UI will automatically adapt to autosaving progress at an interval that you define, and will store all autosaved changes as a new Draft version. Never lose your work - and publish changes to the live document only when you're ready.

View File

@@ -1,7 +1,7 @@
import type { CollectionConfig } from 'payload/types'
import { admins } from './access/admins'
import { adminsAndUser } from './access/adminsAndUser'
import adminsAndUser from './access/adminsAndUser'
import { anyone } from './access/anyone'
import { checkRole } from './access/checkRole'
import { loginAfterCreate } from './hooks/loginAfterCreate'
@@ -25,7 +25,6 @@ export const Users: CollectionConfig = {
create: anyone,
update: adminsAndUser,
delete: admins,
unlock: admins,
admin: ({ req: { user } }) => checkRole(['admin'], user),
},
hooks: {

View File

@@ -1,4 +1,4 @@
import type { Access } from 'payload'
import type { Access } from 'payload/config'
import { checkRole } from './checkRole'

View File

@@ -1,17 +1,19 @@
import type { Access } from 'payload'
import type { Access } from 'payload/config'
import { checkRole } from './checkRole'
export const adminsAndUser: Access = ({ req: { user } }) => {
const adminsAndUser: Access = ({ req: { user } }) => {
if (user) {
if (checkRole(['admin'], user)) {
return true
}
return {
id: { equals: user.id },
id: user.id,
}
}
return false
}
export default adminsAndUser

View File

@@ -1,3 +1,3 @@
import type { Access } from 'payload'
import type { Access } from 'payload/config'
export const anyone: Access = () => true

View File

@@ -1,6 +1,6 @@
import type { User } from '../../payload-types'
export const checkRole = (allRoles: User['roles'] = [], user: User | null = null): boolean => {
export const checkRole = (allRoles: User['roles'] = [], user: User = undefined): boolean => {
if (user) {
if (
allRoles.some((role) => {
@@ -8,9 +8,8 @@ export const checkRole = (allRoles: User['roles'] = [], user: User | null = null
return individualRole === role
})
})
) {
return true
}
)
{return true}
}
return false

View File

@@ -1,4 +1,4 @@
import type { FieldHook } from 'payload'
import type { FieldHook } from 'payload/types'
import type { User } from '../../payload-types'

View File

@@ -1,6 +1,7 @@
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import express from 'express'
import { buildConfig } from 'payload'
import { fileURLToPath } from 'url'

View File

@@ -1,4 +1,5 @@
import express from 'express'
import type { Request, Response } from 'express'
import { parse } from 'url'
import next from 'next'

View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

View File

@@ -3,7 +3,6 @@
"version": "1.0.0",
"description": "Payload Live Preview example.",
"license": "MIT",
"type": "module",
"main": "dist/server.js",
"scripts": {
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,8 @@
import type { MigrateUpArgs } from '@payloadcms/db-mongodb'
import type { Page } from '../payload-types'
import { DefaultDocumentIDType } from 'payload'
export const home = (id: DefaultDocumentIDType): Partial<Page> => ({
export const home: Partial<Page> = {
slug: 'home',
richText: [
{
@@ -42,26 +41,11 @@ export const home = (id: DefaultDocumentIDType): Partial<Page> => ({
{
text: ' you can edit this page in the admin panel and see the changes reflected here in real time.',
},
...(id
? [
{
text: ' To get started, visit ',
},
{
type: 'link',
children: [{ text: 'this page' }],
linkType: 'custom',
newTab: true,
url: `/admin/collections/pages/${id}/preview`,
},
{ text: '.' },
]
: []),
],
},
],
title: 'Home',
})
}
export const examplePage: Partial<Page> = {
slug: 'example-page',
@@ -99,18 +83,11 @@ export async function up({ payload }: MigrateUpArgs): Promise<void> {
data: examplePage as any, // eslint-disable-line
})
const { id: ogHomePageID } = await payload.create({
collection: 'pages',
data: {
title: 'Home',
richText: [],
},
})
const homepageJSON = JSON.parse(JSON.stringify(home))
const { id: homePageID } = await payload.update({
id: ogHomePageID,
const { id: homePageID } = await payload.create({
collection: 'pages',
data: home(ogHomePageID),
data: homepageJSON,
})
await payload.updateGlobal({
@@ -144,7 +121,7 @@ export async function up({ payload }: MigrateUpArgs): Promise<void> {
type: 'custom',
label: 'Dashboard',
reference: undefined,
url: '/admin',
url: 'http://localhost:3000/admin',
},
},
],

View File

@@ -6,66 +6,10 @@
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Brisbane'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
pages: Page;
users: User;

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.40.0",
"version": "3.38.0",
"private": true,
"type": "module",
"scripts": {
@@ -84,7 +84,7 @@
"publish-prerelease": "pnpm --filter releaser publish-prerelease",
"reinstall": "pnpm clean:all && pnpm install",
"release": "pnpm --filter releaser release --tag latest",
"runts": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" node --no-deprecation --no-experimental-strip-types --import @swc-node/register/esm-register",
"runts": "cross-env NODE_OPTIONS=--no-deprecation node --no-deprecation --import @swc-node/register/esm-register",
"script:build-template-with-local-pkgs": "pnpm --filter scripts build-template-with-local-pkgs",
"script:gen-templates": "pnpm --filter scripts gen-templates",
"script:gen-templates:build": "pnpm --filter scripts gen-templates --build",
@@ -93,17 +93,17 @@
"script:pack": "pnpm --filter scripts pack-all-to-dest",
"pretest": "pnpm build",
"test": "pnpm test:int && pnpm test:components && pnpm test:e2e",
"test:components": "cross-env NODE_OPTIONS=\" --no-deprecation --no-experimental-strip-types\" jest --config=jest.components.config.js",
"test:components": "cross-env NODE_OPTIONS=\" --no-deprecation\" jest --config=jest.components.config.js",
"test:e2e": "pnpm runts ./test/runE2E.ts",
"test:e2e:debug": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PWDEBUG=1 DISABLE_LOGGING=true playwright test",
"test:e2e:headed": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true playwright test --headed",
"test:e2e:debug": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 PWDEBUG=1 DISABLE_LOGGING=true playwright test",
"test:e2e:headed": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 DISABLE_LOGGING=true playwright test --headed",
"test:e2e:prod": "pnpm prepare-run-test-against-prod && pnpm runts ./test/runE2E.ts --prod",
"test:e2e:prod:ci": "pnpm prepare-run-test-against-prod:ci && pnpm runts ./test/runE2E.ts --prod",
"test:int": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:int:postgres": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:int:sqlite": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=sqlite DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:int": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:int:postgres": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:int:sqlite": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=sqlite DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:types": "tstyche",
"test:unit": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=jest.config.js --runInBand",
"test:unit": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=jest.config.js --runInBand",
"translateNewKeys": "pnpm --filter translations run translateNewKeys"
},
"lint-staged": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/admin-bar",
"version": "3.40.0",
"version": "3.38.0",
"description": "An admin bar for React apps using Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "create-payload-app",
"version": "3.40.0",
"version": "3.38.0",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -16,15 +16,12 @@ export const configurePluginProject = ({
const devPayloadConfigPath = path.resolve(projectDirPath, './dev/payload.config.ts')
const devTsConfigPath = path.resolve(projectDirPath, './dev/tsconfig.json')
const indexTsPath = path.resolve(projectDirPath, './src/index.ts')
const devImportMapPath = path.resolve(projectDirPath, './dev/app/(payload)/admin/importMap.js')
const devPayloadConfig = fse.readFileSync(devPayloadConfigPath, 'utf8')
const devTsConfig = fse.readFileSync(devTsConfigPath, 'utf8')
const indexTs = fse.readFileSync(indexTsPath, 'utf-8')
const devImportMap = fse.readFileSync(devImportMapPath, 'utf-8')
const updatedTsConfig = devTsConfig.replaceAll('plugin-package-name-placeholder', projectName)
const updatedImportMap = devImportMap.replaceAll('plugin-package-name-placeholder', projectName)
let updatedIndexTs = indexTs.replaceAll('plugin-package-name-placeholder', projectName)
const pluginExportVariableName = toCamelCase(projectName)
@@ -46,5 +43,4 @@ export const configurePluginProject = ({
fse.writeFileSync(devPayloadConfigPath, updatedPayloadConfig)
fse.writeFileSync(devTsConfigPath, updatedTsConfig)
fse.writeFileSync(indexTsPath, updatedIndexTs)
fse.writeFileSync(devImportMapPath, updatedImportMap)
}

View File

@@ -0,0 +1,8 @@
import { readFileSync } from 'fs'
import { fileURLToPath } from 'node:url'
import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const packageJson = JSON.parse(readFileSync(path.resolve(dirname, '../../package.json'), 'utf-8'))
export const PACKAGE_VERSION = packageJson.version

View File

@@ -63,30 +63,6 @@ describe('createProject', () => {
expect(packageJson.name).toStrictEqual(projectName)
})
it('updates project name in plugin template importMap file', async () => {
const projectName = 'my-custom-plugin'
const template: ProjectTemplate = {
name: 'plugin',
type: 'plugin',
description: 'Template for creating a Payload plugin',
url: 'https://github.com/payloadcms/payload/templates/plugin',
}
await createProject({
cliArgs: { ...args, '--local-template': 'plugin' } as CliArgs,
packageManager,
projectDir,
projectName,
template,
})
const importMapPath = path.resolve(projectDir, './dev/app/(payload)/admin/importMap.js')
const importMapFile = fse.readFileSync(importMapPath, 'utf-8')
expect(importMapFile).not.toContain('plugin-package-name-placeholder')
expect(importMapFile).toContain('my-custom-plugin')
})
it('creates example', async () => {
const projectName = 'custom-server-example'
const example: ProjectExample = {

View File

@@ -1,8 +1,9 @@
import type { ProjectTemplate } from '../types.js'
import { error, info } from '../utils/log.js'
import { PACKAGE_VERSION } from './constants.js'
export function validateTemplate({ templateName }: { templateName: string }): boolean {
export function validateTemplate(templateName: string): boolean {
const validTemplates = getValidTemplates()
if (!validTemplates.map((t) => t.name).includes(templateName)) {
error(`'${templateName}' is not a valid template.`)
@@ -19,13 +20,13 @@ export function getValidTemplates(): ProjectTemplate[] {
name: 'blank',
type: 'starter',
description: 'Blank 3.0 Template',
url: `https://github.com/payloadcms/payload/templates/blank#main`,
url: `https://github.com/payloadcms/payload/templates/blank#v${PACKAGE_VERSION}`,
},
{
name: 'website',
type: 'starter',
description: 'Website Template',
url: `https://github.com/payloadcms/payload/templates/website#main`,
url: `https://github.com/payloadcms/payload/templates/website#v${PACKAGE_VERSION}`,
},
{
name: 'plugin',

View File

@@ -1,3 +1,4 @@
import execa from 'execa'
import fse from 'fs-extra'
import { fileURLToPath } from 'node:url'
import path from 'path'
@@ -8,7 +9,6 @@ const dirname = path.dirname(filename)
import type { NextAppDetails } from '../types.js'
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
import { getLatestPackageVersion } from '../utils/getLatestPackageVersion.js'
import { info } from '../utils/log.js'
import { getPackageManager } from './get-package-manager.js'
import { installPackages } from './install-packages.js'
@@ -36,8 +36,15 @@ export async function updatePayloadInProject(
const packageManager = await getPackageManager({ projectDir })
// Fetch latest Payload version
const latestPayloadVersion = await getLatestPackageVersion({ packageName: 'payload' })
// Fetch latest Payload version from npm
const { exitCode: getLatestVersionExitCode, stdout: latestPayloadVersion } = await execa('npm', [
'show',
'payload',
'version',
])
if (getLatestVersionExitCode !== 0) {
throw new Error('Failed to fetch latest Payload version')
}
if (payloadVersion === latestPayloadVersion) {
return { message: `Payload v${payloadVersion} is already up to date.`, success: true }

View File

@@ -8,6 +8,7 @@ import path from 'path'
import type { CliArgs } from './types.js'
import { configurePayloadConfig } from './lib/configure-payload-config.js'
import { PACKAGE_VERSION } from './lib/constants.js'
import { createProject } from './lib/create-project.js'
import { parseExample } from './lib/examples.js'
import { generateSecret } from './lib/generate-secret.js'
@@ -19,7 +20,6 @@ import { parseTemplate } from './lib/parse-template.js'
import { selectDb } from './lib/select-db.js'
import { getValidTemplates, validateTemplate } from './lib/templates.js'
import { updatePayloadInProject } from './lib/update-payload-in-project.js'
import { getLatestPackageVersion } from './utils/getLatestPackageVersion.js'
import { debug, error, info } from './utils/log.js'
import {
feedbackOutro,
@@ -78,18 +78,13 @@ export class Main {
async init(): Promise<void> {
try {
const debugFlag = this.args['--debug']
const LATEST_VERSION = await getLatestPackageVersion({
debug: debugFlag,
packageName: 'payload',
})
if (this.args['--help']) {
helpMessage()
process.exit(0)
}
const debugFlag = this.args['--debug']
// eslint-disable-next-line no-console
console.log('\n')
p.intro(chalk.bgCyan(chalk.black(' create-payload-app ')))
@@ -205,7 +200,7 @@ export class Main {
const templateArg = this.args['--template']
if (templateArg) {
const valid = validateTemplate({ templateName: templateArg })
const valid = validateTemplate(templateArg)
if (!valid) {
helpMessage()
process.exit(1)
@@ -235,7 +230,7 @@ export class Main {
}
if (debugFlag) {
debug(`Using ${exampleArg ? 'examples' : 'templates'} from git tag: v${LATEST_VERSION}`)
debug(`Using ${exampleArg ? 'examples' : 'templates'} from git tag: v${PACKAGE_VERSION}`)
}
if (!exampleArg) {

View File

@@ -1,34 +0,0 @@
/**
* Fetches the latest version of a package from the NPM registry.
*
* Used in determining the latest version of Payload to use in the generated templates.
*/
export async function getLatestPackageVersion({
debug = false,
packageName = 'payload',
}: {
debug?: boolean
/**
* Package name to fetch the latest version for based on the NPM registry URL
*
* Eg. for `'payload'`, it will fetch the version from `https://registry.npmjs.org/payload`
*
* @default 'payload'
*/
packageName?: string
}) {
try {
const response = await fetch(`https://registry.npmjs.org/${packageName}`)
const data = await response.json()
const latestVersion = data['dist-tags'].latest
if (debug) {
console.log(`Found latest version of ${packageName}: ${latestVersion}`)
}
return latestVersion
} catch (error) {
console.error('Error fetching Payload version:', error)
throw error
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "3.40.0",
"version": "3.38.0",
"description": "The officially supported MongoDB database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -417,7 +417,7 @@ export const sanitizeQueryValue = ({
return buildExistsQuery(
formattedValue,
path,
!['checkbox', 'relationship', 'upload'].includes(field.type),
!['relationship', 'upload'].includes(field.type),
)
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "3.40.0",
"version": "3.38.0",
"description": "The officially supported Postgres database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -51,6 +51,13 @@ export const connect: Connect = async function connect(
) {
const { hotReload } = options
this.schema = {
pgSchema: this.pgSchema,
...this.tables,
...this.relations,
...this.enums,
}
try {
if (!this.pool) {
this.pool = new this.pg.Pool(this.poolOptions)

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-sqlite",
"version": "3.40.0",
"version": "3.38.0",
"description": "The officially supported SQLite database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -15,6 +15,11 @@ export const connect: Connect = async function connect(
) {
const { hotReload } = options
this.schema = {
...this.tables,
...this.relations,
}
try {
if (!this.client) {
this.client = createClient(this.clientConfig)

View File

@@ -36,9 +36,4 @@ export const init: Init = async function init(this: SQLiteAdapter) {
})
await executeSchemaHooks({ type: 'afterSchemaInit', adapter: this })
this.schema = {
...this.tables,
...this.relations,
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-vercel-postgres",
"version": "3.40.0",
"version": "3.38.0",
"description": "Vercel Postgres adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -16,6 +16,13 @@ export const connect: Connect = async function connect(
) {
const { hotReload } = options
this.schema = {
pgSchema: this.pgSchema,
...this.tables,
...this.relations,
...this.enums,
}
try {
const logger = this.logger || false

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/drizzle",
"version": "3.40.0",
"version": "3.38.0",
"description": "A library of shared functions used by different payload database adapters",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -28,8 +28,6 @@ export async function migrateReset(this: DrizzleAdapter): Promise<void> {
const req = await createLocalReq({}, payload)
existingMigrations.reverse()
// Rollback all migrations in order
for (const migration of existingMigrations) {
const migrationFile = migrationFiles.find((m) => m.name === migration.name)

View File

@@ -35,11 +35,4 @@ export const init: Init = async function init(this: BasePostgresAdapter) {
})
await executeSchemaHooks({ type: 'afterSchemaInit', adapter: this })
this.schema = {
pgSchema: this.pgSchema,
...this.tables,
...this.relations,
...this.enums,
}
}

View File

@@ -7,6 +7,14 @@ export const withDefault = (column: RawColumn, field: FieldAffectingData): RawCo
return column
}
if (typeof field.defaultValue === 'string' && field.defaultValue.includes("'")) {
const escapedString = field.defaultValue.replaceAll("'", "''")
return {
...column,
default: escapedString,
}
}
return {
...column,
default: field.defaultValue,

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-nodemailer",
"version": "3.40.0",
"version": "3.38.0",
"description": "Payload Nodemailer Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-resend",
"version": "3.40.0",
"version": "3.38.0",
"description": "Payload Resend Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/graphql",
"version": "3.40.0",
"version": "3.38.0",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.40.0",
"version": "3.38.0",
"description": "The official React SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-vue",
"version": "3.40.0",
"version": "3.38.0",
"description": "The official Vue SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview",
"version": "3.40.0",
"version": "3.38.0",
"description": "The official live preview JavaScript SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.40.0",
"version": "3.38.0",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -59,7 +59,6 @@ export const DocumentTabLink: React.FC<{
className={[baseClass, isActive && `${baseClass}--active`].filter(Boolean).join(' ')}
disabled={isActive}
el={!isActive || href !== pathname ? 'link' : 'div'}
margin={false}
newTab={newTab}
size="medium"
to={!isActive || href !== pathname ? hrefWithLocale : undefined}

View File

@@ -30,7 +30,7 @@ export const DefaultNavClient: React.FC<{
const [folderCollectionSlugs] = React.useState<string[]>(() => {
return collections.reduce<string[]>((acc, collection) => {
if (collection.folders) {
if (collection.admin.folders) {
acc.push(collection.slug)
}
return acc

View File

@@ -99,7 +99,6 @@ export const POST =
(config: Promise<SanitizedConfig> | SanitizedConfig) => async (request: Request) => {
const originalRequest = request.clone()
const req = await createPayloadRequest({
canSetHeaders: true,
config,
request,
})

View File

@@ -8,7 +8,7 @@ describe('getSafeRedirect', () => {
'should allow safe relative path: %s',
(input) => {
// If the input is a clean relative path, it should be returned as-is
expect(getSafeRedirect({ redirectTo: input, fallbackTo: fallback })).toBe(input)
expect(getSafeRedirect(input, fallback)).toBe(input)
},
)
@@ -17,7 +17,7 @@ describe('getSafeRedirect', () => {
'should fallback on invalid or non-string input: %s',
(input) => {
// If the input is not a valid string, it should return the fallback
expect(getSafeRedirect({ redirectTo: input as any, fallbackTo: fallback })).toBe(fallback)
expect(getSafeRedirect(input as any, fallback)).toBe(fallback)
},
)
@@ -36,24 +36,20 @@ describe('getSafeRedirect', () => {
'%2Fjavascript:alert(1)', // encoded JavaScript scheme
])('should block unsafe redirect: %s', (input) => {
// All of these should return the fallback because theyre unsafe
expect(getSafeRedirect({ redirectTo: input, fallbackTo: fallback })).toBe(fallback)
expect(getSafeRedirect(input, fallback)).toBe(fallback)
})
// Input with extra spaces should still be properly handled
it('should trim whitespace before evaluating', () => {
// A valid path with surrounding spaces should still be accepted
expect(getSafeRedirect({ redirectTo: ' /dashboard ', fallbackTo: fallback })).toBe(
'/dashboard',
)
expect(getSafeRedirect(' /dashboard ', fallback)).toBe('/dashboard')
// An unsafe path with spaces should still be rejected
expect(getSafeRedirect({ redirectTo: ' //example.com ', fallbackTo: fallback })).toBe(
fallback,
)
expect(getSafeRedirect(' //example.com ', fallback)).toBe(fallback)
})
// If decoding the input fails (e.g., invalid percent encoding), it should not crash
it('should return fallback on invalid encoding', () => {
expect(getSafeRedirect({ redirectTo: '%E0%A4%A', fallbackTo: fallback })).toBe(fallback)
expect(getSafeRedirect('%E0%A4%A', fallback)).toBe(fallback)
})
})

View File

@@ -1,22 +1,17 @@
export const getSafeRedirect = ({
allowAbsoluteUrls = false,
fallbackTo = '/',
redirectTo,
}: {
allowAbsoluteUrls?: boolean
fallbackTo?: string
redirectTo: string | string[]
}): string => {
if (typeof redirectTo !== 'string') {
return fallbackTo
export const getSafeRedirect = (
redirectParam: string | string[],
fallback: string = '/',
): string => {
if (typeof redirectParam !== 'string') {
return fallback
}
// Normalize and decode the path
let redirectPath: string
try {
redirectPath = decodeURIComponent(redirectTo.trim())
redirectPath = decodeURIComponent(redirectParam.trim())
} catch {
return fallbackTo // invalid encoding
return fallback // invalid encoding
}
const isSafeRedirect =
@@ -35,10 +30,5 @@ export const getSafeRedirect = ({
// Prevent attempts to redirect to full URLs using "/http:" or "/https:"
!redirectPath.toLowerCase().startsWith('/http')
const isAbsoluteSafeRedirect =
allowAbsoluteUrls &&
// Must be a valid absolute URL with http or https
/^https?:\/\/\S+$/i.test(redirectPath)
return isSafeRedirect || isAbsoluteSafeRedirect ? redirectPath : fallbackTo
return isSafeRedirect ? redirectPath : fallback
}

View File

@@ -49,13 +49,11 @@ const reqCache = selectiveCache<Result>('req')
* As access control and getting the request locale is dependent on the current URL and
*/
export const initReq = async function ({
canSetHeaders,
configPromise,
importMap,
key,
overrides,
}: {
canSetHeaders?: boolean
configPromise: Promise<SanitizedConfig> | SanitizedConfig
importMap: ImportMap
key: string
@@ -79,7 +77,6 @@ export const initReq = async function ({
})
const { responseHeaders, user } = await executeAuthStrategies({
canSetHeaders,
headers,
payload,
})

View File

@@ -76,8 +76,9 @@ export const buildBrowseByFolderView = async (
const { breadcrumbs, documents, subfolders } = await getFolderData({
folderID,
req: initPageResult.req,
payload: initPageResult.req.payload,
search: query?.search as string,
user: initPageResult.req.user,
})
const resolvedFolderID = breadcrumbs[breadcrumbs.length - 1]?.id
@@ -87,7 +88,7 @@ export const buildBrowseByFolderView = async (
((resolvedFolderID && folderID && folderID !== resolvedFolderID) ||
(folderID && !resolvedFolderID))
) {
redirect(
return redirect(
formatAdminURL({
adminRoute,
path: config.admin.routes.browseByFolder,

View File

@@ -11,9 +11,6 @@ export const BrowseByFolder: React.FC<BuildFolderViewArgs> = async (args) => {
const { View } = await buildBrowseByFolderView(args)
return View
} catch (error) {
if (error?.message === 'NEXT_REDIRECT') {
throw error
}
if (error.message === 'not-found') {
notFound()
} else {

View File

@@ -112,8 +112,9 @@ export const buildCollectionFolderView = async (
const { breadcrumbs, documents, subfolders } = await getFolderData({
collectionSlug,
folderID,
req: initPageResult.req,
payload: initPageResult.req.payload,
search: query?.search as string,
user: initPageResult.req.user,
})
const resolvedFolderID = breadcrumbs[breadcrumbs.length - 1]?.id
@@ -123,7 +124,7 @@ export const buildCollectionFolderView = async (
((resolvedFolderID && folderID && folderID !== resolvedFolderID) ||
(folderID && !resolvedFolderID))
) {
redirect(
return redirect(
formatAdminURL({
adminRoute,
path: `/collections/${collectionSlug}/${config.folders.slug}`,

View File

@@ -11,9 +11,6 @@ export const CollectionFolderView: React.FC<BuildCollectionFolderViewStateArgs>
const { View } = await buildCollectionFolderView(args)
return View
} catch (error) {
if (error?.message === 'NEXT_REDIRECT') {
throw error
}
if (error.message === 'not-found') {
notFound()
} else {

View File

@@ -20,11 +20,7 @@ export const resolveAllFilterOptions = async ({
return
}
if (
(field.type === 'relationship' || field.type === 'upload') &&
'filterOptions' in field &&
field.filterOptions
) {
if ('name' in field && 'filterOptions' in field && field.filterOptions) {
const options = await resolveFilterOptions(field.filterOptions, {
id: undefined,
blockData: undefined,

View File

@@ -71,7 +71,6 @@ const getAbsoluteUrl = (url) => {
}
const PreviewView: React.FC<Props> = ({
BeforeDocumentControls,
collectionConfig,
config,
Description,
@@ -495,7 +494,6 @@ const PreviewView: React.FC<Props> = ({
/>
<DocumentControls
apiURL={apiURL}
BeforeDocumentControls={BeforeDocumentControls}
customComponents={{
PreviewButton,
PublishButton,
@@ -606,7 +604,6 @@ export const LivePreviewClient: React.FC<
>
<PreviewView
apiRoute={apiRoute}
BeforeDocumentControls={props.BeforeDocumentControls}
collectionConfig={collectionConfig}
config={config}
Description={props.Description}

View File

@@ -1,11 +1,5 @@
import type {
BeforeDocumentControlsServerPropsOnly,
DocumentViewServerProps,
LivePreviewConfig,
ServerProps,
} from 'payload'
import type { DocumentViewServerProps, LivePreviewConfig } from 'payload'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import React from 'react'
import './index.scss'
@@ -18,13 +12,6 @@ export async function LivePreviewView(props: DocumentViewServerProps) {
let livePreviewConfig: LivePreviewConfig = req.payload.config?.admin?.livePreview
const serverProps: ServerProps = {
i18n: req.i18n,
payload: req.payload,
user: req.user,
// TODO: Add remaining serverProps
}
if (collectionConfig) {
livePreviewConfig = {
...(livePreviewConfig || {}),
@@ -39,10 +26,6 @@ export async function LivePreviewView(props: DocumentViewServerProps) {
}
}
const BeforeDocumentControls =
collectionConfig?.admin?.components?.edit?.beforeDocumentControls ||
globalConfig?.admin?.components?.elements?.beforeDocumentControls
const breakpoints: LivePreviewConfig['breakpoints'] = [
...(livePreviewConfig?.breakpoints || []),
{
@@ -71,15 +54,6 @@ export async function LivePreviewView(props: DocumentViewServerProps) {
return (
<LivePreviewClient
BeforeDocumentControls={
BeforeDocumentControls
? RenderServerComponent({
Component: BeforeDocumentControls,
importMap: req.payload.importMap,
serverProps: serverProps satisfies BeforeDocumentControlsServerPropsOnly,
})
: null
}
breakpoints={breakpoints}
Description={props.Description}
initialData={doc}

View File

@@ -16,10 +16,11 @@ import {
useConfig,
useTranslation,
} from '@payloadcms/ui'
import { formatAdminURL, getLoginOptions, getSafeRedirect } from 'payload/shared'
import { formatAdminURL, getLoginOptions } from 'payload/shared'
import type { LoginFieldProps } from '../LoginField/index.js'
import { getSafeRedirect } from '../../../utilities/getSafeRedirect.js'
import { LoginField } from '../LoginField/index.js'
import './index.scss'
@@ -91,7 +92,7 @@ export const LoginForm: React.FC<{
initialState={initialState}
method="POST"
onSuccess={handleLogin}
redirect={getSafeRedirect({ fallbackTo: adminRoute, redirectTo: searchParams?.redirect })}
redirect={getSafeRedirect(searchParams?.redirect, adminRoute)}
waitForAutocomplete
>
<div className={`${baseClass}__inputWrap`}>

Some files were not shown because too many files have changed in this diff Show More