Compare commits
48 Commits
plugin-red
...
payload/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52f8d4f9f0 | ||
|
|
f1fa374ed1 | ||
|
|
6b691eee43 | ||
|
|
be3beabb9b | ||
|
|
1fa00cc25c | ||
|
|
f70943524b | ||
|
|
a67080a291 | ||
|
|
69a99445c9 | ||
|
|
00d8480062 | ||
|
|
7424ba9090 | ||
|
|
657d14c07b | ||
|
|
fbf8ab72a4 | ||
|
|
997f158149 | ||
|
|
c3be5d1d5e | ||
|
|
250bcd8189 | ||
|
|
a71d37b398 | ||
|
|
5c5523195c | ||
|
|
bff4cf518f | ||
|
|
8015e999cd | ||
|
|
0c905f0da7 | ||
|
|
e691a90a4c | ||
|
|
0b2da4fba7 | ||
|
|
1c6d6788a3 | ||
|
|
ecc7978184 | ||
|
|
1b9ee64a67 | ||
|
|
22b02226c3 | ||
|
|
e4102b88d8 | ||
|
|
a099f55a69 | ||
|
|
1f1445c798 | ||
|
|
741a5e3650 | ||
|
|
365047a3fb | ||
|
|
42c06acd18 | ||
|
|
f2c8ac4a9a | ||
|
|
05e8914db7 | ||
|
|
35191bdd66 | ||
|
|
98890eee1f | ||
|
|
ef43629502 | ||
|
|
c703497924 | ||
|
|
7a4607897d | ||
|
|
1cad1a6954 | ||
|
|
5caad706bb | ||
|
|
9e8f14a897 | ||
|
|
f3748a1534 | ||
|
|
aa048d5409 | ||
|
|
fee81bfbc4 | ||
|
|
aafd538cf8 | ||
|
|
1b42bd207d | ||
|
|
9fac2ef24e |
147
CHANGELOG.md
147
CHANGELOG.md
@@ -1,3 +1,150 @@
|
||||
## [2.6.0](https://github.com/payloadcms/payload/compare/v2.5.0...v2.6.0) (2024-01-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **db-mongodb:** add transactionOptions ([f2c8ac4](https://github.com/payloadcms/payload/commit/f2c8ac4a9aa9120339af6759170f5a708469698d))
|
||||
* extend locales to have fallbackLocales ([9fac2ef](https://github.com/payloadcms/payload/commit/9fac2ef24e2ade4cf55b0d6a0e7f67e0edf57539))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* "The punycode module is deprecated" warning by updating nodemailer ([00d8480](https://github.com/payloadcms/payload/commit/00d8480062d99dee56ef61a955f48a92efa6cbea))
|
||||
* adjusts json field joi schema to allow editorOptions ([bff4cf5](https://github.com/payloadcms/payload/commit/bff4cf518f748efb9179f112c606d11d25db3d99))
|
||||
* **db-postgres:** Wait for transaction to complete on commit ([#4582](https://github.com/payloadcms/payload/issues/4582)) ([a71d37b](https://github.com/payloadcms/payload/commit/a71d37b39806cd5956378a10246802d01d06c2dd))
|
||||
* detect language from request headers accept-language ([#4656](https://github.com/payloadcms/payload/issues/4656)) ([69a9944](https://github.com/payloadcms/payload/commit/69a99445c9f1638a962a9c08ffe0bdc22e538bf6))
|
||||
* graphql multiple locales ([98890ee](https://github.com/payloadcms/payload/commit/98890eee1f527c8f245b2353d7e1caca4d2a7d8c))
|
||||
* navigation locks when modal is closed with esc ([#4664](https://github.com/payloadcms/payload/issues/4664)) ([be3beab](https://github.com/payloadcms/payload/commit/be3beabb9bafa137aa89e84cf47246017e969be8))
|
||||
* req.locale and req.fallbackLocale get reassigned in local operations ([aa048d5](https://github.com/payloadcms/payload/commit/aa048d5409acd42b8f56367a16934085df9fbce2))
|
||||
* resets actions array when navigating out of view with actions ([#4585](https://github.com/payloadcms/payload/issues/4585)) ([5c55231](https://github.com/payloadcms/payload/commit/5c5523195ccfa94a9bf42441e2a378f87836e64d))
|
||||
* **richtext-lexical:** z-index issues ([#4570](https://github.com/payloadcms/payload/issues/4570)) ([8015e99](https://github.com/payloadcms/payload/commit/8015e999cd5834f532a200ef03fd392d04b3209f))
|
||||
* tab field error when using the same interface name ([#4657](https://github.com/payloadcms/payload/issues/4657)) ([f1fa374](https://github.com/payloadcms/payload/commit/f1fa374ed12b50fdf210f17ae1dda603f09a9726))
|
||||
|
||||
## [2.5.0](https://github.com/payloadcms/payload/compare/v2.4.0...v2.5.0) (2023-12-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add Chinese Traditional translation ([#4372](https://github.com/payloadcms/payload/issues/4372)) ([50253f6](https://github.com/payloadcms/payload/commit/50253f617c22d0d185bbac7f9d4304cddbc01f06))
|
||||
* add context to auth and globals local API ([#4449](https://github.com/payloadcms/payload/issues/4449)) ([168d629](https://github.com/payloadcms/payload/commit/168d6296974042c3ff2a113f9f6c2bded7ba2b3e))
|
||||
* adds new `actions` property to admin customization ([#4468](https://github.com/payloadcms/payload/issues/4468)) ([9e8f14a](https://github.com/payloadcms/payload/commit/9e8f14a897e77f6933eedb2410956a468f4187c3))
|
||||
* async live preview urls ([#4339](https://github.com/payloadcms/payload/issues/4339)) ([5f17324](https://github.com/payloadcms/payload/commit/5f173241df6dc316d498767b1c81718e9c2b9a51))
|
||||
* pass path to FieldDescription ([#4364](https://github.com/payloadcms/payload/issues/4364)) ([3b8a27d](https://github.com/payloadcms/payload/commit/3b8a27d199b3969cbca6ca750450798cb70f21e8))
|
||||
* **plugin-form-builder:** Lexical support ([#4487](https://github.com/payloadcms/payload/issues/4487)) ([c6c5cab](https://github.com/payloadcms/payload/commit/c6c5cabfbb7eb954eea51170a6af7582b1f9b84b))
|
||||
* prevent querying relationship when filterOptions returns false ([#4392](https://github.com/payloadcms/payload/issues/4392)) ([c1bd338](https://github.com/payloadcms/payload/commit/c1bd338d0d5e899f3892f1d18e355c00b265447a))
|
||||
* **richtext-lexical:** improve floating select menu Dropdown classNames ([#4444](https://github.com/payloadcms/payload/issues/4444)) ([9331204](https://github.com/payloadcms/payload/commit/9331204295bfeaf7dd10bc075f42995b2cab2de4))
|
||||
* **richtext-lexical:** improve link URL validation ([#4442](https://github.com/payloadcms/payload/issues/4442)) ([9babf68](https://github.com/payloadcms/payload/commit/9babf6804ce04d5828167eb8e7717727fe1cd472))
|
||||
* **richtext-lexical:** lazy import React components to prevent client-only code from leaking into the server ([#4290](https://github.com/payloadcms/payload/issues/4290)) ([5de347f](https://github.com/payloadcms/payload/commit/5de347ffffca3bf38315d3d87d2ccc5c28cd2723))
|
||||
* **richtext-lexical:** Link & Relationship Feature: field-level configurable allowed relationships ([#4182](https://github.com/payloadcms/payload/issues/4182)) ([7af8f29](https://github.com/payloadcms/payload/commit/7af8f29b4a8dddf389356e4db142f8d434cdc964))
|
||||
* **richtext-lexical:** link node: change doc data format to be consistent with relationship field ([#4504](https://github.com/payloadcms/payload/issues/4504)) ([cc0ba89](https://github.com/payloadcms/payload/commit/cc0ba895188f40181c6ba3779f66d547d4ea66f9))
|
||||
* **richtext-lexical:** rename TreeviewFeature into TreeViewFeature ([#4520](https://github.com/payloadcms/payload/issues/4520)) ([c49fd66](https://github.com/payloadcms/payload/commit/c49fd6692231b68ca61b079103a0fd7aa4673be1))
|
||||
* **richtext-lexical:** Slate to Lexical converter: add blockquote conversion, convert custom link fields ([#4486](https://github.com/payloadcms/payload/issues/4486)) ([31f8f3c](https://github.com/payloadcms/payload/commit/31f8f3cac6bfd08f3adfa0a026a57c4b1b510045))
|
||||
* **richtext-lexical:** Upload html serializer: Output picture element if the image has multiple sizes, improve absolute URL creation ([e558894](https://github.com/payloadcms/payload/commit/e55889480fceb8995646621923159d92de6e89c9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds bg color for year/month select options in datepicker ([#4508](https://github.com/payloadcms/payload/issues/4508)) ([07371b9](https://github.com/payloadcms/payload/commit/07371b9cad111999f2df4e1f709d6b95cd511c15))
|
||||
* correctly fetches externally stored files when passing uploadEdits ([#4505](https://github.com/payloadcms/payload/issues/4505)) ([228d45c](https://github.com/payloadcms/payload/commit/228d45cf52e592cea6377cd93648fba75d73c88d))
|
||||
* cursor jumping around inside json field ([#4453](https://github.com/payloadcms/payload/issues/4453)) ([6300037](https://github.com/payloadcms/payload/commit/63000373e66fb39443f882689e0ecf5c11ed8ad0))
|
||||
* **db-mongodb:** documentDB unique constraint throws incorrect error ([#4513](https://github.com/payloadcms/payload/issues/4513)) ([05e8914](https://github.com/payloadcms/payload/commit/05e8914db70fa64bfb2d15ecfb58e9c229d71108))
|
||||
* **db-postgres:** findOne correctly querying with where queries ([#4550](https://github.com/payloadcms/payload/issues/4550)) ([8bc31cd](https://github.com/payloadcms/payload/commit/8bc31cd5923517ab39ae1427aa0d0fb19d876dab))
|
||||
* **db-postgres:** querying nested blocks fields ([#4404](https://github.com/payloadcms/payload/issues/4404)) ([6e9ae65](https://github.com/payloadcms/payload/commit/6e9ae65374124ee000cc2988ef77247c94b0dd18))
|
||||
* **db-postgres:** sorting on a not-configured field throws error ([#4382](https://github.com/payloadcms/payload/issues/4382)) ([dbaecda](https://github.com/payloadcms/payload/commit/dbaecda0e92fcb0fa67b4c5ac085e025f02de53a))
|
||||
* defaultValues computed on new globals ([#4380](https://github.com/payloadcms/payload/issues/4380)) ([b6cffce](https://github.com/payloadcms/payload/commit/b6cffcea07b9fa21698b00b8bbed6f27197ded41))
|
||||
* disallow duplicate fieldNames to be used on the same level in the config ([#4381](https://github.com/payloadcms/payload/issues/4381)) ([a1d66b8](https://github.com/payloadcms/payload/commit/a1d66b83e0dbea21e8da549b73cd25c537a57938))
|
||||
* ensure ui fields do not make it into gql schemas ([#4457](https://github.com/payloadcms/payload/issues/4457)) ([3a20ddc](https://github.com/payloadcms/payload/commit/3a20ddc5f85162a316006f22ba66ee1c7aab99e3))
|
||||
* format fields within tab for list controls ([#4516](https://github.com/payloadcms/payload/issues/4516)) ([2650c70](https://github.com/payloadcms/payload/commit/2650c70960a7374307a8862c3940c97d337d1d30))
|
||||
* formats locales with multiple labels for versions locale selector ([#4495](https://github.com/payloadcms/payload/issues/4495)) ([8257661](https://github.com/payloadcms/payload/commit/8257661c47b5b968a57fb2228d7045d876a3f484))
|
||||
* graphql schema generation for fields without queryable subfields ([#4463](https://github.com/payloadcms/payload/issues/4463)) ([13e3e06](https://github.com/payloadcms/payload/commit/13e3e0671353ca34e603fece57a12199f2082ca0))
|
||||
* handles null upload field values ([#4397](https://github.com/payloadcms/payload/issues/4397)) ([cf9a370](https://github.com/payloadcms/payload/commit/cf9a3704df21ce8b32feb0680793cba804cd66f7))
|
||||
* **live-preview:** populates rte uploads and relationships ([#4379](https://github.com/payloadcms/payload/issues/4379)) ([4090aeb](https://github.com/payloadcms/payload/commit/4090aebb0e94e776258f0c1c761044a4744a1857))
|
||||
* **live-preview:** sends raw js objects through window.postMessage instead of json ([#4354](https://github.com/payloadcms/payload/issues/4354)) ([03a3872](https://github.com/payloadcms/payload/commit/03a387233d1b8876a2fcaa5f3b3fd5ed512c0bc4))
|
||||
* make admin navigation transition smoother ([#4217](https://github.com/payloadcms/payload/issues/4217)) ([eb6572e](https://github.com/payloadcms/payload/commit/eb6572e9e56e680cad331c1bc5da47e91306deb9))
|
||||
* omit field default value if read access returns false ([#4518](https://github.com/payloadcms/payload/issues/4518)) ([3e9ef84](https://github.com/payloadcms/payload/commit/3e9ef849cd8e69e1e8d7f2f653f0647e93c8ab39))
|
||||
* pin ts-node versions which are causing swc errors ([#4447](https://github.com/payloadcms/payload/issues/4447)) ([b9c0248](https://github.com/payloadcms/payload/commit/b9c024882350d14edd57f0f662a2269ed37975e3))
|
||||
* properly spreads collection fields into non-tabbed configs [#50](https://github.com/payloadcms/payload/issues/50) ([#51](https://github.com/payloadcms/payload/issues/51)) ([7e88159](https://github.com/payloadcms/payload/commit/7e88159e99e2afdc10addc02cf299c11fe188be7))
|
||||
* **plugin-form-builder:** removes use of slate in rich-text serializer ([#4451](https://github.com/payloadcms/payload/issues/4451)) ([3df52a8](https://github.com/payloadcms/payload/commit/3df52a88568622f8fafeabad47c7501229e4ea5f))
|
||||
* **plugin-nested-docs:** properly exports field utilities ([#4462](https://github.com/payloadcms/payload/issues/4462)) ([1cc87bd](https://github.com/payloadcms/payload/commit/1cc87bd8ea575dfa2e1f5ce5b38414bbba95b2cb))
|
||||
* **richtext-*:** loosen RichTextAdapter types due to re-occuring ts strict mode errors ([#4416](https://github.com/payloadcms/payload/issues/4416)) ([48f1299](https://github.com/payloadcms/payload/commit/48f1299fcba3e3811c6a7f31499f238537f9a5e3))
|
||||
* **richtext-lexical:** Blocks field: should not prompt for unsaved changes due to value comparison between null and non-existent props ([#4450](https://github.com/payloadcms/payload/issues/4450)) ([548e78c](https://github.com/payloadcms/payload/commit/548e78c598cb6d029e7cc40f80d9855754f043bc))
|
||||
* **richtext-lexical:** do not add unnecessary paragraph before upload, relationship and blocks nodes ([#4441](https://github.com/payloadcms/payload/issues/4441)) ([5c2739e](https://github.com/payloadcms/payload/commit/5c2739ebd144620cfd4ff02531f5812dd62ac61d))
|
||||
* **richtext-lexical:** lexicalHTML field not working when used inside of Blocks field ([128f9c4](https://github.com/payloadcms/payload/commit/128f9c4e7e6e20dd1ee221f49428a5bce5179c5f))
|
||||
* **richtext-lexical:** lexicalHTML field now works when used inside of row fields ([#4440](https://github.com/payloadcms/payload/issues/4440)) ([0421173](https://github.com/payloadcms/payload/commit/0421173f9e2d6db1b6a94b25884ea807921f2d09))
|
||||
* **richtext-lexical:** not all types of URLs are validated correctly ([ac7f980](https://github.com/payloadcms/payload/commit/ac7f9809bc2b9fb6a52b48c10f7d51414801e4de))
|
||||
* searching by id sends undefined in where query param ([#4464](https://github.com/payloadcms/payload/issues/4464)) ([46e8c01](https://github.com/payloadcms/payload/commit/46e8c01fbed68856be68804f2bd9744c4c6f5a95))
|
||||
* simplifies query validation and fixes nested relationship fields ([#4391](https://github.com/payloadcms/payload/issues/4391)) ([4b5453e](https://github.com/payloadcms/payload/commit/4b5453e8e5484f7afcadbf5bccf8369b552969c6))
|
||||
* updates return value of empty arrays in getDataByPath ([#4553](https://github.com/payloadcms/payload/issues/4553)) ([f3748a1](https://github.com/payloadcms/payload/commit/f3748a1534a13e6d844aadd9f0e3e6acbe483d03))
|
||||
* upload editing error with plugin-cloud ([#4170](https://github.com/payloadcms/payload/issues/4170)) ([fcbe574](https://github.com/payloadcms/payload/commit/fcbe5744d945dc43642cdaa2007ddc252ecafafa))
|
||||
* upload related issues, cropping, fetching local file, external preview image ([#4461](https://github.com/payloadcms/payload/issues/4461)) ([45c472d](https://github.com/payloadcms/payload/commit/45c472d6b35c41e597038089ad1755cdb88193b6))
|
||||
* uploads files after validation ([#4218](https://github.com/payloadcms/payload/issues/4218)) ([65adfd2](https://github.com/payloadcms/payload/commit/65adfd21ed538b79628dc4f8ce9e1a5a1bba6aed))
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
#### @payloadcms/richtext-lexical
|
||||
|
||||
* **richtext-lexical:** rename TreeviewFeature into TreeViewFeature ([#4520](https://github.com/payloadcms/payload/issues/4520)) ([c49fd66](https://github.com/payloadcms/payload/commit/c49fd6692231b68ca61b079103a0fd7aa4673be1))
|
||||
|
||||
If you import TreeviewFeature, you have to rename the import to use TreeViewFeature (capitalized "V")
|
||||
|
||||
* **richtext-lexical:** link node: change doc data format to be consistent with relationship field ([#4504](https://github.com/payloadcms/payload/issues/4504)) ([cc0ba89](https://github.com/payloadcms/payload/commit/cc0ba895188f40181c6ba3779f66d547d4ea66f9))
|
||||
|
||||
An unpopulated, internal link node no longer saves the doc id under fields.doc.value.id. Now, it saves it under fields.doc.value.
|
||||
Migration inside of payload is automatic. If you are reading from the link node inside of your frontend though, you will have to adjust it.
|
||||
|
||||
* **richtext-lexical:** improve floating select menu Dropdown classNames ([#4444](https://github.com/payloadcms/payload/issues/4444)) ([9331204](https://github.com/payloadcms/payload/commit/9331204295bfeaf7dd10bc075f42995b2cab2de4))
|
||||
|
||||
Dropdown component has a new mandatory sectionKey prop
|
||||
|
||||
* **richtext-lexical:** lazy import React components to prevent client-only code from leaking into the server ([#4290](https://github.com/payloadcms/payload/issues/4290)) ([5de347f](https://github.com/payloadcms/payload/commit/5de347ffffca3bf38315d3d87d2ccc5c28cd2723))
|
||||
|
||||
1. Most important: If you are updating `@payloadcms/richtext-lexical` to v0.4.0 or higher, you will HAVE to update payload to the latest version as well. If you don't update it, payload likely won't start up due to validation errors. It's generally good practice to upgrade packages prefixed with @payloadcms/ together with payload and keep the versions in sync.
|
||||
|
||||
2. `@payloadcms/richtext-slate` is not affected by this.
|
||||
|
||||
3. Every single property in the `Feature` interface which accepts a React component now no longer accepts a React component, but a function which imports a React component instead. This is done to ensure no unnecessary client-only code is leaked to the server when importing Features on a server.
|
||||
Here's an example migration:
|
||||
|
||||
Old:
|
||||
|
||||
```ts
|
||||
import { BlockIcon } from '../../lexical/ui/icons/Block'
|
||||
...
|
||||
Icon: BlockIcon,
|
||||
```
|
||||
|
||||
New:
|
||||
|
||||
```ts
|
||||
// import { BlockIcon } from '../../lexical/ui/icons/Block' // <= Remove this import
|
||||
...
|
||||
Icon: () =>
|
||||
// @ts-expect-error
|
||||
import('../../lexical/ui/icons/Block').then((module) => module.BlockIcon),
|
||||
```
|
||||
|
||||
Or alternatively, if you're using default exports instead of named exports:
|
||||
|
||||
```ts
|
||||
// import BlockIcon from '../../lexical/ui/icons/Block' // <= Remove this import
|
||||
...
|
||||
Icon: () =>
|
||||
// @ts-expect-error
|
||||
import('../../lexical/ui/icons/Block'),
|
||||
```
|
||||
|
||||
4. The types for `SanitizedEditorConfig` and `EditorConfig` have changed. Their respective `lexical` property no longer expects the `LexicalEditorConfig`. It now expects a function returning the `LexicalEditorConfig`. You will have to adjust this if you adjusted that property anywhere, e.g. when initializing the lexical field editor property, or when initializing a new headless editor.
|
||||
|
||||
5. The following exports are now exported from the `@payloadcms/richtext-lexical/components` subpath exports instead of `@payloadcms/richtext-lexical`:
|
||||
|
||||
- `ToolbarButton`
|
||||
- `ToolbarDropdown`
|
||||
- `RichTextCell`
|
||||
- `RichTextField`
|
||||
- `defaultEditorLexicalConfig`
|
||||
|
||||
You will have to adjust your imports, only if you import any of those properties in your project.
|
||||
|
||||
## @payloadcms/richtext-*
|
||||
|
||||
### [@payloadcms/richtext-lexical 0.4.1](https://github.com/payloadcms/payload/compare/richtext-lexical/0.4.0...richtext-lexical/0.4.1) (2023-12-07)
|
||||
|
||||
@@ -27,14 +27,15 @@ You can override a set of admin panel-wide components by providing a component t
|
||||
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
|
||||
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
|
||||
| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
|
||||
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/main/test/admin/components/AfterDashboard/index.tsx) |
|
||||
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/main/test/admin/components/AfterDashboard/index.tsx) |
|
||||
| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. |
|
||||
| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. |
|
||||
| **`logout.Button`** | A custom React component. |
|
||||
| **`graphics.Icon`** | Used as a graphic within the `Nav` component. Often represents a condensed version of a full logo. |
|
||||
| **`graphics.Logo`** | The full logo to be used in contexts like the `Login` view. |
|
||||
| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) |
|
||||
| **`views`** | Override or create new views within the Payload Admin UI. [More](#views) |
|
||||
| **`actions`** | Array of custom components to be rendered in the Payload Admin UI header, providing additional interactivity and functionality. |
|
||||
| **`views`** | Override or create new views within the Payload Admin UI. [More](#views) |
|
||||
|
||||
Here is a full example showing how to swap some of these components for your own.
|
||||
|
||||
@@ -50,6 +51,7 @@ import {
|
||||
MyCustomAccount,
|
||||
MyCustomDashboard,
|
||||
MyProvider,
|
||||
MyCustomAdminAction,
|
||||
} from './customComponents'
|
||||
|
||||
export default buildConfig({
|
||||
@@ -60,6 +62,7 @@ export default buildConfig({
|
||||
Icon: MyCustomIcon,
|
||||
Logo: MyCustomLogo,
|
||||
},
|
||||
actions: [MyCustomAdminAction],
|
||||
views: {
|
||||
Account: MyCustomAccount,
|
||||
Dashboard: MyCustomDashboard,
|
||||
@@ -243,7 +246,11 @@ To swap out any of these views, simply pass in your custom component to the `adm
|
||||
|
||||
_For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component)._
|
||||
|
||||
To swap specific _nested_ views within the parent `Edit` view, you can use the `admin.components.views.Edit` property on the globals's config. This will only replace the nested view, leaving the page breadcrumbs, title, tabs, etc intact.
|
||||
**Customizing Nested Views within 'Edit' in Collections**
|
||||
|
||||
The `Edit` view in collections consists of several nested views, each serving a unique purpose. You can customize these nested views using the `admin.components.views.Edit` property in the collection's configuration. This approach allows you to replace specific nested views while keeping the overall structure of the `Edit` view intact, including the page breadcrumbs, title, tabs, etc.
|
||||
|
||||
Here's an example of how you can customize nested views within the `Edit` view in collections, including the use of the `actions` property:
|
||||
|
||||
```ts
|
||||
// Collection.ts
|
||||
@@ -253,7 +260,29 @@ To swap specific _nested_ views within the parent `Edit` view, you can use the `
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Default: MyCustomDefaultTab,
|
||||
Default: {
|
||||
Component: MyCustomDefaultTab,
|
||||
actions: [CollectionEditButton], // Custom actions for the default edit view
|
||||
},
|
||||
API: {
|
||||
Component: MyCustomAPIView,
|
||||
actions: [CollectionAPIButton], // Custom actions for API view
|
||||
},
|
||||
LivePreview: {
|
||||
Component: MyCustomLivePreviewView,
|
||||
actions: [CollectionLivePreviewButton], // Custom actions for Live Preview
|
||||
},
|
||||
Version: {
|
||||
Component: MyCustomVersionView,
|
||||
actions: [CollectionVersionButton], // Custom actions for Version view
|
||||
},
|
||||
Versions: {
|
||||
Component: MyCustomVersionsView,
|
||||
actions: [CollectionVersionsButton], // Custom actions for Versions view
|
||||
},
|
||||
},
|
||||
List: {
|
||||
actions: [CollectionListButton],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -261,6 +290,8 @@ To swap specific _nested_ views within the parent `Edit` view, you can use the `
|
||||
}
|
||||
```
|
||||
|
||||
**Adding New Tabs to 'Edit' View**
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Globals
|
||||
@@ -301,7 +332,11 @@ To swap out any of these views, simply pass in your custom component to the `adm
|
||||
|
||||
_For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component)._
|
||||
|
||||
To swap specific _nested_ views within the parent `Edit` view, you can use the `admin.components.views.Edit` property on the globals's config. This will only replace the nested view, leaving the page breadcrumbs, title, and tabs intact.
|
||||
**Customizing Nested Views within 'Edit' in Globals**
|
||||
|
||||
Similar to collections, Globals allow for detailed customization within the `Edit` view. This includes the ability to swap specific nested views while maintaining the overall structure of the `Edit` view. You can use the `admin.components.views.Edit` property in the Globals configuration to achieve this, and this will only replace the nested view, leaving the page breadcrumbs, title, and tabs intact.
|
||||
|
||||
Here's how you can customize nested views within the `Edit` view in Globals, including the use of the `actions` property:
|
||||
|
||||
```ts
|
||||
// Global.ts
|
||||
@@ -311,7 +346,26 @@ To swap specific _nested_ views within the parent `Edit` view, you can use the `
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Default: MyCustomDefaultTab,
|
||||
Default: {
|
||||
Component: MyCustomGlobalDefaultTab,
|
||||
actions: [GlobalEditButton], // Custom actions for the default edit view
|
||||
},
|
||||
API: {
|
||||
Component: MyCustomGlobalAPIView,
|
||||
actions: [GlobalAPIButton], // Custom actions for API view
|
||||
},
|
||||
LivePreview: {
|
||||
Component: MyCustomGlobalLivePreviewView,
|
||||
actions: [GlobalLivePreviewButton], // Custom actions for Live Preview
|
||||
},
|
||||
Version: {
|
||||
Component: MyCustomGlobalVersionView,
|
||||
actions: [GlobalVersionButton], // Custom actions for Version view
|
||||
},
|
||||
Versions: {
|
||||
Component: MyCustomGlobalVersionsView,
|
||||
actions: [GlobalVersionsButton], // Custom actions for Versions view
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -324,7 +324,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
},
|
||||
{
|
||||
drawerTitle: 'addFieldRow',
|
||||
drawerDescription: 'A useful method to programtically add a row to an array or block field.',
|
||||
drawerDescription: 'A useful method to programmatically add a row to an array or block field.',
|
||||
drawerSlug: 'addFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
@@ -434,7 +434,7 @@ export const CustomArrayManager = () => {
|
||||
},
|
||||
{
|
||||
drawerTitle: 'removeFieldRow',
|
||||
drawerDescription: 'A useful method to programtically remove a row from an array or block field.',
|
||||
drawerDescription: 'A useful method to programmatically remove a row from an array or block field.',
|
||||
drawerSlug: 'removeFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
@@ -531,7 +531,7 @@ export const CustomArrayManager = () => {
|
||||
},
|
||||
{
|
||||
drawerTitle: 'replaceFieldRow',
|
||||
drawerDescription: 'A useful method to programtically replace a row from an array or block field.',
|
||||
drawerDescription: 'A useful method to programmatically replace a row from an array or block field.',
|
||||
drawerSlug: 'replaceFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
|
||||
@@ -6,11 +6,13 @@ desc: Add and maintain as many locales as you need by adding Localization to you
|
||||
keywords: localization, internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All localization support is opt-in by default. To do so, follow the two steps below.
|
||||
Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All
|
||||
localization support is opt-in by default. To do so, follow the two steps below.
|
||||
|
||||
### Enabling in the Payload config
|
||||
|
||||
Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.
|
||||
Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a
|
||||
list of all locales that you'd like to support as well as set a few other options.
|
||||
|
||||
**Example Payload config set up for localization:**
|
||||
|
||||
@@ -57,7 +59,8 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
**Example Payload config set up for localization with full locales objects (including [internationalization](/docs/configuration/i18n) support):**
|
||||
**Example Payload config set up for localization with full locales objects (
|
||||
including [internationalization](/docs/configuration/i18n) support):**
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
@@ -93,35 +96,60 @@ export default buildConfig({
|
||||
|
||||
**`locales`**
|
||||
|
||||
Array-based list of all locales that you would like to support. These can be strings of locale codes or objects with a `label`, a locale `code`, and the `rtl` (right-to-left) property. The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.
|
||||
Array-based list of all the languages that you would like to support. This can be an array containing strings for each
|
||||
language code you want your project to store and serve or objects with a `label`, a locale `code`, `rtl` (
|
||||
right-to-left), and `fallbackLocale` property. The locale codes do not need to be in any specific format. It's up to you
|
||||
to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter
|
||||
language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.
|
||||
|
||||
### Locale Properties:
|
||||
|
||||
| Option | Description |
|
||||
|----------------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`code`** \* | Unique code to identify the language throughout the APIs for `locale` and `fallbackLocale` |
|
||||
| **`label`** | A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use. |
|
||||
| **`rtl`** | A boolean that when true will make the admin UI display in Right-To-Left. |
|
||||
| **`fallbackLocale`** | The code for this language to fallback to when properties of a document are not present. |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
**`defaultLocale`**
|
||||
|
||||
Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale.
|
||||
Required string that matches one of the locale codes from the array provided. By default, if no locale is specified,
|
||||
documents will be returned in this locale.
|
||||
|
||||
**`fallback`**
|
||||
|
||||
Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated.
|
||||
Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a
|
||||
localized value corresponding to the requested locale, then if this property is enabled, the document will automatically
|
||||
fall back to the fallback locale value. If this property is not enabled, the value will not be populated.
|
||||
|
||||
### Field by field localization
|
||||
|
||||
Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config to support localization, you need to specify each field that you would like to localize.
|
||||
Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config
|
||||
to support localization, you need to specify each field that you would like to localize.
|
||||
|
||||
**Here is an example of how to enable localization for a field:**
|
||||
|
||||
```js
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
// highlight-start
|
||||
localized: true,
|
||||
type
|
||||
:
|
||||
'text',
|
||||
// highlight-start
|
||||
localized
|
||||
:
|
||||
true,
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
With the above configuration, the `title` field will now be saved in the database as an object of all locales instead of a single string.
|
||||
With the above configuration, the `title` field will now be saved in the database as an object of all locales instead of
|
||||
a single string.
|
||||
|
||||
All field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s.
|
||||
All field types with a `name` property support the `localized` property—even the more complex field types like `array`s
|
||||
and `block`s.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong>
|
||||
@@ -143,7 +171,8 @@ All field types with a `name` property support the `localized` property—even t
|
||||
|
||||
### Retrieving localized docs
|
||||
|
||||
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be used.
|
||||
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be
|
||||
used.
|
||||
|
||||
##### REST API
|
||||
|
||||
@@ -155,7 +184,8 @@ Specify your desired locale by providing the `locale` query parameter directly i
|
||||
|
||||
**`?fallback-locale=`**
|
||||
|
||||
Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a valid locale as provided to your base Payload config, or `'null'`, `'false'`, or `'none'` to disable falling back.
|
||||
Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a
|
||||
valid locale as provided to your base Payload config, or `'null'`, `'false'`, or `'none'` to disable falling back.
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -167,7 +197,9 @@ fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none');
|
||||
|
||||
In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.
|
||||
|
||||
The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground).
|
||||
The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum
|
||||
values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious
|
||||
to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground).
|
||||
|
||||
The `fallbackLocale` arg will accept valid locales as well as `none` to disable falling back.
|
||||
|
||||
@@ -175,11 +207,11 @@ The `fallbackLocale` arg will accept valid locales as well as `none` to disable
|
||||
|
||||
```graphql
|
||||
query {
|
||||
Posts(locale: de, fallbackLocale: none) {
|
||||
docs {
|
||||
title
|
||||
Posts(locale: de, fallbackLocale: none) {
|
||||
docs {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -191,7 +223,9 @@ query {
|
||||
|
||||
##### Local API
|
||||
|
||||
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options` argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid locale as well as `'null'`, `'false'`, `false`, and `'none'`.
|
||||
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`
|
||||
argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid
|
||||
locale as well as `'null'`, `'false'`, `false`, and `'none'`.
|
||||
|
||||
**Example:**
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ desc: Payload has supported MongoDB natively since we started. The flexible natu
|
||||
keywords: MongoDB, documentation, typescript, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
To use Payload with MongoDB, install the package `@payloadcms/db-mongodb`. It will come with everything you need to store your Payload data in MongoDB.
|
||||
To use Payload with MongoDB, install the package `@payloadcms/db-mongodb`. It will come with everything you need to
|
||||
store your Payload data in MongoDB.
|
||||
|
||||
Then from there, pass it to your Payload config as follows:
|
||||
|
||||
@@ -29,16 +30,18 @@ export default buildConfig({
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
|
||||
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
|
||||
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
| Option | Description |
|
||||
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
|
||||
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
|
||||
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
| `transactionOptions` | An object with configuration properties used in [transactions](https://www.mongodb.com/docs/manual/core/transactions/) or `false` which will disable the use of transactions. | |
|
||||
|
||||
### Access to Mongoose models
|
||||
|
||||
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work with directly.
|
||||
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work
|
||||
with directly.
|
||||
|
||||
You can access Mongoose models as follows:
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
})
|
||||
|
||||
// Should this call fail, it will not rollback other changes
|
||||
// because the req (and its transationID) is not passed through
|
||||
// because the req (and its transactionID) is not passed through
|
||||
const safelyIgnoredAsync = req.payload.create({
|
||||
collection: 'my-slug',
|
||||
data: {
|
||||
|
||||
@@ -133,7 +133,7 @@ import * as React from 'react';
|
||||
import { SelectInput, useField } from 'payload/components/forms';
|
||||
import { useAuth } from 'payload/components/utilities';
|
||||
|
||||
type customSelectProps = {
|
||||
type CustomSelectProps = {
|
||||
path: string;
|
||||
options: {
|
||||
label: string;
|
||||
@@ -164,7 +164,7 @@ export const CustomSelectComponent: React.FC<CustomSelectProps> = ({ path, optio
|
||||
name={path}
|
||||
options={adjustedOptions}
|
||||
value={value}
|
||||
onChange={() => setValue(e.value)}
|
||||
onChange={(e) => setValue(e.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -287,5 +287,5 @@ import {
|
||||
|
||||
## Examples
|
||||
|
||||
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) contains an official [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommere) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end. You can also check out [How to Build An E-Commerce Site With Next.js](https://payloadcms.com/blog/how-to-build-an-e-commerce-site-with-nextjs) post for a bit more context around this template.
|
||||
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) contains an official [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end. You can also check out [How to Build An E-Commerce Site With Next.js](https://payloadcms.com/blog/how-to-build-an-e-commerce-site-with-nextjs) post for a bit more context around this template.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -23,15 +23,17 @@
|
||||
"bson-objectid": "2.0.4",
|
||||
"deepmerge": "4.3.1",
|
||||
"get-port": "5.1.1",
|
||||
"mongoose": "6.12.0",
|
||||
"mongoose": "6.12.3",
|
||||
"mongoose-aggregate-paginate-v2": "1.0.6",
|
||||
"mongoose-paginate-v2": "1.7.22",
|
||||
"prompts": "2.4.2",
|
||||
"http-status": "1.6.2",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/mongoose-aggregate-paginate-v2": "1.0.9",
|
||||
"mongodb": "4.17.1",
|
||||
"mongodb-memory-server": "8.13.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -48,6 +48,13 @@ export const connect: Connect = async function connect(this: MongooseAdapter, pa
|
||||
try {
|
||||
this.connection = (await mongoose.connect(urlToConnect, connectionOptions)).connection
|
||||
|
||||
const client = this.connection.getClient()
|
||||
|
||||
if (!client.options.replicaSet || this.transactionOptions === false) {
|
||||
this.transactionOptions = false
|
||||
this.beginTransaction = undefined
|
||||
}
|
||||
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
this.payload.logger.info('---- DROPPING DATABASE ----')
|
||||
await mongoose.connection.dropDatabase()
|
||||
|
||||
@@ -3,9 +3,8 @@ import type { Document, PayloadRequest } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import handleError from './utilities/handleError'
|
||||
import { withSession } from './withSession'
|
||||
import { ValidationError } from 'payload/errors'
|
||||
import { i18nInit } from 'payload/utilities'
|
||||
|
||||
export const create: Create = async function create(
|
||||
this: MongooseAdapter,
|
||||
@@ -17,18 +16,7 @@ export const create: Create = async function create(
|
||||
try {
|
||||
;[doc] = await Model.create([data], options)
|
||||
} catch (error) {
|
||||
// Handle uniqueness error from MongoDB
|
||||
throw error.code === 11000 && error.keyValue
|
||||
? new ValidationError(
|
||||
[
|
||||
{
|
||||
field: Object.keys(error.keyValue)[0],
|
||||
message: req.t('error:valueMustBeUnique'),
|
||||
},
|
||||
],
|
||||
req?.t ?? i18nInit(this.payload.config.i18n).t,
|
||||
)
|
||||
: error
|
||||
handleError(error, req)
|
||||
}
|
||||
|
||||
// doc.toJSON does not do stuff like converting ObjectIds to string, or date strings to date objects. That's why we use JSON.parse/stringify here
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { TransactionOptions } from 'mongodb'
|
||||
import type { ClientSession, ConnectOptions, Connection } from 'mongoose'
|
||||
import type { Payload } from 'payload'
|
||||
import type { BaseDatabaseAdapter } from 'payload/database'
|
||||
@@ -7,8 +8,6 @@ import mongoose from 'mongoose'
|
||||
import path from 'path'
|
||||
import { createDatabaseAdapter } from 'payload/database'
|
||||
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types'
|
||||
|
||||
import type { CollectionModel, GlobalModel } from './types'
|
||||
|
||||
import { connect } from './connect'
|
||||
@@ -39,6 +38,8 @@ import { updateGlobalVersion } from './updateGlobalVersion'
|
||||
import { updateOne } from './updateOne'
|
||||
import { updateVersion } from './updateVersion'
|
||||
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types'
|
||||
|
||||
export interface Args {
|
||||
/** Set to false to disable auto-pluralization of collection names, Defaults to true */
|
||||
autoPluralization?: boolean
|
||||
@@ -50,6 +51,7 @@ export interface Args {
|
||||
/** Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false */
|
||||
disableIndexHints?: boolean
|
||||
migrationDir?: string
|
||||
transactionOptions?: TransactionOptions | false
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
url: false | string
|
||||
}
|
||||
@@ -81,6 +83,7 @@ declare module 'payload' {
|
||||
globals: GlobalModel
|
||||
mongoMemoryServer: any
|
||||
sessions: Record<number | string, ClientSession>
|
||||
transactionOptions: TransactionOptions
|
||||
versions: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
@@ -92,15 +95,21 @@ export function mongooseAdapter({
|
||||
connectOptions,
|
||||
disableIndexHints = false,
|
||||
migrationDir: migrationDirArg,
|
||||
transactionOptions,
|
||||
url,
|
||||
}: Args): MongooseAdapterResult {
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
const migrationDir = findMigrationDir(migrationDirArg)
|
||||
let beginTransactionFunction = beginTransaction
|
||||
mongoose.set('strictQuery', false)
|
||||
|
||||
extendWebpackConfig(payload.config)
|
||||
extendViteConfig(payload.config)
|
||||
|
||||
if (transactionOptions === false) {
|
||||
beginTransactionFunction = undefined
|
||||
}
|
||||
|
||||
return createDatabaseAdapter<MongooseAdapter>({
|
||||
name: 'mongoose',
|
||||
|
||||
@@ -113,11 +122,12 @@ export function mongooseAdapter({
|
||||
globals: undefined,
|
||||
mongoMemoryServer: undefined,
|
||||
sessions: {},
|
||||
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
||||
url,
|
||||
versions: {},
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction,
|
||||
beginTransaction: beginTransactionFunction,
|
||||
commitTransaction,
|
||||
connect,
|
||||
create,
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
// @ts-expect-error // TODO: Fix this import
|
||||
import type { TransactionOptions } from 'mongodb'
|
||||
import type { BeginTransaction } from 'payload/database'
|
||||
|
||||
import { APIError } from 'payload/errors'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
let transactionsNotAvailable: boolean
|
||||
import type { MongooseAdapter } from '../index'
|
||||
|
||||
export const beginTransaction: BeginTransaction = async function beginTransaction(
|
||||
options: TransactionOptions = {},
|
||||
this: MongooseAdapter,
|
||||
options: TransactionOptions,
|
||||
) {
|
||||
let id = null
|
||||
if (!this.connection) {
|
||||
throw new APIError('beginTransaction called while no connection to the database exists')
|
||||
}
|
||||
|
||||
if (transactionsNotAvailable) return id
|
||||
|
||||
const client = this.connection.getClient()
|
||||
if (!client.options.replicaSet) {
|
||||
transactionsNotAvailable = true
|
||||
} else {
|
||||
id = uuid()
|
||||
if (!this.sessions[id]) {
|
||||
this.sessions[id] = await client.startSession()
|
||||
}
|
||||
if (this.sessions[id].inTransaction()) {
|
||||
this.payload.logger.warn('beginTransaction called while transaction already exists')
|
||||
} else {
|
||||
await this.sessions[id].startTransaction(options)
|
||||
}
|
||||
const id = uuid()
|
||||
|
||||
if (!this.sessions[id]) {
|
||||
this.sessions[id] = client.startSession()
|
||||
}
|
||||
if (this.sessions[id].inTransaction()) {
|
||||
this.payload.logger.warn('beginTransaction called while transaction already exists')
|
||||
} else {
|
||||
this.sessions[id].startTransaction(options || (this.transactionOptions as TransactionOptions))
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import type { UpdateOne } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { ValidationError } from 'payload/errors'
|
||||
import { i18nInit } from 'payload/utilities'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import handleError from './utilities/handleError'
|
||||
import sanitizeInternalFields from './utilities/sanitizeInternalFields'
|
||||
import { withSession } from './withSession'
|
||||
|
||||
@@ -31,18 +29,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
try {
|
||||
result = await Model.findOneAndUpdate(query, data, options)
|
||||
} catch (error) {
|
||||
// Handle uniqueness error from MongoDB
|
||||
throw error.code === 11000 && error.keyValue
|
||||
? new ValidationError(
|
||||
[
|
||||
{
|
||||
field: Object.keys(error.keyValue)[0],
|
||||
message: 'Value must be unique',
|
||||
},
|
||||
],
|
||||
req?.t ?? i18nInit(this.payload.config.i18n).t,
|
||||
)
|
||||
: error
|
||||
handleError(error, req)
|
||||
}
|
||||
|
||||
result = JSON.parse(JSON.stringify(result))
|
||||
|
||||
23
packages/db-mongodb/src/utilities/handleError.ts
Normal file
23
packages/db-mongodb/src/utilities/handleError.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import httpStatus from 'http-status'
|
||||
import { APIError, ValidationError } from 'payload/errors'
|
||||
|
||||
const handleError = (error, req) => {
|
||||
// Handle uniqueness error from MongoDB
|
||||
if (error.code === 11000 && error.keyValue) {
|
||||
throw new ValidationError(
|
||||
[
|
||||
{
|
||||
field: Object.keys(error.keyValue)[0],
|
||||
message: req.t('error:valueMustBeUnique'),
|
||||
},
|
||||
],
|
||||
req.t,
|
||||
)
|
||||
} else if (error.code === 11000) {
|
||||
throw new APIError(req.t('error:valueMustBeUnique'), httpStatus.BAD_REQUEST)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export default handleError
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -128,15 +128,16 @@ export const traverseFields = ({
|
||||
with: {},
|
||||
}
|
||||
|
||||
if (adapter.tables[`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}_locales`])
|
||||
withBlock.with._locales = _locales
|
||||
const tableName = `${topLevelTableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
|
||||
if (adapter.tables[`${tableName}_locales`]) withBlock.with._locales = _locales
|
||||
topLevelArgs.with[blockKey] = withBlock
|
||||
|
||||
traverseFields({
|
||||
_locales,
|
||||
adapter,
|
||||
currentArgs: withBlock,
|
||||
currentTableName,
|
||||
currentTableName: tableName,
|
||||
depth,
|
||||
fields: block.fields,
|
||||
path: '',
|
||||
|
||||
@@ -11,11 +11,11 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
|
||||
try {
|
||||
id = uuid()
|
||||
|
||||
let reject: (value?: unknown) => void
|
||||
let resolve: (value?: unknown) => void
|
||||
let reject: () => Promise<void>
|
||||
let resolve: () => Promise<void>
|
||||
let transaction: DrizzleTransaction
|
||||
|
||||
let transactionReady: (value?: unknown) => void
|
||||
let transactionReady: () => void
|
||||
|
||||
// Drizzle only exposes a transactions API that is sufficient if you
|
||||
// can directly pass around the `tx` argument. But our operations are spread
|
||||
@@ -24,13 +24,19 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
|
||||
// and will call them in our respective transaction methods
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.drizzle
|
||||
const done = this.drizzle
|
||||
.transaction(async (tx) => {
|
||||
transaction = tx
|
||||
await new Promise((res, rej) => {
|
||||
await new Promise<void>((res, rej) => {
|
||||
resolve = () => {
|
||||
res()
|
||||
return done
|
||||
}
|
||||
reject = () => {
|
||||
rej()
|
||||
return done
|
||||
}
|
||||
transactionReady()
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -39,7 +45,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
|
||||
|
||||
// Need to wait until the transaction is ready
|
||||
// before binding its `resolve` and `reject` methods below
|
||||
await new Promise((resolve) => (transactionReady = resolve))
|
||||
await new Promise<void>((resolve) => (transactionReady = resolve))
|
||||
|
||||
this.sessions[id] = {
|
||||
db: transaction,
|
||||
|
||||
@@ -7,9 +7,9 @@ export const commitTransaction: CommitTransaction = async function commitTransac
|
||||
}
|
||||
|
||||
try {
|
||||
this.sessions[id].resolve()
|
||||
await this.sessions[id].resolve()
|
||||
} catch (err: unknown) {
|
||||
this.sessions[id].reject()
|
||||
await this.sessions[id].reject()
|
||||
}
|
||||
|
||||
delete this.sessions[id]
|
||||
|
||||
@@ -56,8 +56,8 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
db: DrizzleTransaction
|
||||
reject: () => void
|
||||
resolve: () => void
|
||||
reject: () => Promise<void>
|
||||
resolve: () => Promise<void>
|
||||
}
|
||||
}
|
||||
tables: Record<string, GenericTable>
|
||||
@@ -86,8 +86,8 @@ declare module 'payload' {
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
db: DrizzleTransaction
|
||||
reject: () => void
|
||||
resolve: () => void
|
||||
reject: () => Promise<void>
|
||||
resolve: () => Promise<void>
|
||||
}
|
||||
}
|
||||
tables: Record<string, GenericTable>
|
||||
|
||||
@@ -62,7 +62,7 @@ module.exports = {
|
||||
'partition-by-comment': true,
|
||||
groups: ['top', 'unknown'],
|
||||
'custom-groups': {
|
||||
top: ['_id', 'id', 'name'],
|
||||
top: ['_id', 'id', 'name', 'slug', 'type'],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.4.0",
|
||||
"version": "2.6.0",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -105,7 +105,7 @@
|
||||
"minimist": "1.2.8",
|
||||
"mkdirp": "1.0.4",
|
||||
"monaco-editor": "0.38.0",
|
||||
"nodemailer": "6.9.4",
|
||||
"nodemailer": "6.9.8",
|
||||
"object-to-formdata": "4.5.1",
|
||||
"passport": "0.6.0",
|
||||
"passport-anonymous": "1.0.1",
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
|
||||
// place the localizer outside the `overflow: hidden` container so that the popup is visible
|
||||
// this means we need to use a placeholder div so that the space is retained in the DOM
|
||||
[dir='rtl'] &__localizer {
|
||||
right: unset;
|
||||
left: base(4.5)
|
||||
}
|
||||
|
||||
&__localizer {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@@ -98,11 +103,57 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__controls {
|
||||
&__actions-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: calc(var(--base) / 2);
|
||||
margin-right: var(--base);
|
||||
}
|
||||
|
||||
&__gradient-placeholder {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: var(--base);
|
||||
height: var(--base);
|
||||
background: linear-gradient(to right, transparent, var(--theme-bg));
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--base) / 2);
|
||||
flex-shrink: 0;
|
||||
max-width: 600px;
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__last-action {
|
||||
margin-right: var(--base);
|
||||
}
|
||||
|
||||
@include large-break {
|
||||
&__actions {
|
||||
max-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
&__gradient-placeholder {
|
||||
right: var(--base);
|
||||
}
|
||||
|
||||
&__actions {
|
||||
max-width: 300px;
|
||||
margin-right: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
@@ -129,5 +180,14 @@
|
||||
// TODO: overflow the step header instead of hide it
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__gradient-placeholder {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
max-width: 150px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import Account from '../../graphics/Account'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { Hamburger } from '../Hamburger'
|
||||
import Localizer from '../Localizer'
|
||||
@@ -22,8 +23,30 @@ export const AppHeader: React.FC = () => {
|
||||
routes: { admin: adminRoute },
|
||||
} = useConfig()
|
||||
|
||||
const { actions } = useActions()
|
||||
|
||||
const { navOpen } = useNav()
|
||||
|
||||
const customControlsRef = useRef<HTMLDivElement>(null)
|
||||
const [isScrollable, setIsScrollable] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const checkIsScrollable = () => {
|
||||
const el = customControlsRef.current
|
||||
if (el) {
|
||||
const scrollable = el.scrollWidth > el.clientWidth
|
||||
setIsScrollable(scrollable)
|
||||
}
|
||||
}
|
||||
|
||||
checkIsScrollable()
|
||||
window.addEventListener('resize', checkIsScrollable)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkIsScrollable)
|
||||
}
|
||||
}, [actions])
|
||||
|
||||
return (
|
||||
<header className={[baseClass, navOpen && `${baseClass}--nav-open`].filter(Boolean).join(' ')}>
|
||||
<div className={`${baseClass}__bg`} />
|
||||
@@ -36,22 +59,33 @@ export const AppHeader: React.FC = () => {
|
||||
<div className={`${baseClass}__step-nav-wrapper`}>
|
||||
<StepNav className={`${baseClass}__step-nav`} />
|
||||
</div>
|
||||
<div className={`${baseClass}__controls`}>
|
||||
{localization && (
|
||||
<LocalizerLabel
|
||||
ariaLabel="invisible"
|
||||
className={`${baseClass}__localizer-spacing`}
|
||||
/>
|
||||
)}
|
||||
<Link
|
||||
aria-label={t('authentication:account')}
|
||||
className={`${baseClass}__account`}
|
||||
tabIndex={0}
|
||||
to={`${adminRoute}/account`}
|
||||
>
|
||||
<Account />
|
||||
</Link>
|
||||
<div className={`${baseClass}__actions-wrapper`}>
|
||||
<div className={`${baseClass}__actions`} ref={customControlsRef}>
|
||||
{Array.isArray(actions) &&
|
||||
actions.map((Component, i) => (
|
||||
<div
|
||||
className={
|
||||
isScrollable && i === actions.length - 1 ? `${baseClass}__last-action` : ''
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
<Component />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{isScrollable && <div className={`${baseClass}__gradient-placeholder`} />}
|
||||
</div>
|
||||
{localization && (
|
||||
<LocalizerLabel ariaLabel="invisible" className={`${baseClass}__localizer-spacing`} />
|
||||
)}
|
||||
<Link
|
||||
aria-label={t('authentication:account')}
|
||||
className={`${baseClass}__account`}
|
||||
tabIndex={0}
|
||||
to={`${adminRoute}/account`}
|
||||
>
|
||||
<Account />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -215,6 +215,15 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
||||
return null
|
||||
}
|
||||
|
||||
const listComponent = selectedCollectionConfig?.admin?.components?.views?.List
|
||||
let ListToRender = null
|
||||
|
||||
if (listComponent && typeof listComponent === 'function') {
|
||||
ListToRender = listComponent
|
||||
} else if (typeof listComponent === 'object' && typeof listComponent.Component === 'function') {
|
||||
ListToRender = listComponent.Component
|
||||
}
|
||||
|
||||
return (
|
||||
<TableColumnsProvider
|
||||
cellProps={[
|
||||
@@ -235,7 +244,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
||||
>
|
||||
<DocumentInfoProvider collection={selectedCollectionConfig}>
|
||||
<RenderCustomComponent
|
||||
CustomComponent={selectedCollectionConfig?.admin?.components?.views?.List}
|
||||
CustomComponent={ListToRender}
|
||||
DefaultComponent={DefaultList}
|
||||
componentProps={{
|
||||
collection: {
|
||||
|
||||
@@ -97,7 +97,7 @@ const RelationshipField: React.FC<Props> = (props) => {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setErrorLoading(t('errors:unspecific'))
|
||||
setErrorLoading(t('error:unspecific'))
|
||||
}
|
||||
}
|
||||
}, Promise.resolve())
|
||||
|
||||
@@ -10,6 +10,10 @@ const getDataByPath = <T = unknown>(fields: Fields, path: string): T => {
|
||||
Object.keys(fields).forEach((key) => {
|
||||
if (!fields[key].disableFormData && (key.indexOf(`${path}.`) === 0 || key === path)) {
|
||||
data[key.replace(pathPrefixToRemove, '')] = fields[key].value
|
||||
|
||||
if (fields[key]?.rows && fields[key].rows.length === 0) {
|
||||
data[key.replace(pathPrefixToRemove, '')] = []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -17,9 +17,16 @@ const Component: React.FC<{
|
||||
onCancel: () => void
|
||||
onConfirm: () => void
|
||||
}> = ({ isActive, onCancel, onConfirm }) => {
|
||||
const { closeModal, openModal } = useModal()
|
||||
const { closeModal, openModal, modalState } = useModal()
|
||||
const { t } = useTranslation('general')
|
||||
|
||||
// Manually check for modal state as 'esc' key will not trigger the nav inactivity
|
||||
useEffect(() => {
|
||||
if (!modalState?.[modalSlug]?.isOpen && isActive) {
|
||||
onCancel()
|
||||
}
|
||||
}, [modalState])
|
||||
|
||||
useEffect(() => {
|
||||
if (isActive) openModal(modalSlug)
|
||||
else closeModal(modalSlug)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types'
|
||||
import type { Props } from './types'
|
||||
|
||||
import { Hamburger } from '../../elements/Hamburger'
|
||||
@@ -15,7 +16,9 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'template-default'
|
||||
|
||||
const Default: React.FC<Props> = ({ children, className }) => {
|
||||
const Default: React.FC<
|
||||
Props & { collection?: SanitizedCollectionConfig; global?: SanitizedGlobalConfig }
|
||||
> = ({ children, className }) => {
|
||||
const {
|
||||
admin: {
|
||||
components: { Nav: CustomNav } = {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
|
||||
type ActionsContextType = {
|
||||
actions: React.ComponentType<any>[]
|
||||
setViewActions: (actions: React.ComponentType<any>[]) => void
|
||||
}
|
||||
|
||||
const ActionsContext = createContext<ActionsContextType>({
|
||||
actions: [],
|
||||
setViewActions: () => {},
|
||||
})
|
||||
|
||||
export const useActions = () => useContext(ActionsContext)
|
||||
|
||||
export const ActionsProvider = ({ children }) => {
|
||||
const [viewActions, setViewActions] = useState([])
|
||||
const [adminActions, setAdminActions] = useState([])
|
||||
|
||||
const {
|
||||
admin: {
|
||||
components: { actions: configAdminActions },
|
||||
},
|
||||
} = useConfig()
|
||||
|
||||
useEffect(() => {
|
||||
setAdminActions(configAdminActions || [])
|
||||
}, [configAdminActions])
|
||||
|
||||
const combinedActions = [...viewActions, ...adminActions]
|
||||
|
||||
return (
|
||||
<ActionsContext.Provider value={{ actions: combinedActions, setViewActions }}>
|
||||
{children}
|
||||
</ActionsContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { Gutter } from '../../elements/Gutter'
|
||||
import { CheckboxInput } from '../../forms/field-types/Checkbox/Input'
|
||||
import SelectInput from '../../forms/field-types/Select/Input'
|
||||
import { MinimizeMaximize } from '../../icons/MinimizeMaximize'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||
import { useLocale } from '../../utilities/Locale'
|
||||
@@ -182,6 +183,8 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
const { code } = useLocale()
|
||||
const url = createURL(apiURL)
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const draftsEnabled = collection?.versions?.drafts || global?.versions?.drafts
|
||||
const docEndpoint = global ? `/globals/${global.slug}` : `/${collection.slug}/${id}`
|
||||
|
||||
@@ -210,6 +213,18 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
fetchData()
|
||||
}, [i18n.language, fetchURL, authenticated])
|
||||
|
||||
React.useEffect(() => {
|
||||
const editConfig = (collection || global)?.admin?.components?.views?.Edit
|
||||
const apiActions =
|
||||
editConfig && 'API' in editConfig && 'actions' in editConfig.API ? editConfig.API.actions : []
|
||||
|
||||
setViewActions(apiActions)
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [collection, global, setViewActions])
|
||||
|
||||
const localeOptions =
|
||||
localization &&
|
||||
localization.locales.map((locale) => ({ label: locale.label, value: locale.code }))
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react'
|
||||
import type { SanitizedGlobalConfig } from '../../../../exports/types'
|
||||
|
||||
import { useStepNav } from '../../elements/StepNav'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { useAuth } from '../../utilities/Auth'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
|
||||
@@ -19,6 +20,8 @@ const Dashboard: React.FC = () => {
|
||||
globals,
|
||||
} = useConfig()
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredGlobals(
|
||||
globals.filter((global) => permissions?.globals?.[global.slug]?.read?.permission),
|
||||
@@ -29,6 +32,10 @@ const Dashboard: React.FC = () => {
|
||||
setStepNav([])
|
||||
}, [setStepNav])
|
||||
|
||||
useEffect(() => {
|
||||
setViewActions([])
|
||||
}, [setViewActions])
|
||||
|
||||
return (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { FieldTypes } from '../../forms/field-types'
|
||||
@@ -8,6 +8,7 @@ import { getTranslation } from '../../../../utilities/getTranslation'
|
||||
import { DocumentHeader } from '../../elements/DocumentHeader'
|
||||
import { FormLoadingOverlayToggle } from '../../elements/Loading'
|
||||
import Form from '../../forms/Form'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { OperationContext } from '../../utilities/OperationProvider'
|
||||
import { SetStepNav } from '../collections/Edit/SetStepNav'
|
||||
import { GlobalRoutes } from './Routes'
|
||||
@@ -37,10 +38,32 @@ const DefaultGlobalView: React.FC<DefaultGlobalViewProps> = (props) => {
|
||||
permissions,
|
||||
} = props
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const { label } = global
|
||||
|
||||
const hasSavePermission = permissions?.update?.permission
|
||||
|
||||
useEffect(() => {
|
||||
const path = location.pathname
|
||||
|
||||
if (!path.endsWith(global.slug)) {
|
||||
return
|
||||
}
|
||||
|
||||
const editConfig = global?.admin?.components?.views?.Edit
|
||||
const defaultActions =
|
||||
editConfig && 'Default' in editConfig && 'actions' in editConfig.Default
|
||||
? editConfig.Default.actions
|
||||
: []
|
||||
|
||||
setViewActions(defaultActions)
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [global.slug, location.pathname, global?.admin?.components?.views?.Edit, setViewActions])
|
||||
|
||||
return (
|
||||
<main className={baseClass}>
|
||||
<OperationContext.Provider value="update">
|
||||
|
||||
@@ -12,6 +12,7 @@ import { getTranslation } from '../../../../utilities/getTranslation'
|
||||
import { DocumentControls } from '../../elements/DocumentControls'
|
||||
import { DocumentFields } from '../../elements/DocumentFields'
|
||||
import { LeaveWithoutSaving } from '../../modals/LeaveWithoutSaving'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||
import { useLocale } from '../../utilities/Locale'
|
||||
@@ -142,6 +143,11 @@ export const LivePreviewView: React.FC<
|
||||
const documentInfo = useDocumentInfo()
|
||||
const locale = useLocale()
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const collection = documentInfo.collection
|
||||
const global = documentInfo.global
|
||||
|
||||
let livePreviewConfig: LivePreviewConfig = config?.admin?.livePreview
|
||||
|
||||
if ('collection' in props) {
|
||||
@@ -179,6 +185,20 @@ export const LivePreviewView: React.FC<
|
||||
getURL() // eslint-disable-line @typescript-eslint/no-floating-promises
|
||||
}, [data, documentInfo, locale, livePreviewConfig])
|
||||
|
||||
useEffect(() => {
|
||||
const editConfig = (collection || global)?.admin?.components?.views?.Edit
|
||||
const livePreviewActions =
|
||||
editConfig && 'LivePreview' in editConfig && 'actions' in editConfig.LivePreview
|
||||
? editConfig.LivePreview.actions
|
||||
: []
|
||||
|
||||
setViewActions(livePreviewActions)
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [collection, global, setViewActions])
|
||||
|
||||
const breakpoints: LivePreviewConfig['breakpoints'] = [
|
||||
...(livePreviewConfig?.breakpoints || []),
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ import { requests } from '../../../api'
|
||||
import { LoadingOverlayToggle } from '../../elements/Loading'
|
||||
import StayLoggedIn from '../../modals/StayLoggedIn'
|
||||
import DefaultTemplate from '../../templates/Default'
|
||||
import { ActionsProvider } from '../../utilities/ActionsProvider'
|
||||
import { useAuth } from '../../utilities/Auth'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { DocumentInfoProvider } from '../../utilities/DocumentInfo'
|
||||
@@ -146,37 +147,39 @@ export const Routes: React.FC = () => {
|
||||
{user ? (
|
||||
<Fragment>
|
||||
{canAccessAdmin && (
|
||||
<DefaultTemplate>
|
||||
<Switch>
|
||||
<Route exact path={`${match.url}/`}>
|
||||
<Dashboard />
|
||||
</Route>
|
||||
<Route path={`${match.url}/account`}>
|
||||
<DocumentInfoProvider
|
||||
collection={collections.find(({ slug }) => slug === userSlug)}
|
||||
id={user.id}
|
||||
>
|
||||
<Account />
|
||||
</DocumentInfoProvider>
|
||||
</Route>
|
||||
{collectionRoutes({
|
||||
collections,
|
||||
match,
|
||||
permissions,
|
||||
user,
|
||||
})}
|
||||
{globalRoutes({
|
||||
globals,
|
||||
locale,
|
||||
match,
|
||||
permissions,
|
||||
user,
|
||||
})}
|
||||
<Route path={`${match.url}*`}>
|
||||
<NotFound />
|
||||
</Route>
|
||||
</Switch>
|
||||
</DefaultTemplate>
|
||||
<ActionsProvider>
|
||||
<DefaultTemplate>
|
||||
<Switch>
|
||||
<Route exact path={`${match.url}/`}>
|
||||
<Dashboard />
|
||||
</Route>
|
||||
<Route path={`${match.url}/account`}>
|
||||
<DocumentInfoProvider
|
||||
collection={collections.find(({ slug }) => slug === userSlug)}
|
||||
id={user.id}
|
||||
>
|
||||
<Account />
|
||||
</DocumentInfoProvider>
|
||||
</Route>
|
||||
{collectionRoutes({
|
||||
collections,
|
||||
match,
|
||||
permissions,
|
||||
user,
|
||||
})}
|
||||
{globalRoutes({
|
||||
globals,
|
||||
locale,
|
||||
match,
|
||||
permissions,
|
||||
user,
|
||||
})}
|
||||
<Route path={`${match.url}*`}>
|
||||
<NotFound />
|
||||
</Route>
|
||||
</Switch>
|
||||
</DefaultTemplate>
|
||||
</ActionsProvider>
|
||||
)}
|
||||
{canAccessAdmin === false && <Unauthorized />}
|
||||
</Fragment>
|
||||
|
||||
@@ -14,6 +14,7 @@ import usePayloadAPI from '../../../hooks/usePayloadAPI'
|
||||
import { formatDate } from '../../../utilities/formatDate'
|
||||
import { Gutter } from '../../elements/Gutter'
|
||||
import { useStepNav } from '../../elements/StepNav'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { useAuth } from '../../utilities/Auth'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||
@@ -39,6 +40,8 @@ const VersionView: React.FC<Props> = ({ collection, global }) => {
|
||||
} = useConfig()
|
||||
const { setStepNav } = useStepNav()
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const {
|
||||
params: { id, versionID },
|
||||
} = useRouteMatch<{ id?: string; versionID: string }>()
|
||||
@@ -173,6 +176,20 @@ const VersionView: React.FC<Props> = ({ collection, global }) => {
|
||||
setStepNav(nav)
|
||||
}, [setStepNav, collection, global, dateFormat, doc, mostRecentDoc, admin, id, locale, t, i18n])
|
||||
|
||||
useEffect(() => {
|
||||
const editConfig = (collection || global)?.admin?.components?.views?.Edit
|
||||
const versionActions =
|
||||
editConfig && 'Version' in editConfig && 'actions' in editConfig.Version
|
||||
? editConfig.Version.actions
|
||||
: []
|
||||
|
||||
setViewActions(versionActions)
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [collection, global, setViewActions])
|
||||
|
||||
let metaTitle: string
|
||||
let metaDesc: string
|
||||
const formattedCreatedAt = doc?.createdAt
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { IndexProps } from './types'
|
||||
|
||||
import { getTranslation } from '../../../../utilities/getTranslation'
|
||||
import usePayloadAPI from '../../../hooks/usePayloadAPI'
|
||||
import { useActions } from '../../utilities/ActionsProvider'
|
||||
import { useAuth } from '../../utilities/Auth'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { EditDepthContext } from '../../utilities/EditDepth'
|
||||
@@ -19,6 +20,8 @@ const VersionsView: React.FC<IndexProps> = (props) => {
|
||||
|
||||
const [fetchURL, setFetchURL] = useState('')
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const {
|
||||
routes: { admin, api },
|
||||
serverURL,
|
||||
@@ -45,18 +48,22 @@ const VersionsView: React.FC<IndexProps> = (props) => {
|
||||
// 1. "components.Edit"
|
||||
// 2. "components.Edit.Versions"
|
||||
// 3. "components.Edit.Versions.Component"
|
||||
const Edit = collection?.admin?.components?.views?.Edit
|
||||
const EditCollection = collection?.admin?.components?.views?.Edit
|
||||
|
||||
CustomVersionsView =
|
||||
typeof Edit === 'function'
|
||||
? Edit
|
||||
: typeof Edit === 'object' && typeof Edit.Versions === 'function'
|
||||
? Edit.Versions
|
||||
: typeof Edit?.Versions === 'object' &&
|
||||
'Component' in Edit.Versions &&
|
||||
typeof Edit.Versions.Component === 'function'
|
||||
? Edit.Versions.Component
|
||||
: undefined
|
||||
if (typeof EditCollection === 'function') {
|
||||
CustomVersionsView = EditCollection
|
||||
} else if (
|
||||
typeof EditCollection === 'object' &&
|
||||
typeof EditCollection.Versions === 'function'
|
||||
) {
|
||||
CustomVersionsView = EditCollection.Versions
|
||||
} else if (
|
||||
typeof EditCollection?.Versions === 'object' &&
|
||||
'Component' in EditCollection.Versions &&
|
||||
typeof EditCollection.Versions.Component === 'function'
|
||||
) {
|
||||
CustomVersionsView = EditCollection.Versions.Component
|
||||
}
|
||||
}
|
||||
|
||||
if (global) {
|
||||
@@ -66,18 +73,19 @@ const VersionsView: React.FC<IndexProps> = (props) => {
|
||||
editURL = `${admin}/globals/${global.slug}`
|
||||
|
||||
// See note above about cascading component definitions
|
||||
const Edit = global?.admin?.components?.views?.Edit
|
||||
const EditGlobal = global?.admin?.components?.views?.Edit
|
||||
|
||||
CustomVersionsView =
|
||||
typeof Edit === 'function'
|
||||
? Edit
|
||||
: typeof Edit === 'object' && typeof Edit.Versions === 'function'
|
||||
? Edit.Versions
|
||||
: typeof Edit?.Versions === 'object' &&
|
||||
'Component' in Edit.Versions &&
|
||||
typeof Edit.Versions.Component === 'function'
|
||||
? Edit.Versions.Component
|
||||
: undefined
|
||||
if (typeof EditGlobal === 'function') {
|
||||
CustomVersionsView = EditGlobal
|
||||
} else if (typeof EditGlobal === 'object' && typeof EditGlobal.Versions === 'function') {
|
||||
CustomVersionsView = EditGlobal.Versions
|
||||
} else if (
|
||||
typeof EditGlobal?.Versions === 'object' &&
|
||||
'Component' in EditGlobal.Versions &&
|
||||
typeof EditGlobal.Versions.Component === 'function'
|
||||
) {
|
||||
CustomVersionsView = EditGlobal.Versions.Component
|
||||
}
|
||||
}
|
||||
|
||||
const [{ data, isLoading }] = usePayloadAPI(docURL, { initialParams: { draft: 'true' } })
|
||||
@@ -120,6 +128,20 @@ const VersionsView: React.FC<IndexProps> = (props) => {
|
||||
setParams(params)
|
||||
}, [setParams, page, sort, limit, serverURL, api, id, global, collection])
|
||||
|
||||
useEffect(() => {
|
||||
const editConfig = (collection || global)?.admin?.components?.views?.Edit
|
||||
const versionsActions =
|
||||
editConfig && 'Versions' in editConfig && 'actions' in editConfig.Versions
|
||||
? editConfig.Versions.actions
|
||||
: []
|
||||
|
||||
setViewActions(versionsActions)
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [collection, global, setViewActions])
|
||||
|
||||
return (
|
||||
<EditDepthContext.Provider value={1}>
|
||||
<RenderCustomComponent
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import type { FieldTypes } from '../../../forms/field-types'
|
||||
import type { CollectionEditViewProps } from '../../types'
|
||||
@@ -8,6 +9,7 @@ import { getTranslation } from '../../../../../utilities/getTranslation'
|
||||
import { DocumentHeader } from '../../../elements/DocumentHeader'
|
||||
import { FormLoadingOverlayToggle } from '../../../elements/Loading'
|
||||
import Form from '../../../forms/Form'
|
||||
import { useActions } from '../../../utilities/ActionsProvider'
|
||||
import { useAuth } from '../../../utilities/Auth'
|
||||
import { useDocumentEvents } from '../../../utilities/DocumentEvents'
|
||||
import { OperationContext } from '../../../utilities/OperationProvider'
|
||||
@@ -43,12 +45,16 @@ const DefaultEditView: React.FC<DefaultEditViewProps> = (props) => {
|
||||
onSave: onSaveFromProps,
|
||||
} = props
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const { reportUpdate } = useDocumentEvents()
|
||||
|
||||
const { auth } = collection
|
||||
|
||||
const classes = [baseClass, isEditing && `${baseClass}--is-editing`].filter(Boolean).join(' ')
|
||||
|
||||
const location = useLocation()
|
||||
|
||||
const onSave = useCallback(
|
||||
async (json) => {
|
||||
reportUpdate({
|
||||
@@ -72,6 +78,25 @@ const DefaultEditView: React.FC<DefaultEditViewProps> = (props) => {
|
||||
|
||||
const operation = isEditing ? 'update' : 'create'
|
||||
|
||||
useEffect(() => {
|
||||
const path = location.pathname
|
||||
|
||||
if (!(path.endsWith(id) || path.endsWith('/create'))) {
|
||||
return
|
||||
}
|
||||
const editConfig = collection?.admin?.components?.views?.Edit
|
||||
const defaultActions =
|
||||
editConfig && 'Default' in editConfig && 'actions' in editConfig.Default
|
||||
? editConfig.Default.actions
|
||||
: []
|
||||
|
||||
setViewActions(defaultActions)
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [id, location.pathname, collection?.admin?.components?.views?.Edit, setViewActions])
|
||||
|
||||
return (
|
||||
<main className={classes}>
|
||||
<OperationContext.Provider value={operation}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import usePayloadAPI from '../../../../hooks/usePayloadAPI'
|
||||
import { useUseTitleField } from '../../../../hooks/useUseAsTitle'
|
||||
import { useStepNav } from '../../../elements/StepNav'
|
||||
import { TableColumnsProvider } from '../../../elements/TableColumns'
|
||||
import { useActions } from '../../../utilities/ActionsProvider'
|
||||
import { useAuth } from '../../../utilities/Auth'
|
||||
import { useConfig } from '../../../utilities/Config'
|
||||
import { usePreferences } from '../../../utilities/Preferences'
|
||||
@@ -60,6 +61,9 @@ const ListView: React.FC<ListIndexProps> = (props) => {
|
||||
routes: { admin, api },
|
||||
serverURL,
|
||||
} = useConfig()
|
||||
|
||||
const { setViewActions } = useActions()
|
||||
|
||||
const preferenceKey = `${collection.slug}-list`
|
||||
const { permissions } = useAuth()
|
||||
const { setStepNav } = useStepNav()
|
||||
@@ -75,6 +79,16 @@ const ListView: React.FC<ListIndexProps> = (props) => {
|
||||
const [{ data }, { setParams }] = usePayloadAPI(fetchURL, { initialParams: { page: 1 } })
|
||||
const titleField = useUseTitleField(collection)
|
||||
|
||||
useEffect(() => {
|
||||
if (CustomList && typeof CustomList === 'object' && 'actions' in CustomList) {
|
||||
setViewActions(CustomList.actions || [])
|
||||
}
|
||||
|
||||
return () => {
|
||||
setViewActions([])
|
||||
}
|
||||
}, [CustomList, setViewActions])
|
||||
|
||||
useEffect(() => {
|
||||
setStepNav([
|
||||
{
|
||||
@@ -205,10 +219,18 @@ const ListView: React.FC<ListIndexProps> = (props) => {
|
||||
}
|
||||
}, [data, history, resetParams])
|
||||
|
||||
let ListToRender = null
|
||||
|
||||
if (CustomList && typeof CustomList === 'function') {
|
||||
ListToRender = CustomList
|
||||
} else if (typeof CustomList === 'object' && typeof CustomList.Component === 'function') {
|
||||
ListToRender = CustomList.Component
|
||||
}
|
||||
|
||||
return (
|
||||
<TableColumnsProvider collection={collection}>
|
||||
<RenderCustomComponent
|
||||
CustomComponent={CustomList}
|
||||
CustomComponent={ListToRender}
|
||||
DefaultComponent={DefaultList}
|
||||
componentProps={{
|
||||
collection: { ...collection, fields },
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
|
||||
import formatName from '../../../graphql/utilities/formatName'
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import access from '../../operations/access'
|
||||
|
||||
const formatConfigNames = (results, configs) => {
|
||||
@@ -19,7 +20,7 @@ const formatConfigNames = (results, configs) => {
|
||||
function accessResolver(payload: Payload) {
|
||||
async function resolver(_, args, context) {
|
||||
const options = {
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
const accessResults = await access(options)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import forgotPassword from '../../operations/forgotPassword'
|
||||
|
||||
function forgotPasswordResolver(collection: Collection): any {
|
||||
@@ -12,7 +13,7 @@ function forgotPasswordResolver(collection: Collection): any {
|
||||
},
|
||||
disableEmail: args.disableEmail,
|
||||
expiration: args.expiration,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
await forgotPassword(options)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import init from '../../operations/init'
|
||||
|
||||
function initResolver(collection: string) {
|
||||
async function resolver(_, args, context) {
|
||||
const options = {
|
||||
collection,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
return init(options)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import login from '../../operations/login'
|
||||
|
||||
function loginResolver(collection: Collection) {
|
||||
@@ -12,7 +13,7 @@ function loginResolver(collection: Collection) {
|
||||
password: args.password,
|
||||
},
|
||||
depth: 0,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
res: context.res,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import logout from '../../operations/logout'
|
||||
|
||||
function logoutResolver(collection: Collection): any {
|
||||
async function resolver(_, args, context) {
|
||||
const options = {
|
||||
collection,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
res: context.res,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import me from '../../operations/me'
|
||||
|
||||
function meResolver(collection: Collection): any {
|
||||
@@ -8,7 +9,7 @@ function meResolver(collection: Collection): any {
|
||||
const options = {
|
||||
collection,
|
||||
depth: 0,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
return me(options)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import getExtractJWT from '../../getExtractJWT'
|
||||
import refresh from '../../operations/refresh'
|
||||
|
||||
@@ -18,7 +19,7 @@ function refreshResolver(collection: Collection) {
|
||||
const options = {
|
||||
collection,
|
||||
depth: 0,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
res: context.res,
|
||||
token,
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import resetPassword from '../../operations/resetPassword'
|
||||
|
||||
function resetPasswordResolver(collection: Collection) {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
if (args.locale) req.locale = args.locale
|
||||
if (args.fallbackLocale) req.fallbackLocale = args.fallbackLocale
|
||||
|
||||
const options = {
|
||||
api: 'GraphQL',
|
||||
collection,
|
||||
data: args,
|
||||
depth: 0,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
res: context.res,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import unlock from '../../operations/unlock'
|
||||
|
||||
function unlockResolver(collection: Collection) {
|
||||
@@ -8,7 +9,7 @@ function unlockResolver(collection: Collection) {
|
||||
const options = {
|
||||
collection,
|
||||
data: { email: args.email },
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await unlock(options)
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Collection } from '../../../collections/config/types'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import verifyEmail from '../../operations/verifyEmail'
|
||||
|
||||
function verifyEmailResolver(collection: Collection) {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
if (args.locale) req.locale = args.locale
|
||||
if (args.fallbackLocale) req.fallbackLocale = args.fallbackLocale
|
||||
|
||||
const options = {
|
||||
api: 'GraphQL',
|
||||
collection,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
res: context.res,
|
||||
token: args.token,
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context,
|
||||
data,
|
||||
depth,
|
||||
fallbackLocale,
|
||||
locale,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
res,
|
||||
@@ -46,6 +46,12 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -56,8 +62,6 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.payload = payload
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.locale = undefined
|
||||
req.fallbackLocale = undefined
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
@@ -73,7 +77,10 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
}
|
||||
|
||||
if (locale) args.req.locale = locale
|
||||
if (fallbackLocale) args.req.fallbackLocale = fallbackLocale
|
||||
if (fallbackLocale) {
|
||||
args.req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
}
|
||||
|
||||
return login<TSlug>(args)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,13 @@ const collectionSchema = joi.object().keys({
|
||||
// References
|
||||
}),
|
||||
),
|
||||
List: componentSchema,
|
||||
List: joi.alternatives().try(
|
||||
componentSchema,
|
||||
joi.object({
|
||||
Component: componentSchema,
|
||||
actions: joi.array().items(componentSchema),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
}),
|
||||
defaultColumns: joi.array().items(joi.string()),
|
||||
|
||||
@@ -273,7 +273,12 @@ export type CollectionAdminOptions = {
|
||||
}
|
||||
)
|
||||
| AdminViewComponent
|
||||
List?: React.ComponentType<ListProps>
|
||||
List?:
|
||||
| {
|
||||
Component?: React.ComponentType<ListProps>
|
||||
actions?: React.ComponentType<any>[]
|
||||
}
|
||||
| React.ComponentType<ListProps>
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { GeneratedTypes } from '../../../'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import create from '../../operations/create'
|
||||
|
||||
export type Resolver<TSlug extends keyof GeneratedTypes['collections']> = (
|
||||
@@ -29,16 +29,17 @@ export default function createResolver<TSlug extends keyof GeneratedTypes['colle
|
||||
collection: Collection,
|
||||
): Resolver<TSlug> {
|
||||
return async function resolver(_, args, context) {
|
||||
if (args.locale) {
|
||||
context.req.locale = args.locale
|
||||
}
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req.locale = args.locale || locale
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
data: args.data,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await create(options)
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { GeneratedTypes } from '../../../'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import deleteByID from '../../operations/deleteByID'
|
||||
|
||||
export type Resolver<TSlug extends keyof GeneratedTypes['collections']> = (
|
||||
@@ -24,14 +24,19 @@ export default function getDeleteResolver<TSlug extends keyof GeneratedTypes['co
|
||||
collection: Collection,
|
||||
): Resolver<TSlug> {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await deleteByID(options)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CollectionPermission, GlobalPermission } from '../../../auth'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import { docAccess } from '../../operations/docAccess'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -19,7 +19,7 @@ export function docAccessResolver(): Resolver {
|
||||
async function resolver(_, args, context) {
|
||||
return docAccess({
|
||||
id: args.id,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Where } from '../../../types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import find from '../../operations/find'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -28,8 +28,13 @@ export type Resolver = (
|
||||
|
||||
export default function findResolver(collection: Collection): Resolver {
|
||||
return async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
@@ -37,7 +42,7 @@ export default function findResolver(collection: Collection): Resolver {
|
||||
draft: args.draft,
|
||||
limit: args.limit,
|
||||
page: args.page,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
sort: args.sort,
|
||||
where: args.where,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { GeneratedTypes } from '../../../'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import findByID from '../../operations/findByID'
|
||||
|
||||
export type Resolver<T> = (
|
||||
@@ -23,16 +23,20 @@ export default function findByIDResolver<T extends keyof GeneratedTypes['collect
|
||||
collection: Collection,
|
||||
): Resolver<GeneratedTypes['collections'][T]> {
|
||||
return async function resolver(_, args, context) {
|
||||
const { req } = context
|
||||
if (args.locale) req.locale = args.locale
|
||||
if (args.fallbackLocale) req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await findByID(options)
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { TypeWithVersion } from '../../../versions/types'
|
||||
import type { Collection, TypeWithID } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import findVersionByID from '../../operations/findVersionByID'
|
||||
|
||||
export type Resolver<T extends TypeWithID = any> = (
|
||||
@@ -24,15 +24,20 @@ export type Resolver<T extends TypeWithID = any> = (
|
||||
|
||||
export default function findVersionByIDResolver(collection: Collection): Resolver {
|
||||
return async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await findVersionByID(options)
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Where } from '../../../types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import findVersions from '../../operations/findVersions'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -28,15 +28,20 @@ export type Resolver = (
|
||||
|
||||
export default function findVersionsResolver(collection: Collection): Resolver {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
depth: 0,
|
||||
limit: args.limit,
|
||||
page: args.page,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
sort: args.sort,
|
||||
where: args.where,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Response } from 'express'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import restoreVersion from '../../operations/restoreVersion'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -24,7 +24,7 @@ export default function restoreVersionResolver(collection: Collection): Resolver
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await restoreVersion(options)
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { GeneratedTypes } from '../../../'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import updateByID from '../../operations/updateByID'
|
||||
|
||||
export type Resolver<TSlug extends keyof GeneratedTypes['collections']> = (
|
||||
@@ -27,8 +27,13 @@ export default function updateResolver<TSlug extends keyof GeneratedTypes['colle
|
||||
collection: Collection,
|
||||
): Resolver<TSlug> {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
@@ -37,7 +42,7 @@ export default function updateResolver<TSlug extends keyof GeneratedTypes['colle
|
||||
data: args.data,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await updateByID<TSlug>(options)
|
||||
|
||||
@@ -49,10 +49,10 @@ export default async function createLocal<TSlug extends keyof GeneratedTypes['co
|
||||
depth,
|
||||
disableVerificationEmail,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
file,
|
||||
filePath,
|
||||
locale = null,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
overwriteExistingFiles = false,
|
||||
req = {} as PayloadRequest,
|
||||
@@ -62,8 +62,11 @@ export default async function createLocal<TSlug extends keyof GeneratedTypes['co
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -73,8 +76,9 @@ export default async function createLocal<TSlug extends keyof GeneratedTypes['co
|
||||
}
|
||||
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale ?? req?.locale ?? defaultLocale
|
||||
req.fallbackLocale = fallbackLocale !== 'undefined' ? fallbackLocale : defaultLocale
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.payload = payload
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.files = {
|
||||
|
||||
@@ -61,18 +61,21 @@ async function deleteLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale,
|
||||
locale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
req: incomingReq = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
user,
|
||||
where,
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -82,9 +85,12 @@ async function deleteLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
}
|
||||
|
||||
const req = {
|
||||
fallbackLocale: typeof fallbackLocale !== 'undefined' ? fallbackLocale : defaultLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n: i18nInit(payload.config.i18n),
|
||||
locale: locale ?? defaultLocale,
|
||||
locale: locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
transactionID: incomingReq?.transactionID,
|
||||
|
||||
@@ -44,9 +44,9 @@ export default async function findLocal<T extends keyof GeneratedTypes['collecti
|
||||
depth,
|
||||
disableErrors,
|
||||
draft = false,
|
||||
fallbackLocale,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
limit,
|
||||
locale = null,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
pagination = true,
|
||||
@@ -59,8 +59,11 @@ export default async function findLocal<T extends keyof GeneratedTypes['collecti
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -69,19 +72,10 @@ export default async function findLocal<T extends keyof GeneratedTypes['collecti
|
||||
)
|
||||
}
|
||||
|
||||
let fallbackLocaleToUse = defaultLocale
|
||||
|
||||
if (typeof req.fallbackLocale !== 'undefined') {
|
||||
fallbackLocaleToUse = req.fallbackLocale
|
||||
}
|
||||
|
||||
if (typeof fallbackLocale !== 'undefined') {
|
||||
fallbackLocaleToUse = fallbackLocale
|
||||
}
|
||||
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale ?? req?.locale ?? defaultLocale
|
||||
req.fallbackLocale = fallbackLocaleToUse
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.payload = payload
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ export default async function findByIDLocal<T extends keyof GeneratedTypes['coll
|
||||
depth,
|
||||
disableErrors = false,
|
||||
draft = false,
|
||||
fallbackLocale,
|
||||
locale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
@@ -50,8 +50,11 @@ export default async function findByIDLocal<T extends keyof GeneratedTypes['coll
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -60,19 +63,10 @@ export default async function findByIDLocal<T extends keyof GeneratedTypes['coll
|
||||
)
|
||||
}
|
||||
|
||||
let fallbackLocaleToUse = defaultLocale
|
||||
|
||||
if (typeof req.fallbackLocale !== 'undefined') {
|
||||
fallbackLocaleToUse = req.fallbackLocale
|
||||
}
|
||||
|
||||
if (typeof fallbackLocale !== 'undefined') {
|
||||
fallbackLocaleToUse = fallbackLocale
|
||||
}
|
||||
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale ?? req?.locale ?? defaultLocale
|
||||
req.fallbackLocale = fallbackLocaleToUse
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.payload = payload
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
context,
|
||||
depth,
|
||||
disableErrors = false,
|
||||
fallbackLocale,
|
||||
locale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
@@ -47,8 +47,11 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -59,19 +62,10 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
)
|
||||
}
|
||||
|
||||
let fallbackLocaleToUse = defaultLocale
|
||||
|
||||
if (typeof req.fallbackLocale !== 'undefined') {
|
||||
fallbackLocaleToUse = req.fallbackLocale
|
||||
}
|
||||
|
||||
if (typeof fallbackLocale !== 'undefined') {
|
||||
fallbackLocaleToUse = fallbackLocale
|
||||
}
|
||||
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale ?? req?.locale ?? defaultLocale
|
||||
req.fallbackLocale = fallbackLocaleToUse
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.payload = payload
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
limit,
|
||||
locale = null,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
req: incomingReq,
|
||||
@@ -52,8 +52,11 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -64,9 +67,12 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
const req = {
|
||||
fallbackLocale: typeof fallbackLocale !== 'undefined' ? fallbackLocale : defaultLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale: locale ?? defaultLocale,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
transactionID: incomingReq?.transactionID,
|
||||
|
||||
@@ -35,8 +35,8 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale = null,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
@@ -44,6 +44,12 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -55,7 +61,10 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
const req = {
|
||||
fallbackLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
|
||||
@@ -74,10 +74,10 @@ async function updateLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
data,
|
||||
depth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
file,
|
||||
filePath,
|
||||
locale = null,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
overwriteExistingFiles = false,
|
||||
req: incomingReq,
|
||||
@@ -88,8 +88,11 @@ async function updateLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
const defaultLocale = payload.config.localization
|
||||
? payload.config.localization?.defaultLocale
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
@@ -99,7 +102,10 @@ async function updateLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
}
|
||||
|
||||
const req = {
|
||||
fallbackLocale: typeof fallbackLocale !== 'undefined' ? fallbackLocale : defaultLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
files: {
|
||||
file: file ?? (await getFileByPath(filePath)),
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ export default joi.object({
|
||||
},
|
||||
components: joi.object().keys({
|
||||
Nav: component,
|
||||
actions: joi.array().items(component),
|
||||
afterDashboard: joi.array().items(component),
|
||||
afterLogin: joi.array().items(component),
|
||||
afterNavLinks: joi.array().items(component),
|
||||
@@ -137,6 +138,7 @@ export default joi.object({
|
||||
joi.array().items(
|
||||
joi.object().keys({
|
||||
code: joi.string(),
|
||||
fallbackLocale: joi.string(),
|
||||
label: joi
|
||||
.alternatives()
|
||||
.try(
|
||||
|
||||
@@ -14,6 +14,7 @@ export const documentTabSchema = {
|
||||
export const customViewSchema = joi.object({
|
||||
Component: componentSchema,
|
||||
Tab: joi.alternatives().try(documentTabSchema, componentSchema),
|
||||
actions: joi.array().items(componentSchema),
|
||||
path: joi.string(),
|
||||
})
|
||||
|
||||
|
||||
@@ -11,11 +11,7 @@ import type { DeepRequired } from 'ts-essentials'
|
||||
import type { InlineConfig } from 'vite'
|
||||
import type { Configuration } from 'webpack'
|
||||
|
||||
import type {
|
||||
DocumentTab,
|
||||
DocumentTabComponent,
|
||||
DocumentTabConfig,
|
||||
} from '../admin/components/elements/DocumentHeader/Tabs/types'
|
||||
import type { DocumentTab } from '../admin/components/elements/DocumentHeader/Tabs/types'
|
||||
import type { RichTextAdapter } from '../admin/components/forms/field-types/RichText/types'
|
||||
import type { ContextType } from '../admin/components/utilities/DocumentInfo/types'
|
||||
import type { User } from '../auth/types'
|
||||
@@ -272,6 +268,9 @@ export type EditViewConfig =
|
||||
Component: AdminViewComponent
|
||||
path: string
|
||||
}
|
||||
| {
|
||||
actions?: React.ComponentType<any>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Override existing views
|
||||
@@ -288,6 +287,10 @@ export type Locale = {
|
||||
* @example "en"
|
||||
*/
|
||||
code: string
|
||||
/**
|
||||
* Code of another locale to use when reading documents with fallback, if not specified defaultLocale is used
|
||||
*/
|
||||
fallbackLocale?: string
|
||||
/**
|
||||
* label of supported locale
|
||||
* @example "English"
|
||||
@@ -397,6 +400,10 @@ export type Config = {
|
||||
* Replace the navigation with a custom component
|
||||
*/
|
||||
Nav?: React.ComponentType<any>
|
||||
/**
|
||||
* Add custom components to the top right of the Admin Panel
|
||||
*/
|
||||
actions?: React.ComponentType<any>[]
|
||||
/**
|
||||
* Add custom components after the collection overview
|
||||
*/
|
||||
|
||||
@@ -116,8 +116,8 @@ export interface BaseDatabaseAdapter {
|
||||
sessions?: {
|
||||
[id: string]: {
|
||||
db: unknown
|
||||
reject: () => void
|
||||
resolve: () => void
|
||||
reject: () => Promise<void>
|
||||
resolve: () => Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,3 +28,6 @@ export {
|
||||
DescriptionComponent,
|
||||
DescriptionFunction,
|
||||
} from '../../admin/components/forms/FieldDescription/types'
|
||||
|
||||
export { useNav } from '../../admin/components/elements/Nav/context'
|
||||
export { default as NavGroup } from '../../admin/components/elements/NavGroup'
|
||||
|
||||
@@ -48,15 +48,15 @@ const middleware = (payload: Payload): any => {
|
||||
qsMiddleware({ arrayLimit: 1000, depth: 10 }),
|
||||
bodyParser.urlencoded({ extended: true }),
|
||||
compression(payload.config.express.compression),
|
||||
localizationMiddleware(payload.config.localization),
|
||||
localizationMiddleware,
|
||||
express.json(payload.config.express.json),
|
||||
fileUpload({
|
||||
parseNested: true,
|
||||
...payload.config.upload,
|
||||
}),
|
||||
convertPayload,
|
||||
corsHeaders(payload.config),
|
||||
authenticate(payload.config),
|
||||
corsHeaders(payload.config),
|
||||
...(payload.config.express.middleware || []),
|
||||
...(payload.config.express.postMiddleware || []),
|
||||
]
|
||||
|
||||
@@ -174,6 +174,7 @@ export const json = baseField.keys({
|
||||
Error: componentSchema,
|
||||
Label: componentSchema,
|
||||
}),
|
||||
editorOptions: joi.object().unknown(), // Editor['options'] @monaco-editor/react
|
||||
}),
|
||||
defaultValue: joi.alternatives().try(joi.array(), joi.object()),
|
||||
type: joi.string().valid('json').required(),
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { CollectionPermission, GlobalPermission } from '../../../auth'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { SanitizedGlobalConfig } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import { docAccess } from '../../operations/docAccess'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -17,7 +17,7 @@ export function docAccessResolver(global: SanitizedGlobalConfig): Resolver {
|
||||
async function resolver(_, context) {
|
||||
return docAccess({
|
||||
globalConfig: global,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type { Document } from '../../../types'
|
||||
import type { SanitizedGlobalConfig } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import findOne from '../../operations/findOne'
|
||||
|
||||
export default function findOneResolver(globalConfig: SanitizedGlobalConfig): Document {
|
||||
@@ -17,7 +17,7 @@ export default function findOneResolver(globalConfig: SanitizedGlobalConfig): Do
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
globalConfig,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty(context.req, 'transactionID'),
|
||||
slug,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Document } from '../../../types'
|
||||
import type { SanitizedGlobalConfig } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import findVersionByID from '../../operations/findVersionByID'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -32,7 +32,7 @@ export default function findVersionByIDResolver(globalConfig: SanitizedGlobalCon
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
globalConfig,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await findVersionByID(options)
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Document, Where } from '../../../types'
|
||||
import type { SanitizedGlobalConfig } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import findVersions from '../../operations/findVersions'
|
||||
|
||||
export type Resolver = (
|
||||
@@ -30,7 +30,7 @@ export default function findVersionsResolver(globalConfig: SanitizedGlobalConfig
|
||||
globalConfig,
|
||||
limit: args.limit,
|
||||
page: args.page,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
sort: args.sort,
|
||||
where: args.where,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Document } from '../../../types'
|
||||
import type { SanitizedGlobalConfig } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import restoreVersion from '../../operations/restoreVersion'
|
||||
|
||||
type Resolver = (
|
||||
@@ -23,7 +23,7 @@ export default function restoreVersionResolver(globalConfig: SanitizedGlobalConf
|
||||
id: args.id,
|
||||
depth: 0,
|
||||
globalConfig,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await restoreVersion(options)
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { GeneratedTypes } from '../../../'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { SanitizedGlobalConfig } from '../../config/types'
|
||||
|
||||
import isolateTransactionID from '../../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import update from '../../operations/update'
|
||||
|
||||
type Resolver<TSlug extends keyof GeneratedTypes['globals']> = (
|
||||
@@ -36,7 +36,7 @@ export default function updateResolver<TSlug extends keyof GeneratedTypes['globa
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
globalConfig,
|
||||
req: isolateTransactionID(context.req),
|
||||
req: isolateObjectProperty<PayloadRequest>(context.req, 'transactionID'),
|
||||
slug,
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export default async function findOneLocal<T extends keyof GeneratedTypes['globa
|
||||
context,
|
||||
depth,
|
||||
draft = false,
|
||||
fallbackLocale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
showHiddenFields,
|
||||
@@ -39,9 +39,13 @@ export default async function findOneLocal<T extends keyof GeneratedTypes['globa
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
@@ -50,7 +54,10 @@ export default async function findOneLocal<T extends keyof GeneratedTypes['globa
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
const req = {
|
||||
fallbackLocale: fallbackLocale ?? options.req?.fallbackLocale ?? defaultLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale: locale ?? options.req?.locale ?? defaultLocale,
|
||||
payload,
|
||||
|
||||
@@ -33,7 +33,7 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
context,
|
||||
depth,
|
||||
disableErrors = false,
|
||||
fallbackLocale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
@@ -43,6 +43,13 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
@@ -50,7 +57,10 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
}
|
||||
|
||||
const req = {
|
||||
fallbackLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
|
||||
@@ -34,7 +34,7 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
const {
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
limit,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
@@ -48,6 +48,13 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
@@ -55,7 +62,10 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
}
|
||||
|
||||
const req = {
|
||||
fallbackLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
|
||||
@@ -30,7 +30,7 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
id,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
@@ -40,6 +40,13 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
@@ -47,7 +54,10 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
}
|
||||
|
||||
const req = {
|
||||
fallbackLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
|
||||
@@ -34,7 +34,7 @@ export default async function updateLocal<TSlug extends keyof GeneratedTypes['gl
|
||||
data,
|
||||
depth,
|
||||
draft,
|
||||
fallbackLocale = null,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
@@ -44,6 +44,13 @@ export default async function updateLocal<TSlug extends keyof GeneratedTypes['gl
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
@@ -51,7 +58,10 @@ export default async function updateLocal<TSlug extends keyof GeneratedTypes['gl
|
||||
}
|
||||
|
||||
const req = {
|
||||
fallbackLocale,
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
|
||||
@@ -22,7 +22,6 @@ export default function registerGraphQLSchema(payload: Payload): void {
|
||||
blockInputTypes: {},
|
||||
blockTypes: {},
|
||||
groupTypes: {},
|
||||
tabTypes: {},
|
||||
}
|
||||
|
||||
if (payload.config.localization) {
|
||||
|
||||
@@ -508,7 +508,7 @@ function buildObjectType({
|
||||
const interfaceName =
|
||||
tab?.interfaceName || combineParentName(parentName, toWords(tab.name, true))
|
||||
|
||||
if (!payload.types.tabTypes[interfaceName]) {
|
||||
if (!payload.types.groupTypes[interfaceName]) {
|
||||
const objectType = buildObjectType({
|
||||
name: interfaceName,
|
||||
fields: tab.fields,
|
||||
@@ -518,14 +518,18 @@ function buildObjectType({
|
||||
})
|
||||
|
||||
if (Object.keys(objectType.getFields()).length) {
|
||||
return {
|
||||
...tabSchema,
|
||||
[tab.name]: { type: objectType },
|
||||
}
|
||||
payload.types.groupTypes[interfaceName] = objectType
|
||||
}
|
||||
}
|
||||
|
||||
return tabSchema
|
||||
if (!payload.types.groupTypes[interfaceName]) {
|
||||
return tabSchema
|
||||
}
|
||||
|
||||
return {
|
||||
...tabSchema,
|
||||
[tab.name]: { type: payload.types.groupTypes[interfaceName] },
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { ObjMap } from 'graphql/jsutils/ObjMap'
|
||||
import type { GraphQLFieldResolver } from 'graphql/type/definition'
|
||||
import type { GraphQLFieldConfig } from 'graphql/type/definition'
|
||||
import type { GraphQLFieldConfig, GraphQLFieldResolver } from 'graphql/type/definition'
|
||||
|
||||
import type { PayloadRequest } from '../../express/types'
|
||||
|
||||
import isolateTransactionID from '../../utilities/isolateTransactionID'
|
||||
import isolateObjectProperty from '../../utilities/isolateObjectProperty'
|
||||
|
||||
type PayloadContext = { req: PayloadRequest }
|
||||
|
||||
@@ -12,7 +11,12 @@ function wrapCustomResolver<TSource, TArgs, TResult>(
|
||||
resolver: GraphQLFieldResolver<TSource, PayloadContext, TArgs, TResult>,
|
||||
): GraphQLFieldResolver<TSource, PayloadContext, TArgs, TResult> {
|
||||
return (source, args, context, info) => {
|
||||
return resolver(source, args, { ...context, req: isolateTransactionID(context.req) }, info)
|
||||
return resolver(
|
||||
source,
|
||||
args,
|
||||
{ ...context, req: isolateObjectProperty(context.req, 'transactionID') },
|
||||
info,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +1,38 @@
|
||||
import type { SanitizedLocalizationConfig } from '../config/types'
|
||||
|
||||
/**
|
||||
* sets request locale
|
||||
*
|
||||
* @param localization
|
||||
* @returns {Function}
|
||||
*/
|
||||
export default function localizationMiddleware(localization: SanitizedLocalizationConfig | false) {
|
||||
const middleware = (req, res, next) => {
|
||||
if (localization) {
|
||||
const validLocales = [...localization.localeCodes, 'all']
|
||||
const validFallbackLocales = [...localization.localeCodes, 'null']
|
||||
|
||||
let requestedLocale = req.query.locale || localization.defaultLocale
|
||||
let requestedFallbackLocale = req.query['fallback-locale'] || localization.defaultLocale
|
||||
|
||||
if (req.body) {
|
||||
if (req.body.locale) requestedLocale = req.body.locale
|
||||
if (req.body['fallback-locale']) {
|
||||
requestedFallbackLocale = req.body['fallback-locale']
|
||||
}
|
||||
}
|
||||
|
||||
if (requestedFallbackLocale === 'none') requestedFallbackLocale = 'null'
|
||||
if (requestedLocale === '*' || requestedLocale === 'all') {
|
||||
requestedLocale = 'all'
|
||||
}
|
||||
|
||||
if (validLocales.find((locale) => locale === requestedLocale)) {
|
||||
req.locale = requestedLocale
|
||||
}
|
||||
|
||||
if (validFallbackLocales.find((locale) => locale === requestedFallbackLocale)) {
|
||||
req.fallbackLocale = requestedFallbackLocale
|
||||
}
|
||||
}
|
||||
|
||||
return next()
|
||||
export default function localizationMiddleware(req, res, next) {
|
||||
const localization = req.payload.config.localization
|
||||
if (!localization) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
return middleware
|
||||
const validLocales = [...localization.localeCodes, 'all']
|
||||
const validFallbackLocales = [...localization.localeCodes, 'null']
|
||||
|
||||
let requestedLocale = req.query.locale || localization.defaultLocale
|
||||
let requestedFallbackLocale = req.query['fallback-locale']
|
||||
|
||||
if (req.body) {
|
||||
if (req.body.locale) requestedLocale = req.body.locale
|
||||
if (req.body['fallback-locale']) {
|
||||
requestedFallbackLocale = req.body['fallback-locale']
|
||||
}
|
||||
}
|
||||
|
||||
if (requestedFallbackLocale === 'none') requestedFallbackLocale = 'null'
|
||||
if (requestedLocale === '*' || requestedLocale === 'all') {
|
||||
requestedLocale = 'all'
|
||||
}
|
||||
req.fallbackLocale = validFallbackLocales.find((locale) => locale === requestedFallbackLocale)
|
||||
|
||||
req.locale = validLocales.find((locale) => locale === requestedLocale)
|
||||
|
||||
if (!req.fallbackLocale) {
|
||||
req.fallbackLocale =
|
||||
localization.locales.find(({ code }) => req.locale === code)?.fallbackLocale ||
|
||||
localization.defaultLocale
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
@@ -256,7 +256,6 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
||||
fallbackLocaleInputType?: any
|
||||
groupTypes: any
|
||||
localeInputType?: any
|
||||
tabTypes: any
|
||||
}
|
||||
|
||||
unlock = async <T extends keyof TGeneratedTypes['collections']>(
|
||||
|
||||
@@ -5,10 +5,10 @@ import translations from './index'
|
||||
export const defaultOptions: InitOptions = {
|
||||
debug: false,
|
||||
detection: {
|
||||
caches: ['cookie', 'localStorage'],
|
||||
caches: ['header', 'cookie', 'localStorage'],
|
||||
lookupCookie: 'lng',
|
||||
lookupLocalStorage: 'lng',
|
||||
order: ['cookie', 'localStorage'],
|
||||
order: ['header', 'cookie', 'localStorage'],
|
||||
},
|
||||
fallbackLng: 'en',
|
||||
interpolation: {
|
||||
|
||||
27
packages/payload/src/utilities/isolateObjectProperty.ts
Normal file
27
packages/payload/src/utilities/isolateObjectProperty.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Creates a proxy for the given object that has its own property
|
||||
*/
|
||||
export default function isolateObjectProperty<T>(object: T, key): T {
|
||||
const delegate = {}
|
||||
const handler = {
|
||||
deleteProperty(target, p): boolean {
|
||||
return Reflect.deleteProperty(p === key ? delegate : target, p)
|
||||
},
|
||||
get(target, p, receiver) {
|
||||
return Reflect.get(p === key ? delegate : target, p, receiver)
|
||||
},
|
||||
has(target, p) {
|
||||
return Reflect.has(p === key ? delegate : target, p)
|
||||
},
|
||||
set(target, p, newValue, receiver) {
|
||||
if (p === key) {
|
||||
// in case of transactionID we must ignore any receiver, because
|
||||
// "If provided and target does not have a setter for propertyKey, the property will be set on receiver instead."
|
||||
return Reflect.set(delegate, p, newValue)
|
||||
} else {
|
||||
return Reflect.set(target, p, newValue, receiver)
|
||||
}
|
||||
},
|
||||
}
|
||||
return new Proxy(object, handler)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { PayloadRequest } from '../express/types'
|
||||
|
||||
/**
|
||||
* Creates a proxy for the given request that has its own TransactionID
|
||||
*/
|
||||
export default function isolateTransactionID(req: PayloadRequest): PayloadRequest {
|
||||
const delegate = {}
|
||||
const handler: ProxyHandler<PayloadRequest> = {
|
||||
deleteProperty(target, p): boolean {
|
||||
return Reflect.deleteProperty(p === 'transactionID' ? delegate : target, p)
|
||||
},
|
||||
get(target, p, receiver) {
|
||||
return Reflect.get(p === 'transactionID' ? delegate : target, p, receiver)
|
||||
},
|
||||
has(target, p) {
|
||||
return Reflect.has(p === 'transactionID' ? delegate : target, p)
|
||||
},
|
||||
set(target, p, newValue, receiver) {
|
||||
if (p === 'transactionID') {
|
||||
// in case of transactionID we must ignore any receiver, because
|
||||
// "If provided and target does not have a setter for propertyKey, the property will be set on receiver instead."
|
||||
return Reflect.set(delegate, p, newValue)
|
||||
} else {
|
||||
return Reflect.set(target, p, newValue, receiver)
|
||||
}
|
||||
},
|
||||
}
|
||||
return new Proxy(req, handler)
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
"@aws-sdk/credential-providers": "^3.289.0",
|
||||
"@aws-sdk/lib-storage": "^3.267.0",
|
||||
"amazon-cognito-identity-js": "^6.1.2",
|
||||
"nodemailer": "^6.9.1",
|
||||
"nodemailer": "6.9.8",
|
||||
"resend": "^0.17.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"version": "1.0.15",
|
||||
"version": "1.1.0",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-form-builder.git",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "1.0.9",
|
||||
"version": "1.0.10",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -19,7 +19,7 @@ const Search =
|
||||
// write any config defaults here
|
||||
}
|
||||
|
||||
// add a beforeChange hook to every search-enabled collection
|
||||
// add afterChange and afterDelete hooks to every search-enabled collection
|
||||
const collectionsWithSearchHooks = config?.collections
|
||||
?.map((collection) => {
|
||||
const { hooks: existingHooks } = collection
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user