Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Ribbens
8a77320841 chore: add localized to version field 2023-07-25 15:21:21 -04:00
880 changed files with 21217 additions and 78501 deletions

View File

@@ -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:

View File

@@ -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
View File

@@ -18,12 +18,5 @@
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "yarn run dev versions",
"name": "Debug Versions",
"request": "launch",
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
]
}
}

View File

@@ -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)

View File

@@ -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>
&nbsp;
<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>
&nbsp;
<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>
&nbsp;
<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>&nbsp;·&nbsp;<a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a>&nbsp;·&nbsp;<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>Dont 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)&nbsp;·&nbsp;[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

View File

@@ -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.

View File

@@ -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

View File

@@ -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. |

View File

@@ -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: [ /** */],
})
```

View File

@@ -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

View File

@@ -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!

View File

@@ -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)
![Array field in Payload admin panel](https://payloadcms.com/images/docs/fields/array.jpg)
_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) |

View File

@@ -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`.
![Blocks field in Payload admin panel](https://payloadcms.com/images/docs/fields/blocks.jpg)
_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) |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |
| ------------- | ------------------------------------------------------------------------------------ |

View File

@@ -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 fields](https://payloadcms.com/images/docs/fields/richText/rte-link-fields-modal.jpg)
_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 upload element](https://payloadcms.com/images/docs/fields/richText/rte-upload-element.jpg)
_RichText field using the upload element_
*RichText field using the upload element*
![RichText upload element modal](https://payloadcms.com/images/docs/fields/richText/rte-upload-fields-modal.jpg)
_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';
```

View File

@@ -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 |

View File

@@ -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.

View File

@@ -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](https://payloadcms.com/images/docs/fields/tabs/tabs.jpg)
_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._

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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';
```

View File

@@ -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.

View File

@@ -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._

View File

@@ -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`**

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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",

View File

@@ -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 || '',

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -1,5 +1,5 @@
{
"name": "payload-auth-next-app",
"name": "payload-nextjs-auth-example",
"version": "0.1.0",
"private": true,
"scripts": {

View File

@@ -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

View File

@@ -1,5 +1,5 @@
{
"name": "payload-auth-next-pages",
"name": "payload-nextjs-auth-example",
"version": "1.0.0",
"scripts": {
"dev": "next dev -p 3001",

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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"
},

View File

@@ -1,5 +0,0 @@
import { NextResponse } from 'next/server'
export async function GET(): Promise<NextResponse> {
return NextResponse.json({ success: true })
}

View File

@@ -1,5 +0,0 @@
import { NextResponse } from 'next/server'
export async function POST(): Promise<NextResponse> {
return NextResponse.json({ success: true })
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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
}

View File

@@ -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'),
},

View File

@@ -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',
},
})

View File

@@ -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}`)
})

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 ?? []
}

View File

@@ -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() })
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]: '',
}
}
}, {})

View File

@@ -1,5 +1,5 @@
{
"name": "payload-example-form-builder-website",
"name": "form-builder-example-website",
"version": "0.1.0",
"private": true,
"scripts": {

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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