Compare commits
1 Commits
v1.15.4
...
feat/draft
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a77320841 |
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,18 +2,15 @@
|
||||
|
||||
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
|
||||
|
||||
- [ ] I have read and understand the [CONTRIBUTING.md](../CONTRIBUTING.md) document in this repository.
|
||||
- [ ] I have read and understand the CONTRIBUTING.md document in this repository
|
||||
|
||||
## Type of change
|
||||
|
||||
<!-- Please delete options that are not relevant. -->
|
||||
|
||||
- [ ] Chore (non-breaking change which does not add functionality)
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Change to the [templates](../templates/) directory (does not affect core functionality)
|
||||
- [ ] Change to the [examples](../examples/) directory (does not affect core functionality)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
## Checklist:
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x]
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -18,12 +18,5 @@
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "yarn run dev versions",
|
||||
"name": "Debug Versions",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
144
CHANGELOG.md
144
CHANGELOG.md
@@ -1,149 +1,5 @@
|
||||
|
||||
|
||||
## [1.15.4](https://github.com/payloadcms/payload/compare/v1.15.3...v1.15.4) (2023-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **#3274:** sets full user data from fetchFullUser instead of partial jwt data ([#3279](https://github.com/payloadcms/payload/issues/3279)) ([cf12b5f](https://github.com/payloadcms/payload/commit/cf12b5fc703be6341b2b23efebd8aa85e8602567)), closes [#3274](https://github.com/payloadcms/payload/issues/3274)
|
||||
* aligns depth behaviour between local api and admin panel ([#3276](https://github.com/payloadcms/payload/issues/3276)) ([5096c37](https://github.com/payloadcms/payload/commit/5096c378743f4c5eb5f4f2f7e67e5e206cc9da40))
|
||||
* appends versions key to incoming where query ([#3287](https://github.com/payloadcms/payload/issues/3287)) ([9cd5e5a](https://github.com/payloadcms/payload/commit/9cd5e5aefaf0ee2af4f577da9578bb31bf4b0acb))
|
||||
* change scoping of `force` parameter to prevent false negation; ([#3278](https://github.com/payloadcms/payload/issues/3278)) ([429a88a](https://github.com/payloadcms/payload/commit/429a88a5a18e8905c63dfe00b78b3e71d56758fd))
|
||||
|
||||
## [1.15.3](https://github.com/payloadcms/payload/compare/v1.15.2...v1.15.3) (2023-09-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* draft globals always displaying unpublish button ([9bc072c](https://github.com/payloadcms/payload/commit/9bc072ccaf318c61b2c4e2a553604a24ff6a188e))
|
||||
* globals not saving updatedAt and createdAt and version dates correctly ([9fbabc8](https://github.com/payloadcms/payload/commit/9fbabc8fd6a3bea5628bea8d0acc915ddb33bb5c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improves query speed for version enabled collections ([16e94d4](https://github.com/payloadcms/payload/commit/16e94d401bd7cb82de53142c5f9a325abd31a81a))
|
||||
|
||||
## [1.15.2](https://github.com/payloadcms/payload/compare/v1.15.1...v1.15.2) (2023-08-25)
|
||||
|
||||
## [1.15.1](https://github.com/payloadcms/payload/compare/v1.15.0...v1.15.1) (2023-08-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* arrays in richtext uploads ([#3222](https://github.com/payloadcms/payload/issues/3222)) ([cb8e07f](https://github.com/payloadcms/payload/commit/cb8e07f85232a26c265872faf408644424312af6))
|
||||
* correct out of order dark-mode color variables ([#3197](https://github.com/payloadcms/payload/issues/3197)) ([3a15e07](https://github.com/payloadcms/payload/commit/3a15e077c6914aba3ef26e453fee23c89f3db829))
|
||||
* mutation type with tabs missing previous tabs ([#3196](https://github.com/payloadcms/payload/issues/3196)) ([6d3b863](https://github.com/payloadcms/payload/commit/6d3b8636f4e14a4e4155279353fa06e86fe2b25c))
|
||||
|
||||
# [1.15.0](https://github.com/payloadcms/payload/compare/v1.14.0...v1.15.0) (2023-08-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* query support for geo within and intersects + dynamic GraphQL operator types ([#3183](https://github.com/payloadcms/payload/issues/3183)) ([739abdc](https://github.com/payloadcms/payload/commit/739abdcd81176b3e812470eeea97b1be0d8c4a27))
|
||||
|
||||
# [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))
|
||||
|
||||
|
||||
### 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))
|
||||
|
||||
## [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))
|
||||
|
||||
## [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))
|
||||
|
||||
## [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))
|
||||
|
||||
# [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))
|
||||
|
||||
|
||||
### 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))
|
||||
|
||||
# [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))
|
||||
|
||||
|
||||
### 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))
|
||||
|
||||
## [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))
|
||||
|
||||
|
||||
### 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))
|
||||
|
||||
## [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))
|
||||
|
||||
## [1.11.5](https://github.com/payloadcms/payload/compare/v1.11.4...v1.11.5) (2023-07-25)
|
||||
|
||||
|
||||
|
||||
97
README.md
97
README.md
@@ -1,13 +1,37 @@
|
||||
<a href="https://payloadcms.com">
|
||||
<img width="100%" src="src/admin/assets/images/github-banner-alt.jpg" alt="Payload headless CMS Admin panel built with React" />
|
||||
</a>
|
||||
<p style="border: none; margin-bottom:0; padding-bottom: 0;" align="center">
|
||||
<a href="https://payloadcms.com">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/payload-logo-light.svg">
|
||||
<img width="350" alt="Payload Logo" src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/payload-logo-dark.svg">
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3 align="center">The most powerful TypeScript CMS</h3>
|
||||
<p align="center">Code-first Headless CMS that bridges the gap between CMS and application framework</p>
|
||||
|
||||
<h3 align="center">
|
||||
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the docs</strong></a>
|
||||
·
|
||||
<a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a>
|
||||
<br />
|
||||
</h3>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<p align="left">
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" />
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/payloadcms/payload/actions">
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/tests.yml?style=flat-square">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/payloadcms/payload/commits">
|
||||
<img src="https://img.shields.io/github/commit-activity/m/payloadcms/payload?style=flat-square" alt="git commit activity"/>
|
||||
</a>
|
||||
|
||||
<a href="https://discord.gg/payload">
|
||||
<img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" />
|
||||
</a>
|
||||
@@ -20,44 +44,25 @@
|
||||
<img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" />
|
||||
</a>
|
||||
</p>
|
||||
<hr/>
|
||||
<h4>
|
||||
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a> · <a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a> · <a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a> · <a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a> · <a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
|
||||
</h4>
|
||||
<hr/>
|
||||
<h3>Benefits over a regular CMS</h3>
|
||||
<ul>
|
||||
<li>Don’t hit some third-party SaaS API, hit your own API</li>
|
||||
<li>Use your own database and own your data</li>
|
||||
<li>It's just Express - do what you want outside of Payload</li>
|
||||
<li>No need to learn how Payload works - if you know JS, you know Payload</li>
|
||||
<li>No vendor lock-in</li>
|
||||
<li>Avoid microservices hell - get everything (even auth) in one place</li>
|
||||
<li>Never touch ancient WP code again</li>
|
||||
<li>Build faster, never hit a roadblock</li>
|
||||
<li>Both admin and backend are 100% extensible</li>
|
||||
</ul>
|
||||
|
||||
## ☁️ Deploy instantly with Payload Cloud.
|
||||
Create a cloud account, connect your GitHub, and [deploy in minutes](https://payloadcms.com/new).
|
||||
<br />
|
||||
|
||||
## 🚀 Get started by self-hosting completely free, forever.
|
||||
<a href="https://payloadcms.com">
|
||||
<img src="https://cms.payloadcms.com/media/payload-github-header.jpg" alt="Payload headless CMS Admin panel built with React" />
|
||||
</a>
|
||||
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
|
||||
<br />
|
||||
|
||||
```text
|
||||
npx create-payload-app
|
||||
```
|
||||
## ⭐ Why Payload?
|
||||
|
||||
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
|
||||
Payload is a CMS that has been designed for developers from the ground up to deliver them what they need to build great digital products. If you know JavaScript, you know Payload. It's a _code-first_ CMS, which allows us to do a lot of things right:
|
||||
|
||||
## 🖱️ 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.
|
||||
- Payload gives you everything you need, but then steps back and lets you build what you want in JavaScript or TypeScript - with no unnecessary complexity brought by GUIs. You'll understand how your CMS works because you will have written it exactly how you want it.
|
||||
- Bring your own Express server and do whatever you need on top of Payload. Payload doesn't impose anything on you or your app.
|
||||
- Completely control the Admin panel by using your own React components. Swap out fields or even entire views with ease.
|
||||
- Use your data however and wherever you need thanks to auto-generated, yet fully extensible REST, GraphQL, and Local Node APIs.
|
||||
|
||||
[All Official Templates](https://github.com/orgs/payloadcms/repositories?q=topic%3Apayload-template) · [Community Templates](https://github.com/topics/payload-template)
|
||||
|
||||
**If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.**
|
||||
<a target="_blank" href="https://payloadcms.com/" rel="dofollow"><strong>Read more on our website</strong></a>
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@@ -81,6 +86,18 @@ Eliminate the need to combine Shopify and a CMS, and instead do it all with Payl
|
||||
|
||||
<a target="_blank" href="https://github.com/payloadcms/payload/discussions"><strong>Request Feature</strong></a>
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
|
||||
|
||||
From there, the easiest way to get started with Payload is to use the `create-payload-app` package:
|
||||
|
||||
```text
|
||||
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).
|
||||
|
||||
## 🗒️ Documentation
|
||||
|
||||
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
@@ -89,11 +106,13 @@ Check out the [Payload website](https://payloadcms.com/docs/getting-started/what
|
||||
|
||||
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./contributing.md).
|
||||
|
||||
## 📚 Examples
|
||||
## 📚 Examples and Templates
|
||||
|
||||
The examples directory is a great resource for learning how to setup Payload in a variety of different ways.
|
||||
The examples directory is a great resource for learning how to setup Payload in a variety of different ways. There are also a number of templates to help get you going very quickly. If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
|
||||
|
||||
[Examples Directory](./examples)
|
||||
- [Examples Directory](./examples)
|
||||
- [Official Templates](https://github.com/orgs/payloadcms/repositories?q=topic%3Apayload-template)
|
||||
- [Community Templates](https://github.com/topics/payload-template)
|
||||
|
||||
## 🔌 Plugins
|
||||
|
||||
|
||||
@@ -20,12 +20,6 @@ Payload documentation can be found directly within its codebase and you can feel
|
||||
|
||||
If you're an incredibly awesome person and want to help us make Payload even better through new features or additions, we would be thrilled to work with you.
|
||||
|
||||
## Design Contributions
|
||||
|
||||
When it comes to design-related changes or additions, it's crucial for us to ensure a cohesive user experience and alignment with our broader design vision. Before embarking on any implementation that would affect the design or UI/UX, we ask that you **first share your design proposal** with us for review and approval.
|
||||
|
||||
Our design review ensures that proposed changes fit seamlessly with other components, both existing and planned. This step is meant to prevent unintentional design inconsistencies and to save you from investing time in implementing features that might need significant design alterations later.
|
||||
|
||||
### Before Starting
|
||||
|
||||
To help us work on new features, you can create a new feature request post in [GitHub Discussion](https://github.com/payloadcms/payload/discussions) or discuss it in our [Discord](https://discord.com/invite/payload). New functionality often has large implications across the entire Payload repo, so it is best to discuss the architecture and approach before starting work on a pull request.
|
||||
@@ -65,27 +59,6 @@ If you wish to use to your own Mongo database for the `test` directory instead o
|
||||
|
||||
NOTE: 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 E-Mail and `test` as password.
|
||||
|
||||
### Commits
|
||||
|
||||
We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for our commit messages. Please follow this format when creating commits. Here are some examples:
|
||||
|
||||
- `feat: adds new feature`
|
||||
- `fix: fixes bug`
|
||||
- `docs: adds documentation`
|
||||
- `chore: does chore`
|
||||
|
||||
Here's a breakdown of the format. At the top-level, we use the following types to categorize our commits:
|
||||
|
||||
- `feat`: new feature that adds functionality. These are automatically added to the changelog when creating new releases.
|
||||
- `fix`: a fix to an existing feature. These are automatically added to the changelog when creating new releases.
|
||||
- `docs`: changes to [docs](./docs) only. These do not appear in the changelog.
|
||||
- `chore`: changes to code that is neither a fix nor a feature (e.g. refactoring, adding tests, etc.). These do not appear in the changelog.
|
||||
|
||||
If you are committing to [templates](./templates) or [examples](./examples), use the `chore` type with the proper scope, like this:
|
||||
|
||||
- `chore(templates): adds feature to template`
|
||||
- `chore(examples): fixes bug in example`
|
||||
|
||||
## Pull Requests
|
||||
|
||||
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR message.
|
||||
@@ -138,487 +138,25 @@ The `useForm` hook can be used to interact with the form itself, and sends back
|
||||
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: |
|
||||
The `useForm` hook returns an object with the following properties:
|
||||
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Action',
|
||||
'Description',
|
||||
'Example',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>fields</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Deprecated. This property cannot be relied on as up-to-date.",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>submit</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to trigger the form to submit",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>dispatchFields</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Dispatch actions to the form field state",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>validateForm</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Trigger a validation of the form state",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>createFormData</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Create a <code>multipart/form-data</code> object from the current form's state</>,
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>disabled</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Boolean denoting whether or not the form is disabled",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getFields</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Gets all fields from state',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getField</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Gets a single field from state by path',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getData</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Returns the data stored in the form',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getSiblingData</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Returns form sibling data for the given field path',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setModified</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>modified</code> state</>,
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setProcessing</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>processing</code> state</>,
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setSubmitted</code></strong>,
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>submitted</code> state</>,
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>formRef</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'The ref from the form HTML element',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>reset</code></strong>,
|
||||
},
|
||||
{
|
||||
value: 'Method to reset the form to its initial state',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>addFieldRow</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to add a row on an array or block field",
|
||||
},
|
||||
{
|
||||
drawerTitle: 'addFieldRow',
|
||||
drawerDescription: 'A useful method to programtically add a row to an array or block field.',
|
||||
drawerSlug: 'addFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
'Description',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The index of the row to add",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>data</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The data to add to the row",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { addFieldRow } = useForm()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
addFieldRow({
|
||||
path: "arrayField",
|
||||
rowIndex: 0,
|
||||
data: {
|
||||
textField: "text",
|
||||
// blockType: "yourBlockSlug",
|
||||
// ^ if managing a block array, you need to specify the block type
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Add Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
name: "arrayField",
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
name: "textField",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "ui",
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomArrayManager,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>removeFieldRow</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to remove a row from an array or block field",
|
||||
},
|
||||
{
|
||||
drawerTitle: 'removeFieldRow',
|
||||
drawerDescription: 'A useful method to programtically remove a row from an array or block field.',
|
||||
drawerSlug: 'removeFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
'Description',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The index of the row to remove",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { removeFieldRow } = useForm()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
removeFieldRow({
|
||||
path: "arrayField",
|
||||
rowIndex: 0,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Remove Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
name: "arrayField",
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
name: "textField",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "ui",
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomArrayManager,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>replaceFieldRow</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "Method to replace a row from an array or block field",
|
||||
},
|
||||
{
|
||||
drawerTitle: 'replaceFieldRow',
|
||||
drawerDescription: 'A useful method to programtically replace a row from an array or block field.',
|
||||
drawerSlug: 'replaceFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
'Description',
|
||||
]}
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The index of the row to replace",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>data</code></strong>,
|
||||
},
|
||||
{
|
||||
value: "The data to replace within the row",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { replaceFieldRow } = useForm()
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
replaceFieldRow({
|
||||
path: "arrayField",
|
||||
rowIndex: 0,
|
||||
data: {
|
||||
textField: "updated text",
|
||||
// blockType: "yourBlockSlug",
|
||||
// ^ if managing a block array, you need to specify the block type
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Replace Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
name: "arrayField",
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
name: "textField",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "ui",
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomArrayManager,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
],
|
||||
]}
|
||||
/>
|
||||
| Action | Description |
|
||||
|----------------------|---------------------------------------------------------------------|
|
||||
| **`fields`** | Deprecated. This property cannot be relied on as up-to-date. |
|
||||
| **`submit`** | Method to trigger the form to submit |
|
||||
| **`dispatchFields`** | Dispatch actions to the form field state |
|
||||
| **`validateForm`** | Trigger a validation of the form state |
|
||||
| **`createFormData`** | Create a `multipart/form-data` object from the current form's state |
|
||||
| **`disabled`** | Boolean denoting whether or not the form is disabled |
|
||||
| **`getFields`** | Gets all fields from state |
|
||||
| **`getField`** | Gets a single field from state by path |
|
||||
| **`getData`** | Returns the data stored in the form |
|
||||
| **`getSiblingData`** | Returns form sibling data for the given field path |
|
||||
| **`setModified`** | Set the form's `modified` state |
|
||||
| **`setProcessing`** | Set the form's `processing` state |
|
||||
| **`setSubmitted`** | Set the form's `submitted` state |
|
||||
| **`formRef`** | The ref from the form HTML element |
|
||||
| **`reset`** | Method to reset the form to its initial state |
|
||||
|
||||
### useDocumentInfo
|
||||
|
||||
|
||||
@@ -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`. |
|
||||
@@ -35,7 +35,6 @@ All options for the Admin panel are defined in your base Payload config file.
|
||||
| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). |
|
||||
| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||||
| `autoLogin` | Used to automate admin log-in for dev and demonstration convenience. [More](/docs/authentication/config). |
|
||||
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
|
||||
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) |
|
||||
| **`logoutRoute`** | The route for the `logout` page. |
|
||||
|
||||
@@ -249,39 +249,3 @@ If you pass a strategy to the `strategy` property directly, the `name` property
|
||||
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.
|
||||
The `admin.autologin` property is used to configure the how visitors are handled when accessing the admin panel.
|
||||
The default is that all users will have to login and this should not be enabled for environments where data needs to protected.
|
||||
|
||||
#### 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. |
|
||||
|
||||
The recommended way to use this feature is behind an environment variable to ensure it is disabled when in production.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
user: 'users',
|
||||
// highlight-start
|
||||
autoLogin: process.env.PAYLOAD_PUBLIC_ENABLE_AUTOLOGIN === 'true' ? {
|
||||
email: 'test@example.com',
|
||||
password: 'test',
|
||||
prefillOnly: true,
|
||||
} : false,
|
||||
// highlight-end
|
||||
},
|
||||
collections: [ /** */],
|
||||
})
|
||||
```
|
||||
|
||||
@@ -83,11 +83,9 @@ Once enabled, each document that is created within the Collection can be thought
|
||||
|
||||
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -126,32 +126,22 @@ project-name
|
||||
here](/docs/admin/webpack#admin-environment-vars) for more info.
|
||||
</Banner>
|
||||
|
||||
### Customizing & Automating Config Location Detection
|
||||
### Customizing & overriding the config location
|
||||
|
||||
Payload is designed to automatically locate your configuration file. By default, it will first look in the root of your current working directory for a file named `payload.config.js` or `payload.config.ts` if you're using TypeScript.
|
||||
By default, the Payload config must be in the root of your current working directory and named either `payload.config.js` or `payload.config.ts` if you're using TypeScript.
|
||||
|
||||
In development mode, if the configuration file is not found at the root, Payload will attempt to read your `tsconfig.json`, and search in the directory specified in `compilerOptions.rootDir` (typically "src").
|
||||
|
||||
In production mode, Payload will first attempt to find the config file in the output directory specified in `compilerOptions.outDir` of your `tsconfig.json`, then fallback to the source directory (`compilerOptions.rootDir`), and finally will check the 'dist' directory.
|
||||
|
||||
Please ensure your `tsconfig.json` is properly configured if you want Payload to accurately auto-detect your configuration file location. If `tsconfig.json` does not exist or doesn't specify `rootDir` or `outDir`, Payload will default to the current working directory.
|
||||
|
||||
#### Overriding the Config Location
|
||||
|
||||
In addition to the above automated detection, you can specify your own location for the Payload config file. This is done by using the environment variable `PAYLOAD_CONFIG_PATH`. The path you provide via this environment variable can either be absolute or relative to your current working directory. This can be useful in situations where your Payload config is not in a standard location, or you wish to switch between multiple configurations.
|
||||
But, you can specify where your Payload config is located as well as what it's named by using the environment variable `PAYLOAD_CONFIG_PATH`. The path you provide via this environment variable can either be absolute or relative to your current working directory.
|
||||
|
||||
**Example in package.json:**
|
||||
|
||||
```json
|
||||
```
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "PAYLOAD_CONFIG_PATH=path/to/custom-config.js node server.js",
|
||||
}
|
||||
"scripts": {
|
||||
"dev": "PAYLOAD_CONFIG_PATH=path/to/custom-config.js node server.js",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When `PAYLOAD_CONFIG_PATH` is set, Payload will use this path to load the configuration, bypassing all automated detection.
|
||||
|
||||
### Developing within the Config
|
||||
|
||||
Payload comes with `isomorphic-fetch` installed which means that even in Node, you can use the `fetch` API just as you would within the browser. No need to import `axios` or similar, unless you want to!
|
||||
|
||||
@@ -12,29 +12,23 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
Its uses can be simple or highly complex.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight="https://payloadcms.com/images/docs/fields/array.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/array-dark.png"
|
||||
alt="Array field with two Rows in Payload admin panel"
|
||||
caption="Admin panel screenshot of an Array field with two Rows"
|
||||
/>
|
||||
|
||||
**Example uses:**
|
||||
|
||||
- A "slider" with an image ([upload field](/docs/fields/upload)) and a caption ([text field](/docs/fields/text))
|
||||
- Navigational structures where editors can specify nav items containing pages ([relationship field](/docs/fields/relationship)), an "open in new tab" [checkbox field](/docs/fields/checkbox)
|
||||
- Event agenda "timeslots" where you need to specify start & end time ([date field](/docs/fields/date)), label ([text field](/docs/fields/text)), and Learn More page [relationship](/docs/fields/relationship)
|
||||
|
||||

|
||||
_Admin panel screenshot of an Array field with a Row containing two text fields, a read-only text field and a checkbox_
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`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. |
|
||||
| **`fields`** \* | Array of field types to correspond to each row of the Array. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation)
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
|
||||
| **`fields`** \* | Array of field types to correspond to each row of the Array. |
|
||||
| **`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-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) |
|
||||
|
||||
@@ -13,19 +13,15 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
match the shape of one of your provided block configs.
|
||||
</Banner>
|
||||
|
||||
<LightDarkImage
|
||||
srcLight="https://payloadcms.com/images/docs/fields/blocks.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/blocks-dark.png"
|
||||
alt="Admin panel screenshot of add Blocks drawer view"
|
||||
caption="Admin panel screenshot of add Blocks drawer view"
|
||||
/>
|
||||
|
||||
**Example uses:**
|
||||
|
||||
- A layout builder tool that grants editors to design highly customizable page or post layouts. Blocks could include configs such as `Quote`, `CallToAction`, `Slider`, `Content`, `Gallery`, or others.
|
||||
- A form builder tool where available block configs might be `Text`, `Select`, or `Checkbox`.
|
||||
- Virtual event agenda "timeslots" where a timeslot could either be a `Break`, a `Presentation`, or a `BreakoutSession`.
|
||||
|
||||

|
||||
_Admin panel screenshot of a Blocks field type with Call to Action and Number block examples_
|
||||
|
||||
### Field config
|
||||
|
||||
| Option | Description |
|
||||
@@ -33,9 +29,7 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
| **`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. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation)
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
|
||||
| **`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) |
|
||||
| **`access`** | Provide field-level access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -11,13 +11,6 @@ keywords: code, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||
This field uses the `monaco-react` editor syntax highlighting.
|
||||
|
||||
### Config
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -11,13 +11,6 @@ keywords: date, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||
This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepicker) for the Admin panel component.
|
||||
|
||||
### Config
|
||||
|
||||
@@ -6,17 +6,10 @@ 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>
|
||||
<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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -11,13 +11,6 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -11,13 +11,6 @@ keywords: json, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||
This field uses the `monaco-react` editor syntax highlighting.
|
||||
|
||||
### Config
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -11,13 +11,6 @@ keywords: point, geolocation, geospatial, geojson, 2dsphere, config, configurati
|
||||
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'
|
||||
/>
|
||||
|
||||
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.
|
||||
|
||||
### Config
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: radio, fields, config, configuration, documentation, Content Managemen
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -11,13 +11,6 @@ keywords: relationship, fields, config, configuration, documentation, Content Ma
|
||||
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'
|
||||
/>
|
||||
|
||||
**Example uses:**
|
||||
|
||||
- To add `Product` documents to an `Order` document
|
||||
@@ -74,7 +67,7 @@ Set to `false` if you'd like to disable the ability to create new documents from
|
||||
|
||||
Options can be dynamically limited by supplying a [query constraint](/docs/queries/overview), which will be used both for validating input and filtering available relationships in the UI.
|
||||
|
||||
The `filterOptions` property can either be a `Where` query directly, or a function (synchronous or asynchronous) that returns one. When using a function, it will be called with an argument object containing the following properties:
|
||||
The `filterOptions` property can either be a `Where` query directly, or a function that returns one. When using a function, it will be called with an argument object with the following properties:
|
||||
|
||||
| Property | Description |
|
||||
| ------------- | ------------------------------------------------------------------------------------ |
|
||||
|
||||
@@ -6,17 +6,10 @@ desc: The Rich Text field allows dynamic content to be written through the Admin
|
||||
keywords: rich text, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner>
|
||||
<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.
|
||||
</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'
|
||||
/>
|
||||
|
||||
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">
|
||||
@@ -25,22 +18,22 @@ The Admin component is built on the powerful [`slatejs`](https://docs.slatejs.or
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`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) |
|
||||
| **`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-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) |
|
||||
| **`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) |
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`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) |
|
||||
| **`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-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) |
|
||||
| **`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
|
||||
|
||||
@@ -66,11 +59,9 @@ The default `elements` available in Payload are:
|
||||
- `link`
|
||||
- `ol`
|
||||
- `ul`
|
||||
- `textAlign`
|
||||
- `indent`
|
||||
- [`relationship`](#relationship-element)
|
||||
- [`upload`](#upload-element)
|
||||
- [`textAlign`](#text-align)
|
||||
|
||||
**`leaves`**
|
||||
|
||||
@@ -95,17 +86,17 @@ This allows [fields](/docs/fields/overview) to be saved as extra fields on a lin
|
||||
`link.fields` may either be an array of fields (in which case all fields defined in it will be appended below the default fields) or a function that accepts the default fields as only argument and returns an array defining the entirety of fields to be used (thus providing a mechanism of overriding the default fields).
|
||||
|
||||

|
||||
_RichText link with custom fields_
|
||||
*RichText link with custom fields*
|
||||
|
||||
**`upload.collections[collection-name].fields`**
|
||||
|
||||
This allows [fields](/docs/fields/overview) to be saved as meta data on an upload field inside the Rich Text Editor. When this is present, the fields will render inside a modal that can be opened by clicking the "edit" button on the upload element.
|
||||
|
||||

|
||||
_RichText field using the upload element_
|
||||
*RichText field using the upload element*
|
||||
|
||||

|
||||
_RichText upload element modal displaying fields from the config_
|
||||
*RichText upload element modal displaying fields from the config*
|
||||
|
||||
### Relationship element
|
||||
|
||||
@@ -116,15 +107,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.
|
||||
|
||||
### TextAlign element
|
||||
|
||||
Text Alignment is not included by default and can be added to a Rich Text Editor by adding `textAlign` to the list of elements. TextAlign will alter the existing element to include a new `textAlign` field in the resulting JSON. This field can be used in combination with other elements and leaves to position content to the left, center or right.
|
||||
|
||||
### Specifying which elements and leaves to allow
|
||||
|
||||
To specify which default elements or leaves should be allowed to be used for this field, define arrays that contain string names for each element or leaf you wish to enable. To specify a custom element or leaf, pass an object with all corresponding properties as outlined below. View the [example](#example) to reference how this all works.
|
||||
@@ -137,75 +125,71 @@ Once you're up to speed with the general concepts involved, you can pass in your
|
||||
|
||||
**Both custom elements and leaves are defined via the following config:**
|
||||
|
||||
| Property | Description |
|
||||
| --------------- | ---------------------------------------------------------- |
|
||||
| **`name`** \* | The default name to be used as a `type` for this element. |
|
||||
| **`Button`** \* | A React component to be rendered in the Rich Text toolbar. |
|
||||
| **`plugins`** | An array of plugins to provide to the Rich Text editor. |
|
||||
| **`type`** | A type that overrides the default type used by `name` |
|
||||
| Property | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`name`** * | The name to be used as a `type` for this element. |
|
||||
| **`Button`** * | A React component to be rendered in the Rich Text toolbar. |
|
||||
| **`plugins`** | An array of plugins to provide to the Rich Text editor. |
|
||||
|
||||
Custom `Element`s also require the `Element` property set to a React component to be rendered as the `Element` within the rich text editor itself.
|
||||
|
||||
Custom `Leaf` objects follow a similar pattern but require you to define the `Leaf` property instead.
|
||||
|
||||
Specifying custom `Type`s let you extend your custom elements by adding additional fields to your JSON object.
|
||||
|
||||
### Example
|
||||
|
||||
`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
|
||||
defaultValue: [
|
||||
{
|
||||
children: [{ text: "Here is some default content for this field" }],
|
||||
},
|
||||
],
|
||||
name: 'content', // required
|
||||
type: 'richText', // required
|
||||
defaultValue: [{
|
||||
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: [
|
||||
// any plugins that are required by this element go here
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
leaves: [
|
||||
"bold",
|
||||
"italic",
|
||||
'bold',
|
||||
'italic',
|
||||
{
|
||||
name: "highlight",
|
||||
name: 'highlight',
|
||||
Button: CustomHighlightButton,
|
||||
Leaf: CustomHighlightLeaf,
|
||||
plugins: [
|
||||
// any plugins that are required by this leaf go here
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
link: {
|
||||
// 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',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -219,9 +203,9 @@ export const ExampleCollection: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
@@ -232,67 +216,112 @@ For more examples regarding how to define your own elements and leaves, check ou
|
||||
As the Rich Text field saves its content in a JSON format, you'll need to render it as HTML yourself. Here is an example for how to generate JSX / HTML from Rich Text content:
|
||||
|
||||
```ts
|
||||
import React, { Fragment } from "react";
|
||||
import escapeHTML from "escape-html";
|
||||
import { Text } from "slate";
|
||||
import React, { Fragment } from 'react';
|
||||
import escapeHTML from 'escape-html';
|
||||
import { Text } from 'slate';
|
||||
|
||||
const serialize = (children) =>
|
||||
children.map((node, i) => {
|
||||
if (Text.isText(node)) {
|
||||
let text = (
|
||||
<span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
|
||||
const serialize = (children) => children.map((node, i) => {
|
||||
if (Text.isText(node)) {
|
||||
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />;
|
||||
|
||||
if (node.bold) {
|
||||
text = (
|
||||
<strong key={i}>
|
||||
{text}
|
||||
</strong>
|
||||
);
|
||||
}
|
||||
|
||||
if (node.code) {
|
||||
text = (
|
||||
<code key={i}>
|
||||
{text}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
if (node.italic) {
|
||||
text = (
|
||||
<em key={i}>
|
||||
{text}
|
||||
</em>
|
||||
);
|
||||
}
|
||||
|
||||
// Handle other leaf types here...
|
||||
|
||||
return (
|
||||
<Fragment key={i}>
|
||||
{text}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return (
|
||||
<h1 key={i}>
|
||||
{serialize(node.children)}
|
||||
</h1>
|
||||
);
|
||||
// Iterate through all headings here...
|
||||
case 'h6':
|
||||
return (
|
||||
<h6 key={i}>
|
||||
{serialize(node.children)}
|
||||
</h6>
|
||||
);
|
||||
case 'blockquote':
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serialize(node.children)}
|
||||
</blockquote>
|
||||
);
|
||||
case 'ul':
|
||||
return (
|
||||
<ul key={i}>
|
||||
{serialize(node.children)}
|
||||
</ul>
|
||||
);
|
||||
case 'ol':
|
||||
return (
|
||||
<ol key={i}>
|
||||
{serialize(node.children)}
|
||||
</ol>
|
||||
);
|
||||
case 'li':
|
||||
return (
|
||||
<li key={i}>
|
||||
{serialize(node.children)}
|
||||
</li>
|
||||
);
|
||||
case 'link':
|
||||
return (
|
||||
<a
|
||||
href={escapeHTML(node.url)}
|
||||
key={i}
|
||||
>
|
||||
{serialize(node.children)}
|
||||
</a>
|
||||
);
|
||||
|
||||
if (node.bold) {
|
||||
text = <strong key={i}>{text}</strong>;
|
||||
}
|
||||
|
||||
if (node.code) {
|
||||
text = <code key={i}>{text}</code>;
|
||||
}
|
||||
|
||||
if (node.italic) {
|
||||
text = <em key={i}>{text}</em>;
|
||||
}
|
||||
|
||||
// Handle other leaf types here...
|
||||
|
||||
return <Fragment key={i}>{text}</Fragment>;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "h1":
|
||||
return <h1 key={i}>{serialize(node.children)}</h1>;
|
||||
// Iterate through all headings here...
|
||||
case "h6":
|
||||
return <h6 key={i}>{serialize(node.children)}</h6>;
|
||||
case "blockquote":
|
||||
return <blockquote key={i}>{serialize(node.children)}</blockquote>;
|
||||
case "ul":
|
||||
return <ul key={i}>{serialize(node.children)}</ul>;
|
||||
case "ol":
|
||||
return <ol key={i}>{serialize(node.children)}</ol>;
|
||||
case "li":
|
||||
return <li key={i}>{serialize(node.children)}</li>;
|
||||
case "link":
|
||||
return (
|
||||
<a href={escapeHTML(node.url)} key={i}>
|
||||
{serialize(node.children)}
|
||||
</a>
|
||||
);
|
||||
|
||||
default:
|
||||
return <p key={i}>{serialize(node.children)}</p>;
|
||||
}
|
||||
});
|
||||
default:
|
||||
return (
|
||||
<p key={i}>
|
||||
{serialize(node.children)}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
<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
|
||||
@@ -308,12 +337,12 @@ 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: [
|
||||
@@ -321,11 +350,10 @@ export default {
|
||||
const editor = incomingEditor;
|
||||
const { shouldBreakOutOnEnter } = editor;
|
||||
|
||||
editor.shouldBreakOutOnEnter = (element) =>
|
||||
element.type === "large-body" ? true : shouldBreakOutOnEnter(element);
|
||||
editor.shouldBreakOutOnEnter = (element) => (element.type === 'large-body' ? true : shouldBreakOutOnEnter(element));
|
||||
|
||||
return editor;
|
||||
},
|
||||
}
|
||||
],
|
||||
};
|
||||
```
|
||||
@@ -339,5 +367,8 @@ 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';
|
||||
```
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -10,12 +10,6 @@ keywords: select, multi-select, fields, config, configuration, documentation, Co
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
@@ -98,85 +92,3 @@ export const ExampleCollection: CollectionConfig = {
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Customization
|
||||
|
||||
The Select field UI component can be customized by providing a custom React component to the `components` object in the Base config.
|
||||
|
||||
```ts
|
||||
export const CustomSelectField: Field = {
|
||||
name: 'customSelectField',
|
||||
type: 'select', // or 'text' if you have dynamic options
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomSelectComponent({
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can import the existing Select component directly from Payload, then extend and customize it as needed.
|
||||
|
||||
```ts
|
||||
import * as React from 'react';
|
||||
import { SelectInput, useField } from 'payload/components/forms';
|
||||
import { useAuth } from 'payload/components/utilities';
|
||||
|
||||
type customSelectProps = {
|
||||
path: string;
|
||||
options: {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const CustomSelectComponent: React.FC<CustomSelectProps> = ({ path, options }) => {
|
||||
const { value, setValue } = useField<string>({ path });
|
||||
const { user } = useAuth();
|
||||
|
||||
const adjustedOptions = options.filter((option) => {
|
||||
/*
|
||||
A common use case for a custom select
|
||||
is to show different options based on
|
||||
the current user's role.
|
||||
*/
|
||||
return option;
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="field-label">
|
||||
Custom Select
|
||||
</label>
|
||||
<SelectInput
|
||||
path={path}
|
||||
name={path}
|
||||
options={adjustedOptions}
|
||||
value={value}
|
||||
onChange={() => setValue(e.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
If you are looking to create a dynamic select field, the following tutorial will walk you through the process of creating a custom select field that fetches its options from an external API.
|
||||
|
||||
<VideoDrawer
|
||||
id='Efn9OxSjA6Y'
|
||||
label='How to Create a Custom Select Field'
|
||||
drawerTitle='How to Create a Custom Select Field: A Step-by-Step Guide'
|
||||
/>
|
||||
|
||||
If you want to learn more about custom components check out the [Admin > Custom Component](/docs/admin/components#field-component) docs.
|
||||
|
||||
@@ -12,13 +12,8 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||

|
||||
_Tabs field type used to separate Hero fields from Page Layout_
|
||||
|
||||
### Config
|
||||
|
||||
@@ -30,14 +25,14 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
|
||||
|
||||
#### Tab-specific Config
|
||||
|
||||
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.
|
||||
Each tab has its own required `label` and `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. |
|
||||
| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. |
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`name`** | An optional property name to be used when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** \* | The label to render on the tab itself. |
|
||||
| **`fields`** \* | The fields to render within this tab. |
|
||||
| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). (`name` must be present) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: text, fields, config, configuration, documentation, Content Management
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -10,13 +10,6 @@ keywords: textarea, fields, config, configuration, documentation, Content Manage
|
||||
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'
|
||||
/>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
|
||||
@@ -15,13 +15,6 @@ keywords: upload, images media, fields, config, configuration, documentation, Co
|
||||
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'
|
||||
/>
|
||||
|
||||
**Example uses:**
|
||||
|
||||
- To provide a `Page` with a featured image
|
||||
|
||||
@@ -16,7 +16,6 @@ Collections feature the ability to define the following hooks:
|
||||
- [afterRead](#afterread)
|
||||
- [beforeDelete](#beforedelete)
|
||||
- [afterDelete](#afterdelete)
|
||||
- [afterOperation](#afteroperation)
|
||||
|
||||
Additionally, `auth`-enabled collections feature the following hooks:
|
||||
|
||||
@@ -32,7 +31,6 @@ Additionally, `auth`-enabled collections feature the following hooks:
|
||||
All collection Hook properties accept arrays of synchronous or asynchronous functions. Each Hook type receives specific arguments and has the ability to modify specific outputs.
|
||||
|
||||
`collections/exampleHooks.js`
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
@@ -50,7 +48,6 @@ export const ExampleHooks: CollectionConfig = {
|
||||
afterChange: [(args) => {...}],
|
||||
afterRead: [(args) => {...}],
|
||||
afterDelete: [(args) => {...}],
|
||||
afterOperation: [(args) => {...}],
|
||||
|
||||
// Auth-enabled hooks
|
||||
beforeLogin: [(args) => {...}],
|
||||
@@ -65,19 +62,19 @@ export const ExampleHooks: CollectionConfig = {
|
||||
|
||||
### beforeOperation
|
||||
|
||||
The `beforeOperation` hook can be used to modify the arguments that operations accept or execute side-effects that run before an operation begins.
|
||||
The `beforeOperation` Hook type can be used to modify the arguments that operations accept or execute side-effects that run before an operation begins.
|
||||
|
||||
Available Collection operations include `create`, `read`, `update`, `delete`, `login`, `refresh`, and `forgotPassword`.
|
||||
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
|
||||
args, // Original arguments passed into the operation
|
||||
operation, // name of the operation
|
||||
}) => {
|
||||
return args; // return modified operation arguments as necessary
|
||||
};
|
||||
return args; // Return operation arguments as necessary
|
||||
}
|
||||
```
|
||||
|
||||
### beforeValidate
|
||||
@@ -91,7 +88,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
|
||||
@@ -100,7 +97,7 @@ const beforeValidateHook: CollectionBeforeValidateHook = async ({
|
||||
originalDoc, // original document
|
||||
}) => {
|
||||
return data; // Return data to either create or update a document with
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### beforeChange
|
||||
@@ -108,7 +105,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
|
||||
@@ -117,7 +114,7 @@ const beforeChangeHook: CollectionBeforeChangeHook = async ({
|
||||
originalDoc, // original document
|
||||
}) => {
|
||||
return data; // Return data to either create or update a document with
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### afterChange
|
||||
@@ -125,7 +122,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 +130,8 @@ const afterChangeHook: CollectionAfterChangeHook = async ({
|
||||
previousDoc, // document data before updating the collection
|
||||
operation, // name of the operation ie. 'create', 'update'
|
||||
}) => {
|
||||
return doc; // value to be used in subsequent afterChange hooks
|
||||
};
|
||||
return doc;
|
||||
}
|
||||
```
|
||||
|
||||
### beforeRead
|
||||
@@ -142,7 +139,7 @@ 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
|
||||
@@ -150,7 +147,7 @@ const beforeReadHook: CollectionBeforeReadHook = async ({
|
||||
query, // JSON formatted query
|
||||
}) => {
|
||||
return doc;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### afterRead
|
||||
@@ -158,7 +155,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
|
||||
@@ -167,7 +164,7 @@ const afterReadHook: CollectionAfterReadHook = async ({
|
||||
findMany, // boolean to denote if this hook is running against finding one, or finding many
|
||||
}) => {
|
||||
return doc;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### beforeDelete
|
||||
@@ -197,37 +194,19 @@ const afterDeleteHook: CollectionAfterDeleteHook = async ({
|
||||
}) => {...}
|
||||
```
|
||||
|
||||
### afterOperation
|
||||
|
||||
The `afterOperation` hook can be used to modify the result of operations or execute side-effects that run after an operation has completed.
|
||||
|
||||
Available Collection operations include `create`, `find`, `findByID`, `update`, `updateByID`, `delete`, `deleteByID`, `login`, `refresh`, and `forgotPassword`.
|
||||
|
||||
```ts
|
||||
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
|
||||
};
|
||||
```
|
||||
|
||||
### beforeLogin
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### afterLogin
|
||||
@@ -288,7 +267,7 @@ 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
|
||||
@@ -296,7 +275,7 @@ const afterLoginHook: CollectionAfterForgotPasswordHook = async ({
|
||||
token, // user token
|
||||
}) => {
|
||||
return user;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
@@ -319,5 +298,5 @@ import type {
|
||||
CollectionAfterRefreshHook,
|
||||
CollectionAfterMeHook,
|
||||
CollectionAfterForgotPasswordHook,
|
||||
} from "payload/types";
|
||||
} from 'payload/types';
|
||||
```
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
title: Context
|
||||
label: Context
|
||||
order: 50
|
||||
desc: Context allows you to pass in extra data that can be shared between hooks
|
||||
keywords: hooks, context, payload context, payloadcontext, data, extra data, shared data, shared, extra
|
||||
---
|
||||
|
||||
The `context` object in hooks is used to share data across different hooks. The persists throughout the entire lifecycle of a request and is available within every hook. This allows you to add logic to your hooks based on the request state by setting properties to `req.context` and using them elsewhere.
|
||||
|
||||
## When to use Context
|
||||
|
||||
Context gives you a way forward on otherwise difficult problems such as:
|
||||
|
||||
1. **Passing data between hooks**: Needing data in multiple hooks from a 3rd party API, it could be retrieved and used in `beforeChange` and later used again in an `afterChange` hook without having to fetch it twice.
|
||||
2. **Preventing infinite loops**: Calling `payload.update()` on the same document that triggered an `afterChange` hook will create an infinite loop, control the flow by assigning a no-op condition to context
|
||||
3. **Passing data to local API**: Setting values on the `req.context` and pass it to `payload.create()` you can provide additional data to hooks without adding extraneous fields.
|
||||
4. **Passing data between hooks and middleware or custom endpoints**: Hooks could set context across multiple collections and then be used in a final `postMiddleware`.
|
||||
|
||||
## How to Use Context
|
||||
|
||||
Let's see examples on how context can be used in the first two scenarios mentioned above:
|
||||
|
||||
### Passing data between hooks
|
||||
|
||||
To pass data between hooks, you can assign values to context in an earlier hook in the lifecycle of a request and expect it the context in a later hook.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
const Customer: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
hooks: {
|
||||
beforeChange: [async ({ context, data }) => {
|
||||
// assign the customerData to context for use later
|
||||
context.customerData = await fetchCustomerData(data.customerID);
|
||||
return {
|
||||
...data,
|
||||
// some data we use here
|
||||
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: [ /* ... */ ],
|
||||
};
|
||||
```
|
||||
|
||||
### Preventing infinite loops
|
||||
|
||||
Let's say you have an `afterChange` hook, and you want to do a calculation inside the hook (as the document ID needed for the calculation is available in the `afterChange` hook, but not in the `beforeChange` hook). Once that's done, you want to update the document with the result of the calculation.
|
||||
|
||||
Bad example:
|
||||
|
||||
```ts
|
||||
const Customer: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
hooks: {
|
||||
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))
|
||||
},
|
||||
});
|
||||
}],
|
||||
},
|
||||
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.
|
||||
|
||||
Fixed example:
|
||||
|
||||
```ts
|
||||
const MyCollection: CollectionConfig = {
|
||||
slug: 'slug',
|
||||
hooks: {
|
||||
afterChange: [async ({ context, doc }) => {
|
||||
// return if flag was previously set
|
||||
if (context.triggerAfterChange === false) {
|
||||
return;
|
||||
}
|
||||
await payload.update({
|
||||
collection: contextHooksSlug,
|
||||
id: doc.id,
|
||||
data: {
|
||||
...(await fetchCustomerData(data.customerID))
|
||||
},
|
||||
context: {
|
||||
// set a flag to prevent from running again
|
||||
triggerAfterChange: false,
|
||||
},
|
||||
});
|
||||
}],
|
||||
},
|
||||
fields: [ /* ... */ ],
|
||||
};
|
||||
```
|
||||
|
||||
## Typing context
|
||||
|
||||
The default typescript interface for `context` is `{ [key: string]: unknown }`. If you prefer a more strict typing in your project or when authoring plugins for others, you can override this using the `declare` syntax.
|
||||
|
||||
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';
|
||||
|
||||
declare module 'payload' {
|
||||
// Create a new interface that merges your additional fields with the original one
|
||||
export interface RequestContext extends OriginalRequestContext {
|
||||
myObject?: string;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will add a the property `myObject` with a type of string to every context object. Make sure to follow this example correctly, as type augmentation can mess up your types if you do it wrong.
|
||||
@@ -77,7 +77,6 @@ You can specify more options within the Local API vs. REST or GraphQL due to the
|
||||
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
|
||||
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
|
||||
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
|
||||
| `context` | [Context](/docs/hooks/context), which will then be passed to `context` and `req.context`, which can be read by hooks. Useful if you want to pass additional information to the hooks which shouldn't be necessarily part of the document, for example a `triggerBeforeChange` option which can be read by the BeforeChange hook to determine if it should run or not. |
|
||||
|
||||
_There are more options available on an operation by operation basis outlined below._
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ const getPosts = async () => {
|
||||
|
||||
## Sort
|
||||
|
||||
Payload `find` queries support a `sort` parameter through all APIs. Pass the `name` of a top-level field to sort by that field in ascending order. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Because sorting is handled by the database, the field you wish to sort on must be stored in the database to work; not a [virtual field](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges). It is recommended to enable indexing for the fields where sorting is used.
|
||||
Payload `find` queries support a `sort` parameter through all APIs. Pass the `name` of a top-level field to sort by that field in ascending order. Prefix the name of the field with a minus symbol ("-") to sort in descending order.
|
||||
|
||||
**REST example:**
|
||||
**`https://localhost:3000/api/posts?sort=-createdAt`**
|
||||
|
||||
@@ -190,7 +190,9 @@ export const Media: CollectionConfig = {
|
||||
<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.
|
||||
If you specify a function to return an admin thumbnail, but your upload is not
|
||||
an image file type (for example, PDF or TXT) your function will not be used.
|
||||
Instead, Payload will display its generic file upload graphic.
|
||||
</Banner>
|
||||
|
||||
### MimeTypes
|
||||
|
||||
@@ -3,5 +3,5 @@ PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
||||
MONGODB_URI=mongodb://127.0.0.1/payload-example-auth
|
||||
PAYLOAD_SECRET=PAYLOAD_AUTH_EXAMPLE_SECRET_KEY
|
||||
COOKIE_DOMAIN=localhost
|
||||
PAYLOAD_PUBLIC_SEED=true
|
||||
PAYLOAD_SEED=true
|
||||
PAYLOAD_DROP_DATABASE=true
|
||||
@@ -1,6 +1,6 @@
|
||||
# Payload Auth Example
|
||||
|
||||
The [Payload Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth) demonstrates how to implement [Payload Authentication](https://payloadcms.com/docs/authentication/overview). Follow the [Quick Start](#quick-start) to get up and running quickly. There are various fully working front-ends made explicitly for this example, including:
|
||||
This example demonstrates how to implement [Payload Authentication](https://payloadcms.com/docs/authentication/overview). Follow the [Quick Start](#quick-start) to get up and running quickly. There are various fully working front-ends made explicitly for this example, including:
|
||||
|
||||
- [Next.js App Router](../next-app)
|
||||
- [Next.js Pages Router](../next-pages)
|
||||
@@ -11,12 +11,11 @@ Follow the instructions in each respective README to get started. If you are set
|
||||
|
||||
To spin up this example locally, follow these steps:
|
||||
|
||||
1. Clone this repo
|
||||
2. `cd` into this directory and run `yarn` or `npm install`
|
||||
3. `cp .env.example .env` to copy the example environment variables
|
||||
4. `yarn dev` or `npm run dev` to start the server and seed the database
|
||||
5. `open http://localhost:3000/admin` to access the admin panel
|
||||
6. Login with email `demo@payloadcms.com` and password `demo`
|
||||
1. First clone the repo
|
||||
1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env`
|
||||
1. Next `yarn && yarn dev`
|
||||
1. Now `open http://localhost:3000/admin` to access the admin panel
|
||||
1. Login with email `demo@payloadcms.com` and password `demo`
|
||||
|
||||
That's it! Changes made in `./src` will be reflected in your app. See the [Development](#development) section for more details.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"main": "dist/server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_PUBLIC_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"dev": "cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc",
|
||||
"build": "yarn copyfiles && yarn build:payload && yarn build:server",
|
||||
@@ -19,7 +19,7 @@
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
"payload": "^1.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "^0.0.1",
|
||||
@@ -2,15 +2,9 @@ import path from 'path'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
import { Users } from './collections/Users'
|
||||
import BeforeLogin from './components/BeforeLogin'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [Users],
|
||||
admin: {
|
||||
components: {
|
||||
beforeLogin: [BeforeLogin],
|
||||
},
|
||||
},
|
||||
cors: [
|
||||
process.env.PAYLOAD_PUBLIC_SERVER_URL || '',
|
||||
process.env.PAYLOAD_PUBLIC_SITE_URL || '',
|
||||
@@ -25,7 +25,7 @@ const start = async (): Promise<void> => {
|
||||
},
|
||||
})
|
||||
|
||||
if (process.env.PAYLOAD_PUBLIC_SEED === 'true') {
|
||||
if (process.env.PAYLOAD_SEED === 'true') {
|
||||
payload.logger.info('---- SEEDING DATABASE ----')
|
||||
await seed(payload)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
# Payload Auth Example Front-End
|
||||
|
||||
This is a [Payload](https://payloadcms.com) + [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app) made explicitly for the [Payload Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth). It demonstrates how to authenticate your Next.js app using [Payload Authentication](https://payloadcms.com/docs/authentication/overview).
|
||||
This is a [Payload](https://payloadcms.com) + [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app) made explicitly for the [Payload Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth). It demonstrates how to authenticate your Next.js app using [Payload Authentication](https://payloadcms.com/docs/authentication/overview).
|
||||
|
||||
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/master/examples/auth/next-pages).
|
||||
|
||||
@@ -8,7 +8,7 @@ This is a [Payload](https://payloadcms.com) + [Next.js](https://nextjs.org) app
|
||||
|
||||
### Payload
|
||||
|
||||
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/auth/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
|
||||
First you'll need a running Payload app. If you have not done so already, clone down the [`cms`](../cms) folder and follow the setup instructions there to get it up and running. This will provide all the necessary APIs that your Next.js app will be using for authentication.
|
||||
|
||||
### Next.js
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export const LogoutPage: React.FC = props => {
|
||||
{'What would you like to do next? '}
|
||||
<Link href="/">Click here</Link>
|
||||
{` to go to the home page. To log back in, `}
|
||||
<Link href="/login">click here</Link>
|
||||
<Link href="login">click here</Link>
|
||||
{'.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "payload-auth-next-app",
|
||||
"name": "payload-nextjs-auth-example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -8,7 +8,7 @@ This is a [Payload](https://payloadcms.com) + [Next.js](https://nextjs.org) app
|
||||
|
||||
### Payload
|
||||
|
||||
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/auth/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
|
||||
First you'll need a running Payload app. If you have not done so already, clone down the [`cms`](../cms) folder and follow the setup instructions there to get it up and running. This will provide all the necessary APIs that your Next.js app will be using for authentication.
|
||||
|
||||
### Next.js
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "payload-auth-next-pages",
|
||||
"name": "payload-nextjs-auth-example",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3001",
|
||||
|
||||
@@ -33,7 +33,7 @@ const Logout: React.FC = () => {
|
||||
{'What would you like to do next? '}
|
||||
<Link href="/">Click here</Link>
|
||||
{` to go to the home page. To log back in, `}
|
||||
<Link href="/login">click here</Link>
|
||||
<Link href="login">click here</Link>
|
||||
{'.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
const BeforeLogin: React.FC = () => {
|
||||
if (process.env.PAYLOAD_PUBLIC_SEED === 'true') {
|
||||
return (
|
||||
<p>
|
||||
{'Log in with the email '}
|
||||
<strong>demo@payloadcms.com</strong>
|
||||
{' and the password '}
|
||||
<strong>demo</strong>.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export default BeforeLogin
|
||||
@@ -2,5 +2,5 @@ MONGODB_URI=mongodb://127.0.0.1/payload-example-custom-server
|
||||
PAYLOAD_SECRET=PAYLOAD_CUSTOM_SERVER_EXAMPLE_SECRET_KEY
|
||||
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
||||
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
|
||||
PAYLOAD_PUBLIC_SEED=true
|
||||
PAYLOAD_SEED=true
|
||||
PAYLOAD_DROP_DATABASE=true
|
||||
|
||||
@@ -94,7 +94,7 @@ To spin up this example locally, follow the [Quick Start](#quick-start).
|
||||
|
||||
### Seed
|
||||
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_PUBLIC_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates an admin user with email `dev@payloadcms.com`, password `test`, and a `home` page.
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates an admin user with email `dev@payloadcms.com`, password `test`, and a `home` page.
|
||||
|
||||
> NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"seed": "rm -rf media && cross-env PAYLOAD_PUBLIC_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
|
||||
"seed": "rm -rf media && cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc --project tsconfig.server.json",
|
||||
"build:next": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NEXT_BUILD=true node dist/server.js",
|
||||
@@ -24,7 +24,7 @@
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.17.1",
|
||||
"next": "^13.4.8",
|
||||
"payload": "latest",
|
||||
"payload": "^1.8.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(): Promise<NextResponse> {
|
||||
return NextResponse.json({ success: true })
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(): Promise<NextResponse> {
|
||||
return NextResponse.json({ success: true })
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import { getPayloadClient } from '../getPayload'
|
||||
import { Page } from './../payload-types'
|
||||
import { Gutter } from './_components/Gutter'
|
||||
import { RichText } from './_components/RichText'
|
||||
@@ -9,17 +8,11 @@ import { RichText } from './_components/RichText'
|
||||
import classes from './page.module.scss'
|
||||
|
||||
export default async function Home() {
|
||||
const payload = await getPayloadClient()
|
||||
const { docs } = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
slug: {
|
||||
equals: 'home',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const home = docs?.[0] as Page
|
||||
const home: Page = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_SERVER_URL}/api/pages?where[slug][equals]=home`,
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(res => res?.docs?.[0])
|
||||
|
||||
if (!home) {
|
||||
return notFound()
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
const BeforeLogin: React.FC = () => {
|
||||
if (process.env.PAYLOAD_PUBLIC_SEED === 'true') {
|
||||
return (
|
||||
<p>
|
||||
{'Log in with the email '}
|
||||
<strong>demo@payloadcms.com</strong>
|
||||
{' and the password '}
|
||||
<strong>demo</strong>.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export default BeforeLogin
|
||||
@@ -1,59 +0,0 @@
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
import type { Payload } from 'payload'
|
||||
import payload from 'payload'
|
||||
import type { InitOptions } from 'payload/config'
|
||||
|
||||
import { seed as seedData } from './seed'
|
||||
|
||||
dotenv.config({
|
||||
path: path.resolve(__dirname, '../.env'),
|
||||
})
|
||||
|
||||
let cached = (global as any).payload
|
||||
|
||||
if (!cached) {
|
||||
cached = (global as any).payload = { client: null, promise: null }
|
||||
}
|
||||
|
||||
interface Args {
|
||||
initOptions?: Partial<InitOptions>
|
||||
seed?: boolean
|
||||
}
|
||||
|
||||
export const getPayloadClient = async ({ initOptions, seed }: Args = {}): Promise<Payload> => {
|
||||
if (!process.env.MONGODB_URI) {
|
||||
throw new Error('MONGODB_URI environment variable is missing')
|
||||
}
|
||||
|
||||
if (!process.env.PAYLOAD_SECRET) {
|
||||
throw new Error('PAYLOAD_SECRET environment variable is missing')
|
||||
}
|
||||
|
||||
if (cached.client) {
|
||||
return cached.client
|
||||
}
|
||||
|
||||
if (!cached.promise) {
|
||||
cached.promise = payload.init({
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
local: initOptions?.express ? false : true,
|
||||
...(initOptions || {}),
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
process.env.PAYLOAD_DROP_DATABASE = seed ? 'true' : 'false'
|
||||
cached.client = await cached.promise
|
||||
|
||||
if (seed) {
|
||||
await seedData(payload)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
cached.promise = null
|
||||
throw e
|
||||
}
|
||||
|
||||
return cached.client
|
||||
}
|
||||
@@ -8,16 +8,10 @@ dotenv.config({
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
import { Pages } from './collections/Pages'
|
||||
import BeforeLogin from './components/BeforeLogin'
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL || '',
|
||||
collections: [Pages],
|
||||
admin: {
|
||||
components: {
|
||||
beforeLogin: [BeforeLogin],
|
||||
},
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
||||
},
|
||||
|
||||
@@ -5,8 +5,8 @@ export const seed = async (payload: Payload): Promise<void> => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: 'demo@payloadcms.com',
|
||||
password: 'demo',
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'test',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -10,23 +10,33 @@ dotenv.config({
|
||||
})
|
||||
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
import { getPayloadClient } from './getPayload'
|
||||
import { seed } from './seed'
|
||||
|
||||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
|
||||
// Redirect root to the admin panel
|
||||
app.get('/', (_, res) => {
|
||||
res.redirect('/admin')
|
||||
})
|
||||
|
||||
const start = async (): Promise<void> => {
|
||||
const payload = await getPayloadClient({
|
||||
initOptions: {
|
||||
express: app,
|
||||
onInit: async newPayload => {
|
||||
newPayload.logger.info(`Payload Admin URL: ${newPayload.getAdminURL()}`)
|
||||
},
|
||||
await payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET || '',
|
||||
mongoURL: process.env.MONGODB_URI || '',
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`)
|
||||
},
|
||||
seed: process.env.PAYLOAD_PUBLIC_SEED === 'true',
|
||||
})
|
||||
|
||||
if (process.env.PAYLOAD_SEED === 'true') {
|
||||
payload.logger.info('---- SEEDING DATABASE ----')
|
||||
await seed(payload)
|
||||
}
|
||||
|
||||
app.listen(PORT, async () => {
|
||||
payload.logger.info(`App URL: ${process.env.PAYLOAD_PUBLIC_SERVER_URL}`)
|
||||
})
|
||||
|
||||
@@ -8,23 +8,28 @@ dotenv.config({
|
||||
})
|
||||
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
import { getPayloadClient } from './getPayload'
|
||||
import { seed } from './seed'
|
||||
|
||||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
|
||||
const start = async (): Promise<void> => {
|
||||
const payload = await getPayloadClient({
|
||||
initOptions: {
|
||||
express: app,
|
||||
onInit: async newPayload => {
|
||||
newPayload.logger.info(`Payload Admin URL: ${newPayload.getAdminURL()}`)
|
||||
},
|
||||
await payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET || '',
|
||||
mongoURL: process.env.MONGODB_URI || '',
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`)
|
||||
},
|
||||
seed: process.env.PAYLOAD_PUBLIC_SEED === 'true',
|
||||
})
|
||||
|
||||
if (process.env.PAYLOAD_SEED === 'true') {
|
||||
payload.logger.info('---- SEEDING DATABASE ----')
|
||||
await seed(payload)
|
||||
}
|
||||
|
||||
if (process.env.NEXT_BUILD) {
|
||||
app.listen(PORT, async () => {
|
||||
payload.logger.info(`Next.js is now building...`)
|
||||
@@ -42,7 +47,7 @@ const start = async (): Promise<void> => {
|
||||
|
||||
const nextHandler = nextApp.getRequestHandler()
|
||||
|
||||
app.use((req, res) => nextHandler(req, res))
|
||||
app.get('*', (req, res) => nextHandler(req, res))
|
||||
|
||||
nextApp.prepare().then(() => {
|
||||
payload.logger.info('Next.js started')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
import type { RequestCookie } from 'next/dist/compiled/@edge-runtime/cookies'
|
||||
|
||||
import type { Page } from '../../payload-types'
|
||||
|
||||
export const fetchPage = async (
|
||||
slug: string,
|
||||
draft?: boolean,
|
||||
): Promise<Page | undefined | null> => {
|
||||
let payloadToken: RequestCookie | undefined
|
||||
|
||||
if (draft) {
|
||||
const { cookies } = await import('next/headers')
|
||||
payloadToken = cookies().get('payload-token')
|
||||
}
|
||||
|
||||
const pageRes: {
|
||||
docs: Page[]
|
||||
} = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_CMS_URL}/api/pages?where[slug][equals]=${slug}${
|
||||
draft && payloadToken ? '&draft=true' : ''
|
||||
}`,
|
||||
{
|
||||
method: 'GET',
|
||||
// this is the key we'll use to on-demand revalidate pages that use this data
|
||||
// we do this by calling `revalidateTag()` using the same key
|
||||
// see `app/api/revalidate.ts` for more info
|
||||
next: { tags: [`pages_${slug}`] },
|
||||
...(draft && payloadToken
|
||||
? {
|
||||
headers: {
|
||||
Authorization: `JWT ${payloadToken?.value}`,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
).then(res => res.json())
|
||||
|
||||
return pageRes?.docs?.[0] ?? null
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { Page } from '../../payload-types'
|
||||
|
||||
export const fetchPages = async (): Promise<Page[]> => {
|
||||
const pageRes: {
|
||||
docs: Page[]
|
||||
} = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/pages?depth=0&limit=100`).then(res =>
|
||||
res.json(),
|
||||
) // eslint-disable-line function-paren-newline
|
||||
|
||||
return pageRes?.docs ?? []
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { revalidatePath, revalidateTag } from 'next/cache'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
// this endpoint will revalidate a page by tag or path
|
||||
// this is to achieve on-demand revalidation of pages that use this data
|
||||
// send either `collection` and `slug` or `revalidatePath` as query params
|
||||
export async function GET(request: NextRequest): Promise<unknown> {
|
||||
const collection = request.nextUrl.searchParams.get('collection')
|
||||
const slug = request.nextUrl.searchParams.get('slug')
|
||||
const path = request.nextUrl.searchParams.get('path')
|
||||
const secret = request.nextUrl.searchParams.get('secret')
|
||||
|
||||
if (secret !== process.env.NEXT_PRIVATE_REVALIDATION_KEY) {
|
||||
return NextResponse.json({ revalidated: false, now: Date.now() })
|
||||
}
|
||||
|
||||
if (typeof collection === 'string' && typeof slug === 'string') {
|
||||
revalidateTag(`${collection}_${slug}`)
|
||||
return NextResponse.json({ revalidated: true, now: Date.now() })
|
||||
}
|
||||
|
||||
// there is a known limitation with `revalidatePath` where it will not revalidate exact paths of dynamic routes
|
||||
// instead, Next.js expects us to revalidate entire directories, i.e. `revalidatePath('/[slug]')` instead of `/example-page`
|
||||
// for this reason, it is preferred to use `revalidateTag` instead of `revalidatePath`
|
||||
// - https://github.com/vercel/next.js/issues/49387
|
||||
// - https://github.com/vercel/next.js/issues/49778#issuecomment-1547028830
|
||||
if (typeof path === 'string') {
|
||||
revalidatePath(path)
|
||||
return NextResponse.json({ revalidated: true, now: Date.now() })
|
||||
}
|
||||
|
||||
return NextResponse.json({ revalidated: false, now: Date.now() })
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import type { AfterChangeHook } from 'payload/dist/collections/config/types'
|
||||
|
||||
// ensure that the home page is revalidated at '/' instead of '/home'
|
||||
export const formatAppURL = ({ doc }): string => {
|
||||
const pathToUse = doc.slug === 'home' ? '' : doc.slug
|
||||
const { pathname } = new URL(`${process.env.PAYLOAD_PUBLIC_SITE_URL}/${pathToUse}`)
|
||||
return pathname
|
||||
}
|
||||
|
||||
// revalidate the page in the background, so the user doesn't have to wait
|
||||
// notice that the hook itself is not async and we are not awaiting `revalidate`
|
||||
// only revalidate existing docs that are published (not drafts)
|
||||
// send `revalidatePath`, `collection`, and `slug` to the frontend to use in its revalidate route
|
||||
// frameworks may have different ways of doing this, but the idea is the same
|
||||
export const revalidatePage: AfterChangeHook = ({ doc, req, operation }) => {
|
||||
if (operation === 'update' && doc._status === 'published') {
|
||||
const url = formatAppURL({ doc })
|
||||
|
||||
const revalidate = async (): Promise<void> => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${process.env.PAYLOAD_PUBLIC_SITE_URL}/api/revalidate?secret=${process.env.REVALIDATION_KEY}&collection=pages&slug=${doc?.slug}&path=${url}`,
|
||||
)
|
||||
|
||||
if (res.ok) {
|
||||
req.payload.logger.info(`Revalidated path ${url}`)
|
||||
} else {
|
||||
req.payload.logger.error(`Error revalidating path ${url}`)
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
req.payload.logger.error(`Error hitting revalidate route for ${url}`)
|
||||
}
|
||||
}
|
||||
|
||||
revalidate()
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
const BeforeLogin: React.FC = () => {
|
||||
if (process.env.PAYLOAD_PUBLIC_SEED === 'true') {
|
||||
return (
|
||||
<p>
|
||||
{'Log in with the email '}
|
||||
<strong>demo@payloadcms.com</strong>
|
||||
{' and the password '}
|
||||
<strong>demo</strong>.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export default BeforeLogin
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest",
|
||||
"payload": "1.6.29",
|
||||
"handlebars": "^4.7.7",
|
||||
"inline-css": "^4.0.2"
|
||||
},
|
||||
@@ -32,4 +32,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "payload-example-form-builder-cms",
|
||||
"name": "form-builder-example-cms",
|
||||
"description": "The CMS that utilizes Payload's form builder plugin.",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/server.js",
|
||||
@@ -20,7 +20,7 @@
|
||||
"@payloadcms/plugin-seo": "^1.0.8",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
"payload": "^1.7.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.9",
|
||||
@@ -30,4 +30,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +1,41 @@
|
||||
import { FormFieldBlock } from 'payload-plugin-form-builder/dist/types'
|
||||
import { FormFieldBlock } from "payload-plugin-form-builder/dist/types"
|
||||
|
||||
export const buildInitialFormState = (fields: FormFieldBlock[]) => {
|
||||
return fields.reduce((initialSchema, field) => {
|
||||
if (field.blockType === 'checkbox') {
|
||||
return {
|
||||
...initialSchema,
|
||||
[field.name]: false,
|
||||
[field.blockName]: false,
|
||||
}
|
||||
}
|
||||
if (field.blockType === 'country') {
|
||||
return {
|
||||
...initialSchema,
|
||||
[field.name]: '',
|
||||
[field.blockName]: '',
|
||||
}
|
||||
}
|
||||
if (field.blockType === 'email') {
|
||||
return {
|
||||
...initialSchema,
|
||||
[field.name]: '',
|
||||
[field.blockName]: '',
|
||||
}
|
||||
}
|
||||
if (field.blockType === 'text') {
|
||||
return {
|
||||
...initialSchema,
|
||||
[field.name]: '',
|
||||
[field.blockName]: '',
|
||||
}
|
||||
}
|
||||
if (field.blockType === 'select') {
|
||||
return {
|
||||
...initialSchema,
|
||||
[field.name]: '',
|
||||
[field.blockName]: '',
|
||||
}
|
||||
}
|
||||
if (field.blockType === 'state') {
|
||||
return {
|
||||
...initialSchema,
|
||||
[field.name]: '',
|
||||
[field.blockName]: '',
|
||||
}
|
||||
}
|
||||
}, {})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "payload-example-form-builder-website",
|
||||
"name": "form-builder-example-website",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -2166,14 +2166,14 @@ scheduler@^0.23.0:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
semver@^6.3.0:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.7:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
"payload": "^1.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "^0.0.1",
|
||||
@@ -43,4 +43,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
MONGODB_URI=mongodb://127.0.0.1/payload-example-draft-preview
|
||||
MONGODB_URI=mongodb://127.0.0.1/payload-example-preview
|
||||
PAYLOAD_SECRET=ENTER-STRING-HERE
|
||||
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
||||
PAYLOAD_PUBLIC_SITE_URL=http://localhost:3001
|
||||
PAYLOAD_PUBLIC_DRAFT_SECRET=EXAMPLE_DRAFT_SECRET
|
||||
COOKIE_DOMAIN=localhost
|
||||
REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY
|
||||
PAYLOAD_PUBLIC_SEED=true
|
||||
PAYLOAD_SEED=true
|
||||
PAYLOAD_DROP_DATABASE=true
|
||||
@@ -1,11 +1,8 @@
|
||||
# Payload Draft Preview Example
|
||||
# Payload Preview Example
|
||||
|
||||
The [Payload Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview/payload) demonstrates how to implement draft preview in [Payload](https://github.com/payloadcms/payload) using [Versions](https://payloadcms.com/docs/versions/overview) and [Drafts](https://payloadcms.com/docs/versions/drafts). Draft preview allows you to see content on your front-end before it is published. There are various fully working front-ends made explicitly for this example, including:
|
||||
This example demonstrates how to implement preview in [Payload](https://github.com/payloadcms/payload) using [Versions](https://payloadcms.com/docs/versions/overview) and [Drafts](https://payloadcms.com/docs/versions/drafts). Preview allows you to see draft content on your front-end before it is published.
|
||||
|
||||
- [Next.js App Router](../next-app)
|
||||
- [Next.js Pages Router](../next-pages)
|
||||
|
||||
Follow the instructions in each respective README to get started. If you are setting up draft preview for another front-end, please consider contributing to this repo with your own example!
|
||||
There is a fully working Next.js app made explicitly for this example which can be found [here](../next-app). Follow the instructions there to get started. If you are setting up preview for another front-end, please consider contributing to this repo with your own example!
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -20,7 +17,7 @@ That's it! Changes made in `./src` will be reflected in your app. See the [Devel
|
||||
|
||||
## How it works
|
||||
|
||||
Draft preview works by sending the user to your front-end with a `secret` along with their http-only cookies. Your front-end catches the request, verifies the authenticity, then enters into it's own preview mode. Once in preview mode, your front-end can begin securely requesting draft documents from Payload. See [Preview Mode](#preview-mode) for more details.
|
||||
Preview works by sending the user to your front-end with a `secret` along with their http-only cookies. Your front-end catches the request, verifies the authenticity, then enters into it's own preview mode. Once in preview mode, your front-end can begin securely requesting draft documents from Payload.
|
||||
|
||||
### Collections
|
||||
|
||||
@@ -30,11 +27,11 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
|
||||
|
||||
The `users` collection is auth-enabled which provides access to the admin panel. When previewing documents on your front-end, the user's JWT is used to authenticate the request. See [Pages](#pages) for more details.
|
||||
|
||||
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth).
|
||||
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth/cms#readme).
|
||||
|
||||
- #### Pages
|
||||
|
||||
The `pages` collection is draft-enabled and has access control that restricts public users from viewing pages with a `_status` of `draft`. To fetch draft documents on your front-end, simply include the `draft=true` query param along with the `Authorization` header once you have entered [Preview Mode](#preview-mode).
|
||||
The `pages` collection is draft-enabled and has access control that restricts public users from viewing pages with a `draft` status. To fetch draft documents on your front-end, simply include the `draft=true` query param along with the `Authorization` header once you have entered [Preview Mode](#preview-mode).
|
||||
|
||||
```ts
|
||||
const preview = true; // set this based on your own front-end environment (see `Preview Mode` below)
|
||||
@@ -55,7 +52,7 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
|
||||
|
||||
### Preview Mode
|
||||
|
||||
To preview draft documents, the user first needs to have at least one draft document saved. When they click the "preview" button from the Payload admin panel, a custom [preview function](https://payloadcms.com/docs/configuration/collections#preview) routes them to your front-end with a `secret` along with their http-only cookies. An API route on your front-end will verify the secret and token before entering into it's own preview mode. Once in preview mode, it can begin requesting drafts from Payload using the `Authorization` header. See [Pages](#pages) for more details.
|
||||
To enter preview mode, the user first needs to have at least one draft document saved. When they click the "preview" button from the Payload admin panel, a custom [preview function](https://payloadcms.com/docs/configuration/collections#preview) routes them to your front-end with a `secret` and their http-only cookies. An API route on your front-end will verify the secret and token before entering into it's own preview mode. Once in preview mode, it can begin requesting drafts from Payload using the `Authorization` header, see [Pages](#pages) for more details.
|
||||
|
||||
> "Preview mode" looks differently for every front-end framework. For instance, check out the differences between Next.js [Preview Mode](https://nextjs.org/docs/pages/building-your-application/configuring/preview-mode) in the Pages Router and [Draft Mode](https://nextjs.org/docs/pages/building-your-application/configuring/draft-mode) in the App Router. In Next.js, methods are provided that set cookies in your browser, but this may not be the case for all frameworks.
|
||||
|
||||
@@ -67,11 +64,11 @@ If your front-end is statically generated then you may also want to regenerate t
|
||||
|
||||
### Admin Bar
|
||||
|
||||
You might also want to render an admin bar on your front-end so that logged-in users can quickly navigate between the front-end and Payload as they're editing. For React apps, check out the official [Payload Admin Bar](https://github.com/payloadcms/payload-admin-bar). For other frameworks, simply hit the `/me` route with `credentials: 'include'` and render your own admin bar if the user is logged in.
|
||||
You might also want to render an admin bar on your front-end so that logged-in users can quickly navigate between the front-end and Payload as they're editing. For React apps, check out the official [Payload Admin Bar](https://github.com/payloadcms/payload-admin-bar). For other frameworks, simply hit the `/me` route with `credentials: include` and render your own admin bar if the user is logged in in order to display quick links to your CMS.
|
||||
|
||||
### CORS
|
||||
|
||||
The [`cors`](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors), [`csrf`](https://payloadcms.com/docs/production/preventing-abuse#cross-site-request-forgery-csrf), and [`cookies`](https://payloadcms.com/docs/authentication/config#options) settings are configured to ensure that the admin panel and front-end can communicate with each other securely. If you are combining your front-end and admin panel into a single application that runs of a shared port and domain, you can simplify your config by removing these settings.
|
||||
The [`cors`](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors), [`csrf`](https://payloadcms.com/docs/production/preventing-abuse#cross-site-request-forgery-csrf), and [`cookies`](https://payloadcms.com/docs/authentication/config#options) settings are configured to ensure that the admin panel and front-end can communicate with each other securely. If you are combining your front-end and admin panel into a single application that runs of a shared port and domain, you can remove these settings from your config.
|
||||
|
||||
For more details on this, see the [CORS](https://payloadcms.com/docs/production/preventing-abuse#cross-origin-resource-sharing-cors) docs.
|
||||
|
||||
@@ -79,9 +76,10 @@ For more details on this, see the [CORS](https://payloadcms.com/docs/production/
|
||||
|
||||
To spin up this example locally, follow the [Quick Start](#quick-start).
|
||||
|
||||
|
||||
### Seed
|
||||
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_PUBLIC_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates a user with email `demo@payloadcms.com` and password `demo` along with a home page and an example page with two versions, one published and the other draft.
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates a user with email `demo@payloadcms.com` and password `demo` along with a home page and an example page with two versions, one published and the other draft.
|
||||
|
||||
> NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"seed": "rm -rf media && cross-env PAYLOAD_PUBLIC_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
|
||||
"seed": "rm -rf media && cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc",
|
||||
"build": "yarn copyfiles && yarn build:payload && yarn build:server",
|
||||
@@ -20,7 +20,7 @@
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
"payload": "^1.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "^0.0.2",
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user