chore: formatting and linting (#3261)
* chore: lint packages/payload * chore: lint packages/db-postgres * chore: lint packages/db-mongodb * chore: update eslintrc exclusion rules * chore: update eslintrc exclusion rules * chore: lint misc files * chore: run prettier through packages * chore: run eslint on payload again * chore: prettier misc files * chore: prettier docs
This commit is contained in:
@@ -3,7 +3,7 @@ module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['packages/eslint-config-payload/**'],
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Create a bug report for Payload
|
||||
labels: ["possible-bug"]
|
||||
labels: ['possible-bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
7
.github/reproduction-guide.md
vendored
7
.github/reproduction-guide.md
vendored
@@ -9,6 +9,7 @@
|
||||
**NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config.
|
||||
|
||||
## Example test directory file tree
|
||||
|
||||
```text
|
||||
.
|
||||
├── config.ts
|
||||
@@ -27,9 +28,11 @@ The directory split up in this way specifically to reduce friction when creating
|
||||
<br />
|
||||
|
||||
## Testing is optional but encouraged
|
||||
|
||||
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below:
|
||||
|
||||
### Running integration tests (Payload API tests)
|
||||
|
||||
First install [Jest Runner for VSVode](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner).
|
||||
|
||||
There are a couple ways run integration tests:
|
||||
@@ -45,7 +48,9 @@ There are a couple ways run integration tests:
|
||||
```
|
||||
|
||||
### Running E2E tests (Admin Panel UI tests)
|
||||
|
||||
The easiest way to run E2E tests is to install
|
||||
|
||||
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
|
||||
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
|
||||
|
||||
@@ -53,6 +58,6 @@ Once they are installed you can open the `testing` tab in vscode sidebar and dri
|
||||
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
|
||||
|
||||
|
||||
#### Notes
|
||||
|
||||
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
94
.vscode/launch.json
vendored
94
.vscode/launch.json
vendored
@@ -1,112 +1,112 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "pnpm run dev _community",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Community",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Fields",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev:postgres postgres -- -I", // Allow input
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Postgres",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev versions",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Versions",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate",
|
||||
"request": "launch",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - migrate",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:status",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - status",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - status",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:create yass",
|
||||
"request": "launch",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - create",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:down",
|
||||
"request": "launch",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - down",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:reset",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - reset",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - reset",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:refresh",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - refresh",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - refresh",
|
||||
"outputCapture": "std",
|
||||
},
|
||||
]
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
],
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0"
|
||||
}
|
||||
|
||||
547
CHANGELOG.md
547
CHANGELOG.md
@@ -1,457 +1,402 @@
|
||||
|
||||
|
||||
# [1.14.0](https://github.com/payloadcms/payload/compare/v1.13.4...v1.14.0) (2023-08-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* DatePicker showing only selected day by default ([#3169](https://github.com/payloadcms/payload/issues/3169)) ([edcb393](https://github.com/payloadcms/payload/commit/edcb3933cfb4532180c822135ea6a8be928e0fdc))
|
||||
* only allow redirects to /admin sub-routes ([c0f05a1](https://github.com/payloadcms/payload/commit/c0f05a1c38fb9c958de920fabb698b5ecfb661f0))
|
||||
* passes in height to resizeOptions upload option to allow height resize ([#3171](https://github.com/payloadcms/payload/issues/3171)) ([7963d04](https://github.com/payloadcms/payload/commit/7963d04a27888eb5a12d0ab37f2082cd33638abd))
|
||||
* WhereBuilder component does not accept all valid Where queries ([#3087](https://github.com/payloadcms/payload/issues/3087)) ([fdfdfc8](https://github.com/payloadcms/payload/commit/fdfdfc83f36a958971f8e4e4f9f5e51560cb26e0))
|
||||
|
||||
- DatePicker showing only selected day by default ([#3169](https://github.com/payloadcms/payload/issues/3169)) ([edcb393](https://github.com/payloadcms/payload/commit/edcb3933cfb4532180c822135ea6a8be928e0fdc))
|
||||
- only allow redirects to /admin sub-routes ([c0f05a1](https://github.com/payloadcms/payload/commit/c0f05a1c38fb9c958de920fabb698b5ecfb661f0))
|
||||
- passes in height to resizeOptions upload option to allow height resize ([#3171](https://github.com/payloadcms/payload/issues/3171)) ([7963d04](https://github.com/payloadcms/payload/commit/7963d04a27888eb5a12d0ab37f2082cd33638abd))
|
||||
- WhereBuilder component does not accept all valid Where queries ([#3087](https://github.com/payloadcms/payload/issues/3087)) ([fdfdfc8](https://github.com/payloadcms/payload/commit/fdfdfc83f36a958971f8e4e4f9f5e51560cb26e0))
|
||||
|
||||
### Features
|
||||
|
||||
* add afterOperation hook ([#2697](https://github.com/payloadcms/payload/issues/2697)) ([33686c6](https://github.com/payloadcms/payload/commit/33686c6db8373a16d7f6b0192e0701bf15881aa4))
|
||||
* add support for hotkeys ([#1821](https://github.com/payloadcms/payload/issues/1821)) ([942cfec](https://github.com/payloadcms/payload/commit/942cfec286ff050e13417b037cca64b9d757d868))
|
||||
* Added Azerbaijani language file ([#3164](https://github.com/payloadcms/payload/issues/3164)) ([63e3063](https://github.com/payloadcms/payload/commit/63e3063b9ecc1afd62d7a287a798d41215008f2a))
|
||||
* allow async relationship filter options ([#2951](https://github.com/payloadcms/payload/issues/2951)) ([bad3638](https://github.com/payloadcms/payload/commit/bad363882c9d00d3c73547ca3329eba988e728ff))
|
||||
* Improve admin dashboard accessibility ([#3053](https://github.com/payloadcms/payload/issues/3053)) ([e03a8e6](https://github.com/payloadcms/payload/commit/e03a8e6b030e82a17e1cdae5b4032433cf9c75a4))
|
||||
* improve field ops ([#3172](https://github.com/payloadcms/payload/issues/3172)) ([d91b44c](https://github.com/payloadcms/payload/commit/d91b44cbb3fd526caca2a6f4bd30fd06ede3a5da))
|
||||
* make PAYLOAD_CONFIG_PATH optional ([#2839](https://github.com/payloadcms/payload/issues/2839)) ([5744de7](https://github.com/payloadcms/payload/commit/5744de7ec63e3f17df7e02a7cc827818a79dbbb8))
|
||||
* text alignment for richtext editor ([#2803](https://github.com/payloadcms/payload/issues/2803)) ([a0b13a5](https://github.com/payloadcms/payload/commit/a0b13a5b01fa0d7f4c4dffd1895bfe507e5c676d))
|
||||
- add afterOperation hook ([#2697](https://github.com/payloadcms/payload/issues/2697)) ([33686c6](https://github.com/payloadcms/payload/commit/33686c6db8373a16d7f6b0192e0701bf15881aa4))
|
||||
- add support for hotkeys ([#1821](https://github.com/payloadcms/payload/issues/1821)) ([942cfec](https://github.com/payloadcms/payload/commit/942cfec286ff050e13417b037cca64b9d757d868))
|
||||
- Added Azerbaijani language file ([#3164](https://github.com/payloadcms/payload/issues/3164)) ([63e3063](https://github.com/payloadcms/payload/commit/63e3063b9ecc1afd62d7a287a798d41215008f2a))
|
||||
- allow async relationship filter options ([#2951](https://github.com/payloadcms/payload/issues/2951)) ([bad3638](https://github.com/payloadcms/payload/commit/bad363882c9d00d3c73547ca3329eba988e728ff))
|
||||
- Improve admin dashboard accessibility ([#3053](https://github.com/payloadcms/payload/issues/3053)) ([e03a8e6](https://github.com/payloadcms/payload/commit/e03a8e6b030e82a17e1cdae5b4032433cf9c75a4))
|
||||
- improve field ops ([#3172](https://github.com/payloadcms/payload/issues/3172)) ([d91b44c](https://github.com/payloadcms/payload/commit/d91b44cbb3fd526caca2a6f4bd30fd06ede3a5da))
|
||||
- make PAYLOAD_CONFIG_PATH optional ([#2839](https://github.com/payloadcms/payload/issues/2839)) ([5744de7](https://github.com/payloadcms/payload/commit/5744de7ec63e3f17df7e02a7cc827818a79dbbb8))
|
||||
- text alignment for richtext editor ([#2803](https://github.com/payloadcms/payload/issues/2803)) ([a0b13a5](https://github.com/payloadcms/payload/commit/a0b13a5b01fa0d7f4c4dffd1895bfe507e5c676d))
|
||||
|
||||
## [1.13.4](https://github.com/payloadcms/payload/compare/v1.13.3...v1.13.4) (2023-08-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correctly passes block path inside buildFieldSchemaMap ([#3162](https://github.com/payloadcms/payload/issues/3162)) ([3c60abd](https://github.com/payloadcms/payload/commit/3c60abd61aaf24d49712c80bcbd0f1113c22b85a))
|
||||
- correctly passes block path inside buildFieldSchemaMap ([#3162](https://github.com/payloadcms/payload/issues/3162)) ([3c60abd](https://github.com/payloadcms/payload/commit/3c60abd61aaf24d49712c80bcbd0f1113c22b85a))
|
||||
|
||||
## [1.13.3](https://github.com/payloadcms/payload/compare/v1.13.2...v1.13.3) (2023-08-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* unable to add arrays inside secondary named tabs ([#3158](https://github.com/payloadcms/payload/issues/3158)) ([cb04d4a](https://github.com/payloadcms/payload/commit/cb04d4a82a68a764330582b93882d422b32c2527))
|
||||
- unable to add arrays inside secondary named tabs ([#3158](https://github.com/payloadcms/payload/issues/3158)) ([cb04d4a](https://github.com/payloadcms/payload/commit/cb04d4a82a68a764330582b93882d422b32c2527))
|
||||
|
||||
## [1.13.2](https://github.com/payloadcms/payload/compare/v1.13.1...v1.13.2) (2023-08-10)
|
||||
|
||||
## [1.13.1](https://github.com/payloadcms/payload/compare/v1.13.0...v1.13.1) (2023-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* updates addFieldRow and replaceFieldRow rowIndex insertion ([#3145](https://github.com/payloadcms/payload/issues/3145)) ([f5cf546](https://github.com/payloadcms/payload/commit/f5cf546e1918de66998d5f0e5410bfbc1f054567))
|
||||
- updates addFieldRow and replaceFieldRow rowIndex insertion ([#3145](https://github.com/payloadcms/payload/issues/3145)) ([f5cf546](https://github.com/payloadcms/payload/commit/f5cf546e1918de66998d5f0e5410bfbc1f054567))
|
||||
|
||||
# [1.13.0](https://github.com/payloadcms/payload/compare/v1.12.0...v1.13.0) (2023-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `setPreference()` return type ([#3125](https://github.com/payloadcms/payload/issues/3125)) ([463d6bb](https://github.com/payloadcms/payload/commit/463d6bbec66e61523bae3869df88bd98e7617390))
|
||||
* absolute staticURL admin thumbnails ([#3135](https://github.com/payloadcms/payload/issues/3135)) ([1039f39](https://github.com/payloadcms/payload/commit/1039f39c09260537616b22228080466e8df6e981))
|
||||
* adding and replacing similarly shaped block configs ([#3140](https://github.com/payloadcms/payload/issues/3140)) ([8e188cf](https://github.com/payloadcms/payload/commit/8e188cfe61db808c94d726967affdadf2e5abb9f))
|
||||
|
||||
- `setPreference()` return type ([#3125](https://github.com/payloadcms/payload/issues/3125)) ([463d6bb](https://github.com/payloadcms/payload/commit/463d6bbec66e61523bae3869df88bd98e7617390))
|
||||
- absolute staticURL admin thumbnails ([#3135](https://github.com/payloadcms/payload/issues/3135)) ([1039f39](https://github.com/payloadcms/payload/commit/1039f39c09260537616b22228080466e8df6e981))
|
||||
- adding and replacing similarly shaped block configs ([#3140](https://github.com/payloadcms/payload/issues/3140)) ([8e188cf](https://github.com/payloadcms/payload/commit/8e188cfe61db808c94d726967affdadf2e5abb9f))
|
||||
|
||||
### Features
|
||||
|
||||
* default tab labels from name ([#3129](https://github.com/payloadcms/payload/issues/3129)) ([e8f0516](https://github.com/payloadcms/payload/commit/e8f05165eb3a28c00deb11931db01ad1f8c75c74))
|
||||
* radio and select fields are filterable by options ([#3136](https://github.com/payloadcms/payload/issues/3136)) ([b117e73](https://github.com/payloadcms/payload/commit/b117e7346434bfc8edbfa92f5db45f63c57bab08))
|
||||
* recursive saveToJWT field support ([#3130](https://github.com/payloadcms/payload/issues/3130)) ([c6e0908](https://github.com/payloadcms/payload/commit/c6e09080767dad2ab8128ba330b2b344bb25ac6f))
|
||||
- default tab labels from name ([#3129](https://github.com/payloadcms/payload/issues/3129)) ([e8f0516](https://github.com/payloadcms/payload/commit/e8f05165eb3a28c00deb11931db01ad1f8c75c74))
|
||||
- radio and select fields are filterable by options ([#3136](https://github.com/payloadcms/payload/issues/3136)) ([b117e73](https://github.com/payloadcms/payload/commit/b117e7346434bfc8edbfa92f5db45f63c57bab08))
|
||||
- recursive saveToJWT field support ([#3130](https://github.com/payloadcms/payload/issues/3130)) ([c6e0908](https://github.com/payloadcms/payload/commit/c6e09080767dad2ab8128ba330b2b344bb25ac6f))
|
||||
|
||||
# [1.12.0](https://github.com/payloadcms/payload/compare/v1.11.8...v1.12.0) (2023-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* excludes useAsTitle field from searchableFields in collection view ([#3105](https://github.com/payloadcms/payload/issues/3105)) ([8c4d251](https://github.com/payloadcms/payload/commit/8c4d2514b0f195e0059c6063346199785979c70c))
|
||||
* relationship field filter long titles ([#3113](https://github.com/payloadcms/payload/issues/3113)) ([da27a8a](https://github.com/payloadcms/payload/commit/da27a8aadbb103c5f6fe0ccc62c032876851b88f))
|
||||
* wrong links in verification and forgot password emails if serverURL not set ([#3010](https://github.com/payloadcms/payload/issues/3010)) ([6a189c6](https://github.com/payloadcms/payload/commit/6a189c6548b233aba64598af8804a56ec47e45f0))
|
||||
|
||||
- excludes useAsTitle field from searchableFields in collection view ([#3105](https://github.com/payloadcms/payload/issues/3105)) ([8c4d251](https://github.com/payloadcms/payload/commit/8c4d2514b0f195e0059c6063346199785979c70c))
|
||||
- relationship field filter long titles ([#3113](https://github.com/payloadcms/payload/issues/3113)) ([da27a8a](https://github.com/payloadcms/payload/commit/da27a8aadbb103c5f6fe0ccc62c032876851b88f))
|
||||
- wrong links in verification and forgot password emails if serverURL not set ([#3010](https://github.com/payloadcms/payload/issues/3010)) ([6a189c6](https://github.com/payloadcms/payload/commit/6a189c6548b233aba64598af8804a56ec47e45f0))
|
||||
|
||||
### Features
|
||||
|
||||
* add support for sharp resize options ([#2844](https://github.com/payloadcms/payload/issues/2844)) ([144bb81](https://github.com/payloadcms/payload/commit/144bb81721814c19eb4957d4c8fcc845c73e2aa4))
|
||||
* allows for upload relationship drawer to be opened ([#3108](https://github.com/payloadcms/payload/issues/3108)) ([ea73e68](https://github.com/payloadcms/payload/commit/ea73e689ac46f2a7ba3b6c34e7a190944b5d5868))
|
||||
* option to pre-fill login credentials automatically ([#3021](https://github.com/payloadcms/payload/issues/3021)) ([c5756ed](https://github.com/payloadcms/payload/commit/c5756ed4a13b46bc73ae7b23309d6e9980fc81bf))
|
||||
* programmatic control over array and block rows inside the form ([#3110](https://github.com/payloadcms/payload/issues/3110)) ([a78c463](https://github.com/payloadcms/payload/commit/a78c4631b4aabb5b57448ab21ef98749b1cf1935))
|
||||
* set JWT token field name with saveToJWT ([#3126](https://github.com/payloadcms/payload/issues/3126)) ([356f174](https://github.com/payloadcms/payload/commit/356f174b9ff601facb0062d0b65db18803ef2aa2))
|
||||
- add support for sharp resize options ([#2844](https://github.com/payloadcms/payload/issues/2844)) ([144bb81](https://github.com/payloadcms/payload/commit/144bb81721814c19eb4957d4c8fcc845c73e2aa4))
|
||||
- allows for upload relationship drawer to be opened ([#3108](https://github.com/payloadcms/payload/issues/3108)) ([ea73e68](https://github.com/payloadcms/payload/commit/ea73e689ac46f2a7ba3b6c34e7a190944b5d5868))
|
||||
- option to pre-fill login credentials automatically ([#3021](https://github.com/payloadcms/payload/issues/3021)) ([c5756ed](https://github.com/payloadcms/payload/commit/c5756ed4a13b46bc73ae7b23309d6e9980fc81bf))
|
||||
- programmatic control over array and block rows inside the form ([#3110](https://github.com/payloadcms/payload/issues/3110)) ([a78c463](https://github.com/payloadcms/payload/commit/a78c4631b4aabb5b57448ab21ef98749b1cf1935))
|
||||
- set JWT token field name with saveToJWT ([#3126](https://github.com/payloadcms/payload/issues/3126)) ([356f174](https://github.com/payloadcms/payload/commit/356f174b9ff601facb0062d0b65db18803ef2aa2))
|
||||
|
||||
## [1.11.8](https://github.com/payloadcms/payload/compare/v1.11.7...v1.11.8) (2023-07-31)
|
||||
|
||||
## [1.11.7](https://github.com/payloadcms/payload/compare/v1.11.6...v1.11.7) (2023-07-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#3062](https://github.com/payloadcms/payload/issues/3062) ([0280953](https://github.com/payloadcms/payload/commit/02809532b484d9018c6528cfbbbb43abfd55a540))
|
||||
* array row deletion ([#3062](https://github.com/payloadcms/payload/issues/3062)) ([cf9795b](https://github.com/payloadcms/payload/commit/cf9795b8d8b53c48335ff4c32c6c51b3de4f7bc9))
|
||||
* incorrect image rotation after being processed by sharp ([#3081](https://github.com/payloadcms/payload/issues/3081)) ([0a91950](https://github.com/payloadcms/payload/commit/0a91950f052ce40427801e6561a0f676354a2ca4))
|
||||
|
||||
- [#3062](https://github.com/payloadcms/payload/issues/3062) ([0280953](https://github.com/payloadcms/payload/commit/02809532b484d9018c6528cfbbbb43abfd55a540))
|
||||
- array row deletion ([#3062](https://github.com/payloadcms/payload/issues/3062)) ([cf9795b](https://github.com/payloadcms/payload/commit/cf9795b8d8b53c48335ff4c32c6c51b3de4f7bc9))
|
||||
- incorrect image rotation after being processed by sharp ([#3081](https://github.com/payloadcms/payload/issues/3081)) ([0a91950](https://github.com/payloadcms/payload/commit/0a91950f052ce40427801e6561a0f676354a2ca4))
|
||||
|
||||
### Features
|
||||
|
||||
* ability to add context to payload's request object ([#2796](https://github.com/payloadcms/payload/issues/2796)) ([67ba131](https://github.com/payloadcms/payload/commit/67ba131cc61f3d3b30ef9ef7fc150344ca82da2f))
|
||||
- ability to add context to payload's request object ([#2796](https://github.com/payloadcms/payload/issues/2796)) ([67ba131](https://github.com/payloadcms/payload/commit/67ba131cc61f3d3b30ef9ef7fc150344ca82da2f))
|
||||
|
||||
## [1.11.6](https://github.com/payloadcms/payload/compare/v1.11.5...v1.11.6) (2023-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **collections:admin:** Enable adminThumbnail fn execution on all types ([2c74e93](https://github.com/payloadcms/payload/commit/2c74e9396a216a033e2bacdf189b7f28a0f97505))
|
||||
* threads hasMaxRows into ArrayAction components within blocks and arrays ([#3066](https://github.com/payloadcms/payload/issues/3066)) ([d43c83d](https://github.com/payloadcms/payload/commit/d43c83dad1bab5b05f4fcbae7d41de369905797c))
|
||||
- **collections:admin:** Enable adminThumbnail fn execution on all types ([2c74e93](https://github.com/payloadcms/payload/commit/2c74e9396a216a033e2bacdf189b7f28a0f97505))
|
||||
- threads hasMaxRows into ArrayAction components within blocks and arrays ([#3066](https://github.com/payloadcms/payload/issues/3066)) ([d43c83d](https://github.com/payloadcms/payload/commit/d43c83dad1bab5b05f4fcbae7d41de369905797c))
|
||||
|
||||
## [1.11.5](https://github.com/payloadcms/payload/compare/v1.11.4...v1.11.5) (2023-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* admin route not mounting on production serve ([#3071](https://github.com/payloadcms/payload/issues/3071)) ([e718668](https://github.com/payloadcms/payload/commit/e71866856fffefcfb61dd3d29135cccb66939a62))
|
||||
- admin route not mounting on production serve ([#3071](https://github.com/payloadcms/payload/issues/3071)) ([e718668](https://github.com/payloadcms/payload/commit/e71866856fffefcfb61dd3d29135cccb66939a62))
|
||||
|
||||
## [1.11.4](https://github.com/payloadcms/payload/compare/v1.11.3...v1.11.4) (2023-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* if arrayFieldType rows are undefined, page would crash ([#3049](https://github.com/payloadcms/payload/issues/3049)) ([08377cc](https://github.com/payloadcms/payload/commit/08377cc5a7ea9d02350177e2e1d69390ee97af78))
|
||||
|
||||
- if arrayFieldType rows are undefined, page would crash ([#3049](https://github.com/payloadcms/payload/issues/3049)) ([08377cc](https://github.com/payloadcms/payload/commit/08377cc5a7ea9d02350177e2e1d69390ee97af78))
|
||||
|
||||
### Features
|
||||
|
||||
* bump mongoose and mongoose-paginate versions ([#3025](https://github.com/payloadcms/payload/issues/3025)) ([41d3eee](https://github.com/payloadcms/payload/commit/41d3eee35f3855798a5c3372f8ad7c742a7810f7))
|
||||
* improve keyboard focus styles ([#3011](https://github.com/payloadcms/payload/issues/3011)) ([080e619](https://github.com/payloadcms/payload/commit/080e6195ef39ec858fbb115e8f554a8dfc436438))
|
||||
* solidifies bundler adapter pattern ([#3044](https://github.com/payloadcms/payload/issues/3044)) ([641c765](https://github.com/payloadcms/payload/commit/641c765fb921e162c98f09218929348037dd0f88))
|
||||
- bump mongoose and mongoose-paginate versions ([#3025](https://github.com/payloadcms/payload/issues/3025)) ([41d3eee](https://github.com/payloadcms/payload/commit/41d3eee35f3855798a5c3372f8ad7c742a7810f7))
|
||||
- improve keyboard focus styles ([#3011](https://github.com/payloadcms/payload/issues/3011)) ([080e619](https://github.com/payloadcms/payload/commit/080e6195ef39ec858fbb115e8f554a8dfc436438))
|
||||
- solidifies bundler adapter pattern ([#3044](https://github.com/payloadcms/payload/issues/3044)) ([641c765](https://github.com/payloadcms/payload/commit/641c765fb921e162c98f09218929348037dd0f88))
|
||||
|
||||
## [1.11.3](https://github.com/payloadcms/payload/compare/v1.11.2...v1.11.3) (2023-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds backdrop blur to button ([#3006](https://github.com/payloadcms/payload/issues/3006)) ([4233426](https://github.com/payloadcms/payload/commit/42334263bbc6219be92c5728f1a4ac6c8d2d1306))
|
||||
* rich text link element not validating on create ([#3014](https://github.com/payloadcms/payload/issues/3014)) ([60fca40](https://github.com/payloadcms/payload/commit/60fca40780d4ddd8e684a455de55c566ec91e223))
|
||||
|
||||
- adds backdrop blur to button ([#3006](https://github.com/payloadcms/payload/issues/3006)) ([4233426](https://github.com/payloadcms/payload/commit/42334263bbc6219be92c5728f1a4ac6c8d2d1306))
|
||||
- rich text link element not validating on create ([#3014](https://github.com/payloadcms/payload/issues/3014)) ([60fca40](https://github.com/payloadcms/payload/commit/60fca40780d4ddd8e684a455de55c566ec91e223))
|
||||
|
||||
### Features
|
||||
|
||||
* auto-login in config capability ([#3009](https://github.com/payloadcms/payload/issues/3009)) ([733fc0b](https://github.com/payloadcms/payload/commit/733fc0b2d0cf0f2d58c8a28e84776f883774b0e0))
|
||||
* returns queried user alongside refreshed token ([#2813](https://github.com/payloadcms/payload/issues/2813)) ([2fc03f1](https://github.com/payloadcms/payload/commit/2fc03f196e4e5fa0ad3369ec976c0b6889ebda88))
|
||||
* support logger destination ([#2896](https://github.com/payloadcms/payload/issues/2896)) ([cd0bf68](https://github.com/payloadcms/payload/commit/cd0bf68a6150b1adbdb9ee318ac0a06c4476aa4d))
|
||||
- auto-login in config capability ([#3009](https://github.com/payloadcms/payload/issues/3009)) ([733fc0b](https://github.com/payloadcms/payload/commit/733fc0b2d0cf0f2d58c8a28e84776f883774b0e0))
|
||||
- returns queried user alongside refreshed token ([#2813](https://github.com/payloadcms/payload/issues/2813)) ([2fc03f1](https://github.com/payloadcms/payload/commit/2fc03f196e4e5fa0ad3369ec976c0b6889ebda88))
|
||||
- support logger destination ([#2896](https://github.com/payloadcms/payload/issues/2896)) ([cd0bf68](https://github.com/payloadcms/payload/commit/cd0bf68a6150b1adbdb9ee318ac0a06c4476aa4d))
|
||||
|
||||
## [1.11.2](https://github.com/payloadcms/payload/compare/v1.11.1...v1.11.2) (2023-07-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds array, collapsible, tab and group error states ([4925f90](https://github.com/payloadcms/payload/commit/4925f90b5f5c8fb8092bf4e8d88d5e0c1846b094))
|
||||
- adds array, collapsible, tab and group error states ([4925f90](https://github.com/payloadcms/payload/commit/4925f90b5f5c8fb8092bf4e8d88d5e0c1846b094))
|
||||
|
||||
## [1.11.1](https://github.com/payloadcms/payload/compare/v1.11.0...v1.11.1) (2023-07-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2980](https://github.com/payloadcms/payload/issues/2980), locale=all was not iterating through arrays / blocks ([d6bfba7](https://github.com/payloadcms/payload/commit/d6bfba72a6b1a84bc5bb9dd14c7ce31d7afcbc1c))
|
||||
* anchor Button component respect margins ([#2648](https://github.com/payloadcms/payload/issues/2648)) ([1877d22](https://github.com/payloadcms/payload/commit/1877d2247c89ca5c8e1f0e1f989154d54768fed8))
|
||||
- [#2980](https://github.com/payloadcms/payload/issues/2980), locale=all was not iterating through arrays / blocks ([d6bfba7](https://github.com/payloadcms/payload/commit/d6bfba72a6b1a84bc5bb9dd14c7ce31d7afcbc1c))
|
||||
- anchor Button component respect margins ([#2648](https://github.com/payloadcms/payload/issues/2648)) ([1877d22](https://github.com/payloadcms/payload/commit/1877d2247c89ca5c8e1f0e1f989154d54768fed8))
|
||||
|
||||
# [1.11.0](https://github.com/payloadcms/payload/compare/v1.10.5...v1.11.0) (2023-07-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensures fields within blocks respect field level access control ([#2969](https://github.com/payloadcms/payload/issues/2969)) ([5b79067](https://github.com/payloadcms/payload/commit/5b79067cc14874abbd1e1a5b6e619d41571b187f))
|
||||
* ensures rows always have id's ([#2968](https://github.com/payloadcms/payload/issues/2968)) ([04851d0](https://github.com/payloadcms/payload/commit/04851d0dc99e4a3df0a1ac642e9a4b9a3c06d8a1))
|
||||
* GraphQL type for number field ([#2954](https://github.com/payloadcms/payload/issues/2954)) ([29d8bf0](https://github.com/payloadcms/payload/commit/29d8bf0927038d2305218e5a6b811e0c4039d617))
|
||||
* nested richtext bug and test ([#2966](https://github.com/payloadcms/payload/issues/2966)) ([801f609](https://github.com/payloadcms/payload/commit/801f60939b1bb4e33fbabe1f9a3c4a04a47912db))
|
||||
* properly threads custom react-select props through relationship field ([#2973](https://github.com/payloadcms/payload/issues/2973)) ([79393e8](https://github.com/payloadcms/payload/commit/79393e8cf0b79b31fa711536e0bc22b1a251468a))
|
||||
|
||||
- ensures fields within blocks respect field level access control ([#2969](https://github.com/payloadcms/payload/issues/2969)) ([5b79067](https://github.com/payloadcms/payload/commit/5b79067cc14874abbd1e1a5b6e619d41571b187f))
|
||||
- ensures rows always have id's ([#2968](https://github.com/payloadcms/payload/issues/2968)) ([04851d0](https://github.com/payloadcms/payload/commit/04851d0dc99e4a3df0a1ac642e9a4b9a3c06d8a1))
|
||||
- GraphQL type for number field ([#2954](https://github.com/payloadcms/payload/issues/2954)) ([29d8bf0](https://github.com/payloadcms/payload/commit/29d8bf0927038d2305218e5a6b811e0c4039d617))
|
||||
- nested richtext bug and test ([#2966](https://github.com/payloadcms/payload/issues/2966)) ([801f609](https://github.com/payloadcms/payload/commit/801f60939b1bb4e33fbabe1f9a3c4a04a47912db))
|
||||
- properly threads custom react-select props through relationship field ([#2973](https://github.com/payloadcms/payload/issues/2973)) ([79393e8](https://github.com/payloadcms/payload/commit/79393e8cf0b79b31fa711536e0bc22b1a251468a))
|
||||
|
||||
### Features
|
||||
|
||||
* improve typing of ExtendableError and APIError ([#2864](https://github.com/payloadcms/payload/issues/2864)) ([7c47e4b](https://github.com/payloadcms/payload/commit/7c47e4b0d3c63f6f7800daaf424935d6067ffcc4))
|
||||
* narrow endpoint.method type ([#1880](https://github.com/payloadcms/payload/issues/1880)) ([b734a1c](https://github.com/payloadcms/payload/commit/b734a1c422d200cad1085b7e92f8540df4238e32))
|
||||
- improve typing of ExtendableError and APIError ([#2864](https://github.com/payloadcms/payload/issues/2864)) ([7c47e4b](https://github.com/payloadcms/payload/commit/7c47e4b0d3c63f6f7800daaf424935d6067ffcc4))
|
||||
- narrow endpoint.method type ([#1880](https://github.com/payloadcms/payload/issues/1880)) ([b734a1c](https://github.com/payloadcms/payload/commit/b734a1c422d200cad1085b7e92f8540df4238e32))
|
||||
|
||||
## [1.10.5](https://github.com/payloadcms/payload/compare/v1.10.4...v1.10.5) (2023-06-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fields in drawer cannot be edited ([#2949](https://github.com/payloadcms/payload/issues/2949)) ([0c2e41c](https://github.com/payloadcms/payload/commit/0c2e41c4bef9333c47a9b1db0de807696b3f3872)), closes [#2945](https://github.com/payloadcms/payload/issues/2945)
|
||||
* improve versions test suite ([#2941](https://github.com/payloadcms/payload/issues/2941)) ([1d4df99](https://github.com/payloadcms/payload/commit/1d4df99ea78c5f682074ae824dcd8dea18b774e0))
|
||||
* incorrect graphql type generation ([#2898](https://github.com/payloadcms/payload/issues/2898)) ([b36deb4](https://github.com/payloadcms/payload/commit/b36deb4640cad4f494a12ab74b4e4d9a918cd94b))
|
||||
- fields in drawer cannot be edited ([#2949](https://github.com/payloadcms/payload/issues/2949)) ([0c2e41c](https://github.com/payloadcms/payload/commit/0c2e41c4bef9333c47a9b1db0de807696b3f3872)), closes [#2945](https://github.com/payloadcms/payload/issues/2945)
|
||||
- improve versions test suite ([#2941](https://github.com/payloadcms/payload/issues/2941)) ([1d4df99](https://github.com/payloadcms/payload/commit/1d4df99ea78c5f682074ae824dcd8dea18b774e0))
|
||||
- incorrect graphql type generation ([#2898](https://github.com/payloadcms/payload/issues/2898)) ([b36deb4](https://github.com/payloadcms/payload/commit/b36deb4640cad4f494a12ab74b4e4d9a918cd94b))
|
||||
|
||||
## [1.10.4](https://github.com/payloadcms/payload/compare/v1.10.3...v1.10.4) (2023-06-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add locale to displayed API URL ([b22d157](https://github.com/payloadcms/payload/commit/b22d157bd2f1c1a857e2d42bdc5b893549e3db9e))
|
||||
- add locale to displayed API URL ([b22d157](https://github.com/payloadcms/payload/commit/b22d157bd2f1c1a857e2d42bdc5b893549e3db9e))
|
||||
|
||||
## [1.10.3](https://github.com/payloadcms/payload/compare/v1.10.2...v1.10.3) (2023-06-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2937](https://github.com/payloadcms/payload/issues/2937), depth not being respected in graphql rich text fields ([f84b432](https://github.com/payloadcms/payload/commit/f84b4323e2fce57e2e14b181e486ed72cc09ded5))
|
||||
* shows updatedAt date when selecting a version to compare from dropdown ([3c9dab3](https://github.com/payloadcms/payload/commit/3c9dab3b9d5302d8bdf5792f0384cd5aeeb13839))
|
||||
- [#2937](https://github.com/payloadcms/payload/issues/2937), depth not being respected in graphql rich text fields ([f84b432](https://github.com/payloadcms/payload/commit/f84b4323e2fce57e2e14b181e486ed72cc09ded5))
|
||||
- shows updatedAt date when selecting a version to compare from dropdown ([3c9dab3](https://github.com/payloadcms/payload/commit/3c9dab3b9d5302d8bdf5792f0384cd5aeeb13839))
|
||||
|
||||
## [1.10.2](https://github.com/payloadcms/payload/compare/v1.10.1...v1.10.2) (2023-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjusts swc loader to only exclude non ts/tsx files - [#2888](https://github.com/payloadcms/payload/issues/2888) ([#2907](https://github.com/payloadcms/payload/issues/2907)) ([a2d9ef3](https://github.com/payloadcms/payload/commit/a2d9ef3ca618934df58102a7e02e86dbe0ed63da))
|
||||
* autosave on localized fields, adds test ([6893231](https://github.com/payloadcms/payload/commit/6893231f85f702189089a6d78d3f3af63aaa0d82))
|
||||
* broken export of entityToJSONSchema ([#2894](https://github.com/payloadcms/payload/issues/2894)) ([837dccc](https://github.com/payloadcms/payload/commit/837dcccefeffe7bb6e674713b4184c4eb92db8dc))
|
||||
* correctly scopes data variable within bulk update - [#2901](https://github.com/payloadcms/payload/issues/2901) ([#2904](https://github.com/payloadcms/payload/issues/2904)) ([f627277](https://github.com/payloadcms/payload/commit/f627277479e6a4a847e79f54c545712a7186abb9))
|
||||
* safely check for tempFilePath when updating media document ([#2899](https://github.com/payloadcms/payload/issues/2899)) ([8206c0f](https://github.com/payloadcms/payload/commit/8206c0fe8be78a5e0f7c8e64996d73d135b1fcc2))
|
||||
- adjusts swc loader to only exclude non ts/tsx files - [#2888](https://github.com/payloadcms/payload/issues/2888) ([#2907](https://github.com/payloadcms/payload/issues/2907)) ([a2d9ef3](https://github.com/payloadcms/payload/commit/a2d9ef3ca618934df58102a7e02e86dbe0ed63da))
|
||||
- autosave on localized fields, adds test ([6893231](https://github.com/payloadcms/payload/commit/6893231f85f702189089a6d78d3f3af63aaa0d82))
|
||||
- broken export of entityToJSONSchema ([#2894](https://github.com/payloadcms/payload/issues/2894)) ([837dccc](https://github.com/payloadcms/payload/commit/837dcccefeffe7bb6e674713b4184c4eb92db8dc))
|
||||
- correctly scopes data variable within bulk update - [#2901](https://github.com/payloadcms/payload/issues/2901) ([#2904](https://github.com/payloadcms/payload/issues/2904)) ([f627277](https://github.com/payloadcms/payload/commit/f627277479e6a4a847e79f54c545712a7186abb9))
|
||||
- safely check for tempFilePath when updating media document ([#2899](https://github.com/payloadcms/payload/issues/2899)) ([8206c0f](https://github.com/payloadcms/payload/commit/8206c0fe8be78a5e0f7c8e64996d73d135b1fcc2))
|
||||
|
||||
## [1.10.1](https://github.com/payloadcms/payload/compare/v1.10.0...v1.10.1) (2023-06-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* conditional fields perf bug - [#2886](https://github.com/payloadcms/payload/issues/2886) ([#2890](https://github.com/payloadcms/payload/issues/2890)) ([b83d788](https://github.com/payloadcms/payload/commit/b83d788d3cfe12f87dcd63a9df20b939a6f4681e))
|
||||
* cutoff tooltips in relationship field ([#2873](https://github.com/payloadcms/payload/issues/2873)) ([09c6cad](https://github.com/payloadcms/payload/commit/09c6cad3e8462dc3d8b1b6424aafd336c1d7828c))
|
||||
* Relationship hasMany and filterOptions fails above 10 items ([#2891](https://github.com/payloadcms/payload/issues/2891)) ([8128de6](https://github.com/payloadcms/payload/commit/8128de64dff98fdbcf053faef9de3c3f9a733071))
|
||||
- conditional fields perf bug - [#2886](https://github.com/payloadcms/payload/issues/2886) ([#2890](https://github.com/payloadcms/payload/issues/2890)) ([b83d788](https://github.com/payloadcms/payload/commit/b83d788d3cfe12f87dcd63a9df20b939a6f4681e))
|
||||
- cutoff tooltips in relationship field ([#2873](https://github.com/payloadcms/payload/issues/2873)) ([09c6cad](https://github.com/payloadcms/payload/commit/09c6cad3e8462dc3d8b1b6424aafd336c1d7828c))
|
||||
- Relationship hasMany and filterOptions fails above 10 items ([#2891](https://github.com/payloadcms/payload/issues/2891)) ([8128de6](https://github.com/payloadcms/payload/commit/8128de64dff98fdbcf053faef9de3c3f9a733071))
|
||||
|
||||
# [1.10.0](https://github.com/payloadcms/payload/compare/v1.9.5...v1.10.0) (2023-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2831](https://github.com/payloadcms/payload/issues/2831), persists payloadAPI through local operations that accept req ([85d2467](https://github.com/payloadcms/payload/commit/85d2467d73582a372ee34e3ce93403847a1f0689))
|
||||
* [#2842](https://github.com/payloadcms/payload/issues/2842), querying number custom ids with in ([116e9ff](https://github.com/payloadcms/payload/commit/116e9ffe81f44c4b40fa578b4a8fe4bb70fd110c))
|
||||
* default sort with near operator ([#2862](https://github.com/payloadcms/payload/issues/2862)) ([99f3809](https://github.com/payloadcms/payload/commit/99f38098dd4a386437c469becc975ca86c54601f))
|
||||
* deprecate min/max in exchange for minRows and maxRows for relationship field ([#2826](https://github.com/payloadcms/payload/issues/2826)) ([0d8d7f3](https://github.com/payloadcms/payload/commit/0d8d7f358d390184f6f888d77858b4a145e94214))
|
||||
* drawer close on backspace ([#2869](https://github.com/payloadcms/payload/issues/2869)) ([a110ba2](https://github.com/payloadcms/payload/commit/a110ba2dc09cd0824a9b1eb8e011604388277bd8))
|
||||
* drawer fields are read-only if opened from a hasMany relationship ([#2843](https://github.com/payloadcms/payload/issues/2843)) ([542b536](https://github.com/payloadcms/payload/commit/542b5362d3ec8741aff6b1672fab7d2250e7b854))
|
||||
* fields in relationship drawer not usable [#2815](https://github.com/payloadcms/payload/issues/2815) ([#2870](https://github.com/payloadcms/payload/issues/2870)) ([8626dc6](https://github.com/payloadcms/payload/commit/8626dc6b1a926143e7ba505f3edd924432168675))
|
||||
* mobile loading overlay width [#2866](https://github.com/payloadcms/payload/issues/2866) ([#2867](https://github.com/payloadcms/payload/issues/2867)) ([ba9d633](https://github.com/payloadcms/payload/commit/ba9d6336acc779cfec0db312c8e2da912ce58cd4))
|
||||
* near query sorting by distance and pagination ([#2861](https://github.com/payloadcms/payload/issues/2861)) ([1611896](https://github.com/payloadcms/payload/commit/16118960aa6d63f7a429f168ff4305f336b1b1e6))
|
||||
* relationship field query pagination ([#2871](https://github.com/payloadcms/payload/issues/2871)) ([ce84174](https://github.com/payloadcms/payload/commit/ce84174554d9d828cbaaaa9548e5defc0feb4e2b))
|
||||
* slow like queries with lots of records ([4dd703a](https://github.com/payloadcms/payload/commit/4dd703a6bff0ab7d06af234baa975553bd62f176))
|
||||
|
||||
- [#2831](https://github.com/payloadcms/payload/issues/2831), persists payloadAPI through local operations that accept req ([85d2467](https://github.com/payloadcms/payload/commit/85d2467d73582a372ee34e3ce93403847a1f0689))
|
||||
- [#2842](https://github.com/payloadcms/payload/issues/2842), querying number custom ids with in ([116e9ff](https://github.com/payloadcms/payload/commit/116e9ffe81f44c4b40fa578b4a8fe4bb70fd110c))
|
||||
- default sort with near operator ([#2862](https://github.com/payloadcms/payload/issues/2862)) ([99f3809](https://github.com/payloadcms/payload/commit/99f38098dd4a386437c469becc975ca86c54601f))
|
||||
- deprecate min/max in exchange for minRows and maxRows for relationship field ([#2826](https://github.com/payloadcms/payload/issues/2826)) ([0d8d7f3](https://github.com/payloadcms/payload/commit/0d8d7f358d390184f6f888d77858b4a145e94214))
|
||||
- drawer close on backspace ([#2869](https://github.com/payloadcms/payload/issues/2869)) ([a110ba2](https://github.com/payloadcms/payload/commit/a110ba2dc09cd0824a9b1eb8e011604388277bd8))
|
||||
- drawer fields are read-only if opened from a hasMany relationship ([#2843](https://github.com/payloadcms/payload/issues/2843)) ([542b536](https://github.com/payloadcms/payload/commit/542b5362d3ec8741aff6b1672fab7d2250e7b854))
|
||||
- fields in relationship drawer not usable [#2815](https://github.com/payloadcms/payload/issues/2815) ([#2870](https://github.com/payloadcms/payload/issues/2870)) ([8626dc6](https://github.com/payloadcms/payload/commit/8626dc6b1a926143e7ba505f3edd924432168675))
|
||||
- mobile loading overlay width [#2866](https://github.com/payloadcms/payload/issues/2866) ([#2867](https://github.com/payloadcms/payload/issues/2867)) ([ba9d633](https://github.com/payloadcms/payload/commit/ba9d6336acc779cfec0db312c8e2da912ce58cd4))
|
||||
- near query sorting by distance and pagination ([#2861](https://github.com/payloadcms/payload/issues/2861)) ([1611896](https://github.com/payloadcms/payload/commit/16118960aa6d63f7a429f168ff4305f336b1b1e6))
|
||||
- relationship field query pagination ([#2871](https://github.com/payloadcms/payload/issues/2871)) ([ce84174](https://github.com/payloadcms/payload/commit/ce84174554d9d828cbaaaa9548e5defc0feb4e2b))
|
||||
- slow like queries with lots of records ([4dd703a](https://github.com/payloadcms/payload/commit/4dd703a6bff0ab7d06af234baa975553bd62f176))
|
||||
|
||||
### Features
|
||||
|
||||
* automatically redirect a user back to their originally requested URL after login ([#2838](https://github.com/payloadcms/payload/issues/2838)) ([e910688](https://github.com/payloadcms/payload/commit/e9106882f721d43bcc05a1690bda7754b450404e))
|
||||
* hasMany for number field ([#2517](https://github.com/payloadcms/payload/issues/2517)) ([8f086e3](https://github.com/payloadcms/payload/commit/8f086e315cb30be9d399fd3022c16952fb81cb2e)), closes [#2812](https://github.com/payloadcms/payload/issues/2812) [#2821](https://github.com/payloadcms/payload/issues/2821) [#2823](https://github.com/payloadcms/payload/issues/2823) [#2824](https://github.com/payloadcms/payload/issues/2824) [#2814](https://github.com/payloadcms/payload/issues/2814) [#2793](https://github.com/payloadcms/payload/issues/2793) [#2835](https://github.com/payloadcms/payload/issues/2835)
|
||||
* optimizes conditional logic performance ([967f217](https://github.com/payloadcms/payload/commit/967f21734600de1fec8c1227a354ef5a417e54c5))
|
||||
- automatically redirect a user back to their originally requested URL after login ([#2838](https://github.com/payloadcms/payload/issues/2838)) ([e910688](https://github.com/payloadcms/payload/commit/e9106882f721d43bcc05a1690bda7754b450404e))
|
||||
- hasMany for number field ([#2517](https://github.com/payloadcms/payload/issues/2517)) ([8f086e3](https://github.com/payloadcms/payload/commit/8f086e315cb30be9d399fd3022c16952fb81cb2e)), closes [#2812](https://github.com/payloadcms/payload/issues/2812) [#2821](https://github.com/payloadcms/payload/issues/2821) [#2823](https://github.com/payloadcms/payload/issues/2823) [#2824](https://github.com/payloadcms/payload/issues/2824) [#2814](https://github.com/payloadcms/payload/issues/2814) [#2793](https://github.com/payloadcms/payload/issues/2793) [#2835](https://github.com/payloadcms/payload/issues/2835)
|
||||
- optimizes conditional logic performance ([967f217](https://github.com/payloadcms/payload/commit/967f21734600de1fec8c1227a354ef5a417e54c5))
|
||||
|
||||
## [1.9.5](https://github.com/payloadcms/payload/compare/v1.9.4...v1.9.5) (2023-06-16)
|
||||
|
||||
## [1.9.4](https://github.com/payloadcms/payload/compare/v1.9.3...v1.9.4) (2023-06-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* incorrectly return totalDocs=1 instead of the correct count when pagination=false ([2e73938](https://github.com/payloadcms/payload/commit/2e7393853447d2da41ddef79f73e9026719a674b))
|
||||
- incorrectly return totalDocs=1 instead of the correct count when pagination=false ([2e73938](https://github.com/payloadcms/payload/commit/2e7393853447d2da41ddef79f73e9026719a674b))
|
||||
|
||||
## [1.9.3](https://github.com/payloadcms/payload/compare/v1.9.2...v1.9.3) (2023-06-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds custom property to ui field in joi validation ([#2835](https://github.com/payloadcms/payload/issues/2835)) ([56d7745](https://github.com/payloadcms/payload/commit/56d7745139e31c5d42c5191477f409f12589a952))
|
||||
* ensures relations to object ids can be queried on ([c3d6e1b](https://github.com/payloadcms/payload/commit/c3d6e1b490a69f0aadb00e54e46a8774732e6658))
|
||||
- adds custom property to ui field in joi validation ([#2835](https://github.com/payloadcms/payload/issues/2835)) ([56d7745](https://github.com/payloadcms/payload/commit/56d7745139e31c5d42c5191477f409f12589a952))
|
||||
- ensures relations to object ids can be queried on ([c3d6e1b](https://github.com/payloadcms/payload/commit/c3d6e1b490a69f0aadb00e54e46a8774732e6658))
|
||||
|
||||
## [1.9.2](https://github.com/payloadcms/payload/compare/v1.9.1...v1.9.2) (2023-06-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2821](https://github.com/payloadcms/payload/issues/2821) i18n ui field label ([#2823](https://github.com/payloadcms/payload/issues/2823)) ([63cd7fb](https://github.com/payloadcms/payload/commit/63cd7fbd0c91bbf5120e95fd33388a38e593b341))
|
||||
* adds missing dark-mode styles for version differences view ([#2812](https://github.com/payloadcms/payload/issues/2812)) ([346a48f](https://github.com/payloadcms/payload/commit/346a48f871e09a3d5e25b7ff9e45689a104b0f9f))
|
||||
* sanitize reset password result - [#2805](https://github.com/payloadcms/payload/issues/2805) ([#2808](https://github.com/payloadcms/payload/issues/2808)) ([46a5f41](https://github.com/payloadcms/payload/commit/46a5f417217313b049f4b412abb3319634f27262))
|
||||
* user can be created without having to specify an email - [#2801](https://github.com/payloadcms/payload/issues/2801) ([abe3852](https://github.com/payloadcms/payload/commit/abe38520aaaefdfaea4c47130eea04a42a82627b))
|
||||
- [#2821](https://github.com/payloadcms/payload/issues/2821) i18n ui field label ([#2823](https://github.com/payloadcms/payload/issues/2823)) ([63cd7fb](https://github.com/payloadcms/payload/commit/63cd7fbd0c91bbf5120e95fd33388a38e593b341))
|
||||
- adds missing dark-mode styles for version differences view ([#2812](https://github.com/payloadcms/payload/issues/2812)) ([346a48f](https://github.com/payloadcms/payload/commit/346a48f871e09a3d5e25b7ff9e45689a104b0f9f))
|
||||
- sanitize reset password result - [#2805](https://github.com/payloadcms/payload/issues/2805) ([#2808](https://github.com/payloadcms/payload/issues/2808)) ([46a5f41](https://github.com/payloadcms/payload/commit/46a5f417217313b049f4b412abb3319634f27262))
|
||||
- user can be created without having to specify an email - [#2801](https://github.com/payloadcms/payload/issues/2801) ([abe3852](https://github.com/payloadcms/payload/commit/abe38520aaaefdfaea4c47130eea04a42a82627b))
|
||||
|
||||
## [1.9.1](https://github.com/payloadcms/payload/compare/v1.9.0...v1.9.1) (2023-06-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds option to customize filename on upload ([596eea1](https://github.com/payloadcms/payload/commit/596eea1f0a42628464e5269c496360b808c35f97))
|
||||
* collection list view custom components: BeforeList, BeforeListTable, AfterListTable, AfterList ([#2792](https://github.com/payloadcms/payload/issues/2792)) ([38e962f](https://github.com/payloadcms/payload/commit/38e962f2cbcaf9eaa72276969289efdbf670c7c7))
|
||||
- adds option to customize filename on upload ([596eea1](https://github.com/payloadcms/payload/commit/596eea1f0a42628464e5269c496360b808c35f97))
|
||||
- collection list view custom components: BeforeList, BeforeListTable, AfterListTable, AfterList ([#2792](https://github.com/payloadcms/payload/issues/2792)) ([38e962f](https://github.com/payloadcms/payload/commit/38e962f2cbcaf9eaa72276969289efdbf670c7c7))
|
||||
|
||||
# [1.9.0](https://github.com/payloadcms/payload/compare/v1.8.6...v1.9.0) (2023-06-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* custom type interfaces ([#2709](https://github.com/payloadcms/payload/issues/2709)) ([8458a98](https://github.com/payloadcms/payload/commit/8458a98eff0eedf1abfd9ec065a084955a9b8149))
|
||||
- custom type interfaces ([#2709](https://github.com/payloadcms/payload/issues/2709)) ([8458a98](https://github.com/payloadcms/payload/commit/8458a98eff0eedf1abfd9ec065a084955a9b8149))
|
||||
|
||||
## [1.8.6](https://github.com/payloadcms/payload/compare/v1.8.5...v1.8.6) (2023-06-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2711](https://github.com/payloadcms/payload/issues/2711) index sortable field global versions fields ([#2775](https://github.com/payloadcms/payload/issues/2775)) ([576af01](https://github.com/payloadcms/payload/commit/576af01b6f81d24621d522e8d8b9c496eafa6df0))
|
||||
* [#2767](https://github.com/payloadcms/payload/issues/2767) bulk operations missing locales in admin requests ([e30871a](https://github.com/payloadcms/payload/commit/e30871a96ff25f12401a3cc3bc5e12c064eeff3f))
|
||||
* [#2771](https://github.com/payloadcms/payload/issues/2771) relationship field not querying all collections ([#2774](https://github.com/payloadcms/payload/issues/2774)) ([8b767a1](https://github.com/payloadcms/payload/commit/8b767a166aa16659d8880cc68da546251725b20b))
|
||||
* adjusts activation constraint of draggable nodes ([#2773](https://github.com/payloadcms/payload/issues/2773)) ([863be3d](https://github.com/payloadcms/payload/commit/863be3d852af6c6a76021695f895badf23e776ae))
|
||||
* flattens relationships in the update operation for globals [#2766](https://github.com/payloadcms/payload/issues/2766) ([#2776](https://github.com/payloadcms/payload/issues/2776)) ([3677cf6](https://github.com/payloadcms/payload/commit/3677cf688d0e456c42068b4eab0086e64407d938))
|
||||
* improperly typing optional arrays with required fields as required ([f1fc305](https://github.com/payloadcms/payload/commit/f1fc305ac443ecb247622bc89067b129e96146fc))
|
||||
* read-only Auth fields ([#2781](https://github.com/payloadcms/payload/issues/2781)) ([3c72f33](https://github.com/payloadcms/payload/commit/3c72f3303c57e88256266c343225157e0b081bba))
|
||||
* read-only Auth fields ([#2781](https://github.com/payloadcms/payload/issues/2781)) ([60f5522](https://github.com/payloadcms/payload/commit/60f5522e67acb353e6d5ce05f0012241c192d4b4))
|
||||
* recursiveNestedPaths not merging existing fields when hoisting row/collapsible fields ([#2769](https://github.com/payloadcms/payload/issues/2769)) ([536d701](https://github.com/payloadcms/payload/commit/536d7017eebd5a8e14b2936c55a7fccc90d3f530))
|
||||
- [#2711](https://github.com/payloadcms/payload/issues/2711) index sortable field global versions fields ([#2775](https://github.com/payloadcms/payload/issues/2775)) ([576af01](https://github.com/payloadcms/payload/commit/576af01b6f81d24621d522e8d8b9c496eafa6df0))
|
||||
- [#2767](https://github.com/payloadcms/payload/issues/2767) bulk operations missing locales in admin requests ([e30871a](https://github.com/payloadcms/payload/commit/e30871a96ff25f12401a3cc3bc5e12c064eeff3f))
|
||||
- [#2771](https://github.com/payloadcms/payload/issues/2771) relationship field not querying all collections ([#2774](https://github.com/payloadcms/payload/issues/2774)) ([8b767a1](https://github.com/payloadcms/payload/commit/8b767a166aa16659d8880cc68da546251725b20b))
|
||||
- adjusts activation constraint of draggable nodes ([#2773](https://github.com/payloadcms/payload/issues/2773)) ([863be3d](https://github.com/payloadcms/payload/commit/863be3d852af6c6a76021695f895badf23e776ae))
|
||||
- flattens relationships in the update operation for globals [#2766](https://github.com/payloadcms/payload/issues/2766) ([#2776](https://github.com/payloadcms/payload/issues/2776)) ([3677cf6](https://github.com/payloadcms/payload/commit/3677cf688d0e456c42068b4eab0086e64407d938))
|
||||
- improperly typing optional arrays with required fields as required ([f1fc305](https://github.com/payloadcms/payload/commit/f1fc305ac443ecb247622bc89067b129e96146fc))
|
||||
- read-only Auth fields ([#2781](https://github.com/payloadcms/payload/issues/2781)) ([3c72f33](https://github.com/payloadcms/payload/commit/3c72f3303c57e88256266c343225157e0b081bba))
|
||||
- read-only Auth fields ([#2781](https://github.com/payloadcms/payload/issues/2781)) ([60f5522](https://github.com/payloadcms/payload/commit/60f5522e67acb353e6d5ce05f0012241c192d4b4))
|
||||
- recursiveNestedPaths not merging existing fields when hoisting row/collapsible fields ([#2769](https://github.com/payloadcms/payload/issues/2769)) ([536d701](https://github.com/payloadcms/payload/commit/536d7017eebd5a8e14b2936c55a7fccc90d3f530))
|
||||
|
||||
## [1.8.5](https://github.com/payloadcms/payload/compare/v1.8.4...v1.8.5) (2023-06-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allows objectid through relationship validation ([42afa6b](https://github.com/payloadcms/payload/commit/42afa6b48aa924fa0dfc9defadf08ddb029da6c1))
|
||||
- allows objectid through relationship validation ([42afa6b](https://github.com/payloadcms/payload/commit/42afa6b48aa924fa0dfc9defadf08ddb029da6c1))
|
||||
|
||||
## [1.8.4](https://github.com/payloadcms/payload/compare/v1.8.3...v1.8.4) (2023-06-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Bulgarian translation ([#2753](https://github.com/payloadcms/payload/issues/2753)) ([51108c0](https://github.com/payloadcms/payload/commit/51108c02ea346fd41c1b94ef7c339feec8383dd1))
|
||||
|
||||
- Add Bulgarian translation ([#2753](https://github.com/payloadcms/payload/issues/2753)) ([51108c0](https://github.com/payloadcms/payload/commit/51108c02ea346fd41c1b94ef7c339feec8383dd1))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* group row hoisting ([#2683](https://github.com/payloadcms/payload/issues/2683)) ([1626e17](https://github.com/payloadcms/payload/commit/1626e173b7eced83c59e8eb4f70b0bb68fdb0e7a))
|
||||
* graphql where types on rows and collapsible's ([#2758](https://github.com/payloadcms/payload/issues/2758)) ([f978299](https://github.com/payloadcms/payload/commit/f978299868bf352e147070afdf556bf1153bac56))
|
||||
* RichText link custom fields ([#2756](https://github.com/payloadcms/payload/issues/2756)) ([23be263](https://github.com/payloadcms/payload/commit/23be263dd2e75dca448019b1c66d7f6dd3558b37))
|
||||
* adds timestamps to global schemas ([#2738](https://github.com/payloadcms/payload/issues/2738)) ([0986282](https://github.com/payloadcms/payload/commit/0986282f13d8a3b5596c4a241b4da35e6fac6aa1))
|
||||
* adjusts code field joi schema to allow editorOptions ([ed136fb](https://github.com/payloadcms/payload/commit/ed136fbc5146889cd30c641d4947da58b66dfb2f))
|
||||
* fix locale popup overflow ([#2737](https://github.com/payloadcms/payload/issues/2737)) ([8ee9724](https://github.com/payloadcms/payload/commit/8ee9724277d419de78b27a8ffa22f3a599361251))
|
||||
* fix tests by hard-coding the URL in the logger ([2697974](https://github.com/payloadcms/payload/commit/2697974694112440bf1737c4ce535ba77bf4b194))
|
||||
* mongoose connection ([#2754](https://github.com/payloadcms/payload/issues/2754)) ([69b97bb](https://github.com/payloadcms/payload/commit/69b97bbc590c62fffbcd03a42f0e9737e3f7ca01))
|
||||
* removes payload dependency inception ([#2717](https://github.com/payloadcms/payload/issues/2717)) ([6125b66](https://github.com/payloadcms/payload/commit/6125b66286e5315725ca0ae365c81a04c1c1a54c))
|
||||
* searches on correct useAsTitle field in polymorphic list drawers [#2710](https://github.com/payloadcms/payload/issues/2710) ([9ec2a40](https://github.com/payloadcms/payload/commit/9ec2a40274ea9b3a32e43cb992df3897baf62e63))
|
||||
* typing of sendMail function ([e3ff4c4](https://github.com/payloadcms/payload/commit/e3ff4c46cbecf731c9a3c688682bcb33012cb234))
|
||||
* corrects relationship field schema from pr [#2696](https://github.com/payloadcms/payload/issues/2696) ([#2714](https://github.com/payloadcms/payload/issues/2714)) ([8285bac](https://github.com/payloadcms/payload/commit/8285bac2f5eb443b6af160b21726edf3f828a52f))
|
||||
|
||||
- group row hoisting ([#2683](https://github.com/payloadcms/payload/issues/2683)) ([1626e17](https://github.com/payloadcms/payload/commit/1626e173b7eced83c59e8eb4f70b0bb68fdb0e7a))
|
||||
- graphql where types on rows and collapsible's ([#2758](https://github.com/payloadcms/payload/issues/2758)) ([f978299](https://github.com/payloadcms/payload/commit/f978299868bf352e147070afdf556bf1153bac56))
|
||||
- RichText link custom fields ([#2756](https://github.com/payloadcms/payload/issues/2756)) ([23be263](https://github.com/payloadcms/payload/commit/23be263dd2e75dca448019b1c66d7f6dd3558b37))
|
||||
- adds timestamps to global schemas ([#2738](https://github.com/payloadcms/payload/issues/2738)) ([0986282](https://github.com/payloadcms/payload/commit/0986282f13d8a3b5596c4a241b4da35e6fac6aa1))
|
||||
- adjusts code field joi schema to allow editorOptions ([ed136fb](https://github.com/payloadcms/payload/commit/ed136fbc5146889cd30c641d4947da58b66dfb2f))
|
||||
- fix locale popup overflow ([#2737](https://github.com/payloadcms/payload/issues/2737)) ([8ee9724](https://github.com/payloadcms/payload/commit/8ee9724277d419de78b27a8ffa22f3a599361251))
|
||||
- fix tests by hard-coding the URL in the logger ([2697974](https://github.com/payloadcms/payload/commit/2697974694112440bf1737c4ce535ba77bf4b194))
|
||||
- mongoose connection ([#2754](https://github.com/payloadcms/payload/issues/2754)) ([69b97bb](https://github.com/payloadcms/payload/commit/69b97bbc590c62fffbcd03a42f0e9737e3f7ca01))
|
||||
- removes payload dependency inception ([#2717](https://github.com/payloadcms/payload/issues/2717)) ([6125b66](https://github.com/payloadcms/payload/commit/6125b66286e5315725ca0ae365c81a04c1c1a54c))
|
||||
- searches on correct useAsTitle field in polymorphic list drawers [#2710](https://github.com/payloadcms/payload/issues/2710) ([9ec2a40](https://github.com/payloadcms/payload/commit/9ec2a40274ea9b3a32e43cb992df3897baf62e63))
|
||||
- typing of sendMail function ([e3ff4c4](https://github.com/payloadcms/payload/commit/e3ff4c46cbecf731c9a3c688682bcb33012cb234))
|
||||
- corrects relationship field schema from pr [#2696](https://github.com/payloadcms/payload/issues/2696) ([#2714](https://github.com/payloadcms/payload/issues/2714)) ([8285bac](https://github.com/payloadcms/payload/commit/8285bac2f5eb443b6af160b21726edf3f828a52f))
|
||||
|
||||
## [1.8.3](https://github.com/payloadcms/payload/compare/v1.8.3...v1.8.3) (2023-05-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2662](https://github.com/payloadcms/payload/issues/2662), draft=true querying by id ([3b78ab0](https://github.com/payloadcms/payload/commit/3b78ab04c7a68e39afa9936ac692169ed2c8fb74))
|
||||
* [#2685](https://github.com/payloadcms/payload/issues/2685), graphql querying relationships with custom id ([9bb5470](https://github.com/payloadcms/payload/commit/9bb54703423b3f0fdb242a5e63f322d346323b06))
|
||||
* adds credentials to doc access request ([#2705](https://github.com/payloadcms/payload/issues/2705)) ([c716954](https://github.com/payloadcms/payload/commit/c716954e89b0aef976cbcbef9ece981ec9bab233))
|
||||
* prevents add new relationship modal from adding duplicative values to the parent doc [#2688](https://github.com/payloadcms/payload/issues/2688) ([a2a8ac9](https://github.com/payloadcms/payload/commit/a2a8ac9549bd67e6ab578772689684fd2bc64872))
|
||||
* unable to clear relationships or open relationship drawer on mobile [#2691](https://github.com/payloadcms/payload/issues/2691) [#2692](https://github.com/payloadcms/payload/issues/2692) ([782f8ca](https://github.com/payloadcms/payload/commit/782f8ca047178cadb4214702854a0e0cb2d9eaab))
|
||||
- [#2662](https://github.com/payloadcms/payload/issues/2662), draft=true querying by id ([3b78ab0](https://github.com/payloadcms/payload/commit/3b78ab04c7a68e39afa9936ac692169ed2c8fb74))
|
||||
- [#2685](https://github.com/payloadcms/payload/issues/2685), graphql querying relationships with custom id ([9bb5470](https://github.com/payloadcms/payload/commit/9bb54703423b3f0fdb242a5e63f322d346323b06))
|
||||
- adds credentials to doc access request ([#2705](https://github.com/payloadcms/payload/issues/2705)) ([c716954](https://github.com/payloadcms/payload/commit/c716954e89b0aef976cbcbef9ece981ec9bab233))
|
||||
- prevents add new relationship modal from adding duplicative values to the parent doc [#2688](https://github.com/payloadcms/payload/issues/2688) ([a2a8ac9](https://github.com/payloadcms/payload/commit/a2a8ac9549bd67e6ab578772689684fd2bc64872))
|
||||
- unable to clear relationships or open relationship drawer on mobile [#2691](https://github.com/payloadcms/payload/issues/2691) [#2692](https://github.com/payloadcms/payload/issues/2692) ([782f8ca](https://github.com/payloadcms/payload/commit/782f8ca047178cadb4214702854a0e0cb2d9eaab))
|
||||
|
||||
## [1.8.2](https://github.com/payloadcms/payload/compare/v1.8.1...v1.8.2) (2023-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* react webpack alias ([1732bb8](https://github.com/payloadcms/payload/commit/1732bb877ca9688fc87cf44fbf63d05b6be23de2))
|
||||
- react webpack alias ([1732bb8](https://github.com/payloadcms/payload/commit/1732bb877ca9688fc87cf44fbf63d05b6be23de2))
|
||||
|
||||
## [1.8.1](https://github.com/payloadcms/payload/compare/v1.8.0...v1.8.1) (2023-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add dotenv.config() to test/dev.ts ([#2646](https://github.com/payloadcms/payload/issues/2646)) ([7963e75](https://github.com/payloadcms/payload/commit/7963e7540f4899c16a49b47cf5145f46ea0c71cf))
|
||||
|
||||
- add dotenv.config() to test/dev.ts ([#2646](https://github.com/payloadcms/payload/issues/2646)) ([7963e75](https://github.com/payloadcms/payload/commit/7963e7540f4899c16a49b47cf5145f46ea0c71cf))
|
||||
|
||||
### Features
|
||||
|
||||
* allow users to manipulate images without needing to resize them ([#2574](https://github.com/payloadcms/payload/issues/2574)) ([8531687](https://github.com/payloadcms/payload/commit/85316879cd97933ed34588b0cee72798964de281))
|
||||
* export additional graphql types ([#2610](https://github.com/payloadcms/payload/issues/2610)) ([3f185cb](https://github.com/payloadcms/payload/commit/3f185cb18b9677654b92921267ffef408388d0d1))
|
||||
- allow users to manipulate images without needing to resize them ([#2574](https://github.com/payloadcms/payload/issues/2574)) ([8531687](https://github.com/payloadcms/payload/commit/85316879cd97933ed34588b0cee72798964de281))
|
||||
- export additional graphql types ([#2610](https://github.com/payloadcms/payload/issues/2610)) ([3f185cb](https://github.com/payloadcms/payload/commit/3f185cb18b9677654b92921267ffef408388d0d1))
|
||||
|
||||
# [1.8.0](https://github.com/payloadcms/payload/compare/v1.7.5...v1.8.0) (2023-05-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correct casing on graphql type ([219f50b](https://github.com/payloadcms/payload/commit/219f50b0bc7a520655a5ae4f1d8b08fd04c8a3dd))
|
||||
* defaultValue missing from Upload field schema ([7b21eaf](https://github.com/payloadcms/payload/commit/7b21eaf12da64778568b45e56fa8d39e81f11c29))
|
||||
* ensures nested querying works when querying across collections ([09974fa](https://github.com/payloadcms/payload/commit/09974fa68677586c727943cc234311f87bf6da75))
|
||||
* query custom text id fields ([967f2ac](https://github.com/payloadcms/payload/commit/967f2ace0ea1a65570f69e85920f2f55626efde0))
|
||||
* removes deprecated queryHiddenFIelds from local API docs ([5f30dbb](https://github.com/payloadcms/payload/commit/5f30dbb1a5b7c7ab6752c114710f92c159319d3d))
|
||||
* removes queryHiddenFields from example Find operation ([fb4f822](https://github.com/payloadcms/payload/commit/fb4f822d34d0235a537f96515073e2662680412f))
|
||||
* resolve process/browser package in webpack config ([02f27f3](https://github.com/payloadcms/payload/commit/02f27f3de6fdaf5dd0023298fc671a8ae9a1b758))
|
||||
* Row groups in tabs vertical alignment ([#2593](https://github.com/payloadcms/payload/issues/2593)) ([54fac4a](https://github.com/payloadcms/payload/commit/54fac4a5d793b534e25600d2f9470c449f40df1d))
|
||||
* softens columns and filters pill colors ([#2642](https://github.com/payloadcms/payload/issues/2642)) ([9072096](https://github.com/payloadcms/payload/commit/90720964953d392d85982052b3a4843a5450681e))
|
||||
* webp upload formatting ([ccd6ca2](https://github.com/payloadcms/payload/commit/ccd6ca298e69faf04709535df3fcb18eb3d40f1b))
|
||||
|
||||
- correct casing on graphql type ([219f50b](https://github.com/payloadcms/payload/commit/219f50b0bc7a520655a5ae4f1d8b08fd04c8a3dd))
|
||||
- defaultValue missing from Upload field schema ([7b21eaf](https://github.com/payloadcms/payload/commit/7b21eaf12da64778568b45e56fa8d39e81f11c29))
|
||||
- ensures nested querying works when querying across collections ([09974fa](https://github.com/payloadcms/payload/commit/09974fa68677586c727943cc234311f87bf6da75))
|
||||
- query custom text id fields ([967f2ac](https://github.com/payloadcms/payload/commit/967f2ace0ea1a65570f69e85920f2f55626efde0))
|
||||
- removes deprecated queryHiddenFIelds from local API docs ([5f30dbb](https://github.com/payloadcms/payload/commit/5f30dbb1a5b7c7ab6752c114710f92c159319d3d))
|
||||
- removes queryHiddenFields from example Find operation ([fb4f822](https://github.com/payloadcms/payload/commit/fb4f822d34d0235a537f96515073e2662680412f))
|
||||
- resolve process/browser package in webpack config ([02f27f3](https://github.com/payloadcms/payload/commit/02f27f3de6fdaf5dd0023298fc671a8ae9a1b758))
|
||||
- Row groups in tabs vertical alignment ([#2593](https://github.com/payloadcms/payload/issues/2593)) ([54fac4a](https://github.com/payloadcms/payload/commit/54fac4a5d793b534e25600d2f9470c449f40df1d))
|
||||
- softens columns and filters pill colors ([#2642](https://github.com/payloadcms/payload/issues/2642)) ([9072096](https://github.com/payloadcms/payload/commit/90720964953d392d85982052b3a4843a5450681e))
|
||||
- webp upload formatting ([ccd6ca2](https://github.com/payloadcms/payload/commit/ccd6ca298e69faf04709535df3fcb18eb3d40f1b))
|
||||
|
||||
### Features
|
||||
|
||||
* add Arabic translations ([#2641](https://github.com/payloadcms/payload/issues/2641)) ([7d04cf1](https://github.com/payloadcms/payload/commit/7d04cf14fb0587f2208745bb77ed4fd17e99c8d5))
|
||||
* allow full URL in staticURL ([#2562](https://github.com/payloadcms/payload/issues/2562)) ([a9b5dff](https://github.com/payloadcms/payload/commit/a9b5dffa00623eb48302d51b88c3449920c10f46))
|
||||
- add Arabic translations ([#2641](https://github.com/payloadcms/payload/issues/2641)) ([7d04cf1](https://github.com/payloadcms/payload/commit/7d04cf14fb0587f2208745bb77ed4fd17e99c8d5))
|
||||
- allow full URL in staticURL ([#2562](https://github.com/payloadcms/payload/issues/2562)) ([a9b5dff](https://github.com/payloadcms/payload/commit/a9b5dffa00623eb48302d51b88c3449920c10f46))
|
||||
|
||||
## [1.7.5](https://github.com/payloadcms/payload/compare/v1.7.4...v1.7.5) (2023-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make incrementName match multiple digits ([#2609](https://github.com/payloadcms/payload/issues/2609)) ([8dbf0a2](https://github.com/payloadcms/payload/commit/8dbf0a2bd88db1b361ce16bb730613de489f2ed2))
|
||||
|
||||
- make incrementName match multiple digits ([#2609](https://github.com/payloadcms/payload/issues/2609)) ([8dbf0a2](https://github.com/payloadcms/payload/commit/8dbf0a2bd88db1b361ce16bb730613de489f2ed2))
|
||||
|
||||
### Features
|
||||
|
||||
* collection admin.enableRichTextLink property ([#2560](https://github.com/payloadcms/payload/issues/2560)) ([9678992](https://github.com/payloadcms/payload/commit/967899229f458d06a3931d086bcc49299dc310b7))
|
||||
* custom admin buttons ([#2618](https://github.com/payloadcms/payload/issues/2618)) ([1d58007](https://github.com/payloadcms/payload/commit/1d58007606fa7e34007f2a56a3ca653d2cd3404d))
|
||||
- collection admin.enableRichTextLink property ([#2560](https://github.com/payloadcms/payload/issues/2560)) ([9678992](https://github.com/payloadcms/payload/commit/967899229f458d06a3931d086bcc49299dc310b7))
|
||||
- custom admin buttons ([#2618](https://github.com/payloadcms/payload/issues/2618)) ([1d58007](https://github.com/payloadcms/payload/commit/1d58007606fa7e34007f2a56a3ca653d2cd3404d))
|
||||
|
||||
## [1.7.4](https://github.com/payloadcms/payload/compare/v1.7.3...v1.7.4) (2023-05-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* properly import SwcMinifyWebpackPlugin ([#2600](https://github.com/payloadcms/payload/issues/2600)) ([802deac](https://github.com/payloadcms/payload/commit/802deaca03f8506fa4a7adb8fc008205c2c4f013))
|
||||
- properly import SwcMinifyWebpackPlugin ([#2600](https://github.com/payloadcms/payload/issues/2600)) ([802deac](https://github.com/payloadcms/payload/commit/802deaca03f8506fa4a7adb8fc008205c2c4f013))
|
||||
|
||||
## [1.7.3](https://github.com/payloadcms/payload/compare/v1.7.2...v1.7.3) (2023-05-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2592](https://github.com/payloadcms/payload/issues/2592), allows usage of hidden fields within access query constraints ([#2599](https://github.com/payloadcms/payload/issues/2599)) ([a0bb13a](https://github.com/payloadcms/payload/commit/a0bb13a4123b51d770b364ddaee3dde1c5a3da53))
|
||||
* addds workaround for slate isBlock function issue ([#2596](https://github.com/payloadcms/payload/issues/2596)) ([8f6f13d](https://github.com/payloadcms/payload/commit/8f6f13dc93f49f5ba5384a9168ced5baec85e1fb))
|
||||
* bulk operations result type ([#2588](https://github.com/payloadcms/payload/issues/2588)) ([8382faa](https://github.com/payloadcms/payload/commit/8382faa0afc8118f4fb873c657a52c48abb2a6ad))
|
||||
* query on id throws 500 ([#2587](https://github.com/payloadcms/payload/issues/2587)) ([0ba22c3](https://github.com/payloadcms/payload/commit/0ba22c3aafca67be78814357edc668ed11ec4a97))
|
||||
* timestamp queries ([#2583](https://github.com/payloadcms/payload/issues/2583)) ([9c5107e](https://github.com/payloadcms/payload/commit/9c5107e86d70e36ac181c9d3ad51edacf9fc529a))
|
||||
|
||||
- [#2592](https://github.com/payloadcms/payload/issues/2592), allows usage of hidden fields within access query constraints ([#2599](https://github.com/payloadcms/payload/issues/2599)) ([a0bb13a](https://github.com/payloadcms/payload/commit/a0bb13a4123b51d770b364ddaee3dde1c5a3da53))
|
||||
- addds workaround for slate isBlock function issue ([#2596](https://github.com/payloadcms/payload/issues/2596)) ([8f6f13d](https://github.com/payloadcms/payload/commit/8f6f13dc93f49f5ba5384a9168ced5baec85e1fb))
|
||||
- bulk operations result type ([#2588](https://github.com/payloadcms/payload/issues/2588)) ([8382faa](https://github.com/payloadcms/payload/commit/8382faa0afc8118f4fb873c657a52c48abb2a6ad))
|
||||
- query on id throws 500 ([#2587](https://github.com/payloadcms/payload/issues/2587)) ([0ba22c3](https://github.com/payloadcms/payload/commit/0ba22c3aafca67be78814357edc668ed11ec4a97))
|
||||
- timestamp queries ([#2583](https://github.com/payloadcms/payload/issues/2583)) ([9c5107e](https://github.com/payloadcms/payload/commit/9c5107e86d70e36ac181c9d3ad51edacf9fc529a))
|
||||
|
||||
### Features
|
||||
|
||||
* Add new translation for romanian language ([#2556](https://github.com/payloadcms/payload/issues/2556)) ([fbf3a2a](https://github.com/payloadcms/payload/commit/fbf3a2a1b4633e704e467d9aec05f3ae0b900bae))
|
||||
* add persian translations ([#2553](https://github.com/payloadcms/payload/issues/2553)) ([c80f68a](https://github.com/payloadcms/payload/commit/c80f68af943c730996c9cdad87cf84d4d06a5777))
|
||||
* adjust stack trace for api error ([#2598](https://github.com/payloadcms/payload/issues/2598)) ([870838e](https://github.com/payloadcms/payload/commit/870838e7563b6767c53f4dc0288119087e3f9486))
|
||||
* allow customizing the link fields ([#2559](https://github.com/payloadcms/payload/issues/2559)) ([bf65228](https://github.com/payloadcms/payload/commit/bf6522898db353e75db11525ea5a1b58243333d8))
|
||||
* supports collection compound indexes ([#2529](https://github.com/payloadcms/payload/issues/2529)) ([85b3d57](https://github.com/payloadcms/payload/commit/85b3d579d3054aad2de793957cf6454332361327))
|
||||
- Add new translation for romanian language ([#2556](https://github.com/payloadcms/payload/issues/2556)) ([fbf3a2a](https://github.com/payloadcms/payload/commit/fbf3a2a1b4633e704e467d9aec05f3ae0b900bae))
|
||||
- add persian translations ([#2553](https://github.com/payloadcms/payload/issues/2553)) ([c80f68a](https://github.com/payloadcms/payload/commit/c80f68af943c730996c9cdad87cf84d4d06a5777))
|
||||
- adjust stack trace for api error ([#2598](https://github.com/payloadcms/payload/issues/2598)) ([870838e](https://github.com/payloadcms/payload/commit/870838e7563b6767c53f4dc0288119087e3f9486))
|
||||
- allow customizing the link fields ([#2559](https://github.com/payloadcms/payload/issues/2559)) ([bf65228](https://github.com/payloadcms/payload/commit/bf6522898db353e75db11525ea5a1b58243333d8))
|
||||
- supports collection compound indexes ([#2529](https://github.com/payloadcms/payload/issues/2529)) ([85b3d57](https://github.com/payloadcms/payload/commit/85b3d579d3054aad2de793957cf6454332361327))
|
||||
|
||||
## [1.7.2](https://github.com/payloadcms/payload/compare/v1.7.1...v1.7.2) (2023-04-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2521](https://github.com/payloadcms/payload/issues/2521), graphql AND not working with drafts ([e67ca20](https://github.com/payloadcms/payload/commit/e67ca2010831c14938d3f639fcb5374ca62747ba))
|
||||
* document drawer access control [#2545](https://github.com/payloadcms/payload/issues/2545) ([439caf8](https://github.com/payloadcms/payload/commit/439caf815fc99538f14b3a59835dcf49185759dc))
|
||||
* prevent floating point number in image sizes ([#1935](https://github.com/payloadcms/payload/issues/1935)) ([7fcde11](https://github.com/payloadcms/payload/commit/7fcde11fa0b232537de606e44c0af68b122daed2))
|
||||
* prevent sharp toFormat settings fallthrough by using clone ([#2547](https://github.com/payloadcms/payload/issues/2547)) ([90dab3c](https://github.com/payloadcms/payload/commit/90dab3c445d4bdbab0eff286a2b66861d04f2a93))
|
||||
* query localized fields without localization configured ([12edb1c](https://github.com/payloadcms/payload/commit/12edb1cc4b2675d9b0948fb7f3439f61c6e2015d))
|
||||
* read-only styles ([823d022](https://github.com/payloadcms/payload/commit/823d0228c949fe58a7e0f11f95354b240c3ea876))
|
||||
|
||||
- [#2521](https://github.com/payloadcms/payload/issues/2521), graphql AND not working with drafts ([e67ca20](https://github.com/payloadcms/payload/commit/e67ca2010831c14938d3f639fcb5374ca62747ba))
|
||||
- document drawer access control [#2545](https://github.com/payloadcms/payload/issues/2545) ([439caf8](https://github.com/payloadcms/payload/commit/439caf815fc99538f14b3a59835dcf49185759dc))
|
||||
- prevent floating point number in image sizes ([#1935](https://github.com/payloadcms/payload/issues/1935)) ([7fcde11](https://github.com/payloadcms/payload/commit/7fcde11fa0b232537de606e44c0af68b122daed2))
|
||||
- prevent sharp toFormat settings fallthrough by using clone ([#2547](https://github.com/payloadcms/payload/issues/2547)) ([90dab3c](https://github.com/payloadcms/payload/commit/90dab3c445d4bdbab0eff286a2b66861d04f2a93))
|
||||
- query localized fields without localization configured ([12edb1c](https://github.com/payloadcms/payload/commit/12edb1cc4b2675d9b0948fb7f3439f61c6e2015d))
|
||||
- read-only styles ([823d022](https://github.com/payloadcms/payload/commit/823d0228c949fe58a7e0f11f95354b240c3ea876))
|
||||
|
||||
### Features
|
||||
|
||||
* add rich-text blockquote element, change quote node type to blockquote ([ed230a4](https://github.com/payloadcms/payload/commit/ed230a42e0315dc2492b4a26e3bf8b5334e89380))
|
||||
* add user to field conditional logic ([274edc7](https://github.com/payloadcms/payload/commit/274edc74a70202e8c771c5111507b585c3f69377))
|
||||
* exposes id in conditional logic ([c117b32](https://github.com/payloadcms/payload/commit/c117b321474b8318c3a0ddf544e49568e461f0d8))
|
||||
* **imageresizer:** add trim options ([#2073](https://github.com/payloadcms/payload/issues/2073)) ([0406548](https://github.com/payloadcms/payload/commit/0406548fe6127e091db9926ee42e59f9158eff5a))
|
||||
- add rich-text blockquote element, change quote node type to blockquote ([ed230a4](https://github.com/payloadcms/payload/commit/ed230a42e0315dc2492b4a26e3bf8b5334e89380))
|
||||
- add user to field conditional logic ([274edc7](https://github.com/payloadcms/payload/commit/274edc74a70202e8c771c5111507b585c3f69377))
|
||||
- exposes id in conditional logic ([c117b32](https://github.com/payloadcms/payload/commit/c117b321474b8318c3a0ddf544e49568e461f0d8))
|
||||
- **imageresizer:** add trim options ([#2073](https://github.com/payloadcms/payload/issues/2073)) ([0406548](https://github.com/payloadcms/payload/commit/0406548fe6127e091db9926ee42e59f9158eff5a))
|
||||
|
||||
## [1.7.1](https://github.com/payloadcms/payload/compare/v1.7.0...v1.7.1) (2023-04-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds 'use client' for next 13 compatibility ([5e02985](https://github.com/payloadcms/payload/commit/5e029852060d6475eccada35ffbcdd0178d5e690))
|
||||
* graphql variables not being passed properly ([72be80a](https://github.com/payloadcms/payload/commit/72be80abc4082013e052aef1152a5de749a6f3c4))
|
||||
|
||||
- adds 'use client' for next 13 compatibility ([5e02985](https://github.com/payloadcms/payload/commit/5e029852060d6475eccada35ffbcdd0178d5e690))
|
||||
- graphql variables not being passed properly ([72be80a](https://github.com/payloadcms/payload/commit/72be80abc4082013e052aef1152a5de749a6f3c4))
|
||||
|
||||
### Features
|
||||
|
||||
* configuration extension points ([023719d](https://github.com/payloadcms/payload/commit/023719d77554a70493d779ba94bf55058d4caf98))
|
||||
- configuration extension points ([023719d](https://github.com/payloadcms/payload/commit/023719d77554a70493d779ba94bf55058d4caf98))
|
||||
|
||||
## [1.7.0](https://github.com/payloadcms/payload/compare/v1.6.32...v1.7.0) (2023-04-17)
|
||||
|
||||
@@ -829,47 +774,43 @@ We are pulling off a bandaid here and enforcing that `payload.init` is now async
|
||||
To migrate, you need to convert your code everywhere that you run `payload.init` to be asynchronous instead. For example, here is an example of a traditional `payload.init` call which needs to be migrated:
|
||||
|
||||
```js
|
||||
const express = require("express");
|
||||
const payload = require("payload");
|
||||
const express = require('express')
|
||||
const payload = require('payload')
|
||||
|
||||
const app = express();
|
||||
const app = express()
|
||||
|
||||
payload.init({
|
||||
secret: "SECRET_KEY",
|
||||
mongoURL: "mongodb://localhost/payload",
|
||||
secret: 'SECRET_KEY',
|
||||
mongoURL: 'mongodb://localhost/payload',
|
||||
express: app,
|
||||
});
|
||||
})
|
||||
|
||||
app.listen(3000, async () => {
|
||||
console.log(
|
||||
"Express is now listening for incoming connections on port 3000."
|
||||
);
|
||||
});
|
||||
console.log('Express is now listening for incoming connections on port 3000.')
|
||||
})
|
||||
```
|
||||
|
||||
Your `payload.init` call will need to be converted into the following:
|
||||
|
||||
```js
|
||||
const express = require("express");
|
||||
const payload = require("payload");
|
||||
const express = require('express')
|
||||
const payload = require('payload')
|
||||
|
||||
const app = express();
|
||||
const app = express()
|
||||
|
||||
const start = async () => {
|
||||
await payload.init({
|
||||
secret: "SECRET_KEY",
|
||||
mongoURL: "mongodb://localhost/payload",
|
||||
secret: 'SECRET_KEY',
|
||||
mongoURL: 'mongodb://localhost/payload',
|
||||
express: app,
|
||||
});
|
||||
})
|
||||
|
||||
app.listen(3000, async () => {
|
||||
console.log(
|
||||
"Express is now listening for incoming connections on port 3000."
|
||||
);
|
||||
});
|
||||
};
|
||||
console.log('Express is now listening for incoming connections on port 3000.')
|
||||
})
|
||||
}
|
||||
|
||||
start();
|
||||
start()
|
||||
```
|
||||
|
||||
Notice that all we've done is wrapped the `payload.init` and `app.listen` calls with a `start` function that is asynchronous.
|
||||
@@ -880,18 +821,18 @@ Before this release, the Local API methods were configured as generics. For exam
|
||||
|
||||
```ts
|
||||
const post = await payload.findByID<Post>({
|
||||
collection: "posts",
|
||||
id: "id-of-post-here",
|
||||
});
|
||||
collection: 'posts',
|
||||
id: 'id-of-post-here',
|
||||
})
|
||||
```
|
||||
|
||||
Now, you don't need to pass your types and Payload will automatically infer them for you, as well as significantly improve typing throughout the local API. Here's an example:
|
||||
|
||||
```ts
|
||||
const post = await payload.findByID({
|
||||
collection: "posts", // this is now auto-typed
|
||||
id: "id-of-post-here",
|
||||
});
|
||||
collection: 'posts', // this is now auto-typed
|
||||
id: 'id-of-post-here',
|
||||
})
|
||||
|
||||
// `post` will be automatically typed as `Post`
|
||||
```
|
||||
@@ -938,11 +879,11 @@ To migrate, create this file within the root of your Payload project:
|
||||
**migrateVersions.ts**
|
||||
|
||||
```ts
|
||||
const payload = require("payload");
|
||||
const payload = require('payload')
|
||||
|
||||
require("dotenv").config();
|
||||
require('dotenv').config()
|
||||
|
||||
const { PAYLOAD_SECRET, MONGODB_URI } = process.env;
|
||||
const { PAYLOAD_SECRET, MONGODB_URI } = process.env
|
||||
|
||||
// This function ensures that there is at least one corresponding version for any document
|
||||
// within each of your draft-enabled collections.
|
||||
@@ -955,7 +896,7 @@ const ensureAtLeastOneVersion = async () => {
|
||||
secret: PAYLOAD_SECRET,
|
||||
mongoURL: MONGODB_URI,
|
||||
local: true,
|
||||
});
|
||||
})
|
||||
|
||||
// For each collection
|
||||
await Promise.all(
|
||||
@@ -966,14 +907,14 @@ const ensureAtLeastOneVersion = async () => {
|
||||
collection: slug,
|
||||
limit: 0,
|
||||
depth: 0,
|
||||
locale: "all",
|
||||
});
|
||||
locale: 'all',
|
||||
})
|
||||
|
||||
const VersionsModel = payload.versions[slug];
|
||||
const existingCollectionDocIds: Array<string> = [];
|
||||
const VersionsModel = payload.versions[slug]
|
||||
const existingCollectionDocIds: Array<string> = []
|
||||
await Promise.all(
|
||||
docs.map(async (doc) => {
|
||||
existingCollectionDocIds.push(doc.id);
|
||||
existingCollectionDocIds.push(doc.id)
|
||||
// Find at least one version for the doc
|
||||
const versionDocs = await VersionsModel.find(
|
||||
{
|
||||
@@ -981,8 +922,8 @@ const ensureAtLeastOneVersion = async () => {
|
||||
updatedAt: { $gte: doc.updatedAt },
|
||||
},
|
||||
null,
|
||||
{ limit: 1 }
|
||||
).lean();
|
||||
{ limit: 1 },
|
||||
).lean()
|
||||
|
||||
// If there are no corresponding versions,
|
||||
// we need to create one
|
||||
@@ -994,39 +935,37 @@ const ensureAtLeastOneVersion = async () => {
|
||||
autosave: Boolean(versions?.drafts?.autosave),
|
||||
updatedAt: doc.updatedAt,
|
||||
createdAt: doc.createdAt,
|
||||
});
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Unable to create version corresponding with collection ${slug} document ID ${doc.id}`,
|
||||
e?.errors || e
|
||||
);
|
||||
e?.errors || e,
|
||||
)
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Created version corresponding with ${slug} document ID ${doc.id}`
|
||||
);
|
||||
console.log(`Created version corresponding with ${slug} document ID ${doc.id}`)
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
)
|
||||
|
||||
const versionsWithoutParentDocs = await VersionsModel.deleteMany({
|
||||
parent: { $nin: existingCollectionDocIds },
|
||||
});
|
||||
})
|
||||
|
||||
if (versionsWithoutParentDocs.deletedCount > 0) {
|
||||
console.log(
|
||||
`Removing ${versionsWithoutParentDocs.deletedCount} versions for ${slug} collection - parent documents no longer exist`
|
||||
);
|
||||
`Removing ${versionsWithoutParentDocs.deletedCount} versions for ${slug} collection - parent documents no longer exist`,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
)
|
||||
|
||||
console.log("Done!");
|
||||
process.exit(0);
|
||||
};
|
||||
console.log('Done!')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
ensureAtLeastOneVersion();
|
||||
ensureAtLeastOneVersion()
|
||||
```
|
||||
|
||||
Make sure your environment variables match the script's values above and then run `PAYLOAD_CONFIG_PATH=src/payload.config.ts npx ts-node -T migrateVersions.ts` in your terminal. Make sure that you point the command to your Payload config.
|
||||
@@ -1368,32 +1307,32 @@ Any future slugs after updating will be used as-is.
|
||||
|
||||
// Before
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: "case-studies",
|
||||
slug: 'case-studies',
|
||||
labels: {
|
||||
// Before Payload used `labels.singular` to generate types/graphQL schema
|
||||
singular: "Project",
|
||||
plural: "Projects",
|
||||
singular: 'Project',
|
||||
plural: 'Projects',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// After
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
// Now Payload uses `slug` to generate types/graphQL schema
|
||||
slug: "case-studies",
|
||||
slug: 'case-studies',
|
||||
labels: {
|
||||
singular: "Project",
|
||||
plural: "Projects",
|
||||
singular: 'Project',
|
||||
plural: 'Projects',
|
||||
},
|
||||
// To override the usage of slug in graphQL schema generation
|
||||
graphQL: {
|
||||
singularName: "Project",
|
||||
pluralName: "Projects",
|
||||
singularName: 'Project',
|
||||
pluralName: 'Projects',
|
||||
},
|
||||
// To override the usage of slug in type file generation
|
||||
typescript: {
|
||||
interface: "Project",
|
||||
interface: 'Project',
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- **Globals:** are affected if you have a `label` defined that differs from your global slug.
|
||||
@@ -1403,25 +1342,25 @@ Any future slugs after updating will be used as-is.
|
||||
|
||||
// Before
|
||||
const ExampleGlobal: GlobalConfig = {
|
||||
slug: "footer",
|
||||
slug: 'footer',
|
||||
// Before Payload used `label` to generate types/graphQL schema
|
||||
label: "Page Footer",
|
||||
};
|
||||
label: 'Page Footer',
|
||||
}
|
||||
|
||||
// After
|
||||
const ExampleGlobal: GlobalConfig = {
|
||||
// Now Payload uses `slug` to generate types/graphQL schema
|
||||
slug: "footer",
|
||||
label: "Page Footer",
|
||||
slug: 'footer',
|
||||
label: 'Page Footer',
|
||||
// To override the usage of slug in graphQL schema generation
|
||||
graphQL: {
|
||||
name: "PageFooter",
|
||||
name: 'PageFooter',
|
||||
},
|
||||
// To override the usage of slug in type file generation
|
||||
typescript: {
|
||||
interface: "PageFooter",
|
||||
interface: 'PageFooter',
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- **Block Fields:** are affected if you have a `label` defined that differs from your block slug.
|
||||
@@ -2694,25 +2633,25 @@ Now, configs will be sanitized **_before_** plugins are executed **_as well as_*
|
||||
So, where your plugin may have been typed like this before:
|
||||
|
||||
```ts
|
||||
import { SanitizedConfig } from "payload/config";
|
||||
import { SanitizedConfig } from 'payload/config'
|
||||
|
||||
const plugin = (config: SanitizedConfig): SanitizedConfig => {
|
||||
return {
|
||||
...config,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It can now be written like this:
|
||||
|
||||
```ts
|
||||
import { Config } from "payload/config";
|
||||
import { Config } from 'payload/config'
|
||||
|
||||
const plugin = (config: Config): Config => {
|
||||
return {
|
||||
...config,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Features
|
||||
@@ -2944,24 +2883,24 @@ For example, if you have a `pages` collection with no existing access control, a
|
||||
|
||||
```js
|
||||
const Page = {
|
||||
slug: "pages",
|
||||
slug: 'pages',
|
||||
access: {
|
||||
// No `read` access control was set
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
To:
|
||||
|
||||
```js
|
||||
const Page = {
|
||||
slug: "pages",
|
||||
slug: 'pages',
|
||||
access: {
|
||||
// Now we explicitly allow public read access
|
||||
// to this collection's documents
|
||||
read: () => true,
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
If none of your collections or globals should be publicly exposed, you don't need to do anything to upgrade.
|
||||
|
||||
@@ -6,9 +6,10 @@ To report an issue, please follow the steps below:
|
||||
2. Add necessary collections/globals/fields to the `test/_community` directory to recreate the issue you are experiencing
|
||||
3. Create an issue and add a link to your forked repo
|
||||
|
||||
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
|
||||
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/\_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
|
||||
|
||||
## Test directory file tree explanation
|
||||
|
||||
```text
|
||||
.
|
||||
├── config.ts
|
||||
@@ -25,19 +26,21 @@ To report an issue, please follow the steps below:
|
||||
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
|
||||
|
||||
## How to start test collection admin UI
|
||||
|
||||
To start the admin panel so you can manually recreate your issue, you can run the following command:
|
||||
|
||||
```bash
|
||||
# This command will start up Payload using your config
|
||||
# NOTE: it will wipe the test database on restart
|
||||
pnpm dev _community
|
||||
```
|
||||
|
||||
```bash
|
||||
# This command will start up Payload using your config
|
||||
# NOTE: it will wipe the test database on restart
|
||||
pnpm dev _community
|
||||
```
|
||||
|
||||
## Testing is optional but encouraged
|
||||
|
||||
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below.
|
||||
|
||||
### How to run integration tests (Payload API tests)
|
||||
|
||||
There are a couple ways to do this:
|
||||
|
||||
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
|
||||
@@ -51,7 +54,9 @@ There are a couple ways to do this:
|
||||
```
|
||||
|
||||
### How to run E2E tests (Admin Panel UI tests)
|
||||
|
||||
The easiest way to run E2E tests is to install
|
||||
|
||||
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
|
||||
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
|
||||
|
||||
@@ -59,6 +64,6 @@ Once they are installed you can open the `testing` tab in vscode sidebar and dri
|
||||
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
|
||||
|
||||
|
||||
#### Notes
|
||||
|
||||
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
</ul>
|
||||
|
||||
## ☁️ Deploy instantly with Payload Cloud.
|
||||
|
||||
Create a cloud account, connect your GitHub, and [deploy in minutes](https://payloadcms.com/new).
|
||||
|
||||
## 🚀 Get started by self-hosting completely free, forever.
|
||||
@@ -52,7 +53,9 @@ npx create-payload-app
|
||||
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
|
||||
|
||||
## 🖱️ One-click templates
|
||||
|
||||
### 🛒 [E-Commerce](https://github.com/payloadcms/payload/tree/master/templates/ecommerce)
|
||||
|
||||
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Best of all, you can extend it as much as you need.
|
||||
|
||||
[All Official Templates](https://github.com/orgs/payloadcms/repositories?q=topic%3Apayload-template) · [Community Templates](https://github.com/topics/payload-template)
|
||||
|
||||
@@ -11,7 +11,7 @@ You can define Collection-level Access Control within each Collection's `access`
|
||||
## Available Controls
|
||||
|
||||
| Function | Allows/Denies Access |
|
||||
| ------------------------ | -------------------- |
|
||||
| ----------------------- | -------------------------------------------- |
|
||||
| **[`create`](#create)** | Used in the `create` operation |
|
||||
| **[`read`](#read)** | Used in the `find` and `findByID` operations |
|
||||
| **[`update`](#update)** | Used in the `update` operation |
|
||||
@@ -22,11 +22,12 @@ You can define Collection-level Access Control within each Collection's `access`
|
||||
If a Collection supports [`Authentication`](/docs/authentication/overview), the following Access Controls become available:
|
||||
|
||||
| Function | Allows/Denies Access |
|
||||
| ----------------------- | -------------------- |
|
||||
| ----------------------- | -------------------------------------------------------------- |
|
||||
| **[`admin`](#admin)** | Used to restrict access to the Payload Admin panel |
|
||||
| **[`unlock`](#unlock)** | Used to restrict which users can access the `unlock` operation |
|
||||
|
||||
**Example Collection config:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
@@ -51,7 +52,7 @@ Returns a boolean which allows/denies access to the `create` request.
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ----------- |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to create the document with. |
|
||||
|
||||
@@ -78,19 +79,19 @@ Read access functions can return a boolean result or optionally return a [query
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of document requested, if within `findByID` |
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
import { Access } from 'payload/config';
|
||||
import { Access } from 'payload/config'
|
||||
|
||||
const canReadPage: Access = ({ req: { user } }) => {
|
||||
// allow authenticated users
|
||||
if (user) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
// using a query constraint, guest users can access when a field named 'isPublic' is set to true
|
||||
return {
|
||||
@@ -99,7 +100,7 @@ const canReadPage: Access = ({ req: { user } }) => {
|
||||
equals: true,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Update
|
||||
@@ -109,7 +110,7 @@ Update access functions can return a boolean result or optionally return a [quer
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ----------- |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of document requested to update |
|
||||
| **`data`** | The data passed to update the document with |
|
||||
@@ -117,16 +118,16 @@ Update access functions can return a boolean result or optionally return a [quer
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
import { Access } from 'payload/config';
|
||||
import { Access } from 'payload/config'
|
||||
|
||||
const canUpdateUser: Access = ({ req: { user }, id }) => {
|
||||
// allow users with a role of 'admin'
|
||||
if (user.roles && user.roles.some(role => role === 'admin')) {
|
||||
return true;
|
||||
if (user.roles && user.roles.some((role) => role === 'admin')) {
|
||||
return true
|
||||
}
|
||||
// allow any other users to update only oneself
|
||||
return user.id === id;
|
||||
};
|
||||
return user.id === id
|
||||
}
|
||||
```
|
||||
|
||||
### Delete
|
||||
@@ -136,7 +137,7 @@ Similarly to the Update function, returns a boolean or a [query constraint](/doc
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| --------- | --------------------------------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object with additional `user` property, which is the currently logged in user |
|
||||
| **`id`** | `id` of document requested to delete |
|
||||
|
||||
@@ -148,7 +149,7 @@ import { Access } from 'payload/config'
|
||||
const canDeleteCustomer: Access = async ({ req, id }) => {
|
||||
if (!id) {
|
||||
// allow the admin UI to show controls to delete since it is indeterminate without the id
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
// query another collection using the id
|
||||
const result = await req.payload.find({
|
||||
@@ -158,10 +159,10 @@ const canDeleteCustomer: Access = async ({ req, id }) => {
|
||||
where: {
|
||||
customer: { equals: id },
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
return result.totalDocs === 0;
|
||||
};
|
||||
return result.totalDocs === 0
|
||||
}
|
||||
```
|
||||
|
||||
### Admin
|
||||
@@ -171,7 +172,7 @@ If the Collection is [used to access the Payload Admin panel](/docs/admin/overvi
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
|
||||
### Unlock
|
||||
@@ -181,5 +182,5 @@ Determines which users can [unlock](/docs/authentication/operations#unlock) othe
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
|
||||
@@ -11,12 +11,13 @@ Field Access Control is specified with functions inside a field's config. All fi
|
||||
## Available Controls
|
||||
|
||||
| Function | Purpose |
|
||||
| ------------------------ | ------- |
|
||||
| ----------------------- | -------------------------------------------------------------------------------- |
|
||||
| **[`create`](#create)** | Allows or denies the ability to set a field's value when creating a new document |
|
||||
| **[`read`](#read)** | Allows or denies the ability to read a field's value |
|
||||
| **[`update`](#update)** | Allows or denies the ability to update a field's value |
|
||||
|
||||
**Example Collection config:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
@@ -45,7 +46,7 @@ Returns a boolean which allows or denies the ability to set a field's value when
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ----------- |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The full data passed to create the document. |
|
||||
| **`siblingData`** | Immediately adjacent field data passed to create the document. |
|
||||
@@ -57,7 +58,7 @@ Returns a boolean which allows or denies the ability to read a field's value. If
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ----------- |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of the document being read |
|
||||
| **`doc`** | The full document data. |
|
||||
@@ -72,7 +73,7 @@ If `false` is returned and you attempt to update the field's value, the operatio
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ----------- |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of the document being updated |
|
||||
| **`data`** | The full data passed to update the document. |
|
||||
|
||||
@@ -8,30 +8,35 @@ keywords: globals, access control, permissions, documentation, Content Managemen
|
||||
|
||||
You can define Global-level Access Control within each Global's `access` property. All Access Control functions accept one `args` argument.
|
||||
|
||||
**Available argument properties:
|
||||
\*\*Available argument properties:
|
||||
|
||||
## Available Controls
|
||||
|
||||
| Function | Allows/Denies Access |
|
||||
| ------------------------ | -------------------- |
|
||||
| ----------------------- | -------------------------------------- |
|
||||
| **[`read`](#read)** | Used in the `findOne` Global operation |
|
||||
| **[`update`](#update)** | Used in the `update` Global operation |
|
||||
|
||||
**Example Global config:**
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types';
|
||||
import { GlobalConfig } from 'payload/types'
|
||||
|
||||
const Header: GlobalConfig = {
|
||||
slug: "header",
|
||||
slug: 'header',
|
||||
// highlight-start
|
||||
access: {
|
||||
read: ({ req: { user } }) => { /* */ },
|
||||
update: ({ req: { user } }) => { /* */ },
|
||||
read: ({ req: { user } }) => {
|
||||
/* */
|
||||
},
|
||||
update: ({ req: { user } }) => {
|
||||
/* */
|
||||
},
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
}
|
||||
|
||||
export default Header;
|
||||
export default Header
|
||||
```
|
||||
|
||||
### Read
|
||||
@@ -41,7 +46,7 @@ Returns a boolean result or optionally a [query constraint](/docs/queries/overvi
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
|
||||
### Update
|
||||
@@ -51,6 +56,6 @@ Returns a boolean result or optionally a [query constraint](/docs/queries/overvi
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ----------- |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to update the global with. |
|
||||
|
||||
@@ -8,10 +8,7 @@ keywords: overview, access control, permissions, documentation, Content Manageme
|
||||
|
||||
Access control within Payload is extremely powerful while remaining easy and intuitive to manage. Declaring who should have access to what documents is no more complex than writing a simple JavaScript function that either returns a `boolean` or a [`query`](/docs/queries/overview) constraint to restrict which documents users can interact with.
|
||||
|
||||
<YouTube
|
||||
id="DoPLyXG26Dg"
|
||||
title="Overview of Payload Access Control"
|
||||
/>
|
||||
<YouTube id="DoPLyXG26Dg" title="Overview of Payload Access Control" />
|
||||
|
||||
**Example use cases:**
|
||||
|
||||
@@ -32,13 +29,18 @@ Access control within Payload is extremely powerful while remaining easy and int
|
||||
const defaultPayloadAccess = ({ req: { user } }) => {
|
||||
// Return `true` if a user is found
|
||||
// and `false` if it is undefined or null
|
||||
return Boolean(user);
|
||||
return Boolean(user)
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong><br/>
|
||||
In the Local API, all Access Control functions are skipped by default, allowing your server to do whatever it needs. But, you can opt back in by setting the option <strong>overrideAccess</strong> to <strong>false</strong>.
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
In the Local API, all Access Control functions are skipped by default, allowing your server to do
|
||||
whatever it needs. But, you can opt back in by setting the option <strong>
|
||||
overrideAccess
|
||||
</strong>{' '}
|
||||
to <strong>false</strong>.
|
||||
</Banner>
|
||||
|
||||
### Access Control Types
|
||||
@@ -49,12 +51,13 @@ You can manage access within Payload on three different levels:
|
||||
- [Fields](/docs/access-control/fields)
|
||||
- [Globals](/docs/access-control/globals)
|
||||
|
||||
|
||||
### When Access Control is Executed
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong><br/>
|
||||
Access control functions are utilized in two places. It's important to understand how and when your access control is executed.
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
Access control functions are utilized in two places. It's important to understand how and when
|
||||
your access control is executed.
|
||||
</Banner>
|
||||
|
||||
#### As you execute operations
|
||||
@@ -70,8 +73,11 @@ To accomplish this, Payload ships with an `Access` operation, which is executed
|
||||
### Argument Availability
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br/>
|
||||
When your access control functions are executed via the <strong>access</strong> operation, the <strong>id</strong> and <strong>data</strong> arguments will be <strong>undefined</strong>, because Payload is executing your functions without referencing a specific document.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
When your access control functions are executed via the <strong>access</strong> operation, the{' '}
|
||||
<strong>id</strong> and <strong>data</strong> arguments will be <strong>undefined</strong>,
|
||||
because Payload is executing your functions without referencing a specific document.
|
||||
</Banner>
|
||||
|
||||
If you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your access control is being executed via the `access` operation, to determine solely what the user can do within the Admin UI.
|
||||
|
||||
@@ -11,8 +11,10 @@ While designing the Payload Admin panel, we determined it should be as minimal a
|
||||
To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br />
|
||||
Custom components will automatically be provided with all props that the default component would accept.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
Custom components will automatically be provided with all props that the default component would
|
||||
accept.
|
||||
</Banner>
|
||||
|
||||
### Base Component Overrides
|
||||
@@ -41,7 +43,7 @@ You can override a set of admin panel-wide components by providing a component t
|
||||
`payload.config.js`
|
||||
|
||||
```ts
|
||||
import { buildConfig } from "payload/config";
|
||||
import { buildConfig } from 'payload/config'
|
||||
import {
|
||||
MyCustomNav,
|
||||
MyCustomLogo,
|
||||
@@ -49,7 +51,7 @@ import {
|
||||
MyCustomAccount,
|
||||
MyCustomDashboard,
|
||||
MyProvider,
|
||||
} from "./customComponents";
|
||||
} from './customComponents'
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
@@ -66,7 +68,7 @@ export default buildConfig({
|
||||
providers: [MyProvider],
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components)._
|
||||
@@ -93,20 +95,17 @@ You can override components on a Collection-by-Collection basis via each Collect
|
||||
```tsx
|
||||
// Custom Buttons
|
||||
|
||||
import * as React from "react";
|
||||
import * as React from 'react'
|
||||
import {
|
||||
CustomSaveButtonProps,
|
||||
CustomSaveDraftButtonProps,
|
||||
CustomPublishButtonProps,
|
||||
CustomPreviewButtonProps,
|
||||
} from "payload/types";
|
||||
} from 'payload/types'
|
||||
|
||||
export const CustomSaveButton: CustomSaveButtonProps = ({
|
||||
DefaultButton,
|
||||
label,
|
||||
}) => {
|
||||
return <DefaultButton label={label} />;
|
||||
};
|
||||
export const CustomSaveButton: CustomSaveButtonProps = ({ DefaultButton, label }) => {
|
||||
return <DefaultButton label={label} />
|
||||
}
|
||||
|
||||
export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
|
||||
DefaultButton,
|
||||
@@ -114,10 +113,8 @@ export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
|
||||
label,
|
||||
saveDraft,
|
||||
}) => {
|
||||
return (
|
||||
<DefaultButton label={label} disabled={disabled} saveDraft={saveDraft} />
|
||||
);
|
||||
};
|
||||
return <DefaultButton label={label} disabled={disabled} saveDraft={saveDraft} />
|
||||
}
|
||||
|
||||
export const CustomPublishButton: CustomPublishButtonProps = ({
|
||||
DefaultButton,
|
||||
@@ -125,8 +122,8 @@ export const CustomPublishButton: CustomPublishButtonProps = ({
|
||||
label,
|
||||
publish,
|
||||
}) => {
|
||||
return <DefaultButton label={label} disabled={disabled} publish={publish} />;
|
||||
};
|
||||
return <DefaultButton label={label} disabled={disabled} publish={publish} />
|
||||
}
|
||||
|
||||
export const CustomPreviewButton: CustomPreviewButtonProps = ({
|
||||
DefaultButton,
|
||||
@@ -134,8 +131,8 @@ export const CustomPreviewButton: CustomPreviewButtonProps = ({
|
||||
label,
|
||||
preview,
|
||||
}) => {
|
||||
return <DefaultButton label={label} disabled={disabled} preview={preview} />;
|
||||
};
|
||||
return <DefaultButton label={label} disabled={disabled} preview={preview} />
|
||||
}
|
||||
```
|
||||
|
||||
##### Custom Collection List View Example
|
||||
@@ -162,17 +159,17 @@ export const MyCollection: CollectionConfig = {
|
||||
MyListComponent.tsx
|
||||
|
||||
```tsx
|
||||
import React from "react";
|
||||
import { List, type Props } from "payload/components/views/List"; // Payload's default List view component and its props
|
||||
import React from 'react'
|
||||
import { List, type Props } from 'payload/components/views/List' // Payload's default List view component and its props
|
||||
export const MyListComponent: React.FC<Props> = (props) => (
|
||||
<div>
|
||||
<p>
|
||||
Some text before the default list view component. If you just want to do
|
||||
that, you can also use the admin.components.list.BeforeList hook
|
||||
Some text before the default list view component. If you just want to do that, you can also
|
||||
use the admin.components.list.BeforeList hook
|
||||
</p>
|
||||
<List {...props} />
|
||||
</div>
|
||||
);
|
||||
)
|
||||
```
|
||||
|
||||
### Globals
|
||||
@@ -194,10 +191,9 @@ All Payload fields support the ability to swap in your own React components. So,
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
Don't see a built-in field type that you need? Build it! Using a combination
|
||||
of custom validation and custom components, you can override the entirety of
|
||||
how a component functions within the admin panel and effectively create your
|
||||
own field type.
|
||||
Don't see a built-in field type that you need? Build it! Using a combination of custom validation
|
||||
and custom components, you can override the entirety of how a component functions within the admin
|
||||
panel and effectively create your own field type.
|
||||
</Banner>
|
||||
|
||||
**Fields support the following custom components:**
|
||||
@@ -223,15 +219,15 @@ These are the props that will be passed to your custom Cell to use in your own c
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from "react";
|
||||
import "./index.scss";
|
||||
const baseClass = "custom-cell";
|
||||
import React from 'react'
|
||||
import './index.scss'
|
||||
const baseClass = 'custom-cell'
|
||||
|
||||
const CustomCell: React.FC<Props> = (props) => {
|
||||
const { field, colIndex, collection, cellData, rowData } = props;
|
||||
const { field, colIndex, collection, cellData, rowData } = props
|
||||
|
||||
return <span className={baseClass}>{cellData}</span>;
|
||||
};
|
||||
return <span className={baseClass}>{cellData}</span>
|
||||
}
|
||||
```
|
||||
|
||||
## Field Component
|
||||
@@ -243,25 +239,22 @@ When writing your own custom components you can make use of a number of hooks to
|
||||
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
|
||||
|
||||
```tsx
|
||||
import { useField } from "payload/components/forms";
|
||||
import { useField } from 'payload/components/forms'
|
||||
|
||||
type Props = { path: string };
|
||||
type Props = { path: string }
|
||||
|
||||
const CustomTextField: React.FC<Props> = ({ path }) => {
|
||||
// highlight-start
|
||||
const { value, setValue } = useField<Props>({ path });
|
||||
const { value, setValue } = useField<Props>({ path })
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<input onChange={(e) => setValue(e.target.value)} value={value.path} />
|
||||
);
|
||||
};
|
||||
return <input onChange={(e) => setValue(e.target.value)} value={value.path} />
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
For more information regarding the hooks that are available to you while you
|
||||
build custom components, including the <strong>useField</strong> hook, [click
|
||||
here](/docs/admin/hooks).
|
||||
For more information regarding the hooks that are available to you while you build custom
|
||||
components, including the <strong>useField</strong> hook, [click here](/docs/admin/hooks).
|
||||
</Banner>
|
||||
|
||||
## Custom routes
|
||||
@@ -292,9 +285,8 @@ Your custom route components will be given all the props that a React Router `<R
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
It's up to you to secure your custom routes. If your route requires a user to
|
||||
be logged in or to have certain access rights, you should handle that within
|
||||
your route component yourself.
|
||||
It's up to you to secure your custom routes. If your route requires a user to be logged in or to
|
||||
have certain access rights, you should handle that within your route component yourself.
|
||||
</Banner>
|
||||
|
||||
#### Example
|
||||
@@ -311,8 +303,8 @@ To see how to pass in your custom views to create custom routes of your own, tak
|
||||
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to do add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong> Don't forget to pass the **children** prop through
|
||||
the provider component for the admin UI to show
|
||||
<strong>Reminder:</strong> Don't forget to pass the **children** prop through the provider
|
||||
component for the admin UI to show
|
||||
</Banner>
|
||||
|
||||
### Styling Custom Components
|
||||
@@ -332,21 +324,21 @@ When developing custom components you can support multiple languages to be consi
|
||||
For example:
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const CustomComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const { t, i18n } = useTranslation("namespace1");
|
||||
const { t, i18n } = useTranslation('namespace1')
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<ul>
|
||||
<li>{t("key", { variable: "value" })}</li>
|
||||
<li>{t("namespace2:key", { variable: "value" })}</li>
|
||||
<li>{t('key', { variable: 'value' })}</li>
|
||||
<li>{t('namespace2:key', { variable: 'value' })}</li>
|
||||
<li>{i18n.language}</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Getting the current locale
|
||||
@@ -354,18 +346,18 @@ const CustomComponent: React.FC = () => {
|
||||
In any custom component you can get the selected locale with `useLocale` hook. `useLocale` returns the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
|
||||
|
||||
```tsx
|
||||
import { useLocale } from "payload/components/utilities";
|
||||
import { useLocale } from 'payload/components/utilities'
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
// highlight-start
|
||||
const locale = useLocale();
|
||||
const locale = useLocale()
|
||||
// highlight-end
|
||||
|
||||
const trans = {
|
||||
en: "Hello",
|
||||
es: "Hola",
|
||||
};
|
||||
en: 'Hello',
|
||||
es: 'Hola',
|
||||
}
|
||||
|
||||
return <span> {trans[locale.code]} </span>
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -13,15 +13,16 @@ You can add your own CSS by providing your base Payload config with a path to yo
|
||||
To do so, provide your base Payload config with a path to your own stylesheet. It can be either a CSS or SCSS file.
|
||||
|
||||
**Example in payload.config.js:**
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
import { buildConfig } from 'payload/config'
|
||||
import path from 'path'
|
||||
|
||||
const config = buildConfig({
|
||||
admin: {
|
||||
css: path.resolve(__dirname, 'relative/path/to/stylesheet.scss'),
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Overriding built-in styles
|
||||
@@ -43,7 +44,8 @@ You can find the built-in Payload CSS variables within [`./src/admin/scss/app.sc
|
||||
#### Dark mode
|
||||
|
||||
<Banner type="warning">
|
||||
If you're overriding colors or theme elevations, make sure to consider how your changes will affect dark mode.
|
||||
If you're overriding colors or theme elevations, make sure to consider how your changes will
|
||||
affect dark mode.
|
||||
</Banner>
|
||||
|
||||
By default, Payload automatically overrides all `--theme-elevation`s and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.
|
||||
|
||||
@@ -24,7 +24,7 @@ const CustomTextField: React.FC<Props> = ({ path }) => {
|
||||
const { value, setValue } = useField<string>({ path })
|
||||
// highlight-end
|
||||
|
||||
return <input onChange={e => setValue(e.target.value)} value={value.path} />
|
||||
return <input onChange={(e) => setValue(e.target.value)} value={value.path} />
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,7 +57,8 @@ const {
|
||||
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>This hook is great for retrieving only certain fields from form state</strong> because it ensures that it will only cause a rerender when the items that you ask for change.
|
||||
<strong>This hook is great for retrieving only certain fields from form state</strong> because it
|
||||
ensures that it will only cause a rerender when the items that you ask for change.
|
||||
</Banner>
|
||||
|
||||
Thanks to the awesome package [`use-context-selector`](https://github.com/dai-shi/use-context-selector), you can retrieve a specific field's state easily. This is ideal because you can ensure you have an up-to-date field state, and your component will only re-render when _that field's state_ changes.
|
||||
@@ -65,21 +66,19 @@ Thanks to the awesome package [`use-context-selector`](https://github.com/dai-sh
|
||||
You can pass a Redux-like selector into the hook, which will ensure that you retrieve only the field that you want. The selector takes an argument with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
|
||||
|
||||
```tsx
|
||||
import { useFormFields } from 'payload/components/forms';
|
||||
import { useFormFields } from 'payload/components/forms'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// Get only the `amount` field state, and only cause a rerender when that field changes
|
||||
const amount = useFormFields(([fields, dispatch]) => fields.amount);
|
||||
const amount = useFormFields(([fields, dispatch]) => fields.amount)
|
||||
|
||||
// Do the same thing as above, but to the `feePercentage` field
|
||||
const feePercentage = useFormFields(([fields, dispatch]) => fields.feePercentage);
|
||||
const feePercentage = useFormFields(([fields, dispatch]) => fields.feePercentage)
|
||||
|
||||
if (typeof amount?.value !== 'undefined' && typeof feePercentage?.value !== 'undefined') {
|
||||
return (
|
||||
<span>The fee is ${(amount.value * feePercentage.value) / 100}</span>
|
||||
);
|
||||
return <span>The fee is ${(amount.value * feePercentage.value) / 100}</span>
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### useAllFormFields
|
||||
@@ -117,7 +116,7 @@ If you are building a custom component, then you should use `setValue` which is
|
||||
You can send the following actions to the `dispatchFields` function.
|
||||
|
||||
| Action | Description |
|
||||
|------------------------|----------------------------------------------------------------------------|
|
||||
| ---------------------- | -------------------------------------------------------------------------- |
|
||||
| **`ADD_ROW`** | Adds a row of data (useful in array / block field data) |
|
||||
| **`DUPLICATE_ROW`** | Duplicates a row of data (useful in array / block field data) |
|
||||
| **`MODIFY_CONDITION`** | Updates a field's conditional logic result (true / false) |
|
||||
@@ -134,8 +133,12 @@ To see types for each action supported within the `dispatchFields` hook, check o
|
||||
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Warning:</strong><br/>
|
||||
This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields` property will be out of date. You should only leverage this hook if you need to perform actions against the form in response to your users' actions. Do not rely on its returned "fields" as being up-to-date. They will be removed from this hook's response in an upcoming version.
|
||||
<strong>Warning:</strong>
|
||||
<br />
|
||||
This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields`
|
||||
property will be out of date. You should only leverage this hook if you need to perform actions
|
||||
against the form in response to your users' actions. Do not rely on its returned "fields" as being
|
||||
up-to-date. They will be removed from this hook's response in an upcoming version.
|
||||
</Banner>
|
||||
|
||||
The `useForm` hook returns an object with the following properties: |
|
||||
@@ -358,10 +361,14 @@ The `useForm` hook returns an object with the following properties: |
|
||||
]}
|
||||
/>
|
||||
|
||||
<br />
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { addFieldRow } = useForm()
|
||||
@@ -385,7 +392,7 @@ export const CustomArrayManager = () => {
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
@@ -456,10 +463,14 @@ export const CustomArrayManager = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
<br />
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { removeFieldRow } = useForm()
|
||||
@@ -478,7 +489,7 @@ export const CustomArrayManager = () => {
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
@@ -557,10 +568,14 @@ export const CustomArrayManager = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
<br />
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { replaceFieldRow } = useForm()
|
||||
@@ -584,7 +599,7 @@ export const CustomArrayManager = () => {
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
@@ -640,24 +655,24 @@ The `useDocumentInfo` hook provides lots of information about the document curre
|
||||
**Example:**
|
||||
|
||||
```tsx
|
||||
import { useDocumentInfo } from 'payload/components/utilities';
|
||||
import { useDocumentInfo } from 'payload/components/utilities'
|
||||
|
||||
const LinkFromCategoryToPosts: React.FC = () => {
|
||||
// highlight-start
|
||||
const { id } = useDocumentInfo();
|
||||
const { id } = useDocumentInfo()
|
||||
// highlight-end
|
||||
|
||||
// id will be undefined on the create form
|
||||
if (!id) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<a href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`} >
|
||||
<a href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`}>
|
||||
View posts
|
||||
</a>
|
||||
)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### useLocale
|
||||
@@ -665,22 +680,20 @@ const LinkFromCategoryToPosts: React.FC = () => {
|
||||
In any custom component you can get the selected locale object with the `useLocale` hook. `useLocale`gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
|
||||
|
||||
```tsx
|
||||
import { useLocale } from 'payload/components/utilities';
|
||||
import { useLocale } from 'payload/components/utilities'
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
// highlight-start
|
||||
const locale = useLocale();
|
||||
const locale = useLocale()
|
||||
// highlight-end
|
||||
|
||||
const trans = {
|
||||
en: 'Hello',
|
||||
es: 'Hola',
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<span> { trans[locale.code] } </span>
|
||||
);
|
||||
};
|
||||
return <span> {trans[locale.code]} </span>
|
||||
}
|
||||
```
|
||||
|
||||
### useAuth
|
||||
@@ -688,7 +701,7 @@ const Greeting: React.FC = () => {
|
||||
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
|
||||
|
||||
| Property | Description |
|
||||
|--------------------------|-----------------------------------------------------------------------------------------|
|
||||
| ------------------------ | --------------------------------------------------------------------------------------- |
|
||||
| **`user`** | The currently logged in user |
|
||||
| **`logOut`** | A method to log out the currently logged in user |
|
||||
| **`refreshCookie`** | A method to trigger the silent refreshing of a user's auth token |
|
||||
@@ -698,18 +711,16 @@ Useful to retrieve info about the currently logged in user as well as methods fo
|
||||
| **`permissions`** | The permissions of the current user |
|
||||
|
||||
```tsx
|
||||
import { useAuth } from 'payload/components/utilities';
|
||||
import { User } from '../payload-types.ts';
|
||||
import { useAuth } from 'payload/components/utilities'
|
||||
import { User } from '../payload-types.ts'
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
// highlight-start
|
||||
const { user } = useAuth<User>();
|
||||
const { user } = useAuth<User>()
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<span>Hi, {user.email}!</span>
|
||||
);
|
||||
};
|
||||
return <span>Hi, {user.email}!</span>
|
||||
}
|
||||
```
|
||||
|
||||
### useConfig
|
||||
@@ -717,17 +728,15 @@ const Greeting: React.FC = () => {
|
||||
Used to easily fetch the full Payload config.
|
||||
|
||||
```tsx
|
||||
import { useConfig } from 'payload/components/utilities';
|
||||
import { useConfig } from 'payload/components/utilities'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const config = useConfig();
|
||||
const config = useConfig()
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<span>{config.serverURL}</span>
|
||||
);
|
||||
};
|
||||
return <span>{config.serverURL}</span>
|
||||
}
|
||||
```
|
||||
|
||||
### useEditDepth
|
||||
@@ -735,16 +744,14 @@ const MyComponent: React.FC = () => {
|
||||
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
|
||||
|
||||
```tsx
|
||||
import { useEditDepth } from 'payload/components/utilities';
|
||||
import { useEditDepth } from 'payload/components/utilities'
|
||||
|
||||
const MyComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const editDepth = useEditDepth();
|
||||
const editDepth = useEditDepth()
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<span>My component is {editDepth} levels deep</span>
|
||||
)
|
||||
return <span>My component is {editDepth} levels deep</span>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ Payload dynamically generates a beautiful, fully functional React admin panel to
|
||||
The Payload Admin panel is built with Webpack, code-split, highly performant (even with 100+ fields), and written fully in TypeScript.
|
||||
|
||||
<Banner type="success">
|
||||
The Admin panel is meant to be simple enough to give you a starting point but
|
||||
not bring too much complexity, so that you can easily customize it to suit the
|
||||
needs of your application and your editors.
|
||||
The Admin panel is meant to be simple enough to give you a starting point but not bring too much
|
||||
complexity, so that you can easily customize it to suit the needs of your application and your
|
||||
editors.
|
||||
</Banner>
|
||||
|
||||

|
||||
@@ -25,7 +25,7 @@ _Screenshot of the Admin panel while editing a document from an example `AllFiel
|
||||
All options for the Admin panel are defined in your base Payload config file.
|
||||
|
||||
| Option | Description |
|
||||
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
|
||||
| `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
|
||||
@@ -55,13 +55,13 @@ To specify which Collection to use to log in to the Admin panel, pass the `admin
|
||||
`payload.config.js`:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from "payload/config";
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
const config = buildConfig({
|
||||
admin: {
|
||||
user: "admins", // highlight-line
|
||||
user: 'admins', // highlight-line
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
By default, if you have not specified a Collection, Payload will automatically provide you with a `User` Collection which will be used to access the Admin panel. You can customize or override the fields and settings of the default `User` Collection by passing your own collection using `users` as its `slug` to Payload. When this is done, Payload will use your provided `User` Collection instead of its default version.
|
||||
|
||||
@@ -15,8 +15,10 @@ Out of the box, Payload handles the persistence of your users' preferences in a
|
||||
1. The "collapsed" state of blocks, on a document level, as users edit or interact with documents
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br/>
|
||||
All preferences are stored on an individual user basis. Payload automatically recognizes the user that is reading or setting a preference via all provided authentication methods.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
All preferences are stored on an individual user basis. Payload automatically recognizes the user
|
||||
that is reading or setting a preference via all provided authentication methods.
|
||||
</Banner>
|
||||
|
||||
### Use cases
|
||||
@@ -33,7 +35,7 @@ This API is used significantly for internal operations of the Admin panel, as me
|
||||
Payload automatically creates an internally used `payload-preferences` collection that stores user preferences. Each document in the `payload-preferences` collection contains the following shape:
|
||||
|
||||
| Key | Value |
|
||||
|-------------------|-------------------------------------------------------------------|
|
||||
| ----------------- | ----------------------------------------------------------------- |
|
||||
| `id` | A unique ID for each preference stored. |
|
||||
| `key` | A unique `key` that corresponds to the preference. |
|
||||
| `user.value` | The ID of the `user` that is storing its preference. |
|
||||
|
||||
@@ -11,8 +11,9 @@ Payload uses Webpack 5 to build the Admin panel. It comes with support for many
|
||||
To extend the Webpack config, add the `webpack` key to your base Payload config, and provide a function that accepts the default Webpack config as its only argument:
|
||||
|
||||
`payload.config.ts`
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
@@ -20,11 +21,11 @@ export default buildConfig({
|
||||
webpack: (config) => {
|
||||
// Do something with the config here
|
||||
|
||||
return config;
|
||||
}
|
||||
return config
|
||||
},
|
||||
// highlight-end
|
||||
}
|
||||
});
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Aliasing server-only modules
|
||||
@@ -43,73 +44,84 @@ Examples of **non** browser-friendly packages:
|
||||
You may rely on server-only packages such as the above to perform logic in access control functions, hooks, and other contexts (which is great!) but when you boot up your Payload app and try to view it in the browser, you'll likely run into missing dependency issues or other general incompatibilities.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br/>
|
||||
To avoid problems with server code making it to your Webpack bundle, you can use the <strong>alias</strong> Webpack feature to tell Webpack to avoid importing the modules you want to restrict to server-only.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
To avoid problems with server code making it to your Webpack bundle, you can use the{' '}
|
||||
<strong>alias</strong> Webpack feature to tell Webpack to avoid importing the modules you want to
|
||||
restrict to server-only.
|
||||
</Banner>
|
||||
|
||||
<strong>For example, let's say that you have a Collection called `Subscriptions` which relies on Stripe:</strong>
|
||||
<strong>
|
||||
For example, let's say that you have a Collection called `Subscriptions` which relies on Stripe:
|
||||
</strong>
|
||||
|
||||
<br/><br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
`collections/Subscriptions/index.js`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import createStripeSubscription from './hooks/createStripeSubscription';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
import createStripeSubscription from './hooks/createStripeSubscription'
|
||||
|
||||
export const Subscription: CollectionConfig = {
|
||||
slug: 'subscriptions',
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
createStripeSubscription,
|
||||
]
|
||||
beforeChange: [createStripeSubscription],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'stripeSubscriptionID',
|
||||
type: 'text',
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
The collection above features a `beforeChange` hook that creates a Stripe subscription whenever a Subscription document is created in Payload.
|
||||
|
||||
<strong>That hook might look something like this:</strong>
|
||||
|
||||
<br/><br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
`collections/Subscriptions/hooks/createStripeSubscription.js`
|
||||
```js
|
||||
import Stripe from 'stripe';
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
|
||||
```js
|
||||
import Stripe from 'stripe'
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
|
||||
|
||||
const createStripeSubscription = async ({ data, operation }) => {
|
||||
if (operation === 'create') {
|
||||
const dataWithStripeID = {...data};
|
||||
const dataWithStripeID = { ...data }
|
||||
|
||||
// use Stripe to create a Stripe subscription
|
||||
const subscription = await stripe.subscriptions.create({
|
||||
// Configure the subscription accordingly
|
||||
});
|
||||
})
|
||||
|
||||
// Automatically add the Stripe subscription ID
|
||||
// to the data that will be saved to this Subscription doc
|
||||
dataWithStripeID.stripeSubscriptionID = subscription.id;
|
||||
dataWithStripeID.stripeSubscriptionID = subscription.id
|
||||
|
||||
return dataWithStripeID
|
||||
}
|
||||
|
||||
return data;
|
||||
return data
|
||||
}
|
||||
|
||||
export default createStripeSubscription;
|
||||
export default createStripeSubscription
|
||||
```
|
||||
|
||||
<Banner type="error">
|
||||
<strong>Warning:</strong><br/>
|
||||
The above code is NOT production-ready and should not be referenced to create Stripe subscriptions. Although creating a beforeChange hook is a completely valid spot to do things like create subscriptions, the code above is incomplete and insecure, meant for explanation purposes only.
|
||||
<strong>Warning:</strong>
|
||||
<br />
|
||||
The above code is NOT production-ready and should not be referenced to create Stripe
|
||||
subscriptions. Although creating a beforeChange hook is a completely valid spot to do things like
|
||||
create subscriptions, the code above is incomplete and insecure, meant for explanation purposes
|
||||
only.
|
||||
</Banner>
|
||||
|
||||
**As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.**
|
||||
@@ -117,18 +129,20 @@ export default createStripeSubscription;
|
||||
To remedy this issue you can extend the Payload Webpack config to alias your entire `createStripeSubscription` hook to a separate, empty mock file.
|
||||
|
||||
Example in `payload.config.js`:
|
||||
```js
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
import Subscription from './collections/Subscription';
|
||||
|
||||
const createStripeSubscriptionPath = path.resolve(__dirname, 'collections/Subscription/hooks/createStripeSubscription.js');
|
||||
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js');
|
||||
```js
|
||||
import { buildConfig } from 'payload/config'
|
||||
import path from 'path'
|
||||
import Subscription from './collections/Subscription'
|
||||
|
||||
const createStripeSubscriptionPath = path.resolve(
|
||||
__dirname,
|
||||
'collections/Subscription/hooks/createStripeSubscription.js',
|
||||
)
|
||||
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js')
|
||||
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
Subscription
|
||||
],
|
||||
collections: [Subscription],
|
||||
admin: {
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
@@ -137,32 +151,39 @@ export default buildConfig({
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
[createStripeSubscriptionPath]: mockModulePath,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The above code will alias the file at path `createStripeSubscriptionPath` to a mocked module, which might look like this:
|
||||
|
||||
`mocks/emptyObject.js`
|
||||
|
||||
```js
|
||||
export default {};
|
||||
export default {}
|
||||
```
|
||||
|
||||
Now, when Webpack sees that you're attempting to import your `createStripeSubscriptionPath` file, it'll disregard that actual file and load your mock file instead. Not only will your Admin panel now bundle successfully, you will have optimized its filesize by removing unnecessary code! And you might have learned something about Webpack, too.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br/>
|
||||
If changes to your Webpack aliases are not surfacing, they might be [cached](https://webpack.js.org/configuration/cache/) in `node_modules/.cache/webpack`. Try deleting that folder and restarting your server.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
If changes to your Webpack aliases are not surfacing, they might be
|
||||
[cached](https://webpack.js.org/configuration/cache/) in `node_modules/.cache/webpack`. Try
|
||||
deleting that folder and restarting your server.
|
||||
</Banner>
|
||||
|
||||
## Admin environment vars
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br />
|
||||
Be careful about what variables you provide to your client-side code. Analyze every single one to make sure that you're not accidentally leaking anything that an attacker could exploit. Only keys that are safe to be available to everyone in plain text should be provided to your Admin panel.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
Be careful about what variables you provide to your client-side code. Analyze every single one to
|
||||
make sure that you're not accidentally leaking anything that an attacker could exploit. Only keys
|
||||
that are safe to be available to everyone in plain text should be provided to your Admin panel.
|
||||
</Banner>
|
||||
|
||||
By default, `env` variables are **not** provided to the Admin panel for security and safety reasons. But, Payload provides you with a way to still provide `env` vars to your frontend code.
|
||||
|
||||
@@ -41,8 +41,8 @@ Technically, both of these options will work for third-party integrations but th
|
||||
To enable API keys on a collection, set the `useAPIKey` auth option to `true`. From there, a new interface will appear in the Admin panel for each document within the collection that allows you to generate an API key for each user in the Collection.
|
||||
|
||||
<Banner type="success">
|
||||
User API keys are encrypted within the database, meaning that if your database
|
||||
is compromised, your API keys will not be.
|
||||
User API keys are encrypted within the database, meaning that if your database is compromised,
|
||||
your API keys will not be.
|
||||
</Banner>
|
||||
|
||||
#### Authenticating via API Key
|
||||
@@ -52,31 +52,31 @@ To authenticate REST or GraphQL API requests using an API key, set the `Authoriz
|
||||
**For example, using Fetch:**
|
||||
|
||||
```ts
|
||||
import User from '../collections/User';
|
||||
import User from '../collections/User'
|
||||
|
||||
const response = await fetch("http://localhost:3000/api/pages", {
|
||||
const response = await fetch('http://localhost:3000/api/pages', {
|
||||
headers: {
|
||||
Authorization: `${User.slug} API-Key ${YOUR_API_KEY}`,
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
Payload ensures that the same, uniform access control is used across all authentication strategies. This enables you to utilize your existing access control configurations with both API keys and the standard email/password authentication. This consistency can aid in maintaining granular control over your API keys.
|
||||
|
||||
#### API Key *Only* Authentication
|
||||
#### API Key _Only_ Authentication
|
||||
|
||||
If you want to use API keys as the only authentication method for a collection, you can disable the default local strategy by setting `disableLocalStrategy` to `true` on the collection's `auth` property. This will disable the ability to authenticate with email and password, and will only allow for authentication via API key.
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
useAPIKey: true,
|
||||
disableLocalStrategy: true,
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Forgot Password
|
||||
@@ -90,17 +90,16 @@ Function that accepts one argument, containing `{ req, token, user }`, that allo
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
HTML templating can be used to create custom email templates, inline CSS
|
||||
automatically, and more. You can make a reusable function that standardizes
|
||||
all email sent from Payload, which makes sending custom emails more DRY.
|
||||
Payload doesn't ship with an HTML templating engine, so you are free to choose
|
||||
your own.
|
||||
HTML templating can be used to create custom email templates, inline CSS automatically, and more.
|
||||
You can make a reusable function that standardizes all email sent from Payload, which makes
|
||||
sending custom emails more DRY. Payload doesn't ship with an HTML templating engine, so you are
|
||||
free to choose your own.
|
||||
</Banner>
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
@@ -109,7 +108,7 @@ export const Customers: CollectionConfig = {
|
||||
// highlight-start
|
||||
generateEmailHTML: ({ req, token, user }) => {
|
||||
// Use the token provided to allow your user to reset their password
|
||||
const resetPasswordURL = `https://yourfrontend.com/reset-password?token=${token}`;
|
||||
const resetPasswordURL = `https://yourfrontend.com/reset-password?token=${token}`
|
||||
|
||||
return `
|
||||
<!doctype html>
|
||||
@@ -123,22 +122,21 @@ export const Customers: CollectionConfig = {
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
`
|
||||
},
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
If you specify a different URL to send your users to for resetting their
|
||||
password, such as a page on the frontend of your app or similar, you need to
|
||||
handle making the call to the Payload REST or GraphQL reset-password operation
|
||||
yourself on your frontend, using the token that was provided for you. Above,
|
||||
it was passed via query parameter.
|
||||
If you specify a different URL to send your users to for resetting their password, such as a page
|
||||
on the frontend of your app or similar, you need to handle making the call to the Payload REST or
|
||||
GraphQL reset-password operation yourself on your frontend, using the token that was provided for
|
||||
you. Above, it was passed via query parameter.
|
||||
</Banner>
|
||||
|
||||
**`generateEmailSubject`**
|
||||
@@ -173,8 +171,7 @@ Function that accepts one argument, containing `{ req, token, user }`, that allo
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
@@ -183,24 +180,23 @@ export const Customers: CollectionConfig = {
|
||||
// highlight-start
|
||||
generateEmailHTML: ({ req, token, user }) => {
|
||||
// Use the token provided to allow your user to verify their account
|
||||
const url = `https://yourfrontend.com/verify?token=${token}`;
|
||||
const url = `https://yourfrontend.com/verify?token=${token}`
|
||||
|
||||
return `Hey ${user.email}, verify your email by clicking here: ${url}`;
|
||||
}
|
||||
return `Hey ${user.email}, verify your email by clicking here: ${url}`
|
||||
},
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
If you specify a different URL to send your users to for email verification,
|
||||
such as a page on the frontend of your app or similar, you need to handle
|
||||
making the call to the Payload REST or GraphQL verification operation yourself
|
||||
on your frontend, using the token that was provided for you. Above, it was
|
||||
passed via query parameter.
|
||||
If you specify a different URL to send your users to for email verification, such as a page on the
|
||||
frontend of your app or similar, you need to handle making the call to the Payload REST or GraphQL
|
||||
verification operation yourself on your frontend, using the token that was provided for you.
|
||||
Above, it was passed via query parameter.
|
||||
</Banner>
|
||||
|
||||
**`generateEmailSubject`**
|
||||
@@ -231,9 +227,8 @@ As of Payload `1.0.0`, you can add additional authentication strategies to Paylo
|
||||
Behind the scenes, Payload uses PassportJS to power its local authentication strategy, so most strategies listed on the PassportJS website will work seamlessly. Combined with adding custom components to the admin panel's `Login` view, you can create advanced authentication strategies directly within Payload.
|
||||
|
||||
<Banner type="warning">
|
||||
This is an advanced feature, so only attempt this if you are an experienced
|
||||
developer. Otherwise, just let Payload's built-in authentication handle user
|
||||
auth for you.
|
||||
This is an advanced feature, so only attempt this if you are an experienced developer. Otherwise,
|
||||
just let Payload's built-in authentication handle user auth for you.
|
||||
</Banner>
|
||||
|
||||
The `strategies` property is an array that takes objects with the following properties:
|
||||
@@ -250,7 +245,6 @@ However, if you pass a function to `strategy`, `name` is a required property.
|
||||
|
||||
In either case, Payload will prefix the strategy name with the collection `slug` that the strategy is passed to.
|
||||
|
||||
|
||||
### Admin autologin
|
||||
|
||||
For testing and demo purposes you may want to skip forcing the admin user to login in order to access the panel.
|
||||
@@ -260,7 +254,7 @@ The default is that all users will have to login and this should not be enabled
|
||||
#### autoLogin Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| **`email`** | The email address of the user to login as |
|
||||
| **`password`** | The password of the user to login as |
|
||||
| **`prefillOnly`** | If set to true, the login credentials will be prefilled but the user will still need to click the login button. |
|
||||
@@ -270,18 +264,22 @@ The recommended way to use this feature is behind an environment variable to ens
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
user: 'users',
|
||||
// highlight-start
|
||||
autoLogin: process.env.PAYLOAD_PUBLIC_ENABLE_AUTOLOGIN === 'true' ? {
|
||||
autoLogin:
|
||||
process.env.PAYLOAD_PUBLIC_ENABLE_AUTOLOGIN === 'true'
|
||||
? {
|
||||
email: 'test@example.com',
|
||||
password: 'test',
|
||||
prefillOnly: true,
|
||||
} : false,
|
||||
}
|
||||
: false,
|
||||
// highlight-end
|
||||
},
|
||||
collections: [ /** */],
|
||||
collections: [
|
||||
/** */
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
@@ -17,6 +17,7 @@ The Access operation returns what a logged in user can and can't do with the col
|
||||
`GET http://localhost:3000/api/access`
|
||||
|
||||
Example response:
|
||||
|
||||
```ts
|
||||
{
|
||||
canAccessAdmin: true,
|
||||
@@ -77,6 +78,7 @@ Returns either a logged in user with token or null when there is no logged in us
|
||||
`GET http://localhost:3000/api/[collection-slug]/me`
|
||||
|
||||
Example response:
|
||||
|
||||
```ts
|
||||
{
|
||||
user: { // The JWT "payload" ;) from the logged in user
|
||||
@@ -108,6 +110,7 @@ query {
|
||||
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass an Express `res` to the Local API operation, Payload will set a cookie there as well.
|
||||
|
||||
**Example REST API login**:
|
||||
|
||||
```ts
|
||||
const res = await fetch('http://localhost:3000/api/[collection-slug]/login', {
|
||||
method: 'POST',
|
||||
@@ -117,10 +120,10 @@ const res = await fetch('http://localhost:3000/api/[collection-slug]/login', {
|
||||
body: JSON.stringify({
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'this-is-not-our-password...or-is-it?',
|
||||
})
|
||||
}),
|
||||
})
|
||||
|
||||
const json = await res.json();
|
||||
const json = await res.json()
|
||||
|
||||
// JSON will be equal to the following:
|
||||
/*
|
||||
@@ -168,6 +171,7 @@ const result = await payload.login({
|
||||
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
|
||||
|
||||
**Example REST API logout**:
|
||||
|
||||
```ts
|
||||
const res = await fetch('http://localhost:3000/api/[collection-slug]/logout', {
|
||||
method: 'POST',
|
||||
@@ -194,6 +198,7 @@ This operation requires a non-expired token to send back a new one. If the user'
|
||||
If successful, this operation will automatically renew the user's HTTP-only cookie and will send back the updated token in JSON.
|
||||
|
||||
**Example REST API token refresh**:
|
||||
|
||||
```ts
|
||||
const res = await fetch('http://localhost:3000/api/[collection-slug]/refresh-token', {
|
||||
method: 'POST',
|
||||
@@ -202,7 +207,7 @@ const res = await fetch('http://localhost:3000/api/[collection-slug]/refresh-tok
|
||||
},
|
||||
})
|
||||
|
||||
const json = await res.json();
|
||||
const json = await res.json()
|
||||
|
||||
// JSON will be equal to the following:
|
||||
/*
|
||||
@@ -233,7 +238,10 @@ mutation {
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
The Refresh operation will automatically find the user's token in either a JWT header or the HTTP-only cookie. But, you can specify the token you're looking to refresh by providing the REST API with a `token` within the JSON body of the request, or by providing the GraphQL resolver a `token` arg.
|
||||
The Refresh operation will automatically find the user's token in either a JWT header or the
|
||||
HTTP-only cookie. But, you can specify the token you're looking to refresh by providing the REST
|
||||
API with a `token` within the JSON body of the request, or by providing the GraphQL resolver a
|
||||
`token` arg.
|
||||
</Banner>
|
||||
|
||||
### Verify by Email
|
||||
@@ -241,13 +249,14 @@ mutation {
|
||||
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
|
||||
|
||||
**Example REST API user verification**:
|
||||
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/verify/${TOKEN_HERE}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
@@ -274,6 +283,7 @@ If a user locks themselves out and you wish to deliberately unlock them, you can
|
||||
To restrict who is allowed to unlock users, you can utilize the [`unlock`](/docs/access-control/overview#unlock) access control function.
|
||||
|
||||
**Example REST API unlock**:
|
||||
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, {
|
||||
method: 'POST',
|
||||
@@ -308,6 +318,7 @@ The link to reset the user's password contains a token which is what allows the
|
||||
By default, the Forgot Password operations send users to the Payload Admin panel to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](/docs/authentication/config#forgot-password).
|
||||
|
||||
**Example REST API Forgot Password**:
|
||||
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/forgot-password`, {
|
||||
method: 'POST',
|
||||
@@ -317,7 +328,7 @@ const res = await fetch(`http://localhost:3000/api/[collection-slug]/forgot-pass
|
||||
body: JSON.stringify({
|
||||
email: 'dev@payloadcms.com',
|
||||
}),
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
@@ -336,13 +347,18 @@ const token = await payload.forgotPassword({
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
disableEmail: false // you can disable the auto-generation of email via local API
|
||||
});
|
||||
disableEmail: false, // you can disable the auto-generation of email via local API
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br/>
|
||||
You can stop the reset-password email from being sent via using the local API. This is helpful if you need to create user accounts programmatically, but not set their password for them. This effectively generates a reset password token which you can then use to send to a page you create, allowing a user to "complete" their account by setting their password. In the background, you'd use the token to "reset" their password.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
You can stop the reset-password email from being sent via using the local API. This is helpful if
|
||||
you need to create user accounts programmatically, but not set their password for them. This
|
||||
effectively generates a reset password token which you can then use to send to a page you create,
|
||||
allowing a user to "complete" their account by setting their password. In the background, you'd
|
||||
use the token to "reset" their password.
|
||||
</Banner>
|
||||
|
||||
### Reset Password
|
||||
@@ -350,6 +366,7 @@ const token = await payload.forgotPassword({
|
||||
After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.
|
||||
|
||||
**Example REST API Reset Password**:
|
||||
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/reset-password`, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -12,13 +12,14 @@ keywords: authentication, config, configuration, overview, documentation, Conten
|
||||
/>
|
||||
|
||||
<Banner>
|
||||
Payload provides for highly secure and customizable user Authentication out of the box, which allows for users to identify themselves to Payload.
|
||||
Payload provides for highly secure and customizable user Authentication out of the box, which
|
||||
allows for users to identify themselves to Payload.
|
||||
</Banner>
|
||||
|
||||
Authentication is used within the Payload Admin panel itself as well as throughout your app(s) themselves however you determine necessary.
|
||||
|
||||

|
||||
*Admin panel screenshot depicting an Admins Collection with Auth enabled*
|
||||
_Admin panel screenshot depicting an Admins Collection with Auth enabled_
|
||||
|
||||
**Here are some common use cases of Authentication outside of Payload's dashboard itself:**
|
||||
|
||||
@@ -38,7 +39,7 @@ Every Payload Collection can opt-in to supporting Authentication by specifying t
|
||||
Simple example collection:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Admins: CollectionConfig = {
|
||||
slug: 'admins',
|
||||
@@ -56,13 +57,8 @@ export const Admins: CollectionConfig = {
|
||||
name: 'role',
|
||||
type: 'select',
|
||||
required: true,
|
||||
options: [
|
||||
'user',
|
||||
'admin',
|
||||
'editor',
|
||||
'developer',
|
||||
],
|
||||
}
|
||||
options: ['user', 'admin', 'editor', 'developer'],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
@@ -86,8 +82,11 @@ Successfully logging in returns a `JWT` (JSON web token) which is how a user wil
|
||||
You can specify what data gets encoded to the JWT token by setting `saveToJWT` to true in your auth collection fields. If you wish to use a different key other than the field `name`, you can provide it to `saveToJWT` as a string. It is also possible to use `saveToJWT` on fields that are nested in inside groups and tabs. If a group has a `saveToJWT` set it will include the object with all sub-fields in the token. You can set `saveToJWT: false` for any fields you wish to omit. If a field inside a group has `saveToJWT` set, but the group does not, the field will be included at the top level of the token.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br/>
|
||||
You can access the logged-in user from access control functions and hooks via the Express <strong>req</strong>. The logged-in user is automatically added as the <strong>user</strong> property.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
You can access the logged-in user from access control functions and hooks via the Express{' '}
|
||||
<strong>req</strong>. The logged-in user is automatically added as the <strong>user</strong>{' '}
|
||||
property.
|
||||
</Banner>
|
||||
|
||||
### HTTP-only cookies
|
||||
@@ -107,16 +106,19 @@ Fetch example, including credentials:
|
||||
```ts
|
||||
const response = await fetch('http://localhost:3000/api/pages', {
|
||||
credentials: 'include',
|
||||
});
|
||||
})
|
||||
|
||||
const pages = await response.json();
|
||||
const pages = await response.json()
|
||||
```
|
||||
|
||||
For more about how to automatically include cookies in requests from your app to your Payload API, [click here](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included).
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br/>
|
||||
To make sure you have a Payload cookie set properly in your browser after logging in, you can use Chrome's Developer Tools - Application - Cookies - [your-domain-here]. The Chrome Developer tools will still show HTTP-only cookies, even when JavaScript running on the page can't.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
To make sure you have a Payload cookie set properly in your browser after logging in, you can use
|
||||
Chrome's Developer Tools - Application - Cookies - [your-domain-here]. The Chrome Developer tools
|
||||
will still show HTTP-only cookies, even when JavaScript running on the page can't.
|
||||
</Banner>
|
||||
|
||||
### CSRF Protection
|
||||
@@ -128,28 +130,33 @@ For example, let's say you have a very popular app running at coolsite.com. This
|
||||
So, if a user of coolsite.com is logged in and just browsing around on the internet, they might stumble onto a page with bad intentions. That bad page might automatically make requests to all sorts of sites to see if they can find one that they can log into - and coolsite.com might be on their list. If your user was logged in while they visited that evil site, the attacker could do whatever they wanted as if they were your coolsite.com user by just sending requests to the coolsite API (which would automatically include the auth cookie). They could send themselves a bunch of money from your user's account, change the user's password, etc. This is what a CSRF attack is.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>To protect against CSRF attacks, Payload only accepts cookie-based authentication from domains that you explicitly whitelist.</strong>
|
||||
<strong>
|
||||
To protect against CSRF attacks, Payload only accepts cookie-based authentication from domains
|
||||
that you explicitly whitelist.
|
||||
</strong>
|
||||
</Banner>
|
||||
|
||||
To define domains that should allow users to identify themselves via the Payload HTTP-only cookie, use the `csrf` option on the base Payload config to whitelist domains that you trust.
|
||||
|
||||
`payload.config.ts`:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
const config = buildConfig({
|
||||
collections: [
|
||||
// collections here
|
||||
],
|
||||
// highlight-start
|
||||
csrf: [ // whitelist of domains to allow cookie auth from
|
||||
csrf: [
|
||||
// whitelist of domains to allow cookie auth from
|
||||
'https://your-frontend-app.com',
|
||||
'https://your-other-frontend-app.com',
|
||||
],
|
||||
// highlight-end
|
||||
});
|
||||
})
|
||||
|
||||
export default config;
|
||||
export default config
|
||||
```
|
||||
|
||||
### Identifying users via the Authorization Header
|
||||
@@ -157,11 +164,12 @@ export default config;
|
||||
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const request = await fetch('http://localhost:3000', {
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`
|
||||
}
|
||||
Authorization: `JWT ${token}`,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -11,48 +11,47 @@ Because Payload uses your existing Express server, you are free to add whatever
|
||||
This approach has a ton of benefits - it's great for isolation of concerns and limiting scope, but it also means that your additional routes won't have access to Payload's user authentication.
|
||||
|
||||
<Banner type="success">
|
||||
You can make full use of Payload's built-in authentication within your own
|
||||
custom Express endpoints by adding Payload's authentication middleware.
|
||||
You can make full use of Payload's built-in authentication within your own custom Express
|
||||
endpoints by adding Payload's authentication middleware.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
Payload must be initialized before the `payload.authenticate` middleware can
|
||||
be used. This is done by calling `payload.init()` prior to adding the
|
||||
middleware.
|
||||
Payload must be initialized before the `payload.authenticate` middleware can be used. This is done
|
||||
by calling `payload.init()` prior to adding the middleware.
|
||||
</Banner>
|
||||
|
||||
Example in `server.js`:
|
||||
|
||||
```ts
|
||||
import express from "express";
|
||||
import payload from "payload";
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
const app = express();
|
||||
const app = express()
|
||||
|
||||
const start = async () => {
|
||||
await payload.init({
|
||||
secret: "PAYLOAD_SECRET_KEY",
|
||||
mongoURL: "mongodb://localhost/payload",
|
||||
secret: 'PAYLOAD_SECRET_KEY',
|
||||
mongoURL: 'mongodb://localhost/payload',
|
||||
express: app,
|
||||
});
|
||||
})
|
||||
|
||||
const router = express.Router();
|
||||
const router = express.Router()
|
||||
|
||||
// Note: Payload must be initialized before the `payload.authenticate` middleware can be used
|
||||
router.use(payload.authenticate); // highlight-line
|
||||
router.use(payload.authenticate) // highlight-line
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
router.get('/', (req, res) => {
|
||||
if (req.user) {
|
||||
return res.send(`Authenticated successfully as ${req.user.email}.`);
|
||||
return res.send(`Authenticated successfully as ${req.user.email}.`)
|
||||
}
|
||||
|
||||
return res.send("Not authenticated");
|
||||
});
|
||||
return res.send('Not authenticated')
|
||||
})
|
||||
|
||||
app.use("/some-route-here", router);
|
||||
app.use('/some-route-here', router)
|
||||
|
||||
app.listen(3000);
|
||||
};
|
||||
app.listen(3000)
|
||||
}
|
||||
|
||||
start();
|
||||
start()
|
||||
```
|
||||
|
||||
@@ -11,10 +11,10 @@ keywords: configuration, config, settings, project, cloud, payload cloud, deploy
|
||||
Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.
|
||||
|
||||
<Banner type="success">
|
||||
Note: All Payload Cloud teams that deploy a project require a card on file.
|
||||
This helps us prevent fraud and abuse on our platform. If you select a plan
|
||||
with a free trial, you will not be charged until your trial period is over.
|
||||
We’ll remind you 7 days before your trial ends and you can cancel anytime.
|
||||
Note: All Payload Cloud teams that deploy a project require a card on file. This helps us prevent
|
||||
fraud and abuse on our platform. If you select a plan with a free trial, you will not be charged
|
||||
until your trial period is over. We’ll remind you 7 days before your trial ends and you can cancel
|
||||
anytime.
|
||||
</Banner>
|
||||
|
||||
### Project Details
|
||||
@@ -44,8 +44,8 @@ If you are deploying a new project from a template, the following settings will
|
||||
Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.
|
||||
|
||||
<Banner type="warning">
|
||||
Note: For security reasons, any variables you wish to provide to the Admin
|
||||
panel must be prefixed with `PAYLOAD_PUBLIC_`. Learn more
|
||||
Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed
|
||||
with `PAYLOAD_PUBLIC_`. Learn more
|
||||
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
|
||||
</Banner>
|
||||
|
||||
@@ -54,9 +54,8 @@ Any of the features in Payload Cloud that require environment variables will aut
|
||||
Payment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong> All Payload Cloud teams that deploy a project require a
|
||||
card on file. This helps us prevent fraud and abuse on our platform. If you
|
||||
select a plan with a free trial, you will not be charged until your trial
|
||||
period is over. We’ll remind you 7 days before your trial ends and you can
|
||||
cancel anytime.
|
||||
<strong>Note:</strong> All Payload Cloud teams that deploy a project require a card on file. This
|
||||
helps us prevent fraud and abuse on our platform. If you select a plan with a free trial, you will
|
||||
not be charged until your trial period is over. We’ll remind you 7 days before your trial ends and
|
||||
you can cancel anytime.
|
||||
</Banner>
|
||||
|
||||
@@ -13,9 +13,8 @@ Payload Cloud offers various plans tailored to meet your specific needs, includi
|
||||
To get started, you first need to create an account. Head over to [the login screen](https://payloadcms.com/login) and **Register for Free**.
|
||||
|
||||
<Banner type="success">
|
||||
To create your first project, you can either select [a
|
||||
template](#starting-from-a-template) or [import an existing
|
||||
project](#importing-from-an-existing-codebase) from GitHub.
|
||||
To create your first project, you can either select [a template](#starting-from-a-template) or
|
||||
[import an existing project](#importing-from-an-existing-codebase) from GitHub.
|
||||
</Banner>
|
||||
|
||||
## Starting from a Template
|
||||
@@ -32,9 +31,8 @@ Next, select your `GitHub Scope`. If you belong to multiple organizations, they
|
||||
After selecting your scope, create a unique `repository name` and select whether you want your repository to be public or private on GitHub.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong> Public repositories can be accessed by anyone online,
|
||||
while private repositories grant access only to you and anyone you explicitly
|
||||
authorize.
|
||||
<strong>Note:</strong> Public repositories can be accessed by anyone online, while private
|
||||
repositories grant access only to you and anyone you explicitly authorize.
|
||||
</Banner>
|
||||
|
||||
Once you are ready, click **Create Project**. This will clone the selected template to a new repository in your GitHub account, and take you to the configuration page to set up your project for deployment.
|
||||
@@ -47,7 +45,7 @@ Payload Cloud works for any Node.js + MongoDB app. From the New Project page, se
|
||||
_Creating a new project from an existing repository._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong> In order to make use of the features of Payload Cloud
|
||||
in your own codebase, you will need to add the [Cloud
|
||||
Plugin](https://github.com/payloadcms/plugin-cloud) to your Payload app.
|
||||
<strong>Note:</strong> In order to make use of the features of Payload Cloud in your own codebase,
|
||||
you will need to add the [Cloud Plugin](https://github.com/payloadcms/plugin-cloud) to your
|
||||
Payload app.
|
||||
</Banner>
|
||||
|
||||
@@ -9,11 +9,10 @@ keywords: cloud, payload cloud, projects, project, overview, database, file stor
|
||||
### Overview
|
||||
|
||||
<Banner>
|
||||
The overview tab shows your most recent deployment, along with build and
|
||||
deployment logs. From here, you can see your live URL, deployment details like
|
||||
timestamps and commit hash, as well as the status of your deployment. You can
|
||||
also trigger a redeployment manually, which will rebuild your project using
|
||||
the current configuration.
|
||||
The overview tab shows your most recent deployment, along with build and deployment logs. From
|
||||
here, you can see your live URL, deployment details like timestamps and commit hash, as well as
|
||||
the status of your deployment. You can also trigger a redeployment manually, which will rebuild
|
||||
your project using the current configuration.
|
||||
</Banner>
|
||||
|
||||

|
||||
@@ -40,8 +39,8 @@ You can update settings from your Project’s Settings tab. Changes to your buil
|
||||
From the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.
|
||||
|
||||
<Banner>
|
||||
Note: For security reasons, any variables you wish to provide to the Admin
|
||||
panel must be prefixed with `PAYLOAD_PUBLIC_`. Learn more
|
||||
Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed
|
||||
with `PAYLOAD_PUBLIC_`. Learn more
|
||||
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
|
||||
</Banner>
|
||||
### Custom Domains
|
||||
@@ -49,9 +48,8 @@ From the Environment Variables page of the Settings tab, you can add, update and
|
||||
With Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.
|
||||
|
||||
<Banner>
|
||||
Note: do not include the protocol (http:// or https://) or any routes (/page).
|
||||
Only include the domain name and extension, and optionally a subdomain. -
|
||||
your-domain.com - backend.your-domain.com
|
||||
Note: do not include the protocol (http:// or https://) or any routes (/page). Only include the
|
||||
domain name and extension, and optionally a subdomain. - your-domain.com - backend.your-domain.com
|
||||
</Banner>
|
||||
|
||||
Once you click save, a DNS record will be generated for your domain name to point to your live project. Add this record into your DNS provider’s records, and once the records are resolving properly (this can take 1hr to 48hrs in some cases), your domain will now to point to your live project.
|
||||
@@ -60,9 +58,9 @@ You will also need to configure your Payload project to use your specified domai
|
||||
|
||||
```ts
|
||||
export default buildConfig({
|
||||
serverURL: "https://example.com",
|
||||
serverURL: 'https://example.com',
|
||||
// the rest of your config,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Email
|
||||
@@ -84,18 +82,18 @@ Projects generated from a template will come pre-configured with the official Cl
|
||||
`yarn add @payloadcms/plugin-cloud`
|
||||
|
||||
```js
|
||||
import { payloadCloud } from "@payloadcms/plugin-cloud";
|
||||
import { buildConfig } from "payload/config";
|
||||
import { payloadCloud } from '@payloadcms/plugin-cloud'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
plugins: [payloadCloud()],
|
||||
// rest of config
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
**Note:** If your Payload config already has an email with transport, this
|
||||
will take precedence over Payload Cloud's email service.
|
||||
**Note:** If your Payload config already has an email with transport, this will take precedence
|
||||
over Payload Cloud's email service.
|
||||
</Banner>
|
||||
|
||||
##### **Optional configuration**
|
||||
@@ -106,5 +104,5 @@ If you wish to opt-out of any Payload cloud features, the plugin also accepts op
|
||||
payloadCloud({
|
||||
storage: false, // Disable file storage
|
||||
email: false, // Disable email delivery
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
@@ -7,8 +7,8 @@ keywords: team, teams, billing, subscription, payment, plan, plans, cloud, paylo
|
||||
---
|
||||
|
||||
<Banner>
|
||||
Within Payload Cloud, the team management feature offers you the ability to
|
||||
manage your organization, team members, billing, and subscription settings.
|
||||
Within Payload Cloud, the team management feature offers you the ability to manage your
|
||||
organization, team members, billing, and subscription settings.
|
||||
</Banner>
|
||||
|
||||

|
||||
|
||||
@@ -13,10 +13,10 @@ It's often best practice to write your Collections in separate files and then im
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`slug`** * | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| **`fields`** * | Array of field types that will determine the structure and functionality of the data stored within this Collection. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
|
||||
| **`indexes`** * | Array of database indexes to create, including compound indexes that have multiple fields. |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
|
||||
| **`indexes`** \* | Array of database indexes to create, including compound indexes that have multiple fields. |
|
||||
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |
|
||||
| **`hooks`** | Entry points to "tie in" to Collection actions at specific points. [More](/docs/hooks/overview#collection-hooks) |
|
||||
@@ -32,12 +32,12 @@ It's often best practice to write your Collections in separate files and then im
|
||||
| **`pagination`** | Set pagination-specific options for this collection. [More](#pagination) |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
#### Simple collection example
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Orders: CollectionConfig = {
|
||||
slug: 'orders',
|
||||
@@ -52,9 +52,9 @@ export const Orders: CollectionConfig = {
|
||||
type: 'relationship',
|
||||
relationTo: 'customers',
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### More collection config examples
|
||||
@@ -66,7 +66,7 @@ You can find an assortment of [example collection configs](https://github.com/pa
|
||||
You can customize the way that the Admin panel behaves on a collection-by-collection basis by defining the `admin` property on a collection's config.
|
||||
|
||||
| Option | Description |
|
||||
|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this collection from navigation and admin routing. |
|
||||
| `hooks` | Admin-specific hooks for this collection. [More](#admin-hooks) |
|
||||
@@ -95,7 +95,7 @@ If the function is specified, a Preview button will automatically appear in the
|
||||
**Example collection with preview function:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
@@ -109,13 +109,13 @@ export const Posts: CollectionConfig = {
|
||||
admin: {
|
||||
preview: (doc, { locale }) => {
|
||||
if (doc?.slug) {
|
||||
return `https://bigbird.com/preview/posts/${doc.slug}?locale=${locale}`;
|
||||
return `https://bigbird.com/preview/posts/${doc.slug}?locale=${locale}`
|
||||
}
|
||||
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
@@ -123,7 +123,7 @@ export const Posts: CollectionConfig = {
|
||||
Here are a few options that you can specify options for pagination on a collection-by-collection basis:
|
||||
|
||||
| Option | Description |
|
||||
| --------------------------- | -------------|
|
||||
| -------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `defaultLimit` | Integer that specifies the default per-page limit that should be used. Defaults to 10. |
|
||||
| `limits` | Provide an array of integers to use as per-page options for admins to choose from in the List view. |
|
||||
|
||||
@@ -146,8 +146,10 @@ In the List view, there is a "search" box that allows you to quickly find a docu
|
||||
For example, let's say you have a Posts collection with `title`, `metaDescription`, and `tags` fields - and you want all three of those fields to be searchable in the List view. You can simply add `admin.listSearchableFields: ['title', 'metaDescription', 'tags']` - and the admin UI will automatically search on those three fields plus the ID field.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong><br/>
|
||||
If you are adding <strong>listSearchableFields</strong>, make sure you index each of these fields so your admin queries can remain performant.
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
If you are adding <strong>listSearchableFields</strong>, make sure you index each of these fields
|
||||
so your admin queries can remain performant.
|
||||
</Banner>
|
||||
|
||||
### Admin Hooks
|
||||
@@ -161,24 +163,24 @@ The `beforeDuplicate` hook is an async function that accepts an object containin
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { BeforeDuplicate, CollectionConfig } from 'payload/types';
|
||||
import { BeforeDuplicate, CollectionConfig } from 'payload/types'
|
||||
// Your auto-generated Page type
|
||||
import { Page } from '../payload-types.ts';
|
||||
import { Page } from '../payload-types.ts'
|
||||
|
||||
const beforeDuplicate: BeforeDuplicate<Page> = ({ data }) => {
|
||||
return {
|
||||
...data,
|
||||
title: `${data.title} Copy`,
|
||||
uniqueField: data.uniqueField ? `${data.uniqueField}-copy` : '',
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const Page: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
admin: {
|
||||
hooks: {
|
||||
beforeDuplicate,
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
@@ -189,8 +191,8 @@ export const Page: CollectionConfig = {
|
||||
name: 'uniqueField',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -199,14 +201,14 @@ export const Page: CollectionConfig = {
|
||||
You can import collection types as follows:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
// This is the type used for incoming collection configs.
|
||||
// Only the bare minimum properties are marked as required.
|
||||
```
|
||||
|
||||
```ts
|
||||
import { SanitizedCollectionConfig } from 'payload/types';
|
||||
import { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
// This is the type used after an incoming collection config is fully sanitized.
|
||||
// Generally, this is only used internally by Payload.
|
||||
|
||||
@@ -32,29 +32,29 @@ _\* An asterisk denotes that a property is required._
|
||||
#### Simple Global example
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from "payload/types";
|
||||
import { GlobalConfig } from 'payload/types'
|
||||
|
||||
const Nav: GlobalConfig = {
|
||||
slug: "nav",
|
||||
slug: 'nav',
|
||||
fields: [
|
||||
{
|
||||
name: "items",
|
||||
type: "array",
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
required: true,
|
||||
maxRows: 8,
|
||||
fields: [
|
||||
{
|
||||
name: "page",
|
||||
type: "relationship",
|
||||
relationTo: "pages", // "pages" is the slug of an existing collection
|
||||
name: 'page',
|
||||
type: 'relationship',
|
||||
relationTo: 'pages', // "pages" is the slug of an existing collection
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export default Nav;
|
||||
export default Nav
|
||||
```
|
||||
|
||||
#### Global config example
|
||||
@@ -66,7 +66,7 @@ You can find an [example Global config](https://github.com/payloadcms/public-dem
|
||||
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a Global's config.
|
||||
|
||||
| Option | Description |
|
||||
|--------------|-----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this global from navigation and admin routing. |
|
||||
| `components` | Swap in your own React components to be used within this Global. [More](/docs/admin/components#globals) |
|
||||
@@ -87,27 +87,27 @@ If the function is specified, a Preview button will automatically appear in the
|
||||
**Example global with preview function:**
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from "payload/types";
|
||||
import { GlobalConfig } from 'payload/types'
|
||||
|
||||
export const MyGlobal: GlobalConfig = {
|
||||
slug: "my-global",
|
||||
slug: 'my-global',
|
||||
fields: [
|
||||
{
|
||||
name: "slug",
|
||||
type: "text",
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
preview: (doc, { locale }) => {
|
||||
if (doc?.slug) {
|
||||
return `https://bigbird.com/preview/${doc.slug}?locale=${locale}`;
|
||||
return `https://bigbird.com/preview/${doc.slug}?locale=${locale}`
|
||||
}
|
||||
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Access control
|
||||
@@ -127,14 +127,14 @@ Globals support all field types that Payload has to offer—including simple fie
|
||||
You can import global types as follows:
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from "payload/types";
|
||||
import { GlobalConfig } from 'payload/types'
|
||||
|
||||
// This is the type used for incoming global configs.
|
||||
// Only the bare minimum properties are marked as required.
|
||||
```
|
||||
|
||||
```ts
|
||||
import { SanitizedGlobalConfig } from "payload/types";
|
||||
import { SanitizedGlobalConfig } from 'payload/types'
|
||||
|
||||
// This is the type used after an incoming global config is fully sanitized.
|
||||
// Generally, this is only used internally by Payload.
|
||||
|
||||
@@ -13,47 +13,47 @@ While Payload's built-in features come translated, you may want to also translat
|
||||
Here is an example of a simple collection supporting both English and Spanish editors:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Articles: CollectionConfig = {
|
||||
slug: "articles",
|
||||
slug: 'articles',
|
||||
labels: {
|
||||
singular: {
|
||||
en: "Article",
|
||||
es: "Artículo",
|
||||
en: 'Article',
|
||||
es: 'Artículo',
|
||||
},
|
||||
plural: {
|
||||
en: "Articles",
|
||||
es: "Artículos",
|
||||
en: 'Articles',
|
||||
es: 'Artículos',
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
group: { en: "Content", es: "Contenido" },
|
||||
group: { en: 'Content', es: 'Contenido' },
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: "title",
|
||||
type: "text",
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: {
|
||||
en: "Title",
|
||||
es: "Título",
|
||||
en: 'Title',
|
||||
es: 'Título',
|
||||
},
|
||||
admin: {
|
||||
placeholder: { en: "Enter title", es: "Introduce el título" },
|
||||
placeholder: { en: 'Enter title', es: 'Introduce el título' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
type: "radio",
|
||||
name: 'type',
|
||||
type: 'radio',
|
||||
options: [
|
||||
{
|
||||
value: "news",
|
||||
label: { en: "News", es: "Noticias" },
|
||||
value: 'news',
|
||||
label: { en: 'News', es: 'Noticias' },
|
||||
}, // etc...
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Admin UI
|
||||
@@ -62,8 +62,10 @@ The Payload admin panel reads the language settings of a user's browser and disp
|
||||
After a user logs in, they can change their language selection in the `/account` view.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong><br />
|
||||
If there is a language that Payload does not yet support, we accept code [contributions](https://github.com/payloadcms/payload/blob/master/contributing.md).
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
If there is a language that Payload does not yet support, we accept code
|
||||
[contributions](https://github.com/payloadcms/payload/blob/master/contributing.md).
|
||||
</Banner>
|
||||
|
||||
### Node Express
|
||||
@@ -81,28 +83,28 @@ In your Payload config, you can add translations and customize the settings in `
|
||||
**Example Payload config extending i18n:**
|
||||
|
||||
```ts
|
||||
import { buildConfig } from "payload/config";
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
//...
|
||||
i18n: {
|
||||
fallbackLng: "en", // default
|
||||
fallbackLng: 'en', // default
|
||||
debug: false, // default
|
||||
resources: {
|
||||
en: {
|
||||
custom: {
|
||||
// namespace can be anything you want
|
||||
key1: "Translation with {{variable}}", // translation
|
||||
key1: 'Translation with {{variable}}', // translation
|
||||
},
|
||||
// override existing translation keys
|
||||
general: {
|
||||
dashboard: "Home",
|
||||
dashboard: 'Home',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//...
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
See the i18next [configuration options](https://www.i18next.com/overview/configuration-options) to learn more.
|
||||
|
||||
@@ -22,15 +22,11 @@ export default buildConfig({
|
||||
// collections go here
|
||||
],
|
||||
localization: {
|
||||
locales: [
|
||||
'en',
|
||||
'es',
|
||||
'de',
|
||||
],
|
||||
locales: ['en', 'es', 'de'],
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
**Example Payload config set up for localization with full locales objects:**
|
||||
@@ -45,20 +41,20 @@ export default buildConfig({
|
||||
localization: {
|
||||
locales: [
|
||||
{
|
||||
label: "English",
|
||||
code: "en",
|
||||
label: 'English',
|
||||
code: 'en',
|
||||
},
|
||||
{
|
||||
label: "Arabic",
|
||||
code: "ar",
|
||||
label: 'Arabic',
|
||||
code: 'ar',
|
||||
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
|
||||
rtl: true,
|
||||
},
|
||||
],
|
||||
defaultLocale: "en",
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
**Here is a brief explanation of each of the options available within the `localization` property:**
|
||||
@@ -96,17 +92,21 @@ With the above configuration, the `title` field will now be saved in the databas
|
||||
All field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong><br/>
|
||||
Enabling localization for field types that support nested fields will automatically create localized "sets" of all fields contained within the field. For example, if you have a page layout using a blocks field type, you have the choice of either localizing the full layout, by enabling localization on the top-level blocks field, or only certain fields within the layout.
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
Enabling localization for field types that support nested fields will automatically create
|
||||
localized "sets" of all fields contained within the field. For example, if you have a page layout
|
||||
using a blocks field type, you have the choice of either localizing the full layout, by enabling
|
||||
localization on the top-level blocks field, or only certain fields within the layout.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
When converting an existing field to or from `localized: true` the data
|
||||
structure in the document will change for this field and so existing data for
|
||||
this field will be lost. Before changing the localization setting on fields
|
||||
with existing data, you may need to consider a field migration strategy.
|
||||
When converting an existing field to or from `localized: true` the data structure in the document
|
||||
will change for this field and so existing data for this field will be lost. Before changing the
|
||||
localization setting on fields with existing data, you may need to consider a field migration
|
||||
strategy.
|
||||
</Banner>
|
||||
|
||||
### Retrieving localized docs
|
||||
@@ -152,7 +152,9 @@ query {
|
||||
```
|
||||
|
||||
<Banner>
|
||||
In GraphQL, specifying the locale at the top level of a query will automatically apply it throughout all nested relationship fields. You can override this behavior by re-specifying locale arguments in nested related document queries.
|
||||
In GraphQL, specifying the locale at the top level of a query will automatically apply it
|
||||
throughout all nested relationship fields. You can override this behavior by re-specifying locale
|
||||
arguments in nested related document queries.
|
||||
</Banner>
|
||||
|
||||
##### Local API
|
||||
@@ -170,5 +172,9 @@ const posts = await payload.find({
|
||||
```
|
||||
|
||||
<Banner type="alert">
|
||||
<strong>Tip:</strong><br/>The REST and Local APIs can return all localization data in one request by passing 'all' or '*' as the <strong>locale</strong> parameter. The response will be structured so that field values come back as the full objects keyed for each locale instead of the single, translated value.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
The REST and Local APIs can return all localization data in one request by passing 'all' or '*' as
|
||||
the <strong>locale</strong> parameter. The response will be structured so that field values come
|
||||
back as the full objects keyed for each locale instead of the single, translated value.
|
||||
</Banner>
|
||||
|
||||
@@ -13,8 +13,8 @@ Payload is a _config-based_, code-first CMS and application framework. The Paylo
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
This file is included in the Payload admin bundle, so make sure you do not
|
||||
embed any sensitive information.
|
||||
This file is included in the Payload admin bundle, so make sure you do not embed any sensitive
|
||||
information.
|
||||
</Banner>
|
||||
|
||||
## Options
|
||||
@@ -48,21 +48,21 @@ Payload is a _config-based_, code-first CMS and application framework. The Paylo
|
||||
#### Simple example
|
||||
|
||||
```ts
|
||||
import { buildConfig } from "payload/config";
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
{
|
||||
slug: "pages",
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: "title",
|
||||
type: "text",
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
@@ -70,23 +70,23 @@ export default buildConfig({
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: "header",
|
||||
slug: 'header',
|
||||
fields: [
|
||||
{
|
||||
name: "nav",
|
||||
type: "array",
|
||||
name: 'nav',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: "page",
|
||||
type: "relationship",
|
||||
relationTo: "pages",
|
||||
name: 'page',
|
||||
type: 'relationship',
|
||||
relationTo: 'pages',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Full example config
|
||||
@@ -120,10 +120,9 @@ project-name
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
If you use an environment variable to configure any properties that are
|
||||
required for the Admin panel to function (ex. serverURL or any routes), you
|
||||
need to make sure that your Admin panel code can access it. [Click
|
||||
here](/docs/admin/webpack#admin-environment-vars) for more info.
|
||||
If you use an environment variable to configure any properties that are required for the Admin
|
||||
panel to function (ex. serverURL or any routes), you need to make sure that your Admin panel code
|
||||
can access it. [Click here](/docs/admin/webpack#admin-environment-vars) for more info.
|
||||
</Banner>
|
||||
|
||||
### Customizing & Automating Config Location Detection
|
||||
@@ -145,7 +144,7 @@ In addition to the above automated detection, you can specify your own location
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "PAYLOAD_CONFIG_PATH=path/to/custom-config.js node server.js",
|
||||
"dev": "PAYLOAD_CONFIG_PATH=path/to/custom-config.js node server.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -161,14 +160,14 @@ Payload comes with `isomorphic-fetch` installed which means that even in Node, y
|
||||
You can import config types as follows:
|
||||
|
||||
```ts
|
||||
import { Config } from "payload/config";
|
||||
import { Config } from 'payload/config'
|
||||
|
||||
// This is the type used for an incoming Payload config.
|
||||
// Only the bare minimum properties are marked as required.
|
||||
```
|
||||
|
||||
```ts
|
||||
import { SanitizedConfig } from "payload/config";
|
||||
import { SanitizedConfig } from 'payload/config'
|
||||
|
||||
// This is the type used after an incoming Payload config is fully sanitized.
|
||||
// Generally, this is only used internally by Payload.
|
||||
|
||||
@@ -23,7 +23,8 @@ Database transactions allow your application to make a series of database change
|
||||
By default, Payload will use transactions for all operations, as long as it is supported by the configured database. Database changes are contained within all Payload operations and any errors thrown will result in all changes being rolled back without being committed. When transactions are not supported by the database, Payload will continue to operate as expected without them.
|
||||
|
||||
<Banner type="info">
|
||||
<strong>Note:</strong><br/>
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
MongoDB requires a connection to a replicaset in order to make use of transactions.
|
||||
</Banner>
|
||||
|
||||
@@ -37,12 +38,13 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
collection: 'my-slug',
|
||||
data: {
|
||||
some: 'data',
|
||||
}
|
||||
});
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Async Hooks with Transactions
|
||||
|
||||
Since Payload hooks can be async and be written to not await the result, it is possible to have an incorrect success response returned on a request that is rolled back. If you have a hook where you do not `await` the result, then you should **not** pass the `req.transactionID`.
|
||||
|
||||
```ts
|
||||
@@ -53,7 +55,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
collection: 'my-slug',
|
||||
data: {
|
||||
some: 'other data',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// Should this call fail, it will not rollback other changes
|
||||
@@ -65,7 +67,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
collection: 'my-slug',
|
||||
data: {
|
||||
some: 'other data',
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -82,4 +84,3 @@ The following functions can be used for managing transactions:
|
||||
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
|
||||
|
||||
## Migrations
|
||||
|
||||
|
||||
@@ -57,15 +57,16 @@ payload.init({
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
},
|
||||
fromName: "hello",
|
||||
fromAddress: "hello@example.com",
|
||||
fromName: 'hello',
|
||||
fromAddress: 'hello@example.com',
|
||||
},
|
||||
// ...
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
It is best practice to avoid saving credentials or API keys directly in your code, use [environment variables](/docs/configuration/overview#using-environment-variables-in-your-config).
|
||||
It is best practice to avoid saving credentials or API keys directly in your code, use
|
||||
[environment variables](/docs/configuration/overview#using-environment-variables-in-your-config).
|
||||
</Banner>
|
||||
|
||||
### Use an email service
|
||||
@@ -73,10 +74,10 @@ payload.init({
|
||||
Many third party mail providers are available and offer benefits beyond basic SMTP. As an example your payload init could look this if you wanted to use SendGrid.com though the same approach would work for any other [NodeMailer transports](https://nodemailer.com/transports/) shown here or provided by another third party.
|
||||
|
||||
```ts
|
||||
import payload from "payload";
|
||||
import nodemailerSendgrid from "nodemailer-sendgrid";
|
||||
import payload from 'payload'
|
||||
import nodemailerSendgrid from 'nodemailer-sendgrid'
|
||||
|
||||
const sendGridAPIKey = process.env.SENDGRID_API_KEY;
|
||||
const sendGridAPIKey = process.env.SENDGRID_API_KEY
|
||||
|
||||
payload.init({
|
||||
...(sendGridAPIKey
|
||||
@@ -85,12 +86,12 @@ payload.init({
|
||||
transportOptions: nodemailerSendgrid({
|
||||
apiKey: sendGridAPIKey,
|
||||
}),
|
||||
fromName: "Admin",
|
||||
fromAddress: "admin@example.com",
|
||||
fromName: 'Admin',
|
||||
fromAddress: 'admin@example.com',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Use a custom NodeMailer transport
|
||||
@@ -98,11 +99,11 @@ payload.init({
|
||||
To take full control of the mail transport you may wish to use `nodemailer.createTransport()` on your server and provide it to Payload init.
|
||||
|
||||
```ts
|
||||
import payload from "payload";
|
||||
import nodemailer from "nodemailer";
|
||||
import payload from 'payload'
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
const payload = require("payload");
|
||||
const nodemailer = require("nodemailer");
|
||||
const payload = require('payload')
|
||||
const nodemailer = require('nodemailer')
|
||||
|
||||
const transport = await nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
@@ -111,16 +112,16 @@ const transport = await nodemailer.createTransport({
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
payload.init({
|
||||
email: {
|
||||
fromName: "Admin",
|
||||
fromAddress: "admin@example.com",
|
||||
fromName: 'Admin',
|
||||
fromAddress: 'admin@example.com',
|
||||
transport,
|
||||
},
|
||||
// ...
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Sending Mail
|
||||
@@ -136,12 +137,12 @@ To see ethereal credentials, add `logMockCredentials: true` to the email options
|
||||
```ts
|
||||
payload.init({
|
||||
email: {
|
||||
fromName: "Admin",
|
||||
fromAddress: "admin@example.com",
|
||||
fromName: 'Admin',
|
||||
fromAddress: 'admin@example.com',
|
||||
logMockCredentials: true, // Optional
|
||||
},
|
||||
// ...
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
**Console output when starting payload with a mock email instance and logMockCredentials: true**
|
||||
@@ -160,7 +161,8 @@ payload.init({
|
||||
The mock email handler is used when payload is started with neither `transport` or `transportOptions` to know how to deliver email.
|
||||
|
||||
<Banner type="warning">
|
||||
The randomly generated email account username and password will be different each time the Payload server starts.
|
||||
The randomly generated email account username and password will be different each time the Payload
|
||||
server starts.
|
||||
</Banner>
|
||||
|
||||
### Using multiple mail providers
|
||||
|
||||
@@ -7,9 +7,8 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Array field type is used when you need to have a set of "repeating"
|
||||
fields. It stores an array of objects containing the fields that you define.
|
||||
Its uses can be simple or highly complex.
|
||||
The Array field type is used when you need to have a set of "repeating" fields. It stores an array
|
||||
of objects containing the fields that you define. Its uses can be simple or highly complex.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
@@ -61,47 +60,47 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "slider", // required
|
||||
type: "array", // required
|
||||
label: "Image Slider",
|
||||
name: 'slider', // required
|
||||
type: 'array', // required
|
||||
label: 'Image Slider',
|
||||
minRows: 2,
|
||||
maxRows: 10,
|
||||
interfaceName: "CardSlider", // optional
|
||||
interfaceName: 'CardSlider', // optional
|
||||
labels: {
|
||||
singular: "Slide",
|
||||
plural: "Slides",
|
||||
singular: 'Slide',
|
||||
plural: 'Slides',
|
||||
},
|
||||
fields: [
|
||||
// required
|
||||
{
|
||||
name: "title",
|
||||
type: "text",
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "caption",
|
||||
type: "text",
|
||||
name: 'caption',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
components: {
|
||||
RowLabel: ({ data, index }) => {
|
||||
return data?.title || `Slide ${String(index).padStart(2, "0")}`;
|
||||
return data?.title || `Slide ${String(index).padStart(2, '0')}`
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,10 +7,10 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Blocks field type is <strong>incredibly powerful</strong> and can be used
|
||||
as a <em>layout builder</em> as well as to define any other flexible content
|
||||
model you can think of. It stores an array of objects, where each object must
|
||||
match the shape of one of your provided block configs.
|
||||
The Blocks field type is <strong>incredibly powerful</strong> and can be used as a{' '}
|
||||
<em>layout builder</em> as well as to define any other flexible content model you can think of. It
|
||||
stores an array of objects, where each object must match the shape of one of your provided block
|
||||
configs.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
@@ -29,10 +29,10 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
### Field config
|
||||
|
||||
| Option | Description |
|
||||
|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as the heading in the Admin panel or an object with keys for each language. Auto-generated from name if not defined. |
|
||||
| **`blocks`** * | Array of [block configs](/docs/fields/blocks#block-configs) to be made available to this field. |
|
||||
| **`blocks`** \* | Array of [block configs](/docs/fields/blocks#block-configs) to be made available to this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide field-level hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
@@ -62,12 +62,11 @@ Blocks are defined as separate configs of their own.
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
Best practice is to define each block config in its own file, and then import
|
||||
them into your Blocks field as necessary. This way each block config can be
|
||||
easily shared between fields. For instance, using the "layout builder"
|
||||
example, you might want to feature a few of the same blocks in a Post
|
||||
collection as well as a Page collection. Abstracting into their own files
|
||||
trivializes their reusability.
|
||||
Best practice is to define each block config in its own file, and then import them into your
|
||||
Blocks field as necessary. This way each block config can be easily shared between fields. For
|
||||
instance, using the "layout builder" example, you might want to feature a few of the same blocks
|
||||
in a Post collection as well as a Page collection. Abstracting into their own files trivializes
|
||||
their reusability.
|
||||
</Banner>
|
||||
|
||||
| Option | Description |
|
||||
@@ -97,7 +96,7 @@ The Admin panel provides each block with a `blockName` field which optionally al
|
||||
`collections/ExampleCollection.js`
|
||||
|
||||
```ts
|
||||
import { Block, CollectionConfig } from 'payload/types';
|
||||
import { Block, CollectionConfig } from 'payload/types'
|
||||
|
||||
const QuoteBlock: Block = {
|
||||
slug: 'Quote', // required
|
||||
@@ -116,7 +115,7 @@ const QuoteBlock: Block = {
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -132,7 +131,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
@@ -140,5 +139,5 @@ export const ExampleCollection: CollectionConfig = {
|
||||
As you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:
|
||||
|
||||
```ts
|
||||
import type { Block } from 'payload/types';
|
||||
import type { Block } from 'payload/types'
|
||||
```
|
||||
|
||||
@@ -6,22 +6,20 @@ desc: Checkbox field types allow the developer to save a boolean value in the da
|
||||
keywords: checkbox, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Checkbox field type saves a boolean in the database.
|
||||
</Banner>
|
||||
<Banner>The Checkbox field type saves a boolean in the database.</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/checkbox.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/checkbox-dark.png'
|
||||
alt='Checkbox field with text field in Payload admin panel'
|
||||
caption='Admin panel screenshot of Checkbox field with Text field below'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/checkbox.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/checkbox-dark.png"
|
||||
alt="Checkbox field with text field in Payload admin panel"
|
||||
caption="Admin panel screenshot of Checkbox field with Text field below"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`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 a [MongoDB index](https://docs.mongodb.com/manual/indexes/) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
@@ -35,13 +33,14 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -51,7 +50,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
type: 'checkbox', // required
|
||||
label: 'Click me to see fanciness',
|
||||
defaultValue: false,
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,15 +7,16 @@ desc: The Code field type will store any string in the Database. Learn how to us
|
||||
keywords: code, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Code field type saves a string in the database, but provides the Admin panel with a code editor styled interface.
|
||||
<Banner>
|
||||
The Code field type saves a string in the database, but provides the Admin panel with a code
|
||||
editor styled interface.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/code.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/code-dark.png'
|
||||
alt='Shows a Code field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Code field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/code.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/code-dark.png"
|
||||
alt="Shows a Code field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Code field"
|
||||
/>
|
||||
|
||||
This field uses the `monaco-react` editor syntax highlighting.
|
||||
@@ -23,8 +24,8 @@ This field uses the `monaco-react` editor syntax highlighting.
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build a [MongoDB index](https://docs.mongodb.com/manual/indexes/) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
@@ -48,15 +49,16 @@ _\* An asterisk denotes that a property is required._
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`language`** | This property can be set to any language listed [here](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages). |
|
||||
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IDiffEditorConstructionOptions.html). |
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -66,9 +68,9 @@ export const ExampleCollection: CollectionConfig = {
|
||||
type: 'code', // required
|
||||
required: true,
|
||||
admin: {
|
||||
language: 'javascript'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
language: 'javascript',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,41 +6,43 @@ desc: With the Collapsible field, you can place fields within a collapsible layo
|
||||
keywords: row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Collapsible field is presentational-only and only affects the Admin panel. By using it, you can place fields within a nice layout component that can be collapsed / expanded.
|
||||
<Banner>
|
||||
The Collapsible field is presentational-only and only affects the Admin panel. By using it, you
|
||||
can place fields within a nice layout component that can be collapsed / expanded.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/collapsible.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/collapsible-dark.png'
|
||||
alt='Shows a Collapsible field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Collapsible field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/collapsible.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/collapsible-dark.png"
|
||||
alt="Shows a Collapsible field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Collapsible field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | ------------------------------------------------------------------------- |
|
||||
| **`label`** * | A label to render within the header of the collapsible component. This can be a string, function or react component. Function/components receive `({ data, path })` as args. |
|
||||
| **`fields`** * | Array of field types to nest within this Collapsible. |
|
||||
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`label`** \* | A label to render within the header of the collapsible component. This can be a string, function or react component. Function/components receive `({ data, path })` as args. |
|
||||
| **`fields`** \* | Array of field types to nest within this Collapsible. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------- |
|
||||
| ------------------- | ------------------------------- |
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -48,7 +50,8 @@ export const ExampleCollection: CollectionConfig = {
|
||||
{
|
||||
label: ({ data }) => data?.title || 'Untitled',
|
||||
type: 'collapsible', // required
|
||||
fields: [ // required
|
||||
fields: [
|
||||
// required
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
@@ -60,7 +63,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,15 +7,15 @@ keywords: date, fields, config, configuration, documentation, Content Management
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Date field type saves a Date in the database and provides the Admin panel
|
||||
with a customizable time picker interface.
|
||||
The Date field type saves a Date in the database and provides the Admin panel with a customizable
|
||||
time picker interface.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/date.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/date-dark.png'
|
||||
alt='Shows a Date field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Date field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/date.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/date-dark.png"
|
||||
alt="Shows a Date field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Date field"
|
||||
/>
|
||||
|
||||
This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepicker) for the Admin panel component.
|
||||
@@ -75,41 +75,41 @@ When only `pickerAppearance` is set, an equivalent format will be rendered in th
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "dateOnly",
|
||||
type: "date",
|
||||
name: 'dateOnly',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: "dayOnly",
|
||||
displayFormat: "d MMM yyy",
|
||||
pickerAppearance: 'dayOnly',
|
||||
displayFormat: 'd MMM yyy',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timeOnly",
|
||||
type: "date",
|
||||
name: 'timeOnly',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: "timeOnly",
|
||||
displayFormat: "h:mm:ss a",
|
||||
pickerAppearance: 'timeOnly',
|
||||
displayFormat: 'h:mm:ss a',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "monthOnly",
|
||||
type: "date",
|
||||
name: 'monthOnly',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: "monthOnly",
|
||||
displayFormat: "MMMM yyyy",
|
||||
pickerAppearance: 'monthOnly',
|
||||
displayFormat: 'MMMM yyyy',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,22 +6,20 @@ desc: The Email field enforces that the value provided is a valid email address.
|
||||
keywords: email, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Email field enforces that the value provided is a valid email address.
|
||||
</Banner>
|
||||
<Banner>The Email field enforces that the value provided is a valid email address.</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/email.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/email-dark.png'
|
||||
alt='Shows an Email field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of an Email field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/email.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/email-dark.png"
|
||||
alt="Shows an Email field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of an Email field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build a [MongoDB index](https://docs.mongodb.com/manual/indexes/) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
@@ -36,7 +34,7 @@ keywords: email, fields, config, configuration, documentation, Content Managemen
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
|
||||
@@ -53,8 +51,9 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -64,7 +63,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
type: 'email', // required
|
||||
label: 'Contact Email Address',
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,15 +7,15 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Group field allows fields to be nested under a common property name. It
|
||||
also groups fields together visually in the Admin panel.
|
||||
The Group field allows fields to be nested under a common property name. It also groups fields
|
||||
together visually in the Admin panel.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/group.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/group-dark.png'
|
||||
alt='Shows a Group field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Group field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/group.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/group-dark.png"
|
||||
alt="Shows a Group field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Group field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
@@ -51,27 +51,27 @@ Set this property to `true` to hide this field's gutter within the admin panel.
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "pageMeta", // required
|
||||
type: "group", // required
|
||||
interfaceName: "Meta", // optional
|
||||
name: 'pageMeta', // required
|
||||
type: 'group', // required
|
||||
interfaceName: 'Meta', // optional
|
||||
fields: [
|
||||
// required
|
||||
{
|
||||
name: "title",
|
||||
type: "text",
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
minLength: 20,
|
||||
maxLength: 100,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
type: "textarea",
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
minLength: 40,
|
||||
maxLength: 160,
|
||||
@@ -79,5 +79,5 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,15 +7,16 @@ desc: The JSON field type will store any string in the Database. Learn how to us
|
||||
keywords: json, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The JSON field type saves actual JSON in the database, which differs from the Code field that saves the value as a string in the database.
|
||||
<Banner>
|
||||
The JSON field type saves actual JSON in the database, which differs from the Code field that
|
||||
saves the value as a string in the database.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/json.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/json-dark.png'
|
||||
alt='Shows a JSON field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a JSON field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/json.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/json-dark.png"
|
||||
alt="Shows a JSON field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a JSON field"
|
||||
/>
|
||||
|
||||
This field uses the `monaco-react` editor syntax highlighting.
|
||||
@@ -23,8 +24,8 @@ This field uses the `monaco-react` editor syntax highlighting.
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build a [MongoDB index](https://docs.mongodb.com/manual/indexes/) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
@@ -46,14 +47,15 @@ _\* An asterisk denotes that a property is required._
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IDiffEditorConstructionOptions.html). |
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -62,7 +64,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
name: 'customerJSON', // required
|
||||
type: 'json', // required
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,22 +6,23 @@ desc: Number fields store and validate numeric data. Learn how to use and format
|
||||
keywords: number, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Number field stores and validates numeric entry and supports additional numerical validation and formatting features.
|
||||
<Banner>
|
||||
The Number field stores and validates numeric entry and supports additional numerical validation
|
||||
and formatting features.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/number.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/number-dark.png'
|
||||
alt='Shows a Number field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Number field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/number.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/number-dark.png"
|
||||
alt="Shows a Number field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Number field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`min`** | Minimum value accepted. Used in the default `validation` function. |
|
||||
| **`max`** | Maximum value accepted. Used in the default `validation` function. |
|
||||
@@ -41,7 +42,7 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
|
||||
@@ -62,8 +63,9 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -74,8 +76,8 @@ export const ExampleCollection: CollectionConfig = {
|
||||
required: true,
|
||||
admin: {
|
||||
step: 1,
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,8 +7,9 @@ keywords: overview, fields, config, configuration, documentation, Content Manage
|
||||
---
|
||||
|
||||
<Banner type="info">
|
||||
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the shape of the data
|
||||
that they store. Payload offers a wide variety of field types - both simple and complex.
|
||||
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the
|
||||
shape of the data that they store. Payload offers a wide variety of field types - both simple and
|
||||
complex.
|
||||
</Banner>
|
||||
|
||||
Fields are defined as an array on Collections and Globals via the `fields` key. They define the shape of the data that will be stored as well as automatically construct the corresponding Admin UI.
|
||||
@@ -16,8 +17,9 @@ Fields are defined as an array on Collections and Globals via the `fields` key.
|
||||
The required `type` property on a field determines what values it can accept, how it is presented in the API, and how the field will be rendered in the admin interface.
|
||||
|
||||
**Simple collection with two fields:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Page: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
@@ -31,7 +33,7 @@ export const Page: CollectionConfig = {
|
||||
type: 'checkbox', // highlight-line
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Field types
|
||||
@@ -73,11 +75,12 @@ Some fields use their `name` property as a unique identifier to store and retrie
|
||||
Field validation is enforced automatically based on the field type and other properties such as `required` or `min` and `max` value constraints on certain field types. This default behavior can be replaced by providing your own validate function for any field. It will be used on both the frontend and the backend, so it should not rely on any Node-specific packages. The validation function can be either synchronous or asynchronous and expects to return either `true` or a string error message to display in both API responses and within the Admin panel.
|
||||
|
||||
There are two arguments available to custom validation functions.
|
||||
|
||||
1. The value which is currently assigned to the field
|
||||
2. An optional object with dynamic properties for more complex validation having the following:
|
||||
|
||||
| Property | Description |
|
||||
|---------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `data` | An object of the full collection or global document. |
|
||||
| `siblingData` | An object of the document data limited to fields within the same parent to the field. |
|
||||
| `operation` | Will be "create" or "update" depending on the UI action or API call. |
|
||||
@@ -87,8 +90,9 @@ There are two arguments available to custom validation functions.
|
||||
| `payload` | If the `validate` function is being executed on the server, Payload will be exposed for easily running local operations. |
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Orders: CollectionConfig = {
|
||||
slug: 'orders',
|
||||
@@ -99,38 +103,39 @@ export const Orders: CollectionConfig = {
|
||||
validate: async (val, { operation }) => {
|
||||
if (operation !== 'create') {
|
||||
// skip validation on update
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
const response = await fetch(`https://your-api.com/customers/${val}`);
|
||||
const response = await fetch(`https://your-api.com/customers/${val}`)
|
||||
if (response.ok) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
return 'The customer number provided does not match any customers within our records.';
|
||||
return 'The customer number provided does not match any customers within our records.'
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
When supplying a field `validate` function, Payload will use yours in place of the default. To make use of the default field validation in your custom logic you can import, call and return the result as needed.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { text } from 'payload/fields/validations';
|
||||
import { text } from 'payload/fields/validations'
|
||||
|
||||
const field: Field = {
|
||||
name: 'notBad',
|
||||
type: 'text',
|
||||
validate: (val, args) => {
|
||||
if (val === 'bad') {
|
||||
return 'This cannot be "bad"';
|
||||
return 'This cannot be "bad"'
|
||||
}
|
||||
// highlight-start
|
||||
return text(val, args);
|
||||
return text(val, args)
|
||||
// highlight-end
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Customizable ID
|
||||
@@ -140,6 +145,7 @@ Users are then required to provide a custom ID value when creating a record thro
|
||||
Valid ID types are `number` and `text`.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
{
|
||||
fields: [
|
||||
@@ -156,7 +162,7 @@ Example:
|
||||
In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property:
|
||||
|
||||
| Option | Description |
|
||||
|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
|
||||
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
|
||||
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
|
||||
@@ -200,14 +206,14 @@ The `condition` function should return a boolean that will control if the field
|
||||
// highlight-start
|
||||
condition: (data, siblingData, { user }) => {
|
||||
if (data.enableGreeting) {
|
||||
return true;
|
||||
return true
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -225,17 +231,17 @@ Here is an example of a defaultValue function that uses both:
|
||||
|
||||
```ts
|
||||
const translation: {
|
||||
en: 'Written by',
|
||||
es: 'Escrito por',
|
||||
};
|
||||
en: 'Written by'
|
||||
es: 'Escrito por'
|
||||
}
|
||||
|
||||
const field = {
|
||||
name: 'attribution',
|
||||
type: 'text',
|
||||
// highlight-start
|
||||
defaultValue: ({ user, locale }) => (`${translation[locale]} ${user.name}`)
|
||||
defaultValue: ({ user, locale }) => `${translation[locale]} ${user.name}`,
|
||||
// highlight-end
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
@@ -245,6 +251,7 @@ const field = {
|
||||
### Description
|
||||
|
||||
A description can be configured three ways.
|
||||
|
||||
- As a string
|
||||
- As a function that accepts an object containing the field's value, which returns a string
|
||||
- As a React component that accepts value as a prop
|
||||
@@ -262,15 +269,17 @@ As shown above, you can simply provide a string that will show by the field, but
|
||||
maxLength: 20,
|
||||
admin: {
|
||||
description: ({ value }) =>
|
||||
(`${typeof value === 'string' ? 20 - value.length : '20'} characters left`)
|
||||
}
|
||||
}
|
||||
`${typeof value === 'string' ? 20 - value.length : '20'} characters left`,
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This example will display the number of characters allowed as the user types.
|
||||
|
||||
**Component Example:**
|
||||
|
||||
```ts
|
||||
{
|
||||
fields: [
|
||||
@@ -292,6 +301,7 @@ This example will display the number of characters allowed as the user types.
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This component will count the number of characters entered.
|
||||
|
||||
### TypeScript
|
||||
@@ -299,7 +309,5 @@ This component will count the number of characters entered.
|
||||
You can import the internal Payload `Field` type as well as other common field types as follows:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
Field,
|
||||
} from 'payload/types';
|
||||
import type { Field } from 'payload/types'
|
||||
```
|
||||
|
||||
@@ -7,15 +7,16 @@ desc: The Point field type stores coordinates in the database. Learn how to use
|
||||
keywords: point, geolocation, geospatial, geojson, 2dsphere, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Point field type saves a pair of coordinates in the database and assigns an index for location related queries.
|
||||
<Banner>
|
||||
The Point field type saves a pair of coordinates in the database and assigns an index for location
|
||||
related queries.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/point.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/point-dark.png'
|
||||
alt='Shows a Point field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Point field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/point.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/point-dark.png"
|
||||
alt="Shows a Point field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Point field"
|
||||
/>
|
||||
|
||||
The data structure in the database matches the GeoJSON structure to represent point. The Payload APIs simplifies the object data to only the [longitude, latitude] location.
|
||||
@@ -23,8 +24,8 @@ The data structure in the database matches the GeoJSON structure to represent po
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Used as a field label in the Admin panel and to name the generated GraphQL type. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build a [MongoDB index](https://docs.mongodb.com/manual/indexes/) for this field to produce faster queries. To support location queries, point index defaults to `2dsphere`, to disable the index set to `false`. |
|
||||
@@ -39,13 +40,14 @@ The data structure in the database matches the GeoJSON structure to represent po
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -55,8 +57,8 @@ export const ExampleCollection: CollectionConfig = {
|
||||
type: 'point',
|
||||
label: 'Location',
|
||||
},
|
||||
]
|
||||
};
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Querying
|
||||
|
||||
@@ -6,23 +6,24 @@ desc: The Radio field type allows for the selection of one value from a predefin
|
||||
keywords: radio, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Radio Group field type allows for the selection of one value from a predefined set of possible values and presents a radio group-style set of inputs to the Admin panel.
|
||||
<Banner>
|
||||
The Radio Group field type allows for the selection of one value from a predefined set of possible
|
||||
values and presents a radio group-style set of inputs to the Admin panel.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/radio.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/radio-dark.png'
|
||||
alt='Shows a Radio field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Radio field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/radio.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/radio-dark.png"
|
||||
alt="Shows a Radio field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Radio field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| 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 an `label` string and a `value` string. |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`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 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 a [MongoDB index](https://docs.mongodb.com/manual/indexes/) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
@@ -36,11 +37,15 @@ keywords: radio, fields, config, configuration, documentation, Content Managemen
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br/>
|
||||
Option values should be strings that do not contain hyphens or special characters due to GraphQL enumeration naming constraints. Underscores are allowed. If you determine you need your option values to be non-strings or contain special characters, they will be formatted accordingly before being used as a GraphQL enum.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
Option values should be strings that do not contain hyphens or special characters due to GraphQL
|
||||
enumeration naming constraints. Underscores are allowed. If you determine you need your option
|
||||
values to be non-strings or contain special characters, they will be formatted accordingly before
|
||||
being used as a GraphQL enum.
|
||||
</Banner>
|
||||
|
||||
### Admin config
|
||||
@@ -54,8 +59,9 @@ The `layout` property allows for the radio group to be styled as a horizonally o
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -63,7 +69,8 @@ export const ExampleCollection: CollectionConfig = {
|
||||
{
|
||||
name: 'color', // required
|
||||
type: 'radio', // required
|
||||
options: [ // required
|
||||
options: [
|
||||
// required
|
||||
{
|
||||
label: 'Mint',
|
||||
value: 'mint',
|
||||
@@ -76,8 +83,8 @@ export const ExampleCollection: CollectionConfig = {
|
||||
defaultValue: 'mint', // The first value in options.
|
||||
admin: {
|
||||
layout: 'horizontal',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,15 +7,15 @@ keywords: relationship, fields, config, configuration, documentation, Content Ma
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Relationship field is one of the most powerful fields Payload features. It
|
||||
provides for the ability to easily relate documents together.
|
||||
The Relationship field is one of the most powerful fields Payload features. It provides for the
|
||||
ability to easily relate documents together.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/relationship.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/relationship-dark.png'
|
||||
alt='Shows a relationship field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Relationship field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/relationship.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/relationship-dark.png"
|
||||
alt="Shows a relationship field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Relationship field"
|
||||
/>
|
||||
|
||||
**Example uses:**
|
||||
@@ -54,8 +54,8 @@ _\* An asterisk denotes that a property is required._
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
The [Depth](/docs/getting-started/concepts#depth) parameter can be used to
|
||||
automatically populate related documents that are returned by the API.
|
||||
The [Depth](/docs/getting-started/concepts#depth) parameter can be used to automatically populate
|
||||
related documents that are returned by the API.
|
||||
</Banner>
|
||||
|
||||
### Admin config
|
||||
@@ -87,32 +87,32 @@ The `filterOptions` property can either be a `Where` query directly, or a functi
|
||||
### Example
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "purchase",
|
||||
type: "relationship",
|
||||
relationTo: ["products", "services"],
|
||||
name: 'purchase',
|
||||
type: 'relationship',
|
||||
relationTo: ['products', 'services'],
|
||||
filterOptions: ({ relationTo, siblingData }) => {
|
||||
// returns a Where query dynamically by the type of relationship
|
||||
if (relationTo === "products") {
|
||||
if (relationTo === 'products') {
|
||||
return {
|
||||
stock: { greater_than: siblingData.quantity },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (relationTo === "services") {
|
||||
if (relationTo === 'services') {
|
||||
return {
|
||||
isAvailable: { equals: true },
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about writing queries [here](/docs/queries/overview).
|
||||
@@ -120,11 +120,10 @@ You can learn more about writing queries [here](/docs/queries/overview).
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
When a relationship field has both <strong>filterOptions</strong> and a custom{" "}
|
||||
<strong>validate</strong> function, the api will not validate{" "}
|
||||
<strong>filterOptions</strong> unless you call the default relationship field
|
||||
validation function imported from <strong>payload/fields/validations</strong>{" "}
|
||||
in your validate function.
|
||||
When a relationship field has both <strong>filterOptions</strong> and a custom{' '}
|
||||
<strong>validate</strong> function, the api will not validate <strong>filterOptions</strong>{' '}
|
||||
unless you call the default relationship field validation function imported from{' '}
|
||||
<strong>payload/fields/validations</strong> in your validate function.
|
||||
</Banner>
|
||||
|
||||
### How the data is saved
|
||||
|
||||
@@ -7,20 +7,27 @@ keywords: rich text, fields, config, configuration, documentation, Content Manag
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Rich Text field is a powerful way to allow editors to write dynamic content. The content is saved as JSON in the database and can be converted into any format, including HTML, that you need.
|
||||
The Rich Text field is a powerful way to allow editors to write dynamic content. The content is
|
||||
saved as JSON in the database and can be converted into any format, including HTML, that you need.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/richtext.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/richtext-dark.png'
|
||||
alt='Shows a Rich Text field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Rich Text field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/richtext.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/richtext-dark.png"
|
||||
alt="Shows a Rich Text field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Rich Text field"
|
||||
/>
|
||||
|
||||
The Admin component is built on the powerful [`slatejs`](https://docs.slatejs.org/) editor and is meant to be as extensible and customizable as possible.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Consistent with Payload's goal of making you learn as little of Payload as possible, customizing and using the Rich Text Editor does not involve learning how to develop for a <em>Payload</em> rich text editor.</strong> Instead, you can invest your time and effort into learning Slate, an open-source tool that will allow you to apply your learnings elsewhere as well.
|
||||
<strong>
|
||||
Consistent with Payload's goal of making you learn as little of Payload as possible, customizing
|
||||
and using the Rich Text Editor does not involve learning how to develop for a <em>Payload</em>{' '}
|
||||
rich text editor.
|
||||
</strong>{' '}
|
||||
Instead, you can invest your time and effort into learning Slate, an open-source tool that will
|
||||
allow you to apply your learnings elsewhere as well.
|
||||
</Banner>
|
||||
|
||||
### Config
|
||||
@@ -120,7 +127,12 @@ The built-in `relationship` element is a powerful way to reference other Documen
|
||||
Similar to the `relationship` element, the `upload` element is a user-friendly way to reference [Upload-enabled collections](/docs/upload/overview) with a UI specifically designed for media / image-based uploads.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br />Collections are automatically allowed to be selected within the Rich Text relationship and upload elements by default. If you want to disable a collection from being able to be referenced in Rich Text fields, set the collection admin options of <strong>enableRichTextLink</strong> and <strong>enableRichTextRelationship</strong> to false.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
Collections are automatically allowed to be selected within the Rich Text relationship and upload
|
||||
elements by default. If you want to disable a collection from being able to be referenced in Rich
|
||||
Text fields, set the collection admin options of <strong>enableRichTextLink</strong> and{' '}
|
||||
<strong>enableRichTextRelationship</strong> to false.
|
||||
</Banner>
|
||||
|
||||
Relationship and Upload elements are populated dynamically into your Rich Text field' content. Within the REST and Local APIs, any present RichText `relationship` or `upload` elements will respect the `depth` option that you pass, and will be populated accordingly. In GraphQL, each `richText` field accepts an argument of `depth` for you to utilize.
|
||||
@@ -159,29 +171,29 @@ Specifying custom `Type`s let you extend your custom elements by adding addition
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "content", // required
|
||||
type: "richText", // required
|
||||
name: 'content', // required
|
||||
type: 'richText', // required
|
||||
defaultValue: [
|
||||
{
|
||||
children: [{ text: "Here is some default content for this field" }],
|
||||
children: [{ text: 'Here is some default content for this field' }],
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
admin: {
|
||||
elements: [
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"link",
|
||||
"blockquote",
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'link',
|
||||
'blockquote',
|
||||
{
|
||||
name: "cta",
|
||||
name: 'cta',
|
||||
Button: CustomCallToActionButton,
|
||||
Element: CustomCallToActionElement,
|
||||
plugins: [
|
||||
@@ -190,10 +202,10 @@ export const ExampleCollection: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
leaves: [
|
||||
"bold",
|
||||
"italic",
|
||||
'bold',
|
||||
'italic',
|
||||
{
|
||||
name: "highlight",
|
||||
name: 'highlight',
|
||||
Button: CustomHighlightButton,
|
||||
Leaf: CustomHighlightLeaf,
|
||||
plugins: [
|
||||
@@ -205,11 +217,11 @@ export const ExampleCollection: CollectionConfig = {
|
||||
// Inject your own fields into the Link element
|
||||
fields: [
|
||||
{
|
||||
name: "rel",
|
||||
label: "Rel Attribute",
|
||||
type: "select",
|
||||
name: 'rel',
|
||||
label: 'Rel Attribute',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: ["noopener", "noreferrer", "nofollow"],
|
||||
options: ['noopener', 'noreferrer', 'nofollow'],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -226,7 +238,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
For more examples regarding how to define your own elements and leaves, check out the example [`RichText` field](https://github.com/payloadcms/public-demo/blob/master/src/fields/hero.ts) within the Public Demo source code.
|
||||
@@ -296,7 +308,10 @@ const serialize = (children) =>
|
||||
```
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong><br />The above example is for how to render to JSX, although for plain HTML the pattern is similar. Just remove the JSX and return HTML strings instead!
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
The above example is for how to render to JSX, although for plain HTML the pattern is similar.
|
||||
Just remove the JSX and return HTML strings instead!
|
||||
</Banner>
|
||||
|
||||
### Built-in SlateJS Plugins
|
||||
@@ -312,26 +327,26 @@ If you want to utilize this functionality within your own custom elements, you c
|
||||
`customLargeBodyElement.js`:
|
||||
|
||||
```ts
|
||||
import Button from "./Button";
|
||||
import Element from "./Element";
|
||||
import withLargeBody from "./plugin";
|
||||
import Button from './Button'
|
||||
import Element from './Element'
|
||||
import withLargeBody from './plugin'
|
||||
|
||||
export default {
|
||||
name: "large-body",
|
||||
name: 'large-body',
|
||||
Button,
|
||||
Element,
|
||||
plugins: [
|
||||
(incomingEditor) => {
|
||||
const editor = incomingEditor;
|
||||
const { shouldBreakOutOnEnter } = editor;
|
||||
const editor = incomingEditor
|
||||
const { shouldBreakOutOnEnter } = editor
|
||||
|
||||
editor.shouldBreakOutOnEnter = (element) =>
|
||||
element.type === "large-body" ? true : shouldBreakOutOnEnter(element);
|
||||
element.type === 'large-body' ? true : shouldBreakOutOnEnter(element)
|
||||
|
||||
return editor;
|
||||
return editor
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Above, you can see that we are creating a custom SlateJS element with a name of `large-body`. This might render a slightly larger body copy on the frontend of your app(s). We pass it a name, button, and element—but additionally, we pass it a `plugins` array containing a single SlateJS plugin.
|
||||
@@ -343,5 +358,5 @@ The plugin itself extends Payload's built-in `shouldBreakOutOnEnter` Slate funct
|
||||
If you are building your own custom Rich Text elements or leaves, you may benefit from importing the following types:
|
||||
|
||||
```ts
|
||||
import type { RichTextCustomElement, RichTextCustomLeaf } from "payload/types";
|
||||
import type { RichTextCustomElement, RichTextCustomLeaf } from 'payload/types'
|
||||
```
|
||||
|
||||
@@ -6,39 +6,42 @@ desc: With the Row field you can arrange fields next to each other in the Admin
|
||||
keywords: row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Row field is presentational-only and only affects the Admin panel. By using it, you can arrange fields next to each other horizontally.
|
||||
<Banner>
|
||||
The Row field is presentational-only and only affects the Admin panel. By using it, you can
|
||||
arrange fields next to each other horizontally.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/row.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/row-dark.png'
|
||||
alt='Shows a row field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Row field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/row.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/row-dark.png"
|
||||
alt="Shows a row field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Row field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`fields`** * | Array of field types to nest within this Row. |
|
||||
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`fields`** \* | Array of field types to nest within this Row. |
|
||||
| **`admin`** | Admin-specific configuration excluding `description`, `readOnly`, and `hidden`. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
type: 'row', // required
|
||||
fields: [ // required
|
||||
fields: [
|
||||
// required
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
@@ -56,8 +59,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -7,21 +7,21 @@ keywords: select, multi-select, fields, config, configuration, documentation, Co
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Select field provides a dropdown-style interface for choosing options from
|
||||
a predefined list as an enumeration.
|
||||
The Select field provides a dropdown-style interface for choosing options from a predefined list
|
||||
as an enumeration.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/select.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/select-dark.png'
|
||||
alt='Shows a Select field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Select field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/select.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/select-dark.png"
|
||||
alt="Shows a Select field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Select field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| 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. |
|
||||
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many selections instead of only one. |
|
||||
@@ -44,11 +44,10 @@ _\* An asterisk denotes that a property is required._
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
Option values should be strings that do not contain hyphens or special
|
||||
characters due to GraphQL enumeration naming constraints. Underscores are
|
||||
allowed. If you determine you need your option values to be non-strings or
|
||||
contain special characters, they will be formatted accordingly before being
|
||||
used as a GraphQL enum.
|
||||
Option values should be strings that do not contain hyphens or special characters due to GraphQL
|
||||
enumeration naming constraints. Underscores are allowed. If you determine you need your option
|
||||
values to be non-strings or contain special characters, they will be formatted accordingly before
|
||||
being used as a GraphQL enum.
|
||||
</Banner>
|
||||
|
||||
### Admin config
|
||||
@@ -66,8 +65,9 @@ Set to `true` if you'd like this field to be sortable within the Admin UI using
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -94,8 +94,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
value: 'carbon_fiber_dashboard',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -7,19 +7,18 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Tabs field is presentational-only and only affects the Admin panel (unless
|
||||
a tab is named). By using it, you can place fields within a nice layout
|
||||
component that separates certain sub-fields by a tabbed interface.
|
||||
The Tabs field is presentational-only and only affects the Admin panel (unless a tab is named). By
|
||||
using it, you can place fields within a nice layout component that separates certain sub-fields by
|
||||
a tabbed interface.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/tabs.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/tabs-dark.png'
|
||||
alt='Shows a tabs field used to separate Hero and Page layout in the Payload admin panel'
|
||||
caption='Tabs field type used to separate Hero fields from Page Layout'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/tabs.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/tabs-dark.png"
|
||||
alt="Shows a tabs field used to separate Hero and Page layout in the Payload admin panel"
|
||||
caption="Tabs field type used to separate Hero fields from Page Layout"
|
||||
/>
|
||||
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
@@ -33,7 +32,7 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
|
||||
Each tab must have either a `name` or `label` and the required `fields` array. You can also optionally pass a `description` to render within each individual tab.
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`name`** | Groups field data into an object when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | The label to render on the tab itself. Required when name is undefined, defaults to name converted to words. |
|
||||
| **`fields`** \* | The fields to render within this tab. |
|
||||
@@ -47,36 +46,36 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
type: "tabs", // required
|
||||
type: 'tabs', // required
|
||||
tabs: [
|
||||
// required
|
||||
{
|
||||
label: "Tab One Label", // required
|
||||
description: "This will appear within the tab above the fields.",
|
||||
label: 'Tab One Label', // required
|
||||
description: 'This will appear within the tab above the fields.',
|
||||
fields: [
|
||||
// required
|
||||
{
|
||||
name: "someTextField",
|
||||
type: "text",
|
||||
name: 'someTextField',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "tabTwo",
|
||||
label: "Tab Two Label", // required
|
||||
interfaceName: "TabTwo", // optional (`name` must be present)
|
||||
name: 'tabTwo',
|
||||
label: 'Tab Two Label', // required
|
||||
interfaceName: 'TabTwo', // optional (`name` must be present)
|
||||
fields: [
|
||||
// required
|
||||
{
|
||||
name: "numberField", // accessible via tabTwo.numberField
|
||||
type: "number",
|
||||
name: 'numberField', // accessible via tabTwo.numberField
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
@@ -84,5 +83,5 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,22 +6,23 @@ desc: Text field types simply save a string to the database and provide the Admi
|
||||
keywords: text, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Text field type is one of the most commonly used fields. It saves a string to the database and provides the Admin panel with a simple text input.
|
||||
<Banner>
|
||||
The Text field type is one of the most commonly used fields. It saves a string to the database and
|
||||
provides the Admin panel with a simple text input.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/text.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/text-dark.png'
|
||||
alt='Shows a text field and read-only text field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Text field and read-only Text field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/text.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/text-dark.png"
|
||||
alt="Shows a text field and read-only text field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Text field and read-only Text field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
@@ -38,7 +39,7 @@ keywords: text, fields, config, configuration, documentation, Content Management
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
|
||||
@@ -59,8 +60,9 @@ Override the default text direction of the Admin panel for this field. Set to `t
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -69,8 +71,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
name: 'pageTitle', // required
|
||||
type: 'text', // required
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -6,22 +6,23 @@ desc: Textarea field types save a string to the database, similar to the Text fi
|
||||
keywords: textarea, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Textarea field is almost identical to the Text field but it features a slightly larger input that is better suited to edit longer text.
|
||||
<Banner>
|
||||
The Textarea field is almost identical to the Text field but it features a slightly larger input
|
||||
that is better suited to edit longer text.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/textarea.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/textarea-dark.png'
|
||||
alt='Shows a textarea field and read-only textarea field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of a Textarea field and read-only Textarea field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/textarea.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/textarea-dark.png"
|
||||
alt="Shows a textarea field and read-only textarea field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of a Textarea field and read-only Textarea field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
@@ -32,13 +33,13 @@ keywords: textarea, fields, config, configuration, documentation, Content Manage
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values)|
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
|
||||
@@ -59,8 +60,9 @@ Override the default text direction of the Admin panel for this field. Set to `t
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -69,8 +71,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
name: 'metaDescription', // required
|
||||
type: 'textarea', // required
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -7,10 +7,9 @@ keywords: custom field, react component, fields, config, configuration, document
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The UI (user interface) field gives you a ton of power to add your own React
|
||||
components directly into the Admin panel, nested directly within your other
|
||||
fields. It has absolutely no effect on the data of your documents. It is
|
||||
presentational-only.
|
||||
The UI (user interface) field gives you a ton of power to add your own React components directly
|
||||
into the Admin panel, nested directly within your other fields. It has absolutely no effect on the
|
||||
data of your documents. It is presentational-only.
|
||||
</Banner>
|
||||
|
||||
This field is helpful if you need to build in custom functionality via React components within the Admin panel. Think of it as a way for you to "plug in" your own React components directly within your other fields, so you can provide your editors with new controls exactly where you want them to go.
|
||||
@@ -41,14 +40,14 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "myCustomUIField", // required
|
||||
type: "ui", // required
|
||||
name: 'myCustomUIField', // required
|
||||
type: 'ui', // required
|
||||
admin: {
|
||||
components: {
|
||||
Field: MyCustomUIField,
|
||||
@@ -57,5 +56,5 @@ export const ExampleCollection: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,19 +7,23 @@ keywords: upload, images media, fields, config, configuration, documentation, Co
|
||||
---
|
||||
|
||||
<Banner>
|
||||
The Upload field allows for the selection of a Document from a collection supporting Uploads, and formats the selection as a thumbnail in the Admin panel.
|
||||
The Upload field allows for the selection of a Document from a collection supporting Uploads, and
|
||||
formats the selection as a thumbnail in the Admin panel.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br />
|
||||
To use this field, you need to have a Collection configured to allow Uploads. For more information, [click here](/docs/upload/overview) to read about how to enable Uploads on a collection by collection basis.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
To use this field, you need to have a Collection configured to allow Uploads. For more
|
||||
information, [click here](/docs/upload/overview) to read about how to enable Uploads on a
|
||||
collection by collection basis.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight='https://payloadcms.com/images/docs/fields/upload.png'
|
||||
srcDark='https://payloadcms.com/images/docs/fields/upload-dark.png'
|
||||
alt='Shows an upload field in the Payload admin panel'
|
||||
caption='Admin panel screenshot of an Upload field'
|
||||
srcLight="https://payloadcms.com/images/docs/fields/upload.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/upload-dark.png"
|
||||
alt="Shows an upload field in the Payload admin panel"
|
||||
caption="Admin panel screenshot of an Upload field"
|
||||
/>
|
||||
|
||||
**Example uses:**
|
||||
@@ -57,19 +61,19 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: "example-collection",
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
name: "backgroundImage", // required
|
||||
type: "upload", // required
|
||||
relationTo: "media", // required
|
||||
name: 'backgroundImage', // required
|
||||
type: 'upload', // required
|
||||
relationTo: 'media', // required
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Filtering upload options
|
||||
@@ -90,18 +94,22 @@ The `filterOptions` property can either be a `Where` query directly, or a functi
|
||||
|
||||
```ts
|
||||
const uploadField = {
|
||||
name: "image",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
filterOptions: {
|
||||
mimeType: { contains: "image" },
|
||||
mimeType: { contains: 'image' },
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about writing queries [here](/docs/queries/overview).
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong><br />
|
||||
When an upload field has both <strong>filterOptions</strong> and a custom <strong>validate</strong> function, the api will not validate <strong>filterOptions</strong> unless you call the default upload field validation function imported from <strong>payload/fields/validations</strong> in your validate function.
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
When an upload field has both <strong>filterOptions</strong> and a custom{' '}
|
||||
<strong>validate</strong> function, the api will not validate <strong>filterOptions</strong>{' '}
|
||||
unless you call the default upload field validation function imported from{' '}
|
||||
<strong>payload/fields/validations</strong> in your validate function.
|
||||
</Banner>
|
||||
|
||||
@@ -10,9 +10,7 @@ Payload is based around a small and intuitive set of concepts. Before starting t
|
||||
|
||||
### Config
|
||||
|
||||
<Banner type="info">
|
||||
The Payload config is where you configure everything that Payload does.
|
||||
</Banner>
|
||||
<Banner type="info">The Payload config is where you configure everything that Payload does.</Banner>
|
||||
|
||||
By default, the Payload config lives in the root folder of your code and is named `payload.config.js` (`payload.config.ts` if you're using TypeScript), but you can customize its name and where you store it. You can write full functions and even full React components right into your config.
|
||||
|
||||
@@ -29,7 +27,8 @@ They can represent anything you can store in a database - for example - pages, p
|
||||
### Globals
|
||||
|
||||
<Banner type="info">
|
||||
A Global is a "one-off" piece of content that is perfect for storing navigational structures, themes, top-level meta data, and more.
|
||||
A Global is a "one-off" piece of content that is perfect for storing navigational structures,
|
||||
themes, top-level meta data, and more.
|
||||
</Banner>
|
||||
|
||||
Globals are in many ways similar to Collections, but there is only ever **one** instance of a Global, whereas Collections can contain many documents.
|
||||
@@ -37,7 +36,8 @@ Globals are in many ways similar to Collections, but there is only ever **one**
|
||||
### Fields
|
||||
|
||||
<Banner type="info">
|
||||
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the shape of the data that they store.
|
||||
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the
|
||||
shape of the data that they store.
|
||||
</Banner>
|
||||
|
||||
Payload comes with [many different field types](../fields/overview) that give you a ton of flexibility while designing your API. Each Field type has its own potential properties that allow you to customize how they work.
|
||||
@@ -45,7 +45,8 @@ Payload comes with [many different field types](../fields/overview) that give yo
|
||||
### Hooks
|
||||
|
||||
<Banner type="info">
|
||||
Hooks are where you can "tie in" to existing Payload actions to perform your own additional logic or modify how Payload operates altogether.
|
||||
Hooks are where you can "tie in" to existing Payload actions to perform your own additional logic
|
||||
or modify how Payload operates altogether.
|
||||
</Banner>
|
||||
|
||||
Hooks are an extremely powerful concept and are central to extending and customizing your app. Payload provides a wide variety of hooks which you can utilize. For example, imagine if you'd like to send an email every time a document is created in your Orders collection. To do so, you can add an `afterChange` hook function to your Orders collection that receives the Order data and allows you to send an email accordingly.
|
||||
@@ -65,10 +66,11 @@ For more, visit the [Access Control documentation](/docs/access-control/overview
|
||||
### Depth
|
||||
|
||||
<Banner type="info">
|
||||
"Depth" gives you control over how many levels down related documents should be automatically populated when retrieved.
|
||||
"Depth" gives you control over how many levels down related documents should be automatically
|
||||
populated when retrieved.
|
||||
</Banner>
|
||||
|
||||
You can specify population `depth` via query parameter in the REST API and by an option in the local API. *Depth has no effect in the GraphQL API, because there, depth is based on the shape of your queries.*
|
||||
You can specify population `depth` via query parameter in the REST API and by an option in the local API. _Depth has no effect in the GraphQL API, because there, depth is based on the shape of your queries._
|
||||
It is also possible to limit the depth for specific `relation` and `upload` fields using the `maxDepth` property in your configuration.
|
||||
**For example, let's look at the following Collections:** `departments`, `users`, `posts`
|
||||
|
||||
@@ -155,6 +157,8 @@ To populate `user.author.department` in it's entirety you could specify `?depth=
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong><br/>
|
||||
When access control on collections prevents relationship fields from populating, the API response will contain the relationship id instead of the full document.
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
When access control on collections prevents relationship fields from populating, the API response
|
||||
will contain the relationship id instead of the full document.
|
||||
</Banner>
|
||||
|
||||
@@ -15,8 +15,7 @@ Payload requires the following software:
|
||||
- A MongoDB Database
|
||||
|
||||
<Banner type="warning">
|
||||
Before proceeding any further, please ensure that you have the above
|
||||
requirements met.
|
||||
Before proceeding any further, please ensure that you have the above requirements met.
|
||||
</Banner>
|
||||
|
||||
## Quickstart with create-payload-app
|
||||
@@ -36,13 +35,13 @@ Adding Payload to either a new or existing TypeScript + Express app is super str
|
||||
From there, the first step is writing a baseline config. Create a new `payload.config.ts` in your project's `/src` directory (or whatever your root TS dir is). The simplest config contains the following:
|
||||
|
||||
```js
|
||||
import { buildConfig } from "payload/config";
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
// By default, Payload will boot up normally
|
||||
// and you will be provided with a base `User` collection.
|
||||
// But, here is where you define how you'd like Payload to work!
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
Write the above code into your newly created config file. This baseline config will automatically provide you with a default `User` collection. For more information about users and authentication, including how to provide your own user config, jump to the [Authentication](/docs/authentication/config) section.
|
||||
@@ -58,15 +57,13 @@ Now that you've got a baseline Payload config, it's time to initialize Payload.
|
||||
1. Add the following code to `server.ts`:
|
||||
|
||||
```ts
|
||||
import express from "express";
|
||||
import express from 'express'
|
||||
|
||||
const app = express();
|
||||
const app = express()
|
||||
|
||||
app.listen(3000, async () => {
|
||||
console.log(
|
||||
"Express is now listening for incoming connections on port 3000."
|
||||
);
|
||||
});
|
||||
console.log('Express is now listening for incoming connections on port 3000.')
|
||||
})
|
||||
```
|
||||
|
||||
This server doesn't do anything just yet. But, after you have this in place, we can initialize Payload via its asynchronous `init()` method, which accepts a small set of arguments to tell it how to operate.
|
||||
@@ -74,27 +71,25 @@ This server doesn't do anything just yet. But, after you have this in place, we
|
||||
To initialize Payload, update your `server.ts` file to reflect the following code:
|
||||
|
||||
```ts
|
||||
import express from "express";
|
||||
import payload from "payload";
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
require("dotenv").config();
|
||||
const app = express();
|
||||
require('dotenv').config()
|
||||
const app = express()
|
||||
|
||||
const start = async () => {
|
||||
await payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
});
|
||||
})
|
||||
|
||||
app.listen(3000, async () => {
|
||||
console.log(
|
||||
"Express is now listening for incoming connections on port 3000."
|
||||
);
|
||||
});
|
||||
};
|
||||
console.log('Express is now listening for incoming connections on port 3000.')
|
||||
})
|
||||
}
|
||||
|
||||
start();
|
||||
start()
|
||||
```
|
||||
|
||||
A quick reminder: in this configuration, we're making use of two environmental variables, `process.env.PAYLOAD_SECRET` and `process.env.MONGODB_URI`. Often, it's smart to store these values in an `.env` file at the root of your directory and set different values for each of your environments (local, stage, prod, etc). The `dotenv` package is very handy and works well alongside of Payload. A typical `.env` file will look like this:
|
||||
|
||||
@@ -12,9 +12,8 @@ keywords: documentation, getting started, guide, Content Management System, cms,
|
||||
/>
|
||||
|
||||
<Banner type="success">
|
||||
Payload is a headless CMS and application framework. It’s meant to provide a
|
||||
massive boost to your development process, but importantly, stay out of your
|
||||
way as your apps get more complex.
|
||||
Payload is a headless CMS and application framework. It’s meant to provide a massive boost to your
|
||||
development process, but importantly, stay out of your way as your apps get more complex.
|
||||
</Banner>
|
||||
|
||||
Out of the box, Payload gives you a lot of the things that you often need when developing a new website, web app, or native app:
|
||||
|
||||
@@ -11,7 +11,7 @@ You can add your own GraphQL queries and mutations to Payload, making use of all
|
||||
To do so, add your queries and mutations to the main Payload config as follows:
|
||||
|
||||
| Config Path | Description |
|
||||
| -------------------- | -------------|
|
||||
| ------------------- | --------------------------------------------------------------------------- |
|
||||
| `graphQL.queries` | Function that returns an object containing keys to custom GraphQL queries |
|
||||
| `graphQL.mutations` | Function that returns an object containing keys to custom GraphQL mutations |
|
||||
|
||||
@@ -34,8 +34,8 @@ Both `graphQL.queries` and `graphQL.mutations` functions should return an object
|
||||
`payload.config.js`:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
import myCustomQueryResolver from './graphQL/resolvers/myCustomQueryResolver';
|
||||
import { buildConfig } from 'payload/config'
|
||||
import myCustomQueryResolver from './graphQL/resolvers/myCustomQueryResolver'
|
||||
|
||||
export default buildConfig({
|
||||
graphQL: {
|
||||
@@ -57,14 +57,14 @@ export default buildConfig({
|
||||
args: {
|
||||
argNameHere: {
|
||||
type: new GraphQL.GraphQLNonNull(GraphQLString),
|
||||
}
|
||||
},
|
||||
},
|
||||
resolve: myCustomQueryResolver,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// highlight-end
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -77,7 +77,7 @@ Your function will receive four arguments you can make use of:
|
||||
Example
|
||||
|
||||
```ts
|
||||
async (obj, args, context, info) => { }
|
||||
;async (obj, args, context, info) => {}
|
||||
```
|
||||
|
||||
**`obj`**
|
||||
|
||||
@@ -102,8 +102,13 @@ GraphQL Playground is enabled by default for development purposes, but disabled
|
||||
You can even log in using the `login[collection-singular-label-here]` mutation to use the Playground as an authenticated user.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br />
|
||||
To see more regarding how the above queries and mutations are used, visit your GraphQL playground (by default at [http://localhost:3000/api/graphql-playground](http://localhost:3000/api/graphql-playground)) while your server is running. There, you can use the "Schema" and "Docs" buttons on the right to see a ton of detail about how GraphQL operates within Payload.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
To see more regarding how the above queries and mutations are used, visit your GraphQL playground
|
||||
(by default at
|
||||
[http://localhost:3000/api/graphql-playground](http://localhost:3000/api/graphql-playground))
|
||||
while your server is running. There, you can use the "Schema" and "Docs" buttons on the right to
|
||||
see a ton of detail about how GraphQL operates within Payload.
|
||||
</Banner>
|
||||
|
||||
## Query complexity limits
|
||||
|
||||
@@ -70,14 +70,14 @@ The `beforeOperation` hook can be used to modify the arguments that operations a
|
||||
Available Collection operations include `create`, `read`, `update`, `delete`, `login`, `refresh`, and `forgotPassword`.
|
||||
|
||||
```ts
|
||||
import { CollectionBeforeOperationHook } from "payload/types";
|
||||
import { CollectionBeforeOperationHook } from 'payload/types'
|
||||
|
||||
const beforeOperationHook: CollectionBeforeOperationHook = async ({
|
||||
args, // original arguments passed into the operation
|
||||
operation, // name of the operation
|
||||
}) => {
|
||||
return args; // return modified operation arguments as necessary
|
||||
};
|
||||
return args // return modified operation arguments as necessary
|
||||
}
|
||||
```
|
||||
|
||||
### beforeValidate
|
||||
@@ -91,7 +91,7 @@ Please do note that this does not run before the client-side validation. If you
|
||||
3. `validate` runs on the server
|
||||
|
||||
```ts
|
||||
import { CollectionBeforeOperationHook } from "payload/types";
|
||||
import { CollectionBeforeOperationHook } from 'payload/types'
|
||||
|
||||
const beforeValidateHook: CollectionBeforeValidateHook = async ({
|
||||
data, // incoming data to update or create with
|
||||
@@ -99,8 +99,8 @@ const beforeValidateHook: CollectionBeforeValidateHook = async ({
|
||||
operation, // name of the operation ie. 'create', 'update'
|
||||
originalDoc, // original document
|
||||
}) => {
|
||||
return data; // Return data to either create or update a document with
|
||||
};
|
||||
return data // Return data to either create or update a document with
|
||||
}
|
||||
```
|
||||
|
||||
### beforeChange
|
||||
@@ -108,7 +108,7 @@ const beforeValidateHook: CollectionBeforeValidateHook = async ({
|
||||
Immediately following validation, `beforeChange` hooks will run within `create` and `update` operations. At this stage, you can be confident that the data that will be saved to the document is valid in accordance to your field validations. You can optionally modify the shape of data to be saved.
|
||||
|
||||
```ts
|
||||
import { CollectionBeforeChangeHook } from "payload/types";
|
||||
import { CollectionBeforeChangeHook } from 'payload/types'
|
||||
|
||||
const beforeChangeHook: CollectionBeforeChangeHook = async ({
|
||||
data, // incoming data to update or create with
|
||||
@@ -116,8 +116,8 @@ const beforeChangeHook: CollectionBeforeChangeHook = async ({
|
||||
operation, // name of the operation ie. 'create', 'update'
|
||||
originalDoc, // original document
|
||||
}) => {
|
||||
return data; // Return data to either create or update a document with
|
||||
};
|
||||
return data // Return data to either create or update a document with
|
||||
}
|
||||
```
|
||||
|
||||
### afterChange
|
||||
@@ -125,7 +125,7 @@ const beforeChangeHook: CollectionBeforeChangeHook = async ({
|
||||
After a document is created or updated, the `afterChange` hook runs. This hook is helpful to recalculate statistics such as total sales within a global, syncing user profile changes to a CRM, and more.
|
||||
|
||||
```ts
|
||||
import { CollectionAfterChangeHook } from "payload/types";
|
||||
import { CollectionAfterChangeHook } from 'payload/types'
|
||||
|
||||
const afterChangeHook: CollectionAfterChangeHook = async ({
|
||||
doc, // full document data
|
||||
@@ -133,8 +133,8 @@ const afterChangeHook: CollectionAfterChangeHook = async ({
|
||||
previousDoc, // document data before updating the collection
|
||||
operation, // name of the operation ie. 'create', 'update'
|
||||
}) => {
|
||||
return doc;
|
||||
};
|
||||
return doc
|
||||
}
|
||||
```
|
||||
|
||||
### beforeRead
|
||||
@@ -142,15 +142,15 @@ const afterChangeHook: CollectionAfterChangeHook = async ({
|
||||
Runs before `find` and `findByID` operations are transformed for output by `afterRead`. This hook fires before hidden fields are removed and before localized fields are flattened into the requested locale. Using this Hook will provide you with all locales and all hidden fields via the `doc` argument.
|
||||
|
||||
```ts
|
||||
import { CollectionBeforeReadHook } from "payload/types";
|
||||
import { CollectionBeforeReadHook } from 'payload/types'
|
||||
|
||||
const beforeReadHook: CollectionBeforeReadHook = async ({
|
||||
doc, // full document data
|
||||
req, // full express request
|
||||
query, // JSON formatted query
|
||||
}) => {
|
||||
return doc;
|
||||
};
|
||||
return doc
|
||||
}
|
||||
```
|
||||
|
||||
### afterRead
|
||||
@@ -158,7 +158,7 @@ const beforeReadHook: CollectionBeforeReadHook = async ({
|
||||
Runs as the last step before documents are returned. Flattens locales, hides protected fields, and removes fields that users do not have access to.
|
||||
|
||||
```ts
|
||||
import { CollectionAfterReadHook } from "payload/types";
|
||||
import { CollectionAfterReadHook } from 'payload/types'
|
||||
|
||||
const afterReadHook: CollectionAfterReadHook = async ({
|
||||
doc, // full document data
|
||||
@@ -166,8 +166,8 @@ const afterReadHook: CollectionAfterReadHook = async ({
|
||||
query, // JSON formatted query
|
||||
findMany, // boolean to denote if this hook is running against finding one, or finding many
|
||||
}) => {
|
||||
return doc;
|
||||
};
|
||||
return doc
|
||||
}
|
||||
```
|
||||
|
||||
### beforeDelete
|
||||
@@ -204,15 +204,15 @@ The `afterOperation` hook can be used to modify the result of operations or exec
|
||||
Available Collection operations include `create`, `find`, `findByID`, `update`, `updateByID`, `delete`, `deleteByID`, `login`, `refresh`, and `forgotPassword`.
|
||||
|
||||
```ts
|
||||
import { CollectionAfterOperationHook } from "payload/types";
|
||||
import { CollectionAfterOperationHook } from 'payload/types'
|
||||
|
||||
const afterOperationHook: CollectionAfterOperationHook = async ({
|
||||
args, // arguments passed into the operation
|
||||
operation, // name of the operation
|
||||
result, // the result of the operation, before modifications
|
||||
}) => {
|
||||
return result; // return modified result as necessary
|
||||
};
|
||||
return result // return modified result as necessary
|
||||
}
|
||||
```
|
||||
|
||||
### beforeLogin
|
||||
@@ -220,14 +220,14 @@ const afterOperationHook: CollectionAfterOperationHook = async ({
|
||||
For auth-enabled Collections, this hook runs during `login` operations where a user with the provided credentials exist, but before a token is generated and added to the response. You can optionally modify the user that is returned, or throw an error in order to deny the login operation.
|
||||
|
||||
```ts
|
||||
import { CollectionBeforeLoginHook } from "payload/types";
|
||||
import { CollectionBeforeLoginHook } from 'payload/types'
|
||||
|
||||
const beforeLoginHook: CollectionBeforeLoginHook = async ({
|
||||
req, // full express request
|
||||
user, // user being logged in
|
||||
}) => {
|
||||
return user;
|
||||
};
|
||||
return user
|
||||
}
|
||||
```
|
||||
|
||||
### afterLogin
|
||||
@@ -288,15 +288,15 @@ const afterMeHook: CollectionAfterMeHook = async ({
|
||||
For auth-enabled Collections, this hook runs after successful `forgotPassword` operations. Returned values are discarded.
|
||||
|
||||
```ts
|
||||
import { CollectionAfterForgotPasswordHook } from "payload/types";
|
||||
import { CollectionAfterForgotPasswordHook } from 'payload/types'
|
||||
|
||||
const afterLoginHook: CollectionAfterForgotPasswordHook = async ({
|
||||
req, // full express request
|
||||
user, // user being logged in
|
||||
token, // user token
|
||||
}) => {
|
||||
return user;
|
||||
};
|
||||
return user
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
@@ -319,5 +319,5 @@ import type {
|
||||
CollectionAfterRefreshHook,
|
||||
CollectionAfterMeHook,
|
||||
CollectionAfterForgotPasswordHook,
|
||||
} from "payload/types";
|
||||
} from 'payload/types'
|
||||
```
|
||||
|
||||
@@ -31,24 +31,30 @@ For example:
|
||||
const Customer: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
hooks: {
|
||||
beforeChange: [async ({ context, data }) => {
|
||||
beforeChange: [
|
||||
async ({ context, data }) => {
|
||||
// assign the customerData to context for use later
|
||||
context.customerData = await fetchCustomerData(data.customerID);
|
||||
context.customerData = await fetchCustomerData(data.customerID)
|
||||
return {
|
||||
...data,
|
||||
// some data we use here
|
||||
name: context.customerData.name
|
||||
};
|
||||
}],
|
||||
afterChange: [async ({ context, doc, req }) => {
|
||||
name: context.customerData.name,
|
||||
}
|
||||
},
|
||||
],
|
||||
afterChange: [
|
||||
async ({ context, doc, req }) => {
|
||||
// use context.customerData without needing to fetch it again
|
||||
if (context.customerData.contacted === false) {
|
||||
createTodo('Call Customer', context.customerData)
|
||||
}
|
||||
}],
|
||||
},
|
||||
fields: [ /* ... */ ],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
/* ... */
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Preventing infinite loops
|
||||
@@ -61,19 +67,23 @@ Bad example:
|
||||
const Customer: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
hooks: {
|
||||
afterChange: [async ({ doc }) => {
|
||||
afterChange: [
|
||||
async ({ doc }) => {
|
||||
await payload.update({
|
||||
// DANGER: updating the same slug as the collection in an afterChange will create an infinite loop!
|
||||
collection: 'customers',
|
||||
id: doc.id,
|
||||
data: {
|
||||
...(await fetchCustomerData(data.customerID))
|
||||
...(await fetchCustomerData(data.customerID)),
|
||||
},
|
||||
});
|
||||
}],
|
||||
})
|
||||
},
|
||||
fields: [ /* ... */ ],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
/* ... */
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Instead of the above, we need to tell the `afterChange` hook to not run again if it performs the update (and thus not update itself again). We can solve that with context.
|
||||
@@ -84,26 +94,30 @@ Fixed example:
|
||||
const MyCollection: CollectionConfig = {
|
||||
slug: 'slug',
|
||||
hooks: {
|
||||
afterChange: [async ({ context, doc }) => {
|
||||
afterChange: [
|
||||
async ({ context, doc }) => {
|
||||
// return if flag was previously set
|
||||
if (context.triggerAfterChange === false) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
await payload.update({
|
||||
collection: contextHooksSlug,
|
||||
id: doc.id,
|
||||
data: {
|
||||
...(await fetchCustomerData(data.customerID))
|
||||
...(await fetchCustomerData(data.customerID)),
|
||||
},
|
||||
context: {
|
||||
// set a flag to prevent from running again
|
||||
triggerAfterChange: false,
|
||||
},
|
||||
});
|
||||
}],
|
||||
})
|
||||
},
|
||||
fields: [ /* ... */ ],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
/* ... */
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Typing context
|
||||
@@ -113,12 +127,12 @@ The default typescript interface for `context` is `{ [key: string]: unknown }`.
|
||||
This is known as "type augmentation" - a TypeScript feature which allows us to add types to existing objects. Simply put this in any .ts or .d.ts file:
|
||||
|
||||
```ts
|
||||
import { RequestContext as OriginalRequestContext } from 'payload';
|
||||
import { RequestContext as OriginalRequestContext } from 'payload'
|
||||
|
||||
declare module 'payload' {
|
||||
// Create a new interface that merges your additional fields with the original one
|
||||
export interface RequestContext extends OriginalRequestContext {
|
||||
myObject?: string;
|
||||
myObject?: string
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ Field-level hooks offer incredible potential for encapsulating your logic. They
|
||||
## Config
|
||||
|
||||
Example field configuration:
|
||||
|
||||
```ts
|
||||
import { Field } from 'payload/types';
|
||||
|
||||
@@ -48,8 +49,12 @@ const ExampleField: Field = {
|
||||
All field-level hooks are formatted to accept the same arguments, although some arguments may be `undefined` based on which field hook you are utilizing.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br />
|
||||
It's a good idea to conditionally scope your logic based on which operation is executing. For example, if you are writing a <strong>beforeChange</strong> hook, you may want to perform different logic based on if the current <strong>operation</strong> is <strong>create</strong> or <strong>update</strong>.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
It's a good idea to conditionally scope your logic based on which operation is executing. For
|
||||
example, if you are writing a <strong>beforeChange</strong> hook, you may want to perform
|
||||
different logic based on if the current <strong>operation</strong> is <strong>create</strong> or{' '}
|
||||
<strong>update</strong>.
|
||||
</Banner>
|
||||
|
||||
#### Arguments
|
||||
@@ -57,7 +62,7 @@ All field-level hooks are formatted to accept the same arguments, although some
|
||||
Field Hooks receive one `args` argument that contains the following properties:
|
||||
|
||||
| Option | Description |
|
||||
|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`data`** | The data passed to update the document within `create` and `update` operations, and the full document itself in the `afterRead` hook. |
|
||||
| **`siblingData`** | The sibling data passed to a field that the hook is running against. |
|
||||
| **`findMany`** | Boolean to denote if this hook is running against finding one, or finding many within the `afterRead` hook. |
|
||||
@@ -74,8 +79,11 @@ Field Hooks receive one `args` argument that contains the following properties:
|
||||
All field hooks can optionally modify the return value of the field before the operation continues. Field Hooks may optionally return the value that should be used within the field.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important</strong><br/>
|
||||
Due to GraphQL's typed nature, you should never change the type of data that you return from a field, otherwise GraphQL will produce errors. If you need to change the shape or type of data, reconsider Field Hooks and instead evaluate if Collection / Global hooks might suit you better.
|
||||
<strong>Important</strong>
|
||||
<br />
|
||||
Due to GraphQL's typed nature, you should never change the type of data that you return from a
|
||||
field, otherwise GraphQL will produce errors. If you need to change the shape or type of data,
|
||||
reconsider Field Hooks and instead evaluate if Collection / Global hooks might suit you better.
|
||||
</Banner>
|
||||
|
||||
## TypeScript
|
||||
@@ -83,14 +91,14 @@ All field hooks can optionally modify the return value of the field before the o
|
||||
Payload exports a type for field hooks which can be accessed and used as follows:
|
||||
|
||||
```ts
|
||||
import type { FieldHook } from 'payload/types';
|
||||
import type { FieldHook } from 'payload/types'
|
||||
|
||||
// Field hook type is a generic that takes three arguments:
|
||||
// 1: The document type
|
||||
// 2: The value type
|
||||
// 3: The sibling data type
|
||||
|
||||
type ExampleFieldHook = FieldHook<ExampleDocumentType, string, SiblingDataType>;
|
||||
type ExampleFieldHook = FieldHook<ExampleDocumentType, string, SiblingDataType>
|
||||
|
||||
const exampleFieldHook: ExampleFieldHook = (args) => {
|
||||
const {
|
||||
@@ -100,10 +108,10 @@ const exampleFieldHook: ExampleFieldHook = (args) => {
|
||||
originalDoc, // Typed as ExampleDocumentType
|
||||
operation,
|
||||
req,
|
||||
} = args;
|
||||
} = args
|
||||
|
||||
// Do something here...
|
||||
|
||||
return value; // should return a string as typed above, undefined, or null
|
||||
return value // should return a string as typed above, undefined, or null
|
||||
}
|
||||
```
|
||||
|
||||
@@ -19,6 +19,7 @@ Globals feature the ability to define the following hooks:
|
||||
All Global Hook properties accept arrays of synchronous or asynchronous functions. Each Hook type receives specific arguments and has the ability to modify specific outputs.
|
||||
|
||||
`globals/example-hooks.js`
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types';
|
||||
|
||||
@@ -49,7 +50,7 @@ const beforeValidateHook: GlobalBeforeValidateHook = async ({
|
||||
req, // full express request
|
||||
originalDoc, // original document
|
||||
}) => {
|
||||
return data; // Return data to update the document with
|
||||
return data // Return data to update the document with
|
||||
}
|
||||
```
|
||||
|
||||
@@ -65,7 +66,7 @@ const beforeChangeHook: GlobalBeforeChangeHook = async ({
|
||||
req, // full express request
|
||||
originalDoc, // original document
|
||||
}) => {
|
||||
return data; // Return data to update the document with
|
||||
return data // Return data to update the document with
|
||||
}
|
||||
```
|
||||
|
||||
@@ -81,7 +82,7 @@ const afterChangeHook: GlobalAfterChangeHook = async ({
|
||||
previousDoc, // document data before updating the collection
|
||||
req, // full express request
|
||||
}) => {
|
||||
return data;
|
||||
return data
|
||||
}
|
||||
```
|
||||
|
||||
@@ -123,5 +124,5 @@ import type {
|
||||
GlobalAfterChangeHook,
|
||||
GlobalBeforeReadHook,
|
||||
GlobalAfterReadHook,
|
||||
} from 'payload/types';
|
||||
} from 'payload/types'
|
||||
```
|
||||
|
||||
@@ -7,7 +7,9 @@ keywords: hooks, overview, config, configuration, documentation, Content Managem
|
||||
---
|
||||
|
||||
<Banner type="info">
|
||||
Hooks are powerful ways to tie into existing Payload actions in order to add your own logic like integrating with third-party APIs, adding auto-generated data, or modifing Payload's base functionality.
|
||||
Hooks are powerful ways to tie into existing Payload actions in order to add your own logic like
|
||||
integrating with third-party APIs, adding auto-generated data, or modifing Payload's base
|
||||
functionality.
|
||||
</Banner>
|
||||
|
||||
**With Hooks, you can transform Payload from a traditional CMS into a fully-fledged application framework.**
|
||||
|
||||
@@ -11,10 +11,9 @@ keywords: vercel, vercel visual editing, visual editing, content source maps, Co
|
||||

|
||||
|
||||
<Banner type="warning">
|
||||
Vercel Visual Editing is an enterprise-only feature and only available for
|
||||
deployments hosted on Vercel. If you are an existing enterprise customer,
|
||||
[contact our sales team](https://payloadcms.com/for-enterprise) for help with
|
||||
your integration.
|
||||
Vercel Visual Editing is an enterprise-only feature and only available for deployments hosted on
|
||||
Vercel. If you are an existing enterprise customer, [contact our sales
|
||||
team](https://payloadcms.com/for-enterprise) for help with your integration.
|
||||
</Banner>
|
||||
|
||||
### How it works
|
||||
@@ -66,10 +65,10 @@ export default config
|
||||
Now in your Next.js app, include the `?encodeSourceMaps=true` parameter in any of your API requests. For performance reasons, this should only be done when in draft mode or on preview deployments.
|
||||
|
||||
```ts
|
||||
if (isDraftMode || process.env.VERCEL_ENV === "preview") {
|
||||
if (isDraftMode || process.env.VERCEL_ENV === 'preview') {
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_CMS_URL}/api/pages?where[slug][equals]=${slug}&encodeSourceMaps=true`
|
||||
);
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_CMS_URL}/api/pages?where[slug][equals]=${slug}&encodeSourceMaps=true`,
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -88,8 +87,8 @@ To see Visual Editing on your site, you first need to visit any preview deployme
|
||||
The plugin does not encode `date` fields by default, but for some cases like text that uses negative CSS letter-spacing, it may be necessary to split the encoded data out from the rendered text. This way you can safely use the cleaned data as expected.
|
||||
|
||||
```ts
|
||||
import { vercelStegaSplit } from "@vercel/stega";
|
||||
const { cleaned, encoded } = vercelStegaSplit(text);
|
||||
import { vercelStegaSplit } from '@vercel/stega'
|
||||
const { cleaned, encoded } = vercelStegaSplit(text)
|
||||
```
|
||||
|
||||
##### Blocks
|
||||
|
||||
@@ -11,11 +11,10 @@ The Payload Local API gives you the ability to execute the same operations that
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
The Local API is incredibly powerful when used with server-side rendering app
|
||||
frameworks like NextJS. With other headless CMS, you need to request your data
|
||||
from third-party servers which can add significant loading time to your
|
||||
server-rendered pages. With Payload, you don't have to leave your server to
|
||||
gather the data you need. It can be incredibly fast and is definitely a game
|
||||
The Local API is incredibly powerful when used with server-side rendering app frameworks like
|
||||
NextJS. With other headless CMS, you need to request your data from third-party servers which can
|
||||
add significant loading time to your server-rendered pages. With Payload, you don't have to leave
|
||||
your server to gather the data you need. It can be incredibly fast and is definitely a game
|
||||
changer.
|
||||
</Banner>
|
||||
|
||||
@@ -36,14 +35,14 @@ You can import or require `payload` into your own files after it's been initiali
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import payload from "payload";
|
||||
import { CollectionAfterChangeHook } from "payload/types";
|
||||
import payload from 'payload'
|
||||
import { CollectionAfterChangeHook } from 'payload/types'
|
||||
|
||||
const afterChangeHook: CollectionAfterChangeHook = async () => {
|
||||
const posts = await payload.find({
|
||||
collection: "posts",
|
||||
});
|
||||
};
|
||||
collection: 'posts',
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
##### Accessing from the `req`
|
||||
@@ -53,13 +52,11 @@ Payload is available anywhere you have access to the Express `req` - including w
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const afterChangeHook: CollectionAfterChangeHook = async ({
|
||||
req: { payload },
|
||||
}) => {
|
||||
const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } }) => {
|
||||
const posts = await payload.find({
|
||||
collection: "posts",
|
||||
});
|
||||
};
|
||||
collection: 'posts',
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Local options available
|
||||
@@ -67,7 +64,7 @@ const afterChangeHook: CollectionAfterChangeHook = async ({
|
||||
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.
|
||||
|
||||
| Local Option | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
|
||||
| `data` | The data to use within the operation. Required for `create`, `update`. |
|
||||
| `depth` | [Control auto-population](/docs/getting-started/concepts#depth) of nested relationship and upload fields. |
|
||||
@@ -84,9 +81,8 @@ _There are more options available on an operation by operation basis outlined be
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
By default, all access control checks are disabled in the Local API, but you
|
||||
can re-enable them if you'd like, as well as pass a specific user to run the
|
||||
operation with.
|
||||
By default, all access control checks are disabled in the Local API, but you can re-enable them if
|
||||
you'd like, as well as pass a specific user to run the operation with.
|
||||
</Banner>
|
||||
|
||||
## Collections
|
||||
@@ -98,13 +94,13 @@ The following Collection operations are available through the Local API:
|
||||
```js
|
||||
// The created Post document is returned
|
||||
const post = await payload.create({
|
||||
collection: "posts", // required
|
||||
collection: 'posts', // required
|
||||
data: {
|
||||
// required
|
||||
title: "sure",
|
||||
description: "maybe",
|
||||
title: 'sure',
|
||||
description: 'maybe',
|
||||
},
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUserDoc,
|
||||
overrideAccess: true,
|
||||
@@ -117,12 +113,12 @@ const post = await payload.create({
|
||||
// If your collection supports uploads, you can upload
|
||||
// a file directly through the Local API by providing
|
||||
// its full, absolute file path.
|
||||
filePath: path.resolve(__dirname, "./path-to-image.jpg"),
|
||||
filePath: path.resolve(__dirname, './path-to-image.jpg'),
|
||||
|
||||
// Alternatively, you can directly pass a File,
|
||||
// if file is provided, filePath will be omitted
|
||||
file: uploadedFile,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Find
|
||||
@@ -131,18 +127,18 @@ const post = await payload.create({
|
||||
// Result will be a paginated set of Posts.
|
||||
// See /docs/queries/pagination for more.
|
||||
const result = await payload.find({
|
||||
collection: "posts", // required
|
||||
collection: 'posts', // required
|
||||
depth: 2,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
where: {}, // pass a `where` query here
|
||||
sort: "-title",
|
||||
locale: "en",
|
||||
sort: '-title',
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Find by ID
|
||||
@@ -150,15 +146,15 @@ const result = await payload.find({
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
const result = await payload.findByID({
|
||||
collection: "posts", // required
|
||||
id: "507f1f77bcf86cd799439011", // required
|
||||
collection: 'posts', // required
|
||||
id: '507f1f77bcf86cd799439011', // required
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Update by ID
|
||||
@@ -166,15 +162,15 @@ const result = await payload.findByID({
|
||||
```js
|
||||
// Result will be the updated Post document.
|
||||
const result = await payload.update({
|
||||
collection: "posts", // required
|
||||
id: "507f1f77bcf86cd799439011", // required
|
||||
collection: 'posts', // required
|
||||
id: '507f1f77bcf86cd799439011', // required
|
||||
data: {
|
||||
// required
|
||||
title: "sure",
|
||||
description: "maybe",
|
||||
title: 'sure',
|
||||
description: 'maybe',
|
||||
},
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
@@ -183,13 +179,13 @@ const result = await payload.update({
|
||||
// If your collection supports uploads, you can upload
|
||||
// a file directly through the Local API by providing
|
||||
// its full, absolute file path.
|
||||
filePath: path.resolve(__dirname, "./path-to-image.jpg"),
|
||||
filePath: path.resolve(__dirname, './path-to-image.jpg'),
|
||||
|
||||
// If you are uploading a file and would like to replace
|
||||
// the existing file instead of generating a new filename,
|
||||
// you can set the following property to `true`
|
||||
overwriteExistingFiles: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Update Many
|
||||
@@ -201,18 +197,18 @@ const result = await payload.update({
|
||||
// errors: [], // each error also includes the id of the document
|
||||
// }
|
||||
const result = await payload.update({
|
||||
collection: "posts", // required
|
||||
collection: 'posts', // required
|
||||
where: {
|
||||
// required
|
||||
fieldName: { equals: "value" },
|
||||
fieldName: { equals: 'value' },
|
||||
},
|
||||
data: {
|
||||
// required
|
||||
title: "sure",
|
||||
description: "maybe",
|
||||
title: 'sure',
|
||||
description: 'maybe',
|
||||
},
|
||||
depth: 0,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
@@ -221,13 +217,13 @@ const result = await payload.update({
|
||||
// If your collection supports uploads, you can upload
|
||||
// a file directly through the Local API by providing
|
||||
// its full, absolute file path.
|
||||
filePath: path.resolve(__dirname, "./path-to-image.jpg"),
|
||||
filePath: path.resolve(__dirname, './path-to-image.jpg'),
|
||||
|
||||
// If you are uploading a file and would like to replace
|
||||
// the existing file instead of generating a new filename,
|
||||
// you can set the following property to `true`
|
||||
overwriteExistingFiles: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Delete
|
||||
@@ -235,15 +231,15 @@ const result = await payload.update({
|
||||
```js
|
||||
// Result will be the now-deleted Post document.
|
||||
const result = await payload.delete({
|
||||
collection: "posts", // required
|
||||
id: "507f1f77bcf86cd799439011", // required
|
||||
collection: 'posts', // required
|
||||
id: '507f1f77bcf86cd799439011', // required
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Delete Many
|
||||
@@ -255,18 +251,18 @@ const result = await payload.delete({
|
||||
// errors: [], // any errors that occurred, including the id of the errored on document
|
||||
// }
|
||||
const result = await payload.delete({
|
||||
collection: "posts", // required
|
||||
collection: 'posts', // required
|
||||
where: {
|
||||
// required
|
||||
fieldName: { equals: "value" },
|
||||
fieldName: { equals: 'value' },
|
||||
},
|
||||
depth: 0,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
## Auth Operations
|
||||
@@ -284,20 +280,20 @@ If a collection has [`Authentication`](/docs/authentication/overview) enabled, a
|
||||
// }
|
||||
|
||||
const result = await payload.login({
|
||||
collection: "users", // required
|
||||
collection: 'users', // required
|
||||
data: {
|
||||
// required
|
||||
email: "dev@payloadcms.com",
|
||||
password: "rip",
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'rip',
|
||||
},
|
||||
req: req, // pass an Express `req` which will be provided to all hooks
|
||||
res: res, // used to automatically set an HTTP-only auth cookie
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Forgot Password
|
||||
@@ -305,13 +301,13 @@ const result = await payload.login({
|
||||
```js
|
||||
// Returned token will allow for a password reset
|
||||
const token = await payload.forgotPassword({
|
||||
collection: "users", // required
|
||||
collection: 'users', // required
|
||||
data: {
|
||||
// required
|
||||
email: "dev@payloadcms.com",
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
req: req, // pass an Express `req` which will be provided to all hooks
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Reset Password
|
||||
@@ -323,14 +319,14 @@ const token = await payload.forgotPassword({
|
||||
// user: { ... } // the user document that just logged in
|
||||
// }
|
||||
const result = await payload.forgotPassword({
|
||||
collection: "users", // required
|
||||
collection: 'users', // required
|
||||
data: {
|
||||
// required
|
||||
token: "afh3o2jf2p3f...", // the token generated from the forgotPassword operation
|
||||
token: 'afh3o2jf2p3f...', // the token generated from the forgotPassword operation
|
||||
},
|
||||
req: req, // pass an Express `req` which will be provided to all hooks
|
||||
res: res, // used to automatically set an HTTP-only auth cookie
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Unlock
|
||||
@@ -338,14 +334,14 @@ const result = await payload.forgotPassword({
|
||||
```js
|
||||
// Returned result will be a boolean representing success or failure
|
||||
const result = await payload.unlock({
|
||||
collection: "users", // required
|
||||
collection: 'users', // required
|
||||
data: {
|
||||
// required
|
||||
email: "dev@payloadcms.com",
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
req: req, // pass an Express `req` which will be provided to all hooks
|
||||
overrideAccess: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Verify
|
||||
@@ -353,9 +349,9 @@ const result = await payload.unlock({
|
||||
```js
|
||||
// Returned result will be a boolean representing success or failure
|
||||
const result = await payload.verify({
|
||||
collection: "users", // required
|
||||
token: "afh3o2jf2p3f...", // the token saved on the user as `_verificationToken`
|
||||
});
|
||||
collection: 'users', // required
|
||||
token: 'afh3o2jf2p3f...', // the token saved on the user as `_verificationToken`
|
||||
})
|
||||
```
|
||||
|
||||
## Globals
|
||||
@@ -367,14 +363,14 @@ The following Global operations are available through the Local API:
|
||||
```js
|
||||
// Result will be the Header Global.
|
||||
const result = await payload.findGlobal({
|
||||
slug: "header", // required
|
||||
slug: 'header', // required
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Update
|
||||
@@ -382,25 +378,25 @@ const result = await payload.findGlobal({
|
||||
```js
|
||||
// Result will be the updated Header Global.
|
||||
const result = await payload.updateGlobal({
|
||||
slug: "header", // required
|
||||
slug: 'header', // required
|
||||
data: {
|
||||
// required
|
||||
nav: [
|
||||
{
|
||||
url: "https://google.com",
|
||||
url: 'https://google.com',
|
||||
},
|
||||
{
|
||||
url: "https://payloadcms.com",
|
||||
url: 'https://payloadcms.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
## Example Script using Local API
|
||||
@@ -408,36 +404,36 @@ const result = await payload.updateGlobal({
|
||||
The Local API is especially useful for running scripts
|
||||
|
||||
```ts
|
||||
import payload from "payload";
|
||||
import path from "path";
|
||||
import dotenv from "dotenv";
|
||||
import payload from 'payload'
|
||||
import path from 'path'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config({
|
||||
path: path.resolve(__dirname, "../.env"),
|
||||
});
|
||||
path: path.resolve(__dirname, '../.env'),
|
||||
})
|
||||
|
||||
const { PAYLOAD_SECRET, MONGODB_URI } = process.env;
|
||||
const { PAYLOAD_SECRET, MONGODB_URI } = process.env
|
||||
|
||||
const doAction = async (): Promise<void> => {
|
||||
await payload.init({
|
||||
secret: PAYLOAD_SECRET,
|
||||
mongoURL: MONGODB_URI,
|
||||
local: true, // Enables local mode, doesn't spin up a server or frontend
|
||||
});
|
||||
})
|
||||
|
||||
// Perform any Local API operations here
|
||||
await payload.find({
|
||||
collection: "posts",
|
||||
collection: 'posts',
|
||||
// where: {} // optional
|
||||
});
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: "posts",
|
||||
collection: 'posts',
|
||||
data: {},
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
doAction();
|
||||
doAction()
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
@@ -449,12 +445,12 @@ Here is an example of usage:
|
||||
```ts
|
||||
// Properly inferred as `Post` type
|
||||
const post = await payload.create({
|
||||
collection: "posts",
|
||||
collection: 'posts',
|
||||
|
||||
// Data will now be typed as Post and give you type hints
|
||||
data: {
|
||||
title: "my title",
|
||||
description: "my description",
|
||||
title: 'my title',
|
||||
description: 'my description',
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
@@ -9,7 +9,9 @@ keywords: plugins, config, configuration, extensions, custom, documentation, Con
|
||||
Payload comes with a built-in Plugins infrastructure that allows developers to build their own modular and easily reusable sets of functionality.
|
||||
|
||||
<Banner type="success">
|
||||
Because we rely on a simple config-based structure, Payload plugins simply take in a user's existing config and return a modified config with new fields, hooks, collections, admin views, or anything else you can think of.
|
||||
Because we rely on a simple config-based structure, Payload plugins simply take in a user's
|
||||
existing config and return a modified config with new fields, hooks, collections, admin views, or
|
||||
anything else you can think of.
|
||||
</Banner>
|
||||
|
||||
Writing plugins is no more complex than writing regular JavaScript. If you know how [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) works and are up to speed with Payload concepts, writing a plugin will be a breeze.
|
||||
@@ -30,11 +32,11 @@ Writing plugins is no more complex than writing regular JavaScript. If you know
|
||||
The base Payload config allows for a `plugins` property which takes an `array` of [`Plugins`](https://github.com/payloadcms/payload/blob/master/src/config/types.ts#L21).
|
||||
|
||||
```js
|
||||
import { buildConfig } from 'payload/config';
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
// note: these plugins are not real (yet?)
|
||||
import addLastModified from 'payload-add-last-modified';
|
||||
import passwordProtect from 'payload-password-protect';
|
||||
import addLastModified from 'payload-add-last-modified'
|
||||
import passwordProtect from 'payload-password-protect'
|
||||
|
||||
const config = buildConfig({
|
||||
collections: [
|
||||
@@ -50,9 +52,9 @@ const config = buildConfig({
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
// Many plugins require options to be passed.
|
||||
@@ -67,10 +69,10 @@ const config = buildConfig({
|
||||
// ..
|
||||
// To understand how to use the plugins you're interested in,
|
||||
// consult their corresponding documentation
|
||||
]
|
||||
});
|
||||
],
|
||||
})
|
||||
|
||||
export default config;
|
||||
export default config
|
||||
```
|
||||
|
||||
#### When Plugins are initialized
|
||||
@@ -84,21 +86,20 @@ After all plugins are executed, the full config with all plugins will be sanitiz
|
||||
Here is an example for how to automatically add a `lastModifiedBy` field to all Payload collections using a Plugin written in TypeScript.
|
||||
|
||||
```ts
|
||||
import { Config, Plugin } from 'payload/config';
|
||||
import { Config, Plugin } from 'payload/config'
|
||||
|
||||
const addLastModified: Plugin = (incomingConfig: Config): Config => {
|
||||
// Find all incoming auth-enabled collections
|
||||
// so we can create a lastModifiedBy relationship field
|
||||
// to all auth collections
|
||||
const authEnabledCollections = incomingConfig.collections.filter(
|
||||
collection => Boolean(collection.auth)
|
||||
);
|
||||
const authEnabledCollections = incomingConfig.collections.filter((collection) =>
|
||||
Boolean(collection.auth),
|
||||
)
|
||||
|
||||
// Spread the existing config
|
||||
const config: Config = {
|
||||
...incomingConfig,
|
||||
collections: incomingConfig.collections.map((collection) => {
|
||||
|
||||
// Spread each item that we are modifying,
|
||||
// and add our new field - complete with
|
||||
// hooks and proper admin UI config
|
||||
@@ -116,7 +117,7 @@ const addLastModified: Plugin = (incomingConfig: Config): Config => {
|
||||
value: req?.user?.id,
|
||||
relationTo: req?.user?.collection,
|
||||
}),
|
||||
]
|
||||
],
|
||||
},
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
@@ -124,14 +125,14 @@ const addLastModified: Plugin = (incomingConfig: Config): Config => {
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
return config
|
||||
}
|
||||
|
||||
export default addLastModified;
|
||||
export default addLastModified
|
||||
```
|
||||
|
||||
### Available Plugins
|
||||
|
||||
@@ -7,7 +7,8 @@ keywords: deployment, production, config, configuration, documentation, Content
|
||||
---
|
||||
|
||||
<Banner type="success">
|
||||
So you've developed a Payload app, it's fully tested, and running great locally. Now it's time to launch. <strong>Awesome! Great work!</strong> Now, what's next?
|
||||
So you've developed a Payload app, it's fully tested, and running great locally. Now it's time to
|
||||
launch. <strong>Awesome! Great work!</strong> Now, what's next?
|
||||
</Banner>
|
||||
|
||||
There are many ways to deploy Payload to a production environment. When evaluating how you will deploy Payload, you need to consider these main aspects:
|
||||
@@ -35,7 +36,13 @@ When you initialize Payload, you provide it with a `secret` property. This prope
|
||||
Because _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you wield that power responsibly before deploying to Production.
|
||||
|
||||
<Banner type="error">
|
||||
<strong>By default, all Access Control functions require that a user is successfully logged in to Payload to create, read, update, or delete data.</strong> But, if you allow public user registration, for example, you will want to make sure that your access control functions are more strict - permitting <strong>only appropriate users</strong> to perform appropriate actions.
|
||||
<strong>
|
||||
By default, all Access Control functions require that a user is successfully logged in to
|
||||
Payload to create, read, update, or delete data.
|
||||
</strong>{' '}
|
||||
But, if you allow public user registration, for example, you will want to make sure that your
|
||||
access control functions are more strict - permitting <strong>only appropriate users</strong> to
|
||||
perform appropriate actions.
|
||||
</Banner>
|
||||
|
||||
##### Building the Admin panel
|
||||
@@ -83,8 +90,13 @@ If you are using a [persistent filesystem-based cloud host](#persistent-vs-ephem
|
||||
Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and backups.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong><br />
|
||||
If versions are enabled and a collection has many documents you may need a minimum of an m10 mongoDB atlas cluster if you reach a sorting `exceeded memory limit` error to view a collection list in the admin UI. The limitations of the m2 and m5 tier clusters are here: [Atlas M0 (Free Cluster), M2, and M5 Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
If versions are enabled and a collection has many documents you may need a minimum of an m10
|
||||
mongoDB atlas cluster if you reach a sorting `exceeded memory limit` error to view a collection
|
||||
list in the admin UI. The limitations of the m2 and m5 tier clusters are here: [Atlas M0 (Free
|
||||
Cluster), M2, and M5
|
||||
Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
|
||||
</Banner>
|
||||
|
||||
##### DocumentDB
|
||||
@@ -118,8 +130,10 @@ Alternatively, persistent filesystems will never delete your files and can be tr
|
||||
- Many other more traditional web hosts
|
||||
|
||||
<Banner type="error">
|
||||
<strong>Warning:</strong><br />
|
||||
If you rely on Payload's <strong>Upload</strong> functionality, make sure you either use a host with a persistent filesystem or have an integration with a third-party file host like Amazon S3.
|
||||
<strong>Warning:</strong>
|
||||
<br />
|
||||
If you rely on Payload's <strong>Upload</strong> functionality, make sure you either use a host
|
||||
with a persistent filesystem or have an integration with a third-party file host like Amazon S3.
|
||||
</Banner>
|
||||
|
||||
##### Using ephemeral filesystem providers like Heroku
|
||||
@@ -184,13 +198,13 @@ CMD ["node", "dist/server.js"]
|
||||
Here is an example of a docker-compose.yml file that can be used for development
|
||||
|
||||
```yml
|
||||
version: "3"
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
payload:
|
||||
image: node:18-alpine
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- '3000:3000'
|
||||
volumes:
|
||||
- .:/home/node/app
|
||||
- node_modules:/home/node/app/node_modules
|
||||
@@ -207,7 +221,7 @@ services:
|
||||
mongo:
|
||||
image: mongo:latest
|
||||
ports:
|
||||
- "27017:27017"
|
||||
- '27017:27017'
|
||||
command:
|
||||
- --storageEngine=wiredTiger
|
||||
volumes:
|
||||
|
||||
@@ -19,15 +19,20 @@ Set the max number of failed login attempts before a user account is locked out
|
||||
To prevent DDoS, brute-force, and similar attacks, you can set IP-based rate limits so that once a certain threshold of requests has been hit by a single IP, further requests from the same IP will be ignored. The Payload config `rateLimit` property accepts an object with the following properties:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------------- | ----------- |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`window`** | Time in milliseconds to track requests per IP. Defaults to `90000` (15 minutes). |
|
||||
| **`max`** | Number of requests served from a single IP before limiting. Defaults to `500`. |
|
||||
| **`skip`** | Express middleware function that can return true (or promise resulting in true) that will bypass limit. |
|
||||
| **`trustProxy`** | True or false, to enable to allow requests to pass through a proxy such as a load balancer or an `nginx` reverse proxy. |
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Warning:</strong><br/>
|
||||
Very commonly, NodeJS apps are served behind `nginx` reverse proxies and similar. If you use rate-limiting while you're behind a proxy, <strong>all</strong> IP addresses from everyone that uses your API will appear as if they are from a local origin (127.0.0.1), and your users will get rate-limited very quickly without cause. If you plan to host your app behind a proxy, make sure you set <strong>trustProxy</strong> to <strong>true</strong>.
|
||||
<strong>Warning:</strong>
|
||||
<br />
|
||||
Very commonly, NodeJS apps are served behind `nginx` reverse proxies and similar. If you use
|
||||
rate-limiting while you're behind a proxy, <strong>all</strong> IP addresses from everyone that
|
||||
uses your API will appear as if they are from a local origin (127.0.0.1), and your users will get
|
||||
rate-limited very quickly without cause. If you plan to host your app behind a proxy, make sure
|
||||
you set <strong>trustProxy</strong> to <strong>true</strong>.
|
||||
</Banner>
|
||||
|
||||
### Max Depth
|
||||
|
||||
@@ -9,7 +9,11 @@ keywords: query, documents, overview, documentation, Content Management System,
|
||||
Payload provides an extremely granular querying language through all APIs. Each API takes the same syntax and fully supports all options.
|
||||
|
||||
<Banner>
|
||||
<strong>Here, "querying" relates to filtering or searching through documents within a Collection.</strong> You can build queries to pass to Find operations as well as to [restrict which documents certain users can access](/docs/access-control/overview) via access control functions.
|
||||
<strong>
|
||||
Here, "querying" relates to filtering or searching through documents within a Collection.
|
||||
</strong>{' '}
|
||||
You can build queries to pass to Find operations as well as to [restrict which documents certain
|
||||
users can access](/docs/access-control/overview) via access control functions.
|
||||
</Banner>
|
||||
|
||||
### Simple queries
|
||||
@@ -17,22 +21,22 @@ Payload provides an extremely granular querying language through all APIs. Each
|
||||
For example, say you have a collection as follows:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Post: CollectionConfig = {
|
||||
slug: "posts",
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
name: "color",
|
||||
type: "select",
|
||||
options: ["mint", "dark-gray", "white"],
|
||||
name: 'color',
|
||||
type: 'select',
|
||||
options: ['mint', 'dark-gray', 'white'],
|
||||
},
|
||||
{
|
||||
name: "featured",
|
||||
type: "checkbox",
|
||||
name: 'featured',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
You may eventually have a lot of documents within this Collection. If you wanted to find only documents with `color` equal to `mint`, you could write a query as follows:
|
||||
@@ -41,9 +45,9 @@ You may eventually have a lot of documents within this Collection. If you wanted
|
||||
const query = {
|
||||
color: {
|
||||
// property name to filter on
|
||||
equals: "mint", // operator to use and value to compare against
|
||||
equals: 'mint', // operator to use and value to compare against
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The above example demonstrates a simple query but you can get much more complex.
|
||||
@@ -68,7 +72,9 @@ The above example demonstrates a simple query but you can get much more complex.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip</strong>:<br />
|
||||
If you know your users will be querying on certain fields a lot, you can add <strong>index: true</strong> to a field's config which will speed up searches using that field immensely.
|
||||
If you know your users will be querying on certain fields a lot, you can add <strong>
|
||||
index: true
|
||||
</strong> to a field's config which will speed up searches using that field immensely.
|
||||
</Banner>
|
||||
|
||||
### And / Or Logic
|
||||
@@ -81,7 +87,7 @@ const query = {
|
||||
// array of OR conditions
|
||||
{
|
||||
color: {
|
||||
equals: "mint",
|
||||
equals: 'mint',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -89,7 +95,7 @@ const query = {
|
||||
// nested array of AND conditions
|
||||
{
|
||||
color: {
|
||||
equals: "white",
|
||||
equals: 'white',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -100,7 +106,7 @@ const query = {
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Written in plain English, if the above query were passed to a `find` operation, it would translate to finding posts where either the `color` is `mint` OR the `color` is `white` AND `featured` is set to false.
|
||||
@@ -111,11 +117,11 @@ When working with nested properties, which can happen when using relational fiel
|
||||
|
||||
```js
|
||||
const query = {
|
||||
"artists.featured": {
|
||||
'artists.featured': {
|
||||
// nested property name to filter on
|
||||
exists: true, // operator to use and boolean value that needs to be true
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### GraphQL Find Queries
|
||||
@@ -148,29 +154,27 @@ This one isn't too bad, but more complex queries get unavoidably more difficult
|
||||
**For example, using fetch:**
|
||||
|
||||
```js
|
||||
import qs from "qs";
|
||||
import qs from 'qs'
|
||||
|
||||
const query = {
|
||||
color: {
|
||||
equals: "mint",
|
||||
equals: 'mint',
|
||||
},
|
||||
// This query could be much more complex
|
||||
// and QS would handle it beautifully
|
||||
};
|
||||
}
|
||||
|
||||
const getPosts = async () => {
|
||||
const stringifiedQuery = qs.stringify(
|
||||
{
|
||||
where: query, // ensure that `qs` adds the `where` property, too!
|
||||
},
|
||||
{ addQueryPrefix: true }
|
||||
);
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
|
||||
const response = await fetch(
|
||||
`http://localhost:3000/api/posts${stringifiedQuery}`
|
||||
);
|
||||
const response = await fetch(`http://localhost:3000/api/posts${stringifiedQuery}`)
|
||||
// Continue to handle the response below...
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Local API Queries
|
||||
@@ -180,16 +184,16 @@ The Local API's `find` operation accepts an object exactly how you write it. For
|
||||
```js
|
||||
const getPosts = async () => {
|
||||
const posts = await payload.find({
|
||||
collection: "posts",
|
||||
collection: 'posts',
|
||||
where: {
|
||||
color: {
|
||||
equals: "mint",
|
||||
equals: 'mint',
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
return posts;
|
||||
};
|
||||
return posts
|
||||
}
|
||||
```
|
||||
|
||||
## Sort
|
||||
@@ -216,10 +220,10 @@ query {
|
||||
```js
|
||||
const getPosts = async () => {
|
||||
const posts = await payload.find({
|
||||
collection: "posts",
|
||||
sort: "-createdAt",
|
||||
});
|
||||
collection: 'posts',
|
||||
sort: '-createdAt',
|
||||
})
|
||||
|
||||
return posts;
|
||||
};
|
||||
return posts
|
||||
}
|
||||
```
|
||||
|
||||
@@ -11,7 +11,7 @@ All collection `find` queries are paginated automatically. Responses are returne
|
||||
**`Find` response properties:**
|
||||
|
||||
| Property | Description |
|
||||
| ------------- | ---------------------------------------------------------- |
|
||||
| ------------- | --------------------------------------------------------- |
|
||||
| docs | Array of documents in the collection |
|
||||
| totalDocs | Total available documents within the collection |
|
||||
| limit | Limit query parameter - defaults to `10` |
|
||||
@@ -24,6 +24,7 @@ All collection `find` queries are paginated automatically. Responses are returne
|
||||
| nextPage | `number` of next page, `null` if it doesn't exist |
|
||||
|
||||
**Example response:**
|
||||
|
||||
```json
|
||||
{
|
||||
// Document Array // highlight-line
|
||||
@@ -55,6 +56,6 @@ All collection `find` queries are paginated automatically. Responses are returne
|
||||
All Payload APIs support the pagination controls below. With them, you can create paginated lists of documents within your application:
|
||||
|
||||
| Control | Description |
|
||||
| --------- | ----------------------------------------------------------------------------------------- |
|
||||
| ------- | --------------------------------------- |
|
||||
| `limit` | Limits the number of documents returned |
|
||||
| `page` | Get a specific page number |
|
||||
|
||||
@@ -7,8 +7,7 @@ keywords: rest, api, documentation, Content Management System, cms, headless, ja
|
||||
---
|
||||
|
||||
<Banner>
|
||||
A fully functional REST API is automatically generated from your Collection
|
||||
and Global configs.
|
||||
A fully functional REST API is automatically generated from your Collection and Global configs.
|
||||
</Banner>
|
||||
|
||||
All Payload API routes are mounted prefixed to your config's `routes.api` URL segment (default: `/api`).
|
||||
@@ -439,45 +438,45 @@ Globals cannot be created or deleted, so there are only two REST endpoints opene
|
||||
<RestExamples
|
||||
data={[
|
||||
{
|
||||
operation: "Get Global",
|
||||
method: "GET",
|
||||
path: "/api/globals/{global-slug}",
|
||||
description: "Get a global by slug",
|
||||
operation: 'Get Global',
|
||||
method: 'GET',
|
||||
path: '/api/globals/{global-slug}',
|
||||
description: 'Get a global by slug',
|
||||
example: {
|
||||
slug: "getGlobal",
|
||||
slug: 'getGlobal',
|
||||
req: {
|
||||
credentials: true,
|
||||
headers: true,
|
||||
},
|
||||
res: {
|
||||
announcement: "Here is an announcement!",
|
||||
globalType: "announcement",
|
||||
createdAt: "2023-04-28T08:53:56.066Z",
|
||||
updatedAt: "2023-04-28T08:53:56.066Z",
|
||||
id: "644b89a496c64a833fe579c9",
|
||||
announcement: 'Here is an announcement!',
|
||||
globalType: 'announcement',
|
||||
createdAt: '2023-04-28T08:53:56.066Z',
|
||||
updatedAt: '2023-04-28T08:53:56.066Z',
|
||||
id: '644b89a496c64a833fe579c9',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "Update Global",
|
||||
method: "POST",
|
||||
path: "/api/globals/{global-slug}",
|
||||
description: "Update a global by slug",
|
||||
operation: 'Update Global',
|
||||
method: 'POST',
|
||||
path: '/api/globals/{global-slug}',
|
||||
description: 'Update a global by slug',
|
||||
example: {
|
||||
slug: "updateGlobal",
|
||||
slug: 'updateGlobal',
|
||||
req: {
|
||||
headers: true,
|
||||
credentials: true,
|
||||
body: {
|
||||
announcement: "Paging Doctor Scrunt",
|
||||
announcement: 'Paging Doctor Scrunt',
|
||||
},
|
||||
},
|
||||
res: {
|
||||
announcement: "Paging Doctor Scrunt",
|
||||
globalType: "announcement",
|
||||
createdAt: "2023-04-28T08:53:56.066Z",
|
||||
updatedAt: "2023-04-28T08:53:56.066Z",
|
||||
id: "644b89a496c64a833fe579c9",
|
||||
announcement: 'Paging Doctor Scrunt',
|
||||
globalType: 'announcement',
|
||||
createdAt: '2023-04-28T08:53:56.066Z',
|
||||
updatedAt: '2023-04-28T08:53:56.066Z',
|
||||
id: '644b89a496c64a833fe579c9',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -491,65 +490,65 @@ In addition to the dynamically generated endpoints above Payload also has REST e
|
||||
<RestExamples
|
||||
data={[
|
||||
{
|
||||
operation: "Get Preference",
|
||||
method: "GET",
|
||||
path: "/api/payload-preferences/{key}",
|
||||
description: "Get a preference by key",
|
||||
operation: 'Get Preference',
|
||||
method: 'GET',
|
||||
path: '/api/payload-preferences/{key}',
|
||||
description: 'Get a preference by key',
|
||||
example: {
|
||||
slug: "getPreference",
|
||||
slug: 'getPreference',
|
||||
req: {
|
||||
headers: true,
|
||||
credentials: true,
|
||||
},
|
||||
res: {
|
||||
_id: "644bb7a8307b3d363c6edf2c",
|
||||
key: "region",
|
||||
user: "644b8453cd20c7857da5a9b0",
|
||||
userCollection: "users",
|
||||
_id: '644bb7a8307b3d363c6edf2c',
|
||||
key: 'region',
|
||||
user: '644b8453cd20c7857da5a9b0',
|
||||
userCollection: 'users',
|
||||
__v: 0,
|
||||
createdAt: "2023-04-28T12:10:16.689Z",
|
||||
updatedAt: "2023-04-28T12:10:16.689Z",
|
||||
value: "Europe/London",
|
||||
createdAt: '2023-04-28T12:10:16.689Z',
|
||||
updatedAt: '2023-04-28T12:10:16.689Z',
|
||||
value: 'Europe/London',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "Create Preference",
|
||||
method: "POST",
|
||||
path: "/api/payload-preferences/{key}",
|
||||
description: "Create or update a preference by key",
|
||||
operation: 'Create Preference',
|
||||
method: 'POST',
|
||||
path: '/api/payload-preferences/{key}',
|
||||
description: 'Create or update a preference by key',
|
||||
example: {
|
||||
slug: "createPreference",
|
||||
slug: 'createPreference',
|
||||
req: {
|
||||
headers: true,
|
||||
credentials: true,
|
||||
body: {
|
||||
value: "Europe/London",
|
||||
value: 'Europe/London',
|
||||
},
|
||||
},
|
||||
res: {
|
||||
message: "Updated successfully.",
|
||||
message: 'Updated successfully.',
|
||||
doc: {
|
||||
user: "644b8453cd20c7857da5a9b0",
|
||||
key: "region",
|
||||
userCollection: "users",
|
||||
value: "Europe/London",
|
||||
user: '644b8453cd20c7857da5a9b0',
|
||||
key: 'region',
|
||||
userCollection: 'users',
|
||||
value: 'Europe/London',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "Delete Preference",
|
||||
method: "DELETE",
|
||||
path: "/api/payload-preferences/{key}",
|
||||
description: "Delete a preference by key",
|
||||
operation: 'Delete Preference',
|
||||
method: 'DELETE',
|
||||
path: '/api/payload-preferences/{key}',
|
||||
description: 'Delete a preference by key',
|
||||
example: {
|
||||
slug: "deletePreference",
|
||||
slug: 'deletePreference',
|
||||
req: {
|
||||
headers: true,
|
||||
},
|
||||
res: {
|
||||
message: "deletedSuccessfully",
|
||||
message: 'deletedSuccessfully',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -573,37 +572,36 @@ Each endpoint object needs to have:
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
// a collection of 'orders' with an additional route for tracking details, reachable at /api/orders/:id/tracking
|
||||
export const Orders: CollectionConfig = {
|
||||
slug: "orders",
|
||||
slug: 'orders',
|
||||
fields: [
|
||||
/* ... */
|
||||
],
|
||||
// highlight-start
|
||||
endpoints: [
|
||||
{
|
||||
path: "/:id/tracking",
|
||||
method: "get",
|
||||
path: '/:id/tracking',
|
||||
method: 'get',
|
||||
handler: async (req, res, next) => {
|
||||
const tracking = await getTrackingInfo(req.params.id);
|
||||
const tracking = await getTrackingInfo(req.params.id)
|
||||
if (tracking) {
|
||||
res.status(200).send({ tracking });
|
||||
res.status(200).send({ tracking })
|
||||
} else {
|
||||
res.status(404).send({ error: "not found" });
|
||||
res.status(404).send({ error: 'not found' })
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
// highlight-end
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
**req** will have the **payload** object and can be used inside your endpoint
|
||||
handlers for making calls like req.payload.find() that will make use of access
|
||||
control and hooks.
|
||||
**req** will have the **payload** object and can be used inside your endpoint handlers for making
|
||||
calls like req.payload.find() that will make use of access control and hooks.
|
||||
</Banner>
|
||||
|
||||
@@ -12,7 +12,7 @@ keywords: admin, components, custom, customize, documentation, Content Managemen
|
||||
|
||||
This means that your auth cookie is not being set or accepted correctly upon logging in. To resolve heck the following settings in your Payload config:
|
||||
|
||||
- CORS - If you are using the '*', try to explicitly only allow certain domains instead including the one you have specified.
|
||||
- CORS - If you are using the '\*', try to explicitly only allow certain domains instead including the one you have specified.
|
||||
- CSRF - Do you have this set? if so, make sure your domain is whitelisted within the csrf domains. If not, probably not the issue, but probably can't hurt to whitelist it anyway.
|
||||
- Cookie settings. If these are completely undefined, then that's fine. but if you have cookie domain set, or anything similar, make sure you don't have the domain misconfigured
|
||||
|
||||
|
||||
@@ -102,19 +102,19 @@ By generating types, we'll end up with a file containing the following two TypeS
|
||||
|
||||
```ts
|
||||
export interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email?: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
id: string
|
||||
name: string
|
||||
email?: string
|
||||
resetPasswordToken?: string
|
||||
resetPasswordExpiration?: string
|
||||
loginAttempts?: number
|
||||
lockUntil?: string
|
||||
}
|
||||
|
||||
export interface Post {
|
||||
id: string;
|
||||
title?: string;
|
||||
author?: string | User;
|
||||
id: string
|
||||
title?: string
|
||||
author?: string | User
|
||||
}
|
||||
```
|
||||
|
||||
@@ -145,25 +145,24 @@ will generate:
|
||||
```ts
|
||||
// a top level reusable interface!!
|
||||
export interface SharedMeta {
|
||||
title?: string;
|
||||
description?: string;
|
||||
title?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
// example usage inside collection interface
|
||||
export interface Collection1 {
|
||||
// ...other fields
|
||||
meta?: SharedMeta;
|
||||
meta?: SharedMeta
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Naming Collisions</strong>
|
||||
<br />
|
||||
Since these types are hoisted to the top level, you need to be aware that
|
||||
naming collisions can occur. For example, if you have a collection with the
|
||||
name of `Meta` and you also create a interface with the name `Meta` they will
|
||||
collide. It is recommended to scope your interfaces by appending the field
|
||||
type to the end, i.e. `MetaGroup` or similar.
|
||||
Since these types are hoisted to the top level, you need to be aware that naming collisions can
|
||||
occur. For example, if you have a collection with the name of `Meta` and you also create a
|
||||
interface with the name `Meta` they will collide. It is recommended to scope your interfaces by
|
||||
appending the field type to the end, i.e. `MetaGroup` or similar.
|
||||
</Banner>
|
||||
|
||||
### Using your types
|
||||
|
||||
@@ -7,9 +7,8 @@ keywords: uploads, images, media, overview, documentation, Content Management Sy
|
||||
---
|
||||
|
||||
<Banner>
|
||||
Payload provides for everything you need to enable file upload, storage, and
|
||||
management directly on your server—including extremely powerful file access
|
||||
control.
|
||||
Payload provides for everything you need to enable file upload, storage, and management directly
|
||||
on your server—including extremely powerful file access control.
|
||||
</Banner>
|
||||
|
||||

|
||||
@@ -34,14 +33,15 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />A common pattern is to create a <strong>Media</strong> collection and enable{" "}
|
||||
<strong>upload</strong> on that collection.
|
||||
<br />A common pattern is to create a <strong>Media</strong> collection and enable <strong>
|
||||
upload
|
||||
</strong> on that collection.
|
||||
</Banner>
|
||||
|
||||
#### Collection Upload Options
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`staticURL`** \* | The URL path to use to access your uploads. Relative path like `/media` will be served by payload. Full path like `https://example.com/media` needs to be served by another web server. |
|
||||
| **`staticDir`** \* | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. |
|
||||
| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |
|
||||
@@ -58,7 +58,7 @@ _An asterisk denotes that a property above is required._
|
||||
**Example Upload collection:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
@@ -98,7 +98,7 @@ export const Media: CollectionConfig = {
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Payload-wide Upload Options
|
||||
@@ -108,7 +108,7 @@ Payload relies on the [`express-fileupload`](https://www.npmjs.com/package/expre
|
||||
A common example of what you might want to customize within Payload-wide Upload options would be to increase the allowed `fileSize` of uploads sent to Payload:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
@@ -128,7 +128,7 @@ export default buildConfig({
|
||||
fileSize: 5000000, // 5MB, written in bytes
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Image Sizes
|
||||
@@ -152,11 +152,10 @@ If you are using a plugin to send your files off to a third-party file storage h
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
This is a fairly advanced feature. If you do disable local file storage, by
|
||||
default, your admin panel's thumbnails will be broken as you will not have
|
||||
stored a file. It will be totally up to you to use either a plugin or your own
|
||||
hooks to store your files in a permanent manner, as well as provide your own
|
||||
admin thumbnail using <strong>upload.adminThumbnail</strong>.
|
||||
This is a fairly advanced feature. If you do disable local file storage, by default, your admin
|
||||
panel's thumbnails will be broken as you will not have stored a file. It will be totally up to you
|
||||
to use either a plugin or your own hooks to store your files in a permanent manner, as well as
|
||||
provide your own admin thumbnail using <strong>upload.adminThumbnail</strong>.
|
||||
</Banner>
|
||||
|
||||
### Admin Thumbnails
|
||||
@@ -169,7 +168,7 @@ You can specify how Payload retrieves admin thumbnails for your upload-enabled C
|
||||
**Example custom Admin thumbnail:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
@@ -180,17 +179,17 @@ export const Media: CollectionConfig = {
|
||||
// ... image sizes here
|
||||
],
|
||||
// highlight-start
|
||||
adminThumbnail: ({ doc }) =>
|
||||
`https://google.com/custom-path-to-file/${doc.filename}`,
|
||||
adminThumbnail: ({ doc }) => `https://google.com/custom-path-to-file/${doc.filename}`,
|
||||
// highlight-end
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
This function runs in the browser. If your function returns `null` or `false` Payload will show the default generic file thumbnail instead.
|
||||
This function runs in the browser. If your function returns `null` or `false` Payload will show
|
||||
the default generic file thumbnail instead.
|
||||
</Banner>
|
||||
|
||||
### MimeTypes
|
||||
@@ -202,7 +201,7 @@ Some example values are: `image/*`, `audio/*`, `video/*`, `image/png`, `applicat
|
||||
**Example mimeTypes usage:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
@@ -211,7 +210,7 @@ export const Media: CollectionConfig = {
|
||||
staticDir: 'media',
|
||||
mimeTypes: ['image/*', 'application/pdf'], // highlight-line
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Uploading Files
|
||||
@@ -219,9 +218,8 @@ export const Media: CollectionConfig = {
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
Uploading files is currently only possible through the REST and Local APIs due
|
||||
to how GraphQL works. It's difficult and fairly nonsensical to support
|
||||
uploading files through GraphQL.
|
||||
Uploading files is currently only possible through the REST and Local APIs due to how GraphQL
|
||||
works. It's difficult and fairly nonsensical to support uploading files through GraphQL.
|
||||
</Banner>
|
||||
|
||||
To upload a file, use your collection's [`create`](/docs/rest-api/overview#collections) endpoint. Send it all the data that your Collection requires, as well as a `file` key containing the file that you'd like to upload.
|
||||
|
||||
@@ -13,20 +13,20 @@ Extending on Payload's [Draft](/docs/versions/drafts) functionality, you can con
|
||||
</Banner>
|
||||
|
||||

|
||||
*If Autosave is enabled, drafts will be created automatically as the document is modified and the Admin UI adds an indicator describing when the document was last saved to the top right of the sidebar.*
|
||||
_If Autosave is enabled, drafts will be created automatically as the document is modified and the Admin UI adds an indicator describing when the document was last saved to the top right of the sidebar._
|
||||
|
||||
### Options
|
||||
|
||||
Collections and Globals both support the same options for configuring autosave. You can either set `versions.drafts.autosave` to `true`, or pass an object to configure autosave properties.
|
||||
|
||||
| Drafts Autosave Options | Description |
|
||||
| ---------------------------- | -------------|
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `interval` | Define an `interval` in milliseconds to automatically save progress while documents are edited. Document updates are "debounced" at this interval. Defaults to `2000`. |
|
||||
|
||||
**Example config with versions, drafts, and autosave enabled:**
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
@@ -34,7 +34,7 @@ export const Pages: CollectionConfig = {
|
||||
read: ({ req }) => {
|
||||
// If there is a user logged in,
|
||||
// let them retrieve all documents
|
||||
if (req.user) return true;
|
||||
if (req.user) return true
|
||||
|
||||
// If there is no user,
|
||||
// restrict the documents that are returned
|
||||
@@ -43,7 +43,7 @@ export const Pages: CollectionConfig = {
|
||||
_status: {
|
||||
equals: 'published',
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
@@ -54,7 +54,7 @@ export const Pages: CollectionConfig = {
|
||||
// autosave: {
|
||||
// interval: 1500,
|
||||
// },
|
||||
}
|
||||
},
|
||||
},
|
||||
//.. the rest of the Pages config here
|
||||
}
|
||||
@@ -69,5 +69,7 @@ When `autosave` is enabled, all `update` operations within Payload expose a new
|
||||
If we created a new version for each autosave, you'd quickly find a ton of autosaves that clutter up your `_versions` collection within the database. That would be messy quick because `autosave` is typically set to save a document every ~2000ms or so.
|
||||
|
||||
<Banner type="success">
|
||||
Instead of creating a new version each time a document is autosaved, Payload smartly only creates <strong>one</strong> autosave version, and then updates that specific version with each autosave performed. This makes sure that your versions remain nice and tidy.
|
||||
Instead of creating a new version each time a document is autosaved, Payload smartly only creates{' '}
|
||||
<strong>one</strong> autosave version, and then updates that specific version with each autosave
|
||||
performed. This makes sure that your versions remain nice and tidy.
|
||||
</Banner>
|
||||
|
||||
@@ -8,21 +8,19 @@ keywords: version history, drafts, preview, draft, restore, publish, autosave, C
|
||||
|
||||
Payload's Draft functionality builds on top of the Versions functionality to allow you to make changes to your collection documents and globals, but publish only when you're ready. This functionality allows you to build powerful Preview environments for your data, where you can make sure your changes look good before publishing documents.
|
||||
|
||||
<Banner type="warning">
|
||||
Drafts rely on Versions being enabled in order to function.
|
||||
</Banner>
|
||||
<Banner type="warning">Drafts rely on Versions being enabled in order to function.</Banner>
|
||||
|
||||
By enabling Versions with Drafts, your collections and globals can maintain _newer_, and _unpublished_ versions of your documents. It's perfect for cases where you might want to work on a document, update it and save your progress, but not necessarily make it publicly published right away. Drafts are extremely helpful when building preview implementations.
|
||||
|
||||

|
||||
*If Drafts are enabled, the typical Save button is replaced with new actions which allow you to either save a draft, or publish your changes.*
|
||||
_If Drafts are enabled, the typical Save button is replaced with new actions which allow you to either save a draft, or publish your changes._
|
||||
|
||||
### Options
|
||||
|
||||
Collections and Globals both support the same options for configuring drafts. You can either set `versions.drafts` to `true`, or pass an object to configure draft properties.
|
||||
|
||||
| Draft Option | Description |
|
||||
| ---------------------------- | -------------|
|
||||
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `autosave` | Enable `autosave` to automatically save progress while documents are edited. To enable, set to `true` or pass an object with [options](/docs/versions/autosave). |
|
||||
|
||||
### Database changes
|
||||
@@ -33,14 +31,18 @@ By enabling drafts on a collection or a global, Payload will <strong>automatical
|
||||
|
||||
Within the Admin UI, if drafts are enabled, a document can be shown with one of three "statuses":
|
||||
|
||||
1. <strong>Draft</strong> - if a document has never been published, and only draft versions of the document are present
|
||||
1. <strong>Draft</strong> - if a document has never been published, and only draft versions of the document
|
||||
are present
|
||||
1. <strong>Published</strong> - if a document is published and there are no newer drafts available
|
||||
1. <strong>Changed</strong> - if a document has been published, but there are newer drafts available and not yet published
|
||||
1. <strong>Changed</strong> - if a document has been published, but there are newer drafts available
|
||||
and not yet published
|
||||
|
||||
### Draft API
|
||||
|
||||
<Banner type="success">
|
||||
If drafts are enabled on your collection or global, important and powerful changes are made to your REST, GraphQL, and Local APIs that allow you to specify if you are interacting with drafts or with live documents.
|
||||
If drafts are enabled on your collection or global, important and powerful changes are made to
|
||||
your REST, GraphQL, and Local APIs that allow you to specify if you are interacting with drafts or
|
||||
with live documents.
|
||||
</Banner>
|
||||
|
||||
##### Updating or creating drafts
|
||||
@@ -72,7 +74,9 @@ But, if you specify `draft` as `true`, Payload will automatically replace your p
|
||||
### Controlling who can see Collection drafts
|
||||
|
||||
<Banner type="warning">
|
||||
If you're using the <strong>drafts</strong> feature, it's important for you to consider who can view your drafts, and who can view only published documents. Luckily, Payload makes this extremely simple and puts the power completely in your hands.
|
||||
If you're using the <strong>drafts</strong> feature, it's important for you to consider who can
|
||||
view your drafts, and who can view only published documents. Luckily, Payload makes this extremely
|
||||
simple and puts the power completely in your hands.
|
||||
</Banner>
|
||||
|
||||
##### Restricting draft access
|
||||
@@ -82,7 +86,7 @@ You can use the `read` [Access Control](/docs/access-control/collections#read) m
|
||||
Here is an example that utilizes the `_status` field to require a user to be logged in to retrieve drafts:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
@@ -90,7 +94,7 @@ export const Pages: CollectionConfig = {
|
||||
read: ({ req }) => {
|
||||
// If there is a user logged in,
|
||||
// let them retrieve all documents
|
||||
if (req.user) return true;
|
||||
if (req.user) return true
|
||||
|
||||
// If there is no user,
|
||||
// restrict the documents that are returned
|
||||
@@ -99,25 +103,32 @@ export const Pages: CollectionConfig = {
|
||||
_status: {
|
||||
equals: 'published',
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
drafts: true
|
||||
drafts: true,
|
||||
},
|
||||
//.. the rest of the Pages config here
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note regarding adding versions to an existing collection</strong><br/>
|
||||
If you already have a collection with documents, and you <em>opt in</em> to draft functionality after you have already created existing documents, all of your old documents <em>will not have a _status field</em> until you resave them. For this reason, if you are <em>adding</em> versions into an existing collection, you might want to write your access control function to allow for users to read both documents where <strong>_status is equal to "published"</strong> as well as where <strong>_status does not exist</strong>.
|
||||
<strong>Note regarding adding versions to an existing collection</strong>
|
||||
<br />
|
||||
If you already have a collection with documents, and you <em>opt in</em> to draft functionality
|
||||
after you have already created existing documents, all of your old documents{' '}
|
||||
<em>will not have a _status field</em> until you resave them. For this reason, if you are{' '}
|
||||
<em>adding</em> versions into an existing collection, you might want to write your access control
|
||||
function to allow for users to read both documents where{' '}
|
||||
<strong>_status is equal to "published"</strong> as well as where{' '}
|
||||
<strong>_status does not exist</strong>.
|
||||
</Banner>
|
||||
|
||||
Here is an example for how to write an access control function that grants access to both documents where `_status` is equal to "published" and where `_status` does not exist:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
@@ -125,7 +136,7 @@ export const Pages: CollectionConfig = {
|
||||
read: ({ req }) => {
|
||||
// If there is a user logged in,
|
||||
// let them retrieve all documents
|
||||
if (req.user) return true;
|
||||
if (req.user) return true
|
||||
|
||||
// If there is no user,
|
||||
// restrict the documents that are returned
|
||||
@@ -141,14 +152,14 @@ export const Pages: CollectionConfig = {
|
||||
{
|
||||
_status: {
|
||||
exists: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
drafts: true
|
||||
drafts: true,
|
||||
},
|
||||
//.. the rest of the Pages config here
|
||||
}
|
||||
@@ -161,4 +172,3 @@ If a document is published, the Payload Admin UI will be updated to show an "unp
|
||||
### Reverting to published
|
||||
|
||||
If a document is published, and you have made further changes which are saved as a draft, Payload will show a "revert to published" button at the top of the sidebar which will allow you to reject your draft changes and "revert" back to the published state of the document. Your drafts will still be saved, but a new version will be created that will reflect the last published state of the document.
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ keywords: version history, revisions, audit log, draft, publish, restore, autosa
|
||||
---
|
||||
|
||||
<Banner>
|
||||
Payload's powerful Versions functionality allows you to keep a running history
|
||||
of changes over time and extensible to fit any content publishing workflow.
|
||||
Payload's powerful Versions functionality allows you to keep a running history of changes over
|
||||
time and extensible to fit any content publishing workflow.
|
||||
</Banner>
|
||||
|
||||
When enabled, Payload will automatically scaffold a new Collection in your database to store versions of your document(s) over time, and the Admin UI will be extended with additional views that allow you to browse document versions, view diffs in order to see exactly what has changed in your documents (and when they changed), and restore documents back to prior versions easily.
|
||||
@@ -26,9 +26,9 @@ _Comparing an old version to a newer version of a document_
|
||||
- Build a powerful publishing schedule mechanism to create documents and have them become publicly readable automatically at a future date
|
||||
|
||||
<Banner type="success">
|
||||
Versions are extremely performant and totally opt-in. They don't change the
|
||||
shape of your data at all. All versions are stored in a separate Collection
|
||||
and can be turned on and off easily at your discretion.
|
||||
Versions are extremely performant and totally opt-in. They don't change the shape of your data at
|
||||
all. All versions are stored in a separate Collection and can be turned on and off easily at your
|
||||
discretion.
|
||||
</Banner>
|
||||
|
||||
### Options
|
||||
@@ -126,18 +126,18 @@ Versions expose new operations for both collections and globals. They allow you
|
||||
// Result will be a paginated set of Versions.
|
||||
// See /docs/queries/pagination for more.
|
||||
const result = await payload.findVersions({
|
||||
collection: "posts", // required
|
||||
collection: 'posts', // required
|
||||
depth: 2,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
where: {}, // pass a `where` query here
|
||||
sort: "-createdAt",
|
||||
locale: "en",
|
||||
sort: '-createdAt',
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Find by ID
|
||||
@@ -145,15 +145,15 @@ const result = await payload.findVersions({
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
const result = await payload.findVersionByID({
|
||||
collection: "posts", // required
|
||||
id: "507f1f77bcf86cd799439013", // required
|
||||
collection: 'posts', // required
|
||||
id: '507f1f77bcf86cd799439013', // required
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Restore
|
||||
@@ -161,13 +161,13 @@ const result = await payload.findVersionByID({
|
||||
```js
|
||||
// Result will be the restored global document.
|
||||
const result = await payload.restoreVersion({
|
||||
collection: "posts", // required
|
||||
id: "507f1f77bcf86cd799439013", // required
|
||||
collection: 'posts', // required
|
||||
id: '507f1f77bcf86cd799439013', // required
|
||||
depth: 2,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
**Global REST endpoints:**
|
||||
@@ -199,18 +199,18 @@ const result = await payload.restoreVersion({
|
||||
// Result will be a paginated set of Versions.
|
||||
// See /docs/queries/pagination for more.
|
||||
const result = await payload.findGlobalVersions({
|
||||
slug: "header", // required
|
||||
slug: 'header', // required
|
||||
depth: 2,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
where: {}, // pass a `where` query here
|
||||
sort: "-createdAt",
|
||||
locale: "en",
|
||||
sort: '-createdAt',
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Find by ID
|
||||
@@ -218,15 +218,15 @@ const result = await payload.findGlobalVersions({
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
const result = await payload.findGlobalVersionByID({
|
||||
slug: "header", // required
|
||||
id: "507f1f77bcf86cd799439013", // required
|
||||
slug: 'header', // required
|
||||
id: '507f1f77bcf86cd799439013', // required
|
||||
depth: 2,
|
||||
locale: "en",
|
||||
locale: 'en',
|
||||
fallbackLocale: false,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
#### Restore
|
||||
@@ -234,13 +234,13 @@ const result = await payload.findGlobalVersionByID({
|
||||
```js
|
||||
// Result will be the restored global document.
|
||||
const result = await payload.restoreGlobalVersion({
|
||||
slug: "header", // required
|
||||
id: "507f1f77bcf86cd799439013", // required
|
||||
slug: 'header', // required
|
||||
id: '507f1f77bcf86cd799439013', // required
|
||||
depth: 2,
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
showHiddenFields: true,
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Access Control
|
||||
|
||||
154
package.json
154
package.json
@@ -1,70 +1,43 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14",
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"author": {
|
||||
"email": "info@payloadcms.com",
|
||||
"name": "Payload",
|
||||
"url": "https://payloadcms.com"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Payload",
|
||||
"email": "info@payloadcms.com",
|
||||
"url": "https://payloadcms.com"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git"
|
||||
},
|
||||
"homepage": "https://payloadcms.com",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"bin": {
|
||||
"payload": "bin.js"
|
||||
},
|
||||
"workspaces:": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
|
||||
"build:tsc": "tsc",
|
||||
"build:components": "webpack --config dist/bundlers/webpack/components.config.js",
|
||||
"build": "pnpm copyfiles && pnpm build:tsc && pnpm build:components",
|
||||
"build:watch": "nodemon --watch 'src/**' --ext 'ts,tsx' --exec \"pnpm build:tsc\"",
|
||||
"dev": "pnpm --filter payload run dev",
|
||||
"dev:postgres": "pnpm --filter payload run dev:postgres",
|
||||
"dev:generate-types": "pnpm --filter payload run dev:generate-types",
|
||||
"dev:generate-graphql-schema": "pnpm --filter payload run dev:generate-graphql-schema",
|
||||
"pretest": "pnpm build",
|
||||
"test": "pnpm --filter payload run test",
|
||||
"test:int": "pnpm --filter payload run test:int",
|
||||
"test:e2e": "pnpm --filter payload run test:e2e",
|
||||
"test:e2e:headed": "pnpm --filter payload run test:e2e:headed",
|
||||
"test:e2e:debug": "pnpm --filter payload run test:e2e:debug",
|
||||
"test:components": "pnpm --filter payload run test:components",
|
||||
"translateNewKeys": "pnpm --filter payload run translateNewKeys",
|
||||
"clean:cache": "rimraf node_modules/.cache && rimraf packages/payload/node_modules/.cache",
|
||||
"clean": "rimraf dist && rimraf packages/payload/dist",
|
||||
"release:patch": "release-it patch",
|
||||
"release:minor": "release-it minor",
|
||||
"release:major": "release-it major",
|
||||
"release:beta": "release-it pre --preReleaseId=beta --npm.tag=beta --config .release-it.pre.json",
|
||||
"release:canary": "release-it pre --preReleaseId=canary --npm.tag=canary --config .release-it.pre.json",
|
||||
"fix": "eslint \"src/**/*.ts\" --fix",
|
||||
"lint": "eslint \"src/**/*.ts\""
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/payloadcms/payload"
|
||||
},
|
||||
"dependencies": {
|
||||
"turbo": "^1.10.13"
|
||||
},
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/node": "20.5.7",
|
||||
"copyfiles": "2.4.1",
|
||||
"cross-env": "7.0.3",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "5.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14",
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"files": [
|
||||
"bin.js",
|
||||
"dist",
|
||||
"docs",
|
||||
"components",
|
||||
"scss",
|
||||
"*.js",
|
||||
"*.d.ts",
|
||||
"!jest.config.js",
|
||||
"!jest.components.config.js"
|
||||
],
|
||||
"homepage": "https://payloadcms.com",
|
||||
"keywords": [
|
||||
"payload",
|
||||
"cms",
|
||||
@@ -86,29 +59,56 @@
|
||||
"react",
|
||||
"auth"
|
||||
],
|
||||
"dependencies": {
|
||||
"turbo": "^1.10.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/node": "20.5.7",
|
||||
"copyfiles": "2.4.1",
|
||||
"cross-env": "7.0.3",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "5.2.2"
|
||||
},
|
||||
"files": [
|
||||
"bin.js",
|
||||
"dist",
|
||||
"docs",
|
||||
"components",
|
||||
"scss",
|
||||
"*.js",
|
||||
"*.d.ts",
|
||||
"!jest.config.js",
|
||||
"!jest.components.config.js"
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"maintainers": [
|
||||
{
|
||||
"email": "info@payloadcms.com",
|
||||
"name": "Payload",
|
||||
"url": "https://payloadcms.com"
|
||||
}
|
||||
],
|
||||
"name": "payload-monorepo",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm build:tsc && pnpm build:components",
|
||||
"build:components": "webpack --config dist/bundlers/webpack/components.config.js",
|
||||
"build:tsc": "tsc",
|
||||
"build:watch": "nodemon --watch 'src/**' --ext 'ts,tsx' --exec \"pnpm build:tsc\"",
|
||||
"clean": "rimraf dist && rimraf packages/payload/dist",
|
||||
"clean:cache": "rimraf node_modules/.cache && rimraf packages/payload/node_modules/.cache",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
|
||||
"dev": "pnpm --filter payload run dev",
|
||||
"dev:generate-graphql-schema": "pnpm --filter payload run dev:generate-graphql-schema",
|
||||
"dev:generate-types": "pnpm --filter payload run dev:generate-types",
|
||||
"dev:postgres": "pnpm --filter payload run dev:postgres",
|
||||
"fix": "eslint \"src/**/*.ts\" --fix",
|
||||
"lint": "eslint \"src/**/*.ts\"",
|
||||
"pretest": "pnpm build",
|
||||
"release:beta": "release-it pre --preReleaseId=beta --npm.tag=beta --config .release-it.pre.json",
|
||||
"release:canary": "release-it pre --preReleaseId=canary --npm.tag=canary --config .release-it.pre.json",
|
||||
"release:major": "release-it major",
|
||||
"release:minor": "release-it minor",
|
||||
"release:patch": "release-it patch",
|
||||
"test": "pnpm --filter payload run test",
|
||||
"test:components": "pnpm --filter payload run test:components",
|
||||
"test:e2e": "pnpm --filter payload run test:e2e",
|
||||
"test:e2e:debug": "pnpm --filter payload run test:e2e:debug",
|
||||
"test:e2e:headed": "pnpm --filter payload run test:e2e:headed",
|
||||
"test:int": "pnpm --filter payload run test:int",
|
||||
"translateNewKeys": "pnpm --filter payload run translateNewKeys"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"typings": "./dist/index.d.ts",
|
||||
"version": "0.0.0",
|
||||
"workspaces:": [
|
||||
"packages/*"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
|
||||
@@ -1,38 +1,5 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "0.0.1",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"author": "Payload CMS, Inc.",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mongoose-aggregate-paginate-v2": "1.0.9",
|
||||
"mongodb-memory-server": "8.13.0",
|
||||
"payload": "workspace:*",
|
||||
"@payloadcms/eslint-config": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"bson-objectid": "2.0.4",
|
||||
"deepmerge": "4.3.1",
|
||||
@@ -41,5 +8,38 @@
|
||||
"mongoose-aggregate-paginate-v2": "1.0.6",
|
||||
"mongoose-paginate-v2": "1.7.22",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/mongoose-aggregate-paginate-v2": "1.0.9",
|
||||
"mongodb-memory-server": "8.13.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "./src/index.ts",
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./dist/index.js",
|
||||
"types": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
@@ -1,67 +1,61 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import type { ConnectOptions } from 'mongoose';
|
||||
import mongoose from 'mongoose';
|
||||
import type { Connect } from 'payload/database';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import type { ConnectOptions } from 'mongoose'
|
||||
import type { Connect } from 'payload/database'
|
||||
|
||||
export const connect: Connect = async function connect(
|
||||
this: MongooseAdapter,
|
||||
payload,
|
||||
) {
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
export const connect: Connect = async function connect(this: MongooseAdapter, payload) {
|
||||
if (this.url === false) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (!payload.local && typeof this.url !== 'string') {
|
||||
throw new Error('Error: missing MongoDB connection URL.');
|
||||
throw new Error('Error: missing MongoDB connection URL.')
|
||||
}
|
||||
|
||||
let urlToConnect = this.url;
|
||||
let successfulConnectionMessage = 'Connected to MongoDB server successfully!';
|
||||
let urlToConnect = this.url
|
||||
let successfulConnectionMessage = 'Connected to MongoDB server successfully!'
|
||||
|
||||
const connectionOptions: ConnectOptions & { useFacet: undefined } = {
|
||||
autoIndex: true,
|
||||
...this.connectOptions,
|
||||
useFacet: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
if (process.env.PAYLOAD_TEST_MONGO_URL) {
|
||||
urlToConnect = process.env.PAYLOAD_TEST_MONGO_URL;
|
||||
urlToConnect = process.env.PAYLOAD_TEST_MONGO_URL
|
||||
} else {
|
||||
connectionOptions.dbName = 'payloadmemory';
|
||||
const { MongoMemoryServer } = require('mongodb-memory-server');
|
||||
const getPort = require('get-port');
|
||||
connectionOptions.dbName = 'payloadmemory'
|
||||
const { MongoMemoryServer } = require('mongodb-memory-server')
|
||||
const getPort = require('get-port')
|
||||
|
||||
const port = await getPort();
|
||||
const port = await getPort()
|
||||
this.mongoMemoryServer = await MongoMemoryServer.create({
|
||||
instance: {
|
||||
dbName: 'payloadmemory',
|
||||
port,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
urlToConnect = this.mongoMemoryServer.getUri();
|
||||
successfulConnectionMessage = 'Connected to in-memory MongoDB server successfully!';
|
||||
urlToConnect = this.mongoMemoryServer.getUri()
|
||||
successfulConnectionMessage = 'Connected to in-memory MongoDB server successfully!'
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.connection = (
|
||||
await mongoose.connect(urlToConnect, connectionOptions)
|
||||
).connection;
|
||||
this.connection = (await mongoose.connect(urlToConnect, connectionOptions)).connection
|
||||
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
this.payload.logger.info('---- DROPPING DATABASE ----');
|
||||
await mongoose.connection.dropDatabase();
|
||||
this.payload.logger.info('---- DROPPED DATABASE ----');
|
||||
this.payload.logger.info('---- DROPPING DATABASE ----')
|
||||
await mongoose.connection.dropDatabase()
|
||||
this.payload.logger.info('---- DROPPED DATABASE ----')
|
||||
}
|
||||
this.payload.logger.info(successfulConnectionMessage);
|
||||
this.payload.logger.info(successfulConnectionMessage)
|
||||
} catch (err) {
|
||||
this.payload.logger.error(
|
||||
`Error: cannot connect to MongoDB. Details: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
process.exit(1);
|
||||
this.payload.logger.error(`Error: cannot connect to MongoDB. Details: ${err.message}`, err)
|
||||
process.exit(1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { Create } from 'payload/database';
|
||||
import type { Document } from 'payload/types';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { Create } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const create: Create = async function create(
|
||||
this: MongooseAdapter,
|
||||
{ collection, data, req = {} as PayloadRequest },
|
||||
) {
|
||||
const Model = this.collections[collection];
|
||||
const options = withSession(this, req.transactionID);
|
||||
const Model = this.collections[collection]
|
||||
const options = withSession(this, req.transactionID)
|
||||
|
||||
const [doc] = await Model.create([data], options);
|
||||
const [doc] = await Model.create([data], options)
|
||||
|
||||
// doc.toJSON does not do stuff like converting ObjectIds to string, or date strings to date objects. That's why we use JSON.parse/stringify here
|
||||
const result: Document = JSON.parse(JSON.stringify(doc));
|
||||
const verificationToken = doc._verificationToken;
|
||||
const result: Document = JSON.parse(JSON.stringify(doc))
|
||||
const verificationToken = doc._verificationToken
|
||||
|
||||
// custom id type reset
|
||||
result.id = result._id;
|
||||
result.id = result._id
|
||||
if (verificationToken) {
|
||||
result._verificationToken = verificationToken;
|
||||
result._verificationToken = verificationToken
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import { CreateGlobal } from 'payload/database';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import { withSession } from './withSession';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import type { CreateGlobal } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const createGlobal: CreateGlobal = async function createGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ data, slug, req = {} as PayloadRequest },
|
||||
{ data, req = {} as PayloadRequest, slug },
|
||||
) {
|
||||
const Model = this.globals;
|
||||
const Model = this.globals
|
||||
const global = {
|
||||
globalType: slug,
|
||||
...data,
|
||||
};
|
||||
const options = withSession(this, req.transactionID);
|
||||
}
|
||||
const options = withSession(this, req.transactionID)
|
||||
|
||||
let [result] = (await Model.create([global], options)) as any;
|
||||
let [result] = (await Model.create([global], options)) as any
|
||||
|
||||
result = JSON.parse(JSON.stringify(result));
|
||||
result = JSON.parse(JSON.stringify(result))
|
||||
|
||||
// custom id type reset
|
||||
result.id = result._id;
|
||||
result = sanitizeInternalFields(result);
|
||||
result.id = result._id
|
||||
result = sanitizeInternalFields(result)
|
||||
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,45 +1,47 @@
|
||||
import type { CreateVersion } from 'payload/database';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import type { Document } from 'payload/types';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { CreateVersion } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const createVersion: CreateVersion = async function createVersion(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
collectionSlug,
|
||||
parent,
|
||||
versionData,
|
||||
autosave,
|
||||
collectionSlug,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
parent,
|
||||
req = {} as PayloadRequest,
|
||||
updatedAt,
|
||||
versionData,
|
||||
},
|
||||
) {
|
||||
const VersionModel = this.versions[collectionSlug];
|
||||
const options = withSession(this, req.transactionID);
|
||||
const VersionModel = this.versions[collectionSlug]
|
||||
const options = withSession(this, req.transactionID)
|
||||
|
||||
const [doc] = await VersionModel.create(
|
||||
[
|
||||
{
|
||||
parent,
|
||||
version: versionData,
|
||||
autosave,
|
||||
createdAt,
|
||||
parent,
|
||||
updatedAt,
|
||||
version: versionData,
|
||||
},
|
||||
],
|
||||
options,
|
||||
req,
|
||||
);
|
||||
)
|
||||
|
||||
const result: Document = JSON.parse(JSON.stringify(doc));
|
||||
const verificationToken = doc._verificationToken;
|
||||
const result: Document = JSON.parse(JSON.stringify(doc))
|
||||
const verificationToken = doc._verificationToken
|
||||
|
||||
// custom id type reset
|
||||
result.id = result._id;
|
||||
result.id = result._id
|
||||
if (verificationToken) {
|
||||
result._verificationToken = verificationToken;
|
||||
result._verificationToken = verificationToken
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import { DeleteMany } from 'payload/database';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { DeleteMany } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
export const deleteMany: DeleteMany = async function deleteMany(this: MongooseAdapter,
|
||||
{ collection, where, req = {} as PayloadRequest }) {
|
||||
const Model = this.collections[collection];
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: MongooseAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const options = {
|
||||
...withSession(this, req.transactionID),
|
||||
lean: true,
|
||||
};
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
payload: this.payload,
|
||||
where,
|
||||
});
|
||||
})
|
||||
|
||||
await Model.deleteMany(query, options);
|
||||
};
|
||||
await Model.deleteMany(query, options)
|
||||
}
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import { DeleteOne } from 'payload/database';
|
||||
import type { Document } from 'payload/types';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { DeleteOne } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: MongooseAdapter,
|
||||
{ collection, where, req = {} as PayloadRequest },
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const Model = this.collections[collection];
|
||||
const options = withSession(this, req.transactionID);
|
||||
const Model = this.collections[collection]
|
||||
const options = withSession(this, req.transactionID)
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
payload: this.payload,
|
||||
where,
|
||||
});
|
||||
})
|
||||
|
||||
const doc = await Model.findOneAndDelete(query, options).lean();
|
||||
const doc = await Model.findOneAndDelete(query, options).lean()
|
||||
|
||||
let result: Document = JSON.parse(JSON.stringify(doc));
|
||||
let result: Document = JSON.parse(JSON.stringify(doc))
|
||||
|
||||
// custom id type reset
|
||||
result.id = result._id;
|
||||
result = sanitizeInternalFields(result);
|
||||
result.id = result._id
|
||||
result = sanitizeInternalFields(result)
|
||||
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import { DeleteVersions } from 'payload/database';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { DeleteVersions } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
export const deleteVersions: DeleteVersions = async function deleteVersions(this: MongooseAdapter,
|
||||
{ collection, where, locale, req = {} as PayloadRequest }) {
|
||||
const VersionsModel = this.versions[collection];
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const deleteVersions: DeleteVersions = async function deleteVersions(
|
||||
this: MongooseAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const VersionsModel = this.versions[collection]
|
||||
const options = {
|
||||
...withSession(this, req.transactionID),
|
||||
lean: true,
|
||||
};
|
||||
}
|
||||
|
||||
const query = await VersionsModel.buildQuery({
|
||||
payload: this.payload,
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where,
|
||||
});
|
||||
})
|
||||
|
||||
await VersionsModel.deleteMany(query, options);
|
||||
};
|
||||
await VersionsModel.deleteMany(query, options)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { Destroy } from 'payload/database';
|
||||
import { MongooseAdapter } from './index';
|
||||
import type { Destroy } from 'payload/database'
|
||||
|
||||
export const destroy: Destroy = async function destroy(
|
||||
this: MongooseAdapter,
|
||||
) {
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
import type { MongooseAdapter } from './index'
|
||||
|
||||
export const destroy: Destroy = async function destroy(this: MongooseAdapter) {
|
||||
if (this.mongoMemoryServer) {
|
||||
await mongoose.connection.dropDatabase();
|
||||
await mongoose.connection.close();
|
||||
await this.mongoMemoryServer.stop();
|
||||
await mongoose.connection.dropDatabase()
|
||||
await mongoose.connection.close()
|
||||
await this.mongoMemoryServer.stop()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,79 +1,73 @@
|
||||
import type { PaginateOptions } from 'mongoose';
|
||||
import type { Find } from 'payload/database';
|
||||
import { flattenWhereToOperators } from 'payload/database';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import { buildSortParam } from './queries/buildSortParam';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { Find } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { flattenWhereToOperators } from 'payload/database'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam'
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const find: Find = async function find(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
collection,
|
||||
where,
|
||||
page,
|
||||
limit,
|
||||
sort: sortArg,
|
||||
locale,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
},
|
||||
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
|
||||
) {
|
||||
const Model = this.collections[collection];
|
||||
const collectionConfig = this.payload.collections[collection].config;
|
||||
const options = withSession(this, req.transactionID);
|
||||
const Model = this.collections[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const options = withSession(this, req.transactionID)
|
||||
|
||||
let hasNearConstraint = false;
|
||||
let hasNearConstraint = false
|
||||
|
||||
if (where) {
|
||||
const constraints = flattenWhereToOperators(where);
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'));
|
||||
const constraints = flattenWhereToOperators(where)
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
|
||||
}
|
||||
|
||||
let sort;
|
||||
let sort
|
||||
if (!hasNearConstraint) {
|
||||
sort = buildSortParam({
|
||||
sort: sortArg || collectionConfig.defaultSort,
|
||||
fields: collectionConfig.fields,
|
||||
timestamps: true,
|
||||
config: this.payload.config,
|
||||
fields: collectionConfig.fields,
|
||||
locale,
|
||||
});
|
||||
sort: sortArg || collectionConfig.defaultSort,
|
||||
timestamps: true,
|
||||
})
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
payload: this.payload,
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where,
|
||||
});
|
||||
})
|
||||
|
||||
const paginationOptions: PaginateOptions = {
|
||||
page,
|
||||
sort,
|
||||
forceCountFn: hasNearConstraint,
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
useEstimatedCount: hasNearConstraint,
|
||||
forceCountFn: hasNearConstraint,
|
||||
pagination,
|
||||
options,
|
||||
};
|
||||
|
||||
if (limit > 0) {
|
||||
paginationOptions.limit = limit;
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit;
|
||||
page,
|
||||
pagination,
|
||||
sort,
|
||||
useEstimatedCount: hasNearConstraint,
|
||||
}
|
||||
|
||||
const result = await Model.paginate(query, paginationOptions);
|
||||
const docs = JSON.parse(JSON.stringify(result.docs));
|
||||
if (limit > 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
}
|
||||
|
||||
const result = await Model.paginate(query, paginationOptions)
|
||||
const docs = JSON.parse(JSON.stringify(result.docs))
|
||||
|
||||
return {
|
||||
...result,
|
||||
docs: docs.map((doc) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
doc.id = doc._id;
|
||||
return sanitizeInternalFields(doc);
|
||||
doc.id = doc._id
|
||||
return sanitizeInternalFields(doc)
|
||||
}),
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
import { combineQueries } from 'payload/database';
|
||||
import type { FindGlobal } from 'payload/database';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { FindGlobal } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { combineQueries } from 'payload/database'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ slug, locale, where, req = {} as PayloadRequest },
|
||||
{ locale, req = {} as PayloadRequest, slug, where },
|
||||
) {
|
||||
const Model = this.globals;
|
||||
const Model = this.globals
|
||||
const options = {
|
||||
...withSession(this, req.transactionID),
|
||||
lean: true,
|
||||
};
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
where: combineQueries({ globalType: { equals: slug } }, where),
|
||||
payload: this.payload,
|
||||
locale,
|
||||
globalSlug: slug,
|
||||
});
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where: combineQueries({ globalType: { equals: slug } }, where),
|
||||
})
|
||||
|
||||
let doc = (await Model.findOne(query, {}, options)) as any;
|
||||
let doc = (await Model.findOne(query, {}, options)) as any
|
||||
|
||||
if (!doc) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
if (doc._id) {
|
||||
doc.id = doc._id;
|
||||
delete doc._id;
|
||||
doc.id = doc._id
|
||||
delete doc._id
|
||||
}
|
||||
|
||||
doc = JSON.parse(JSON.stringify(doc));
|
||||
doc = sanitizeInternalFields(doc);
|
||||
doc = JSON.parse(JSON.stringify(doc))
|
||||
doc = sanitizeInternalFields(doc)
|
||||
|
||||
return doc;
|
||||
};
|
||||
return doc
|
||||
}
|
||||
|
||||
@@ -1,89 +1,92 @@
|
||||
import { PaginateOptions } from 'mongoose';
|
||||
import type { FindGlobalVersions } from 'payload/database';
|
||||
import { flattenWhereToOperators } from 'payload/database';
|
||||
import { buildVersionGlobalFields } from 'payload/versions';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { buildSortParam } from './queries/buildSortParam';
|
||||
import { withSession } from './withSession';
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { FindGlobalVersions } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { flattenWhereToOperators } from 'payload/database'
|
||||
import { buildVersionGlobalFields } from 'payload/versions'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam'
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
global,
|
||||
where,
|
||||
page,
|
||||
limit,
|
||||
sort: sortArg,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
skip,
|
||||
req = {} as PayloadRequest,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
const Model = this.versions[global];
|
||||
const Model = this.versions[global]
|
||||
const versionFields = buildVersionGlobalFields(
|
||||
this.payload.globals.config.find(({ slug }) => slug === global),
|
||||
);
|
||||
)
|
||||
const options = {
|
||||
...withSession(this, req.transactionID),
|
||||
skip,
|
||||
limit,
|
||||
};
|
||||
|
||||
let hasNearConstraint = false;
|
||||
|
||||
if (where) {
|
||||
const constraints = flattenWhereToOperators(where);
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'));
|
||||
skip,
|
||||
}
|
||||
|
||||
let sort;
|
||||
let hasNearConstraint = false
|
||||
|
||||
if (where) {
|
||||
const constraints = flattenWhereToOperators(where)
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
|
||||
}
|
||||
|
||||
let sort
|
||||
if (!hasNearConstraint) {
|
||||
sort = buildSortParam({
|
||||
sort: sortArg || '-updatedAt',
|
||||
fields: versionFields,
|
||||
timestamps: true,
|
||||
config: this.payload.config,
|
||||
fields: versionFields,
|
||||
locale,
|
||||
});
|
||||
sort: sortArg || '-updatedAt',
|
||||
timestamps: true,
|
||||
})
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
payload: this.payload,
|
||||
locale,
|
||||
where,
|
||||
globalSlug: global,
|
||||
});
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where,
|
||||
})
|
||||
|
||||
const paginationOptions: PaginateOptions = {
|
||||
page,
|
||||
sort,
|
||||
forceCountFn: hasNearConstraint,
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
pagination,
|
||||
offset: skip,
|
||||
useEstimatedCount: hasNearConstraint,
|
||||
forceCountFn: hasNearConstraint,
|
||||
options,
|
||||
};
|
||||
|
||||
if (limit > 0) {
|
||||
paginationOptions.limit = limit;
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit;
|
||||
page,
|
||||
pagination,
|
||||
sort,
|
||||
useEstimatedCount: hasNearConstraint,
|
||||
}
|
||||
|
||||
const result = await Model.paginate(query, paginationOptions);
|
||||
const docs = JSON.parse(JSON.stringify(result.docs));
|
||||
if (limit > 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
}
|
||||
|
||||
const result = await Model.paginate(query, paginationOptions)
|
||||
const docs = JSON.parse(JSON.stringify(result.docs))
|
||||
|
||||
return {
|
||||
...result,
|
||||
docs: docs.map((doc) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
doc.id = doc._id;
|
||||
return sanitizeInternalFields(doc);
|
||||
doc.id = doc._id
|
||||
return sanitizeInternalFields(doc)
|
||||
}),
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
import type { MongooseQueryOptions } from 'mongoose';
|
||||
import type { FindOne } from 'payload/database';
|
||||
import type { Document } from 'payload/types';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { withSession } from './withSession';
|
||||
import type { MongooseQueryOptions } from 'mongoose'
|
||||
import type { FindOne } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const findOne: FindOne = async function findOne(
|
||||
this: MongooseAdapter,
|
||||
{ collection, where, locale, req = {} as PayloadRequest },
|
||||
{ collection, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const Model = this.collections[collection];
|
||||
const Model = this.collections[collection]
|
||||
const options: MongooseQueryOptions = {
|
||||
...withSession(this, req.transactionID),
|
||||
lean: true,
|
||||
};
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
payload: this.payload,
|
||||
locale,
|
||||
where,
|
||||
});
|
||||
|
||||
const doc = await Model.findOne(query, {}, options);
|
||||
|
||||
if (!doc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let result: Document = JSON.parse(JSON.stringify(doc));
|
||||
const query = await Model.buildQuery({
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where,
|
||||
})
|
||||
|
||||
const doc = await Model.findOne(query, {}, options)
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
}
|
||||
|
||||
let result: Document = JSON.parse(JSON.stringify(doc))
|
||||
|
||||
// custom id type reset
|
||||
result.id = result._id;
|
||||
result = sanitizeInternalFields(result);
|
||||
result.id = result._id
|
||||
result = sanitizeInternalFields(result)
|
||||
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,86 +1,89 @@
|
||||
import { PaginateOptions } from 'mongoose';
|
||||
import type { FindVersions } from 'payload/database';
|
||||
import { flattenWhereToOperators } from 'payload/database';
|
||||
import { PayloadRequest } from 'payload/types';
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { buildSortParam } from './queries/buildSortParam';
|
||||
import { withSession } from './withSession';
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { FindVersions } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { flattenWhereToOperators } from 'payload/database'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam'
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const findVersions: FindVersions = async function findVersions(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
collection,
|
||||
where,
|
||||
page,
|
||||
limit,
|
||||
sort: sortArg,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
skip,
|
||||
req = {} as PayloadRequest,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
const Model = this.versions[collection];
|
||||
const collectionConfig = this.payload.collections[collection].config;
|
||||
const Model = this.versions[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const options = {
|
||||
...withSession(this, req.transactionID),
|
||||
skip,
|
||||
limit,
|
||||
};
|
||||
|
||||
let hasNearConstraint = false;
|
||||
|
||||
if (where) {
|
||||
const constraints = flattenWhereToOperators(where);
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'));
|
||||
skip,
|
||||
}
|
||||
|
||||
let sort;
|
||||
let hasNearConstraint = false
|
||||
|
||||
if (where) {
|
||||
const constraints = flattenWhereToOperators(where)
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
|
||||
}
|
||||
|
||||
let sort
|
||||
if (!hasNearConstraint) {
|
||||
sort = buildSortParam({
|
||||
sort: sortArg || '-updatedAt',
|
||||
fields: collectionConfig.fields,
|
||||
timestamps: true,
|
||||
config: this.payload.config,
|
||||
fields: collectionConfig.fields,
|
||||
locale,
|
||||
});
|
||||
sort: sortArg || '-updatedAt',
|
||||
timestamps: true,
|
||||
})
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
payload: this.payload,
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where,
|
||||
});
|
||||
})
|
||||
|
||||
const paginationOptions: PaginateOptions = {
|
||||
page,
|
||||
sort,
|
||||
limit,
|
||||
forceCountFn: hasNearConstraint,
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
pagination,
|
||||
limit,
|
||||
offset: skip,
|
||||
useEstimatedCount: hasNearConstraint,
|
||||
forceCountFn: hasNearConstraint,
|
||||
options,
|
||||
};
|
||||
|
||||
if (limit > 0) {
|
||||
paginationOptions.limit = limit;
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit;
|
||||
page,
|
||||
pagination,
|
||||
sort,
|
||||
useEstimatedCount: hasNearConstraint,
|
||||
}
|
||||
|
||||
const result = await Model.paginate(query, paginationOptions);
|
||||
const docs = JSON.parse(JSON.stringify(result.docs));
|
||||
if (limit > 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
}
|
||||
|
||||
const result = await Model.paginate(query, paginationOptions)
|
||||
const docs = JSON.parse(JSON.stringify(result.docs))
|
||||
|
||||
return {
|
||||
...result,
|
||||
docs: docs.map((doc) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
doc.id = doc._id;
|
||||
return sanitizeInternalFields(doc);
|
||||
doc.id = doc._id
|
||||
return sanitizeInternalFields(doc)
|
||||
}),
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,108 +1,111 @@
|
||||
import type { ClientSession, Connection, ConnectOptions } from 'mongoose';
|
||||
import mongoose from 'mongoose';
|
||||
import { createMigration } from 'payload/database';
|
||||
import type { Payload } from 'payload';
|
||||
import type { DatabaseAdapter } from 'payload/database';
|
||||
import { createDatabaseAdapter } from 'payload/database';
|
||||
import { connect } from './connect';
|
||||
import { init } from './init';
|
||||
import { webpack } from './webpack';
|
||||
import { createGlobal } from './createGlobal';
|
||||
import { createVersion } from './createVersion';
|
||||
import { beginTransaction } from './transactions/beginTransaction';
|
||||
import { rollbackTransaction } from './transactions/rollbackTransaction';
|
||||
import { commitTransaction } from './transactions/commitTransaction';
|
||||
import { queryDrafts } from './queryDrafts';
|
||||
import { find } from './find';
|
||||
import { findGlobalVersions } from './findGlobalVersions';
|
||||
import { findVersions } from './findVersions';
|
||||
import { create } from './create';
|
||||
import { deleteOne } from './deleteOne';
|
||||
import { deleteVersions } from './deleteVersions';
|
||||
import { findGlobal } from './findGlobal';
|
||||
import { findOne } from './findOne';
|
||||
import { updateGlobal } from './updateGlobal';
|
||||
import { updateOne } from './updateOne';
|
||||
import { updateVersion } from './updateVersion';
|
||||
import { deleteMany } from './deleteMany';
|
||||
import { destroy } from './destroy';
|
||||
import type { CollectionModel, GlobalModel } from './types';
|
||||
import type { ClientSession, ConnectOptions, Connection } from 'mongoose'
|
||||
import type { Payload } from 'payload'
|
||||
import type { DatabaseAdapter } from 'payload/database'
|
||||
|
||||
import mongoose from 'mongoose'
|
||||
import { createDatabaseAdapter } from 'payload/database'
|
||||
import { createMigration } from 'payload/database'
|
||||
|
||||
import type { CollectionModel, GlobalModel } from './types'
|
||||
|
||||
import { connect } from './connect'
|
||||
import { create } from './create'
|
||||
import { createGlobal } from './createGlobal'
|
||||
import { createVersion } from './createVersion'
|
||||
import { deleteMany } from './deleteMany'
|
||||
import { deleteOne } from './deleteOne'
|
||||
import { deleteVersions } from './deleteVersions'
|
||||
import { destroy } from './destroy'
|
||||
import { find } from './find'
|
||||
import { findGlobal } from './findGlobal'
|
||||
import { findGlobalVersions } from './findGlobalVersions'
|
||||
import { findOne } from './findOne'
|
||||
import { findVersions } from './findVersions'
|
||||
import { init } from './init'
|
||||
import { queryDrafts } from './queryDrafts'
|
||||
import { beginTransaction } from './transactions/beginTransaction'
|
||||
import { commitTransaction } from './transactions/commitTransaction'
|
||||
import { rollbackTransaction } from './transactions/rollbackTransaction'
|
||||
import { updateGlobal } from './updateGlobal'
|
||||
import { updateOne } from './updateOne'
|
||||
import { updateVersion } from './updateVersion'
|
||||
import { webpack } from './webpack'
|
||||
|
||||
export interface Args {
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
url: string | false;
|
||||
migrationDir?: string;
|
||||
/** Set to false to disable auto-pluralization of collection names, Defaults to true */
|
||||
autoPluralization?: boolean
|
||||
/** Extra configuration options */
|
||||
connectOptions?: ConnectOptions & {
|
||||
/** Set false to disable $facet aggregation in non-supporting databases, Defaults to true */
|
||||
useFacet?: boolean;
|
||||
};
|
||||
/** Set to false to disable auto-pluralization of collection names, Defaults to true */
|
||||
autoPluralization?: boolean;
|
||||
useFacet?: boolean
|
||||
}
|
||||
migrationDir?: string
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
url: false | string
|
||||
}
|
||||
|
||||
export type MongooseAdapter = DatabaseAdapter &
|
||||
Args & {
|
||||
mongoMemoryServer: any;
|
||||
collections: {
|
||||
[slug: string]: CollectionModel;
|
||||
};
|
||||
globals: GlobalModel;
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
mongoMemoryServer: any
|
||||
sessions: Record<number | string, ClientSession>
|
||||
versions: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
sessions: Record<string | number, ClientSession>
|
||||
connection: Connection
|
||||
}
|
||||
|
||||
type MongooseAdapterResult = (args: { payload: Payload }) => MongooseAdapter
|
||||
|
||||
export function mongooseAdapter({
|
||||
url,
|
||||
autoPluralization = true,
|
||||
connectOptions,
|
||||
migrationDir,
|
||||
autoPluralization = true,
|
||||
url,
|
||||
}: Args): MongooseAdapterResult {
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
mongoose.set('strictQuery', false);
|
||||
mongoose.set('strictQuery', false)
|
||||
|
||||
return createDatabaseAdapter<MongooseAdapter>({
|
||||
payload,
|
||||
migrationDir,
|
||||
connection: undefined,
|
||||
mongoMemoryServer: undefined,
|
||||
sessions: {},
|
||||
url,
|
||||
connectOptions: connectOptions || {},
|
||||
autoPluralization,
|
||||
globals: undefined,
|
||||
collections: {},
|
||||
versions: {},
|
||||
connect,
|
||||
destroy,
|
||||
init,
|
||||
webpack,
|
||||
createMigration,
|
||||
beginTransaction,
|
||||
rollbackTransaction,
|
||||
collections: {},
|
||||
commitTransaction,
|
||||
queryDrafts,
|
||||
findOne,
|
||||
find,
|
||||
connect,
|
||||
connectOptions: connectOptions || {},
|
||||
connection: undefined,
|
||||
create,
|
||||
updateOne,
|
||||
deleteOne,
|
||||
deleteMany,
|
||||
findGlobal,
|
||||
createGlobal,
|
||||
updateGlobal,
|
||||
findVersions,
|
||||
findGlobalVersions,
|
||||
createMigration,
|
||||
createVersion,
|
||||
updateVersion,
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
});
|
||||
destroy,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
findOne,
|
||||
findVersions,
|
||||
globals: undefined,
|
||||
init,
|
||||
migrationDir,
|
||||
mongoMemoryServer: undefined,
|
||||
payload,
|
||||
queryDrafts,
|
||||
rollbackTransaction,
|
||||
sessions: {},
|
||||
updateGlobal,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
url,
|
||||
versions: {},
|
||||
webpack,
|
||||
})
|
||||
}
|
||||
|
||||
return adapter;
|
||||
return adapter
|
||||
}
|
||||
|
||||
@@ -1,43 +1,40 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import mongoose, { PaginateOptions } from 'mongoose';
|
||||
import paginate from 'mongoose-paginate-v2';
|
||||
import mongooseAggregatePaginate from 'mongoose-aggregate-paginate-v2';
|
||||
import { buildVersionCollectionFields } from 'payload/versions';
|
||||
import { SanitizedCollectionConfig } from 'payload/types';
|
||||
import { getVersionsModelName } from 'payload/versions';
|
||||
import { buildVersionGlobalFields } from 'payload/versions';
|
||||
import type { Init } from 'payload/database';
|
||||
import getBuildQueryPlugin from './queries/buildQuery';
|
||||
import buildCollectionSchema from './models/buildCollectionSchema';
|
||||
import buildSchema from './models/buildSchema';
|
||||
import type { MongooseAdapter } from '.';
|
||||
import { buildGlobalModel } from './models/buildGlobalModel';
|
||||
import { CollectionModel } from './types';
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { Init } from 'payload/database'
|
||||
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
export const init: Init = async function init(
|
||||
this: MongooseAdapter,
|
||||
) {
|
||||
this.payload.config.collections.forEach(
|
||||
(collection: SanitizedCollectionConfig) => {
|
||||
const schema = buildCollectionSchema(collection, this.payload.config);
|
||||
import mongoose from 'mongoose'
|
||||
import mongooseAggregatePaginate from 'mongoose-aggregate-paginate-v2'
|
||||
import paginate from 'mongoose-paginate-v2'
|
||||
import { buildVersionGlobalFields } from 'payload/versions'
|
||||
import { buildVersionCollectionFields } from 'payload/versions'
|
||||
import { getVersionsModelName } from 'payload/versions'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
import type { CollectionModel } from './types'
|
||||
|
||||
import buildCollectionSchema from './models/buildCollectionSchema'
|
||||
import { buildGlobalModel } from './models/buildGlobalModel'
|
||||
import buildSchema from './models/buildSchema'
|
||||
import getBuildQueryPlugin from './queries/buildQuery'
|
||||
|
||||
export const init: Init = async function init(this: MongooseAdapter) {
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
const schema = buildCollectionSchema(collection, this.payload.config)
|
||||
|
||||
if (collection.versions) {
|
||||
const versionModelName = getVersionsModelName(collection);
|
||||
const versionModelName = getVersionsModelName(collection)
|
||||
|
||||
const versionCollectionFields = buildVersionCollectionFields(collection);
|
||||
const versionCollectionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
const versionSchema = buildSchema(
|
||||
this.payload.config,
|
||||
versionCollectionFields,
|
||||
{
|
||||
const versionSchema = buildSchema(this.payload.config, versionCollectionFields, {
|
||||
disableUnique: true,
|
||||
draftsEnabled: true,
|
||||
options: {
|
||||
timestamps: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
})
|
||||
|
||||
if (collection.indexes) {
|
||||
collection.indexes.forEach((index) => {
|
||||
@@ -45,85 +42,78 @@ export const init: Init = async function init(
|
||||
const versionIndex = {
|
||||
fields: {},
|
||||
options: index.options,
|
||||
};
|
||||
Object.entries(index.fields)
|
||||
.forEach(([key, value]) => {
|
||||
versionIndex.fields[`version.${key}`] = value;
|
||||
});
|
||||
versionSchema.index(versionIndex.fields, versionIndex.options);
|
||||
});
|
||||
}
|
||||
Object.entries(index.fields).forEach(([key, value]) => {
|
||||
versionIndex.fields[`version.${key}`] = value
|
||||
})
|
||||
versionSchema.index(versionIndex.fields, versionIndex.options)
|
||||
})
|
||||
}
|
||||
|
||||
versionSchema.plugin<any, PaginateOptions>(paginate, { useEstimatedCount: true })
|
||||
.plugin(
|
||||
versionSchema.plugin<any, PaginateOptions>(paginate, { useEstimatedCount: true }).plugin(
|
||||
getBuildQueryPlugin({
|
||||
collectionSlug: collection.slug,
|
||||
versionsFields: versionCollectionFields,
|
||||
}),
|
||||
);
|
||||
)
|
||||
|
||||
if (collection.versions?.drafts) {
|
||||
versionSchema.plugin(mongooseAggregatePaginate);
|
||||
versionSchema.plugin(mongooseAggregatePaginate)
|
||||
}
|
||||
|
||||
const model = mongoose.model(
|
||||
versionModelName,
|
||||
versionSchema,
|
||||
versionModelName,
|
||||
) as CollectionModel;
|
||||
) as CollectionModel
|
||||
// this.payload.versions[collection.slug] = model;
|
||||
this.versions[collection.slug] = model;
|
||||
this.versions[collection.slug] = model
|
||||
}
|
||||
|
||||
const model = mongoose.model(
|
||||
collection.slug,
|
||||
schema,
|
||||
this.autoPluralization === true ? undefined : collection.slug,
|
||||
) as CollectionModel;
|
||||
this.collections[collection.slug] = model;
|
||||
) as CollectionModel
|
||||
this.collections[collection.slug] = model
|
||||
|
||||
// TS expect error only needed until we launch 2.0.0
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
this.payload.collections[collection.slug] = {
|
||||
config: collection,
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
const model = buildGlobalModel(this.payload.config);
|
||||
this.globals = model;
|
||||
const model = buildGlobalModel(this.payload.config)
|
||||
this.globals = model
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
if (global.versions) {
|
||||
const versionModelName = getVersionsModelName(global);
|
||||
const versionModelName = getVersionsModelName(global)
|
||||
|
||||
const versionGlobalFields = buildVersionGlobalFields(global);
|
||||
const versionGlobalFields = buildVersionGlobalFields(global)
|
||||
|
||||
const versionSchema = buildSchema(
|
||||
this.payload.config,
|
||||
versionGlobalFields,
|
||||
{
|
||||
indexSortableFields: this.payload.config.indexSortableFields,
|
||||
const versionSchema = buildSchema(this.payload.config, versionGlobalFields, {
|
||||
disableUnique: true,
|
||||
draftsEnabled: true,
|
||||
indexSortableFields: this.payload.config.indexSortableFields,
|
||||
options: {
|
||||
timestamps: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
})
|
||||
|
||||
versionSchema
|
||||
.plugin<any, PaginateOptions>(paginate, { useEstimatedCount: true })
|
||||
.plugin(getBuildQueryPlugin({ versionsFields: versionGlobalFields }));
|
||||
.plugin(getBuildQueryPlugin({ versionsFields: versionGlobalFields }))
|
||||
|
||||
const versionsModel = mongoose.model(
|
||||
versionModelName,
|
||||
versionSchema,
|
||||
versionModelName,
|
||||
) as CollectionModel;
|
||||
this.versions[global.slug] = versionsModel;
|
||||
) as CollectionModel
|
||||
this.versions[global.slug] = versionsModel
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
exports.mongooseAdapter = () => ({});
|
||||
exports.mongooseAdapter = () => ({})
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
import paginate from 'mongoose-paginate-v2';
|
||||
import { PaginateOptions, Schema } from 'mongoose';
|
||||
import { SanitizedConfig } from 'payload/config';
|
||||
import { SanitizedCollectionConfig } from 'payload/types';
|
||||
import getBuildQueryPlugin from '../queries/buildQuery';
|
||||
import buildSchema from './buildSchema';
|
||||
import type { PaginateOptions, Schema } from 'mongoose'
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
const buildCollectionSchema = (collection: SanitizedCollectionConfig, config: SanitizedConfig, schemaOptions = {}): Schema => {
|
||||
const schema = buildSchema(
|
||||
config,
|
||||
collection.fields,
|
||||
{
|
||||
import paginate from 'mongoose-paginate-v2'
|
||||
|
||||
import getBuildQueryPlugin from '../queries/buildQuery'
|
||||
import buildSchema from './buildSchema'
|
||||
|
||||
const buildCollectionSchema = (
|
||||
collection: SanitizedCollectionConfig,
|
||||
config: SanitizedConfig,
|
||||
schemaOptions = {},
|
||||
): Schema => {
|
||||
const schema = buildSchema(config, collection.fields, {
|
||||
draftsEnabled: Boolean(typeof collection?.versions === 'object' && collection.versions.drafts),
|
||||
indexSortableFields: config.indexSortableFields,
|
||||
options: {
|
||||
timestamps: collection.timestamps !== false,
|
||||
minimize: false,
|
||||
timestamps: collection.timestamps !== false,
|
||||
...schemaOptions,
|
||||
},
|
||||
indexSortableFields: config.indexSortableFields,
|
||||
},
|
||||
);
|
||||
})
|
||||
|
||||
if (config.indexSortableFields && collection.timestamps !== false) {
|
||||
schema.index({ updatedAt: 1 });
|
||||
schema.index({ createdAt: 1 });
|
||||
schema.index({ updatedAt: 1 })
|
||||
schema.index({ createdAt: 1 })
|
||||
}
|
||||
if (collection.indexes) {
|
||||
collection.indexes.forEach((index) => {
|
||||
schema.index(index.fields, index.options);
|
||||
});
|
||||
schema.index(index.fields, index.options)
|
||||
})
|
||||
}
|
||||
schema.plugin<any, PaginateOptions>(paginate, { useEstimatedCount: true })
|
||||
.plugin(getBuildQueryPlugin({ collectionSlug: collection.slug }));
|
||||
schema
|
||||
.plugin<any, PaginateOptions>(paginate, { useEstimatedCount: true })
|
||||
.plugin(getBuildQueryPlugin({ collectionSlug: collection.slug }))
|
||||
|
||||
return schema;
|
||||
};
|
||||
return schema
|
||||
}
|
||||
|
||||
export default buildCollectionSchema;
|
||||
export default buildCollectionSchema
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { SanitizedConfig } from 'payload/config';
|
||||
import buildSchema from './buildSchema';
|
||||
import getBuildQueryPlugin from '../queries/buildQuery';
|
||||
import type { GlobalModel } from '../types';
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
import type { GlobalModel } from '../types'
|
||||
|
||||
import getBuildQueryPlugin from '../queries/buildQuery'
|
||||
import buildSchema from './buildSchema'
|
||||
|
||||
export const buildGlobalModel = (config: SanitizedConfig): GlobalModel | null => {
|
||||
if (config.globals && config.globals.length > 0) {
|
||||
const globalsSchema = new mongoose.Schema({}, { discriminatorKey: 'globalType', timestamps: true, minimize: false });
|
||||
const globalsSchema = new mongoose.Schema(
|
||||
{},
|
||||
{ discriminatorKey: 'globalType', minimize: false, timestamps: true },
|
||||
)
|
||||
|
||||
globalsSchema.plugin(getBuildQueryPlugin());
|
||||
globalsSchema.plugin(getBuildQueryPlugin())
|
||||
|
||||
const Globals = mongoose.model('globals', globalsSchema, 'globals') as unknown as GlobalModel;
|
||||
const Globals = mongoose.model('globals', globalsSchema, 'globals') as unknown as GlobalModel
|
||||
|
||||
Object.values(config.globals).forEach((globalConfig) => {
|
||||
const globalSchema = buildSchema(
|
||||
config,
|
||||
globalConfig.fields,
|
||||
{
|
||||
const globalSchema = buildSchema(config, globalConfig.fields, {
|
||||
options: {
|
||||
minimize: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
Globals.discriminator(globalConfig.slug, globalSchema);
|
||||
});
|
||||
})
|
||||
Globals.discriminator(globalConfig.slug, globalSchema)
|
||||
})
|
||||
|
||||
return Globals;
|
||||
return Globals
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user