Compare commits

..

59 Commits

Author SHA1 Message Date
Alessio Gravili
9df5ab8a10 feat(richtext-lexical)!: remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types (#6279)
BREAKING:

`LexicalBlock`, `RichTextFieldRequiredEditor` and `FieldWithRichTextRequiredEditor` types have been removed. Instead of `LexicalBlock`, use `Block`. Instead of `RichTextFieldRequiredEditor`, use `RichTextField`. And instead of `FieldWithRichTextRequiredEditor`, use `Field`.
2024-05-09 09:46:03 -04:00
Elliot DeNolf
6979f5a1b1 ci: register release-canary workflow 2024-05-09 09:22:18 -04:00
Elliot DeNolf
723791b94f chore(release): richtext-lexical/0.10.0 [skip ci] 2024-05-07 15:53:11 -04:00
Elliot DeNolf
aa3833ec83 chore(release): payload/2.16.1 [skip ci] 2024-05-07 15:52:06 -04:00
Dan Ribbens
2972af2af1 chore: add index to version status field (#6256) 2024-05-07 15:47:55 -04:00
Alessio Gravili
857b9a4ac3 feat(richtext-lexical): add maxDepth property to various lexical features (#6250)
Backports #6242
2024-05-07 09:39:48 -04:00
Alessio Gravili
f829b084ba fix(richtext-lexical): export missing HorizontalRuleFeature (#6236) 2024-05-06 13:54:49 -04:00
Elliot DeNolf
6ae682698d chore(release): payload/2.16.0 [skip ci] 2024-05-06 12:47:28 -04:00
Francisco Lourenço
622cdb0440 fix: hide drag handles when admin.isSortable: false (#6225)
Co-authored-by: Kendell Joseph <kendelljoseph@gmail.com>
2024-05-06 12:21:35 -04:00
Jarrod Flesch
cac52da638 fix: graphql upload relations returning null (#6233) 2024-05-06 11:58:48 -04:00
DracoBlue
3cb3c1aceb docs(plugin-cloud): Fix link to local-dev.md (#6222) 2024-05-05 22:27:26 -04:00
Patrik
db4aacebb8 feat: adds disableListColumn, disableListFilter to fields admin props (#6188) 2024-05-03 16:49:06 -04:00
Paul
b735d6aa16 fix(plugin-form-builder): hook overrides not working as intended (#6203) 2024-05-03 17:36:03 -03:00
Elliot DeNolf
fbec3a33e0 chore(release): richtext-slate/1.5.2 [skip ci] 2024-05-03 11:23:16 -04:00
Elliot DeNolf
034be89bdb chore(release): richtext-lexical/0.9.3 [skip ci] 2024-05-03 11:23:00 -04:00
Elliot DeNolf
1f2af0963b chore(release): plugin-cloud/3.0.1 [skip ci] 2024-05-03 11:22:42 -04:00
Elliot DeNolf
20f1ece2d7 chore(release): payload/2.15.0 [skip ci] 2024-05-03 11:21:26 -04:00
Jessica Chowdhury
2be5ad0eba fix: hide unusable fields from collection filter select (#6135) 2024-05-03 11:01:47 -04:00
Kendell Joseph
5c58bd322d feat: add isSortable to arrays and blocks (#5962) 2024-05-03 10:38:02 -04:00
Dan Ribbens
23f3eb1cf0 feat: use filterOptions in list relationship filter (#6156) 2024-05-03 10:26:49 -04:00
Patrik
af67749e49 fix: incorrect localesNotSaved translation (#5996) 2024-05-03 10:22:06 -04:00
Patrik
43dab5c705 fix: sanitizes fields in default edit view for drawer content (#6175) 2024-05-03 10:16:24 -04:00
Patrik
9b7e62dc20 fix: properly adds readonly styles to disabled radio fields (#6176) 2024-05-03 10:07:52 -04:00
Patrik
6e38cc2bcf fix: resets filter state when param state change within route (#6169) 2024-05-02 13:21:51 -04:00
Tylan Davis
83551bfcaa docs: adjust line breaks in code blocks - v2 (#6002) 2024-05-01 15:58:15 -04:00
Francisco Lourenço
7b44d9d28a docs: fix outdated #aliasing-server-only-modules urls (#5014) 2024-05-01 15:52:18 -04:00
ovalice
182d5db6de docs: Fix RowLabel code snippet that causes compilation error (#4947)
Co-authored-by: smarten <user@example.com>
2024-05-01 15:42:43 -04:00
Mina Sameh
93109ec84a docs: edit code of generate email subject in verify auth section (#4607) 2024-05-01 15:31:24 -04:00
Take Weiland
4d9e0f35f0 docs: clarify docs around direction transaction access (#3648) 2024-05-01 15:22:48 -04:00
Carlo Taleon
19327c8d6d docs: slate linebreak serialization in 'Generating HTML' example (#3804) 2024-05-01 15:21:03 -04:00
Elliot DeNolf
831f1ff5be fix(plugin-cloud): purge cache for all sizes (#5301) 2024-05-01 15:15:16 -04:00
Jarrod Flesch
a8ac8b4633 fix: cascade draft arg in nested GraphQL relationship queries (#6141) 2024-04-30 14:19:20 -04:00
Alessio Gravili
36b1f5a763 fix(richtext-lexical): floating toolbar caret positioned incorrectly for some line heights (#6151) 2024-04-30 12:06:02 -04:00
Alessio Gravili
24f697219b fix(richtext-lexical): drag and add block handles disappear too quickly for smaller screen sizes. (#6145) 2024-04-30 10:52:37 -04:00
Jarrod Flesch
3fccd34abe fix: GraphQL nested relationships not respecting req locale (#6117) 2024-04-29 16:32:33 -04:00
Friggo
a38f8e93a6 chore: Czech translation improvement (#6079) 2024-04-28 07:57:47 -04:00
Dan Ribbens
84570e6e3b fix: bulk publish from collection list (#6063) 2024-04-28 07:30:37 -04:00
Friggo
5ad8e0edcb chore: Czech translation improvements (#6077) 2024-04-28 07:27:26 -04:00
Jarrod Flesch
91bac9c0aa fix: version restoration (#6039) 2024-04-26 16:15:14 -04:00
Elliot DeNolf
33f6edc9d5 chore(release): richtext-slate/1.5.1 [skip ci] 2024-04-26 16:05:01 -04:00
Elliot DeNolf
e1f91f5170 chore(release): richtext-lexical/0.9.2 [skip ci] 2024-04-26 16:04:37 -04:00
Elliot DeNolf
0e75dfb5c1 chore(release): plugin-stripe/0.0.17 [skip ci] 2024-04-26 16:04:29 -04:00
Elliot DeNolf
1300e264be chore(release): plugin-seo/2.3.2 [skip ci] 2024-04-26 16:04:21 -04:00
Elliot DeNolf
5600125de7 chore(release): plugin-search/1.1.1 [skip ci] 2024-04-26 16:04:14 -04:00
Elliot DeNolf
22e270f89c chore(release): plugin-redirects/1.0.2 [skip ci] 2024-04-26 16:04:06 -04:00
Elliot DeNolf
593f82bcba chore(release): plugin-form-builder/1.2.2 [skip ci] 2024-04-26 16:03:58 -04:00
Elliot DeNolf
cbf3da1144 chore(release): bundler-vite/0.1.7 [skip ci] 2024-04-26 16:03:49 -04:00
Elliot DeNolf
1a2aab4126 chore(release): payload/2.14.2 [skip ci] 2024-04-26 16:01:09 -04:00
Jacob Fletcher
d0ba694c80 fix(deps): dedupes react (#6058) 2024-04-26 11:43:14 -04:00
Elliot DeNolf
d78df36d9b ci(scripts): safer package details retrieval 2024-04-25 14:50:01 -04:00
Elliot DeNolf
02572d945a chore(release): db-postgres/0.8.2 [skip ci] 2024-04-25 14:39:37 -04:00
Elliot DeNolf
f05a433320 chore(release): richtext-lexical/0.9.1 [skip ci] 2024-04-25 14:38:56 -04:00
Elliot DeNolf
9ce3b3ab29 chore(release): db-mongodb/1.5.1 [skip ci] 2024-04-25 14:38:44 -04:00
Elliot DeNolf
ade637befb chore(release): payload/2.14.1 [skip ci] 2024-04-25 14:37:27 -04:00
Dan Ribbens
c31b8dcaa0 fix(db-postgres): cummulative updates (#6033) 2024-04-25 13:05:49 -04:00
Dan Ribbens
0ffdcc685f fix: disable api key checkbox does not remove api key (#6017) 2024-04-25 09:39:09 -04:00
James Mikrut
881119ba3a Update README.md (#6028) 2024-04-25 09:35:41 -04:00
Alessio Gravili
60372faf36 fix(richtext-lexical): minimize the amount of times sanitizeFields is called (#6018) 2024-04-24 17:51:14 -04:00
Elliot DeNolf
8bca0b0b86 chore(release): db-postgres/0.8.1 [skip ci] 2024-04-24 15:41:48 -04:00
181 changed files with 2786 additions and 921 deletions

View File

@@ -1,4 +1,4 @@
name: pr-title
name: release-canary
on:
workflow_dispatch:
@@ -8,4 +8,4 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Echo
run: echo "Register pr-title workflow"
run: echo "Register release-canary workflow"

7
.vscode/launch.json vendored
View File

@@ -19,6 +19,13 @@
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
}
},
{
"command": "pnpm run dev collections-graphql",
"cwd": "${workspaceFolder}",
"name": "Run Dev GraphQL",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev fields",
"cwd": "${workspaceFolder}",

View File

@@ -1,3 +1,69 @@
## [2.16.1](https://github.com/payloadcms/payload/compare/v2.16.0...v2.16.1) (2024-05-07)
### Features
* **richtext-lexical:** add maxDepth property to various lexical features ([#6250](https://github.com/payloadcms/payload/issues/6250)) ([857b9a4](https://github.com/payloadcms/payload/commit/857b9a4ac3236c740458750f156a3a4274eda210)), closes [#6242](https://github.com/payloadcms/payload/issues/6242)
### Bug Fixes
* **richtext-lexical:** export missing HorizontalRuleFeature ([#6236](https://github.com/payloadcms/payload/issues/6236)) ([f829b08](https://github.com/payloadcms/payload/commit/f829b084ba9649ef596cce4a7bf6ae8c7ccf57e3))
## [2.16.0](https://github.com/payloadcms/payload/compare/v2.15.0...v2.16.0) (2024-05-06)
### Features
* adds disableListColumn, disableListFilter to fields admin props ([#6188](https://github.com/payloadcms/payload/issues/6188)) ([db4aace](https://github.com/payloadcms/payload/commit/db4aacebb801f1cc11ef8732f9f3b78475256641))
### Bug Fixes
* graphql upload relations returning null ([#6233](https://github.com/payloadcms/payload/issues/6233)) ([cac52da](https://github.com/payloadcms/payload/commit/cac52da638a0df4356120a2f61c6aaf25641a5ad))
* hide drag handles when `admin.isSortable: false` ([#6225](https://github.com/payloadcms/payload/issues/6225)) ([622cdb0](https://github.com/payloadcms/payload/commit/622cdb044002b2c3182c3b0432b51befbfb9b979))
* **plugin-form-builder:** hook overrides not working as intended ([#6203](https://github.com/payloadcms/payload/issues/6203)) ([b735d6a](https://github.com/payloadcms/payload/commit/b735d6aa169acca8cb638859d2c8ba43e315f02c))
## [2.15.0](https://github.com/payloadcms/payload/compare/v2.14.2...v2.15.0) (2024-05-03)
### Features
* add isSortable to arrays and blocks ([#5962](https://github.com/payloadcms/payload/issues/5962)) ([5c58bd3](https://github.com/payloadcms/payload/commit/5c58bd322da966fe610959df13dfd49add35a2ef))
* use filterOptions in list relationship filter ([#6156](https://github.com/payloadcms/payload/issues/6156)) ([23f3eb1](https://github.com/payloadcms/payload/commit/23f3eb1cf0b75a4044319d7cd3e5000d5b4e42c4))
### Bug Fixes
* bulk publish from collection list ([#6063](https://github.com/payloadcms/payload/issues/6063)) ([84570e6](https://github.com/payloadcms/payload/commit/84570e6e3bbb81fcae80da92b01bc56d09906072))
* cascade draft arg in nested GraphQL relationship queries ([#6141](https://github.com/payloadcms/payload/issues/6141)) ([a8ac8b4](https://github.com/payloadcms/payload/commit/a8ac8b463349664f3188ae77217f037da72f796b))
* GraphQL nested relationships not respecting req locale ([#6117](https://github.com/payloadcms/payload/issues/6117)) ([3fccd34](https://github.com/payloadcms/payload/commit/3fccd34abe5a332f88f5e950b755cd1d21441fb6))
* hide unusable fields from collection filter select ([#6135](https://github.com/payloadcms/payload/issues/6135)) ([2be5ad0](https://github.com/payloadcms/payload/commit/2be5ad0ebafd1d3c1c0567e2085ccfd593f18271))
* incorrect `localesNotSaved` translation ([#5996](https://github.com/payloadcms/payload/issues/5996)) ([af67749](https://github.com/payloadcms/payload/commit/af67749e49db92e675b63b52190e562468894706))
* **plugin-cloud:** purge cache for all sizes ([#5301](https://github.com/payloadcms/payload/issues/5301)) ([831f1ff](https://github.com/payloadcms/payload/commit/831f1ff5bed7e083cc076e9eb5ff9a2b2f1ed710))
* properly adds `readonly` styles to disabled `radio` fields ([#6176](https://github.com/payloadcms/payload/issues/6176)) ([9b7e62d](https://github.com/payloadcms/payload/commit/9b7e62dc20dca7402c6c68dfb8a5995c211993af))
* resets filter state when param state change within route ([#6169](https://github.com/payloadcms/payload/issues/6169)) ([6e38cc2](https://github.com/payloadcms/payload/commit/6e38cc2bcfb08b608abcb6aac4b4c1f6eea63428))
* **richtext-lexical:** drag and add block handles disappear too quickly for smaller screen sizes. ([#6145](https://github.com/payloadcms/payload/issues/6145)) ([24f6972](https://github.com/payloadcms/payload/commit/24f697219b5071d91a5c37aafb50e2d823b68d4c))
* **richtext-lexical:** floating toolbar caret positioned incorrectly for some line heights ([#6151](https://github.com/payloadcms/payload/issues/6151)) ([36b1f5a](https://github.com/payloadcms/payload/commit/36b1f5a763f782c140e62aa062b4077d6efd0738))
* sanitizes fields in default edit view for drawer content ([#6175](https://github.com/payloadcms/payload/issues/6175)) ([43dab5c](https://github.com/payloadcms/payload/commit/43dab5c7053831a0c71f3a6860113f653cab674f))
* version restoration ([#6039](https://github.com/payloadcms/payload/issues/6039)) ([91bac9c](https://github.com/payloadcms/payload/commit/91bac9c0aa1ff3da052b9c2ad83fa5ac23a16d1d))
## [2.14.2](https://github.com/payloadcms/payload/compare/v2.14.1...v2.14.2) (2024-04-26)
### Bug Fixes
* **deps:** dedupes react ([#6058](https://github.com/payloadcms/payload/issues/6058)) ([d0ba694](https://github.com/payloadcms/payload/commit/d0ba694c80a1b699c4f2cad98b1f0bde1f0d43ca))
## [2.14.1](https://github.com/payloadcms/payload/compare/v2.14.0...v2.14.1) (2024-04-25)
### Bug Fixes
* **db-postgres:** cumulative updates ([#6033](https://github.com/payloadcms/payload/issues/6033)) ([c31b8dc](https://github.com/payloadcms/payload/commit/c31b8dcaa0c43132d8a01e0cc43094f466cc9168))
* disable api key checkbox does not remove api key ([#6017](https://github.com/payloadcms/payload/issues/6017)) ([0ffdcc6](https://github.com/payloadcms/payload/commit/0ffdcc685f4e917a02e62dbaccec7cc8ebbf695d))
* **richtext-lexical:** minimize the amount of times sanitizeFields is called ([#6018](https://github.com/payloadcms/payload/issues/6018)) ([60372fa](https://github.com/payloadcms/payload/commit/60372faf36b7f6d92a61ccbaee0f528e50f5a51a))
## [2.14.0](https://github.com/payloadcms/payload/compare/v2.13.0...v2.14.0) (2024-04-24)

View File

@@ -17,7 +17,7 @@
<hr/>
> [!IMPORTANT]
> 🎉 <strong>Payload 3.0 beta announced!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/30-beta-install-payload-into-any-nextjs-app-with-one-line" rel="dofollow"><strong>announcement post</strong></a>.
> 🎉 <strong>Payload 3.0 beta released!</strong> You can now deploy Payload fully in any Next.js app folder. Read more in the <a target="_blank" href="https://payloadcms.com/blog/30-beta-install-payload-into-any-nextjs-app-with-one-line" rel="dofollow"><strong>announcement post</strong></a>.
<h3>Benefits over a regular CMS</h3>
<ul>

View File

@@ -216,7 +216,7 @@ Example:
{
slug: 'customers',
auth: {
forgotPassword: {
verify: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`;

View File

@@ -49,7 +49,8 @@ export default buildConfig({
{
label: 'Arabic',
code: 'ar',
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
// when current locale is rtl
rtl: true,
},
],
@@ -134,13 +135,9 @@ to support localization, you need to specify each field that you would like to l
```js
{
name: 'title',
type
:
'text',
// highlight-start
localized
:
true,
type: 'text',
// highlight-start
localized: true,
// highlight-end
}
```

View File

@@ -20,7 +20,8 @@ The initial request made to Payload will begin a new transaction and attach it t
```ts
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
// because req.transactionID is assigned from Payload and passed through, my-slug will only persist if the entire request is successful
// because req.transactionID is assigned from Payload and passed through,
// my-slug will only persist if the entire request is successful
await req.payload.create({
req,
collection: 'my-slug',
@@ -60,10 +61,44 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
### Direct Transaction Access
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's local API.
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database in something like a background job, outside the normal request-response flow.
The following functions can be used for managing transactions:
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls. Note that if your database does not support transactions, this will return `null`.\
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.\
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
You can then use the transaction ID with Payload's local API by passing it inside the `PayloadRequest` object.
Here is an example for a "background job" function, which utilizes the direct transaction API to make sure it either succeeds completely or gets rolled back in case of an error.
```ts
async function allOrNothingJob() {
const req = {} as PayloadRequest;
req.transactionID = await payload.db.beginTransaction();
try {
await payload.create({
req, // use our manual transaction
collection: 'my-slug',
data: {
some: 'data'
}
});
await payload.create({
req, // use our manual transaction
collection: 'something-else',
data: {
some: 'data'
}
});
console.log('Everything done.');
if (req.transactionID) await payload.db.commitTransaction(req.transactionID);
} catch (e) {
console.error('Oh no, something went wrong!');
if (req.transactionID) await payload.db.rollbackTransaction(req.transactionID);
}
}
```

View File

@@ -59,6 +59,7 @@ properties:
| Option | Description |
|---------------------------|----------------------------------------------------------------------------------------------------------------------|
| **`initCollapsed`** | Set the initial collapsed state |
| **`isSortable`** | Disable array order sorting by setting this value to `false` |
| **`components.RowLabel`** | Function or React component to be rendered as the label on the array row. Receives `({ data, index, path })` as args |
### Example
@@ -67,6 +68,7 @@ properties:
```ts
import { CollectionConfig } from 'payload/types'
import { RowLabelArgs } from 'payload/dist/admin/components/forms/RowLabel/types'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -101,7 +103,7 @@ export const ExampleCollection: CollectionConfig = {
],
admin: {
components: {
RowLabel: ({ data, index }) => {
RowLabel: ({ data, index }: RowLabelArgs) => {
return data?.title || `Slide ${String(index).padStart(2, '0')}`
},
},

View File

@@ -58,6 +58,7 @@ properties:
| Option | Description |
|---------------------|---------------------------------|
| **`initCollapsed`** | Set the initial collapsed state |
| **`isSortable`** | Disable block order sorting by setting this value to `false` |
### Block configs

View File

@@ -163,19 +163,21 @@ Example:
In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property:
| Option | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
| `className` | Attach a CSS class name to the root DOM element of a field. |
| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
| Option | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
| `className` | Attach a CSS class name to the root DOM element of a field. |
| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| `disableListColumn` | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
| `disableListFilter` | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
### Custom components

View File

@@ -36,7 +36,7 @@ If your Hook simply performs a side-effect, such as updating a CRM, it might be
#### Server-only execution
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/webpack#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/excluding-server-code#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
## Hook Types

View File

@@ -247,7 +247,7 @@ In the template, we have stubbed out a basic `onInitExtension` file that you can
### Webpack
If any of your files use server only packages such as fs, stripe, nodemailer, etc, they will need to be removed from the browser bundle. To do that, you can [alias the file imports with webpack](https://payloadcms.com/docs/admin/webpack#aliasing-server-only-modules).
If any of your files use server only packages such as fs, stripe, nodemailer, etc, they will need to be removed from the browser bundle. To do that, you can [alias the file imports with webpack](https://payloadcms.com/docs/admin/excluding-server-code#aliasing-server-only-modules).
When files are bundled for the browser, the import paths are essentially crawled to determine what files to include in the bundle. To prevent the server only files from making it into the bundle, we can alias their import paths to a file that can be included in the browser. This will short-circuit the import path crawling and ensure browser only code is bundled.

View File

@@ -203,7 +203,8 @@ const Pages: CollectionConfig = {
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
// The HTMLConverter Feature is the feature which manages the HTML serializers. If you do not pass any arguments to it, it will use the default serializers.
// The HTMLConverter Feature is the feature which manages the HTML serializers.
// If you do not pass any arguments to it, it will use the default serializers.
HTMLConverterFeature({}),
],
}),

View File

@@ -270,6 +270,10 @@ const serialize = (children) =>
text = <em key={i}>{text}</em>;
}
if (node.text === '') {
text = <br />;
}
// Handle other leaf types here...
return <Fragment key={i}>{text}</Fragment>;

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/bundler-vite",
"version": "0.1.6",
"version": "0.1.7",
"description": "The officially supported Vite bundler adapter for Payload",
"repository": {
"type": "git",
@@ -41,7 +41,7 @@
},
"peerDependencies": {
"payload": "^2.0.0",
"react-dom": "18.2.0"
"react-dom": "^18.0.0"
},
"publishConfig": {
"main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "1.5.0",
"version": "1.5.1",
"description": "The officially supported MongoDB database adapter for Payload",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "0.8.0",
"version": "0.8.2",
"description": "The officially supported Postgres database adapter for Payload",
"repository": {
"type": "git",

View File

@@ -2,13 +2,13 @@ import type { Count } from 'payload/database'
import type { SanitizedCollectionConfig } from 'payload/types'
import { sql } from 'drizzle-orm'
import toSnakeCase from 'to-snake-case'
import type { ChainedMethods } from './find/chainMethods'
import type { PostgresAdapter } from './types'
import { chainMethods } from './find/chainMethods'
import buildQuery from './queries/buildQuery'
import { getTableName } from './schema/getTableName'
export const count: Count = async function count(
this: PostgresAdapter,
@@ -16,10 +16,7 @@ export const count: Count = async function count(
) {
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const tableName = getTableName({
adapter: this,
config: collectionConfig,
})
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
const db = this.sessions[req.transactionID]?.db || this.drizzle
const table = this.tables[tableName]

View File

@@ -2,8 +2,8 @@ import type { Create } from 'payload/database'
import type { PostgresAdapter } from './types'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
import toSnakeCase from 'to-snake-case'
export const create: Create = async function create(
this: PostgresAdapter,
@@ -12,6 +12,8 @@ export const create: Create = async function create(
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collection = this.payload.collections[collectionSlug].config
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
const result = await upsertRow({
adapter: this,
data,
@@ -19,10 +21,7 @@ export const create: Create = async function create(
fields: collection.fields,
operation: 'create',
req,
tableName: getTableName({
adapter: this,
config: collection,
}),
tableName,
})
return result

View File

@@ -1,9 +1,10 @@
import type { CreateGlobalArgs } from 'payload/database'
import type { PayloadRequest, TypeWithID } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export async function createGlobal<T extends TypeWithID>(
@@ -13,6 +14,8 @@ export async function createGlobal<T extends TypeWithID>(
const db = this.sessions[req.transactionID]?.db || this.drizzle
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
const result = await upsertRow<T>({
adapter: this,
data,
@@ -20,10 +23,7 @@ export async function createGlobal<T extends TypeWithID>(
fields: globalConfig.fields,
operation: 'create',
req,
tableName: getTableName({
adapter: this,
config: globalConfig,
}),
tableName,
})
return result

View File

@@ -4,10 +4,10 @@ import type { PayloadRequest, TypeWithID } from 'payload/types'
import { sql } from 'drizzle-orm'
import { type CreateGlobalVersionArgs } from 'payload/database'
import { buildVersionGlobalFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export async function createGlobalVersion<T extends TypeWithID>(
@@ -16,11 +16,8 @@ export async function createGlobalVersion<T extends TypeWithID>(
) {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const global = this.payload.globals.config.find(({ slug }) => slug === globalSlug)
const tableName = getTableName({
adapter: this,
config: global,
versions: true,
})
const tableName = this.tableNameMap.get(`_${toSnakeCase(global.slug)}${this.versionsSuffix}`)
const result = await upsertRow<TypeWithVersion<T>>({
adapter: this,
@@ -40,9 +37,9 @@ export async function createGlobalVersion<T extends TypeWithID>(
if (global.versions.drafts) {
await db.execute(sql`
UPDATE ${table}
SET latest = false
WHERE ${table.id} != ${result.id};
UPDATE ${table}
SET latest = false
WHERE ${table.id} != ${result.id};
`)
}

View File

@@ -3,10 +3,10 @@ import type { PayloadRequest, TypeWithID } from 'payload/types'
import { sql } from 'drizzle-orm'
import { buildVersionCollectionFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export async function createVersion<T extends TypeWithID>(
@@ -21,11 +21,12 @@ export async function createVersion<T extends TypeWithID>(
) {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collection = this.payload.collections[collectionSlug].config
const tableName = getTableName({
adapter: this,
config: collection,
versions: true,
})
const defaultTableName = toSnakeCase(collection.slug)
const tableName = this.tableNameMap.get(`_${defaultTableName}${this.versionsSuffix}`)
const version = { ...versionData }
if (version.id) delete version.id
const result = await upsertRow<TypeWithVersion<T>>({
adapter: this,
@@ -33,7 +34,7 @@ export async function createVersion<T extends TypeWithID>(
autosave,
latest: true,
parent,
version: versionData,
version,
},
db,
fields: buildVersionCollectionFields(collection),
@@ -43,25 +44,19 @@ export async function createVersion<T extends TypeWithID>(
})
const table = this.tables[tableName]
const relationshipsTable =
this.tables[
getTableName({
adapter: this,
config: collection,
relationships: true,
versions: true,
})
]
this.tables[`_${defaultTableName}${this.versionsSuffix}${this.relationshipsSuffix}`]
if (collection.versions.drafts) {
await db.execute(sql`
UPDATE ${table}
SET latest = false
FROM ${relationshipsTable}
WHERE ${table.id} = ${relationshipsTable.parent}
AND ${relationshipsTable.path} = ${'parent'}
AND ${relationshipsTable[`${collectionSlug}ID`]} = ${parent}
AND ${table.id} != ${result.id};
UPDATE ${table}
SET latest = false
FROM ${relationshipsTable}
WHERE ${table.id} = ${relationshipsTable.parent}
AND ${relationshipsTable.path} = ${'parent'}
AND ${relationshipsTable[`${collectionSlug}ID`]} = ${parent}
AND ${table.id} != ${result.id};
`)
}

View File

@@ -2,11 +2,11 @@ import type { DeleteMany } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import { inArray } from 'drizzle-orm'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const deleteMany: DeleteMany = async function deleteMany(
this: PostgresAdapter,
@@ -14,7 +14,8 @@ export const deleteMany: DeleteMany = async function deleteMany(
) {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collectionConfig = this.payload.collections[collection].config
const tableName = getTableName({ adapter: this, config: collectionConfig })
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
const result = await findMany({
adapter: this,

View File

@@ -2,13 +2,13 @@ import type { DeleteOne } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import { eq } from 'drizzle-orm'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { buildFindManyArgs } from './find/buildFindManyArgs'
import buildQuery from './queries/buildQuery'
import { selectDistinct } from './queries/selectDistinct'
import { getTableName } from './schema/getTableName'
import { transform } from './transform/read'
export const deleteOne: DeleteOne = async function deleteOne(
@@ -17,10 +17,9 @@ export const deleteOne: DeleteOne = async function deleteOne(
) {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collection = this.payload.collections[collectionSlug].config
const tableName = getTableName({
adapter: this,
config: collection,
})
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
let docToDelete: Record<string, unknown>
const { joinAliases, joins, selectFields, where } = await buildQuery({

View File

@@ -3,11 +3,11 @@ import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
import { inArray } from 'drizzle-orm'
import { buildVersionCollectionFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const deleteVersions: DeleteVersions = async function deleteVersion(
this: PostgresAdapter,
@@ -16,11 +16,10 @@ export const deleteVersions: DeleteVersions = async function deleteVersion(
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const tableName = getTableName({
adapter: this,
config: collectionConfig,
versions: true,
})
const tableName = this.tableNameMap.get(
`_${toSnakeCase(collectionConfig.slug)}${this.versionsSuffix}`,
)
const fields = buildVersionCollectionFields(collectionConfig)
const { docs } = await findMany({

View File

@@ -1,10 +1,11 @@
import type { Find } from 'payload/database'
import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const find: Find = async function find(
this: PostgresAdapter,
@@ -21,10 +22,8 @@ export const find: Find = async function find(
) {
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort
const tableName = getTableName({
adapter: this,
config: collectionConfig,
})
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
return findMany({
adapter: this,

View File

@@ -2,12 +2,11 @@
import type { Field } from 'payload/types'
import { fieldAffectsData, tabHasName } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../types'
import type { Result } from './buildFindManyArgs'
import { getTableName } from '../schema/getTableName'
type TraverseFieldArgs = {
_locales: Record<string, unknown>
adapter: PostgresAdapter
@@ -79,20 +78,11 @@ export const traverseFields = ({
with: {},
}
const arrayTableName = getTableName({
adapter,
config: field,
parentTableName: currentTableName,
prefix: `${currentTableName}_${path}`,
})
const arrayTableName = adapter.tableNameMap.get(
`${currentTableName}_${path}${toSnakeCase(field.name)}`,
)
const arrayTableNameWithLocales = getTableName({
adapter,
config: field,
locales: true,
parentTableName: currentTableName,
prefix: `${currentTableName}_${path}`,
})
const arrayTableNameWithLocales = `${arrayTableName}${adapter.localesSuffix}`
if (adapter.tables[arrayTableNameWithLocales]) withArray.with._locales = _locales
currentArgs.with[`${path}${field.name}`] = withArray
@@ -142,15 +132,13 @@ export const traverseFields = ({
with: {},
}
const tableName = getTableName({
adapter,
config: block,
parentTableName: topLevelTableName,
prefix: `${topLevelTableName}_blocks_`,
})
const tableName = adapter.tableNameMap.get(
`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}`,
)
if (adapter.tables[`${tableName}${adapter.localesSuffix}`])
if (adapter.tables[`${tableName}${adapter.localesSuffix}`]) {
withBlock.with._locales = _locales
}
topLevelArgs.with[blockKey] = withBlock
traverseFields({

View File

@@ -1,19 +1,18 @@
import type { FindGlobal } from 'payload/database'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const findGlobal: FindGlobal = async function findGlobal(
this: PostgresAdapter,
{ slug, locale, req, where },
) {
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
const tableName = getTableName({
adapter: this,
config: globalConfig,
})
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
const {
docs: [doc],

View File

@@ -2,11 +2,11 @@ import type { FindGlobalVersions } from 'payload/database'
import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types'
import { buildVersionGlobalFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
this: PostgresAdapter,
@@ -27,11 +27,10 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
)
const sort = typeof sortArg === 'string' ? sortArg : '-createdAt'
const tableName = getTableName({
adapter: this,
config: globalConfig,
versions: true,
})
const tableName = this.tableNameMap.get(
`_${toSnakeCase(globalConfig.slug)}${this.versionsSuffix}`,
)
const fields = buildVersionGlobalFields(globalConfig)
return findMany({

View File

@@ -1,20 +1,19 @@
import type { FindOneArgs } from 'payload/database'
import type { PayloadRequest, SanitizedCollectionConfig, TypeWithID } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export async function findOne<T extends TypeWithID>(
this: PostgresAdapter,
{ collection, locale, req = {} as PayloadRequest, where }: FindOneArgs,
): Promise<T> {
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const tableName = getTableName({
adapter: this,
config: collectionConfig,
})
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
const { docs } = await findMany({
adapter: this,

View File

@@ -2,11 +2,11 @@ import type { FindVersions } from 'payload/database'
import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
import { buildVersionCollectionFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const findVersions: FindVersions = async function findVersions(
this: PostgresAdapter,
@@ -25,11 +25,10 @@ export const findVersions: FindVersions = async function findVersions(
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort
const tableName = getTableName({
adapter: this,
config: collectionConfig,
versions: true,
})
const tableName = this.tableNameMap.get(
`_${toSnakeCase(collectionConfig.slug)}${this.versionsSuffix}`,
)
const fields = buildVersionCollectionFields(collectionConfig)
return findMany({

View File

@@ -41,18 +41,18 @@ import { updateVersion } from './updateVersion'
export type { MigrateDownArgs, MigrateUpArgs } from './types'
export function postgresAdapter(args: Args): PostgresAdapterResult {
const postgresIDType = args.idType || 'serial'
const payloadIDType = postgresIDType === 'serial' ? 'number' : 'text'
function adapter({ payload }: { payload: Payload }) {
const migrationDir = findMigrationDir(args.migrationDir)
const idType = args.idType || 'serial'
return createDatabaseAdapter<PostgresAdapter>({
name: 'postgres',
// Postgres-specific
blockTableNames: {},
drizzle: undefined,
enums: {},
fieldConstraints: {},
idType,
idType: postgresIDType,
localesSuffix: args.localesSuffix || '_locales',
logger: args.logger,
pgSchema: undefined,
@@ -64,6 +64,7 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
schema: {},
schemaName: args.schemaName,
sessions: {},
tableNameMap: new Map<string, string>(),
tables: {},
versionsSuffix: args.versionsSuffix || '_v',
@@ -77,10 +78,7 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
createGlobalVersion,
createMigration,
createVersion,
/**
* This represents how a default ID is treated in Payload as were a field type
*/
defaultIDType: idType === 'serial' ? 'number' : 'text',
defaultIDType: payloadIDType,
deleteMany,
deleteOne,
deleteVersions,

View File

@@ -8,7 +8,7 @@ import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/
import type { PostgresAdapter } from './types'
import { buildTable } from './schema/build'
import { getTableName } from './schema/getTableName'
import { createTableName } from './schema/createTableName'
export const init: Init = async function init(this: PostgresAdapter) {
if (this.schemaName) {
@@ -25,7 +25,7 @@ export const init: Init = async function init(this: PostgresAdapter) {
}
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
const tableName = getTableName({
const tableName = createTableName({
adapter: this,
config: collection,
})
@@ -44,10 +44,11 @@ export const init: Init = async function init(this: PostgresAdapter) {
})
if (collection.versions) {
const versionsTableName = getTableName({
const versionsTableName = createTableName({
adapter: this,
config: collection,
versions: true,
versionsCustomName: true,
})
const versionFields = buildVersionCollectionFields(collection)
@@ -67,7 +68,7 @@ export const init: Init = async function init(this: PostgresAdapter) {
})
this.payload.config.globals.forEach((global) => {
const tableName = getTableName({ adapter: this, config: global })
const tableName = createTableName({ adapter: this, config: global })
buildTable({
adapter: this,
@@ -83,7 +84,12 @@ export const init: Init = async function init(this: PostgresAdapter) {
})
if (global.versions) {
const versionsTableName = getTableName({ adapter: this, config: global, versions: true })
const versionsTableName = createTableName({
adapter: this,
config: global,
versions: true,
versionsCustomName: true,
})
const versionFields = buildVersionGlobalFields(global)
buildTable({

View File

@@ -14,8 +14,6 @@ import { v4 as uuid } from 'uuid'
import type { GenericColumn, GenericTable, PostgresAdapter } from '../types'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
import { getTableName } from '../schema/getTableName'
type Constraint = {
columnName: string
table: GenericTable | PgTableWithColumns<any>
@@ -185,13 +183,7 @@ export const getTableColumnFromPath = ({
case 'group': {
if (locale && field.localized && adapter.payload.config.localization) {
newTableName = getTableName({
adapter,
config: field,
locales: true,
parentTableName: tableName,
prefix: `${tableName}_`,
})
newTableName = `${tableName}${adapter.localesSuffix}`
joins[tableName] = eq(
adapter.tables[tableName].id,
@@ -227,12 +219,9 @@ export const getTableColumnFromPath = ({
case 'select': {
if (field.hasMany) {
newTableName = getTableName({
adapter,
config: field,
parentTableName: `${tableName}_${tableNameSuffix}`,
prefix: `${tableName}_${tableNameSuffix}`,
})
const newTableName = adapter.tableNameMap.get(
`${tableName}_${tableNameSuffix}${toSnakeCase(field.name)}`,
)
if (locale && field.localized && adapter.payload.config.localization) {
joins[newTableName] = and(
@@ -305,12 +294,10 @@ export const getTableColumnFromPath = ({
}
case 'array': {
newTableName = getTableName({
adapter,
config: field,
parentTableName: `${tableName}_${tableNameSuffix}`,
prefix: `${tableName}_${tableNameSuffix}`,
})
newTableName = adapter.tableNameMap.get(
`${tableName}_${tableNameSuffix}${toSnakeCase(field.name)}`,
)
constraintPath = `${constraintPath}${field.name}.%.`
if (locale && field.localized && adapter.payload.config.localization) {
joins[newTableName] = and(
@@ -357,12 +344,11 @@ export const getTableColumnFromPath = ({
const blockTypes = Array.isArray(value) ? value : [value]
blockTypes.forEach((blockType) => {
const block = field.blocks.find((block) => block.slug === blockType)
newTableName = getTableName({
adapter,
config: block,
parentTableName: tableName,
prefix: `${tableName}_blocks_`,
})
newTableName = adapter.tableNameMap.get(
`${tableName}_blocks_${toSnakeCase(block.slug)}`,
)
joins[newTableName] = eq(
adapter.tables[tableName].id,
adapter.tables[newTableName]._parentID,
@@ -382,13 +368,9 @@ export const getTableColumnFromPath = ({
}
const hasBlockField = field.blocks.some((block) => {
newTableName = getTableName({
adapter,
config: block,
parentTableName: tableName,
prefix: `${tableName}_blocks_`,
})
newTableName = adapter.tableNameMap.get(`${tableName}_blocks_${toSnakeCase(block.slug)}`)
constraintPath = `${constraintPath}${field.name}.%.`
let result
const blockConstraints = []
const blockSelectFields = {}
@@ -461,6 +443,7 @@ export const getTableColumnFromPath = ({
aliasRelationshipTableName,
)
// Join in the relationships table
if (locale && field.localized && adapter.payload.config.localization) {
joinAliases.push({
condition: and(
@@ -494,10 +477,9 @@ export const getTableColumnFromPath = ({
if (typeof field.relationTo === 'string') {
const relationshipConfig = adapter.payload.collections[field.relationTo].config
newTableName = getTableName({
adapter,
config: relationshipConfig,
})
newTableName = adapter.tableNameMap.get(toSnakeCase(relationshipConfig.slug))
// parent to relationship join table
relationshipFields = relationshipConfig.fields
@@ -517,13 +499,13 @@ export const getTableColumnFromPath = ({
}
}
} else if (newCollectionPath === 'value') {
const tableColumnsNames = field.relationTo.map(
(relationTo) =>
`"${aliasRelationshipTableName}"."${getTableName({
adapter,
config: adapter.payload.collections[relationTo].config,
})}_id"`,
)
const tableColumnsNames = field.relationTo.map((relationTo) => {
const relationTableName = adapter.tableNameMap.get(
toSnakeCase(adapter.payload.collections[relationTo].config.slug),
)
return `"${aliasRelationshipTableName}"."${relationTableName}_id"`
})
return {
constraints,
field,

View File

@@ -10,8 +10,8 @@ import type { GenericColumn, PostgresAdapter } from '../types'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
import { buildAndOrConditions } from './buildAndOrConditions'
import { createJSONQuery } from './createJSONQuery'
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
import { createJSONQuery } from './createJSONQuery'
import { getTableColumnFromPath } from './getTableColumnFromPath'
import { operatorMap } from './operatorMap'
import { sanitizeQueryValue } from './sanitizeQueryValue'

View File

@@ -2,26 +2,20 @@ import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
import { type QueryDrafts, combineQueries } from 'payload/database'
import { buildVersionCollectionFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { findMany } from './find/findMany'
import { getTableName } from './schema/getTableName'
export const queryDrafts: QueryDrafts = async function queryDrafts({
collection,
limit,
locale,
page = 1,
pagination,
req = {} as PayloadRequest,
sort,
where,
}) {
export const queryDrafts: QueryDrafts = async function queryDrafts(
this: PostgresAdapter,
{ collection, limit, locale, page = 1, pagination, req = {} as PayloadRequest, sort, where },
) {
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const tableName = getTableName({
adapter: this,
config: collectionConfig,
versions: true,
})
const tableName = this.tableNameMap.get(
`_${toSnakeCase(collectionConfig.slug)}${this.versionsSuffix}`,
)
const fields = buildVersionCollectionFields(collectionConfig)
const combinedWhere = combineQueries({ latest: { equals: true } }, where)

View File

@@ -1,28 +1,43 @@
/* eslint-disable no-param-reassign */
import type { Relation } from 'drizzle-orm'
import type {
ForeignKeyBuilder,
IndexBuilder,
PgColumnBuilder,
PgTableWithColumns,
UniqueConstraintBuilder,
} from 'drizzle-orm/pg-core'
import type { Field } from 'payload/types'
import { Field, fieldAffectsData } from 'payload/types'
import { relations } from 'drizzle-orm'
import { index, integer, numeric, serial, timestamp, unique, varchar } from 'drizzle-orm/pg-core'
import { fieldAffectsData } from 'payload/types'
import {
foreignKey,
index,
integer,
numeric,
serial,
timestamp,
unique,
varchar,
} from 'drizzle-orm/pg-core'
import toSnakeCase from 'to-snake-case'
import type { GenericColumns, GenericTable, IDType, PostgresAdapter } from '../types'
import { getTableName } from './getTableName'
import { createTableName } from './createTableName'
import { parentIDColumnMap } from './parentIDColumnMap'
import { setColumnID } from './setColumnID'
import { traverseFields } from './traverseFields'
export type BaseExtraConfig = Record<
string,
(cols: GenericColumns) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder
>
type Args = {
adapter: PostgresAdapter
baseColumns?: Record<string, PgColumnBuilder>
baseExtraConfig?: Record<string, (cols: GenericColumns) => IndexBuilder | UniqueConstraintBuilder>
baseExtraConfig?: BaseExtraConfig
buildNumbers?: boolean
buildRelationships?: boolean
buildTexts?: boolean
@@ -66,13 +81,6 @@ export const buildTable = ({
const columns: Record<string, PgColumnBuilder> = baseColumns
const indexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
let hasLocalizedField = false
let hasLocalizedRelationshipField = false
let hasManyTextField: 'index' | boolean = false
let hasManyNumberField: 'index' | boolean = false
let hasLocalizedManyTextField = false
let hasLocalizedManyNumberField = false
const localesColumns: Record<string, PgColumnBuilder> = {}
const localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
let localesTable: GenericTable | PgTableWithColumns<any>
@@ -89,7 +97,7 @@ export const buildTable = ({
const idColType: IDType = setColumnID({ adapter, columns, fields })
;({
const {
hasLocalizedField,
hasLocalizedManyNumberField,
hasLocalizedManyTextField,
@@ -116,7 +124,7 @@ export const buildTable = ({
rootTableIDColType: rootTableIDColType || idColType,
rootTableName,
versions,
}))
})
if (timestamps) {
columns.createdAt = timestamp('created_at', {
@@ -141,10 +149,12 @@ export const buildTable = ({
return config
}, {})
return Object.entries(indexes).reduce((acc, [colName, func]) => {
const result = Object.entries(indexes).reduce((acc, [colName, func]) => {
acc[colName] = func(cols)
return acc
}, extraConfig)
return result
})
adapter.tables[tableName] = table
@@ -153,9 +163,7 @@ export const buildTable = ({
const localeTableName = `${tableName}${adapter.localesSuffix}`
localesColumns.id = serial('id').primaryKey()
localesColumns._locale = adapter.enums.enum__locales('_locale').notNull()
localesColumns._parentID = parentIDColumnMap[idColType]('_parent_id')
.references(() => table.id, { onDelete: 'cascade' })
.notNull()
localesColumns._parentID = parentIDColumnMap[idColType]('_parent_id').notNull()
localesTable = adapter.pgSchema.table(localeTableName, localesColumns, (cols) => {
return Object.entries(localesIndexes).reduce(
@@ -168,6 +176,11 @@ export const buildTable = ({
cols._locale,
cols._parentID,
),
_parentIdFk: foreignKey({
name: `${localeTableName}_parent_id_fk`,
columns: [cols._parentID],
foreignColumns: [table.id],
}).onDelete('cascade'),
},
)
})
@@ -189,9 +202,7 @@ export const buildTable = ({
const columns: Record<string, PgColumnBuilder> = {
id: serial('id').primaryKey(),
order: integer('order').notNull(),
parent: parentIDColumnMap[idColType]('parent_id')
.references(() => table.id, { onDelete: 'cascade' })
.notNull(),
parent: parentIDColumnMap[idColType]('parent_id').notNull(),
path: varchar('path').notNull(),
text: varchar('text'),
}
@@ -201,19 +212,24 @@ export const buildTable = ({
}
textsTable = adapter.pgSchema.table(textsTableName, columns, (cols) => {
const indexes: Record<string, IndexBuilder> = {
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
orderParentIdx: index(`${textsTableName}_order_parent_idx`).on(cols.order, cols.parent),
parentFk: foreignKey({
name: `${textsTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
}
if (hasManyTextField === 'index') {
indexes.text_idx = index(`${textsTableName}_text_idx`).on(cols.text)
config.text_idx = index(`${textsTableName}_text_idx`).on(cols.text)
}
if (hasLocalizedManyTextField) {
indexes.localeParent = index(`${textsTableName}_locale_parent`).on(cols.locale, cols.parent)
config.localeParent = index(`${textsTableName}_locale_parent`).on(cols.locale, cols.parent)
}
return indexes
return config
})
adapter.tables[textsTableName] = textsTable
@@ -234,9 +250,7 @@ export const buildTable = ({
id: serial('id').primaryKey(),
number: numeric('number'),
order: integer('order').notNull(),
parent: parentIDColumnMap[idColType]('parent_id')
.references(() => table.id, { onDelete: 'cascade' })
.notNull(),
parent: parentIDColumnMap[idColType]('parent_id').notNull(),
path: varchar('path').notNull(),
}
@@ -245,22 +259,27 @@ export const buildTable = ({
}
numbersTable = adapter.pgSchema.table(numbersTableName, columns, (cols) => {
const indexes: Record<string, IndexBuilder> = {
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
orderParentIdx: index(`${numbersTableName}_order_parent_idx`).on(cols.order, cols.parent),
parentFk: foreignKey({
name: `${numbersTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
}
if (hasManyNumberField === 'index') {
indexes.numberIdx = index(`${numbersTableName}_number_idx`).on(cols.number)
config.numberIdx = index(`${numbersTableName}_number_idx`).on(cols.number)
}
if (hasLocalizedManyNumberField) {
indexes.localeParent = index(`${numbersTableName}_locale_parent`).on(
config.localeParent = index(`${numbersTableName}_locale_parent`).on(
cols.locale,
cols.parent,
)
}
return indexes
return config
})
adapter.tables[numbersTableName] = numbersTable
@@ -280,9 +299,7 @@ export const buildTable = ({
const relationshipColumns: Record<string, PgColumnBuilder> = {
id: serial('id').primaryKey(),
order: integer('order'),
parent: parentIDColumnMap[idColType]('parent_id')
.references(() => table.id, { onDelete: 'cascade' })
.notNull(),
parent: parentIDColumnMap[idColType]('parent_id').notNull(),
path: varchar('path').notNull(),
}
@@ -290,36 +307,61 @@ export const buildTable = ({
relationshipColumns.locale = adapter.enums.enum__locales('locale')
}
const relationExtraConfig: BaseExtraConfig = {}
const relationshipsTableName = `${tableName}${adapter.relationshipsSuffix}`
relationships.forEach((relationTo) => {
const relationshipConfig = adapter.payload.collections[relationTo].config
const formattedRelationTo = getTableName({
const formattedRelationTo = createTableName({
adapter,
config: relationshipConfig,
throwValidationError: true,
})
let colType = adapter.idType === 'uuid' ? 'uuid' : 'integer'
const relatedCollectionCustomID = relationshipConfig.fields.find(
(field) => fieldAffectsData(field) && field.name === 'id',
)
if (relatedCollectionCustomID?.type === 'number') colType = 'numeric'
if (relatedCollectionCustomID?.type === 'text') colType = 'varchar'
const relatedCollectionCustomIDType = relatedCollectionCustomID?.type
if (relatedCollectionCustomIDType === 'number') colType = 'numeric'
if (relatedCollectionCustomIDType === 'text') colType = 'varchar'
relationshipColumns[`${relationTo}ID`] = parentIDColumnMap[colType](
`${formattedRelationTo}_id`,
).references(() => adapter.tables[formattedRelationTo].id, { onDelete: 'cascade' })
})
)
const relationshipsTableName = `${tableName}${adapter.relationshipsSuffix}`
relationExtraConfig[`${relationTo}IdFk`] = (cols) =>
foreignKey({
name: `${relationshipsTableName}_${toSnakeCase(relationTo)}_fk`,
columns: [cols[`${relationTo}ID`]],
foreignColumns: [adapter.tables[formattedRelationTo].id],
}).onDelete('cascade')
})
relationshipsTable = adapter.pgSchema.table(
relationshipsTableName,
relationshipColumns,
(cols) => {
const result: Record<string, unknown> = {
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
parentIdx: index(`${relationshipsTableName}_parent_idx`).on(cols.parent),
pathIdx: index(`${relationshipsTableName}_path_idx`).on(cols.path),
}
const result: Record<string, ForeignKeyBuilder | IndexBuilder> = Object.entries(
relationExtraConfig,
).reduce(
(config, [key, func]) => {
config[key] = func(cols)
return config
},
{
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
parentFk: foreignKey({
name: `${relationshipsTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
parentIdx: index(`${relationshipsTableName}_parent_idx`).on(cols.parent),
pathIdx: index(`${relationshipsTableName}_path_idx`).on(cols.path),
},
)
if (hasLocalizedRelationshipField) {
result.localeIdx = index(`${relationshipsTableName}_locale_idx`).on(cols.locale)
@@ -341,7 +383,7 @@ export const buildTable = ({
}
relationships.forEach((relationTo) => {
const relatedTableName = getTableName({
const relatedTableName = createTableName({
adapter,
config: adapter.payload.collections[relationTo].config,
throwValidationError: true,

View File

@@ -14,53 +14,59 @@ type Args = {
name?: string
slug?: string
}
/** Localized tables need to be given the locales suffix */
locales?: boolean
/** For nested tables passed for the user custom dbName functions to handle their own iterations */
parentTableName?: string
/** For sub tables (array for example) this needs to include the parentTableName */
prefix?: string
/** Adds the relationships suffix */
relationships?: boolean
/** For tables based on fields that could have both enumName and dbName (ie: select with hasMany), default: 'dbName' */
target?: 'dbName' | 'enumName'
throwValidationError?: boolean
/** Adds the versions suffix, should only be used on the base collection to duplicate suffixing */
/** Adds the versions suffix to the default table name - should only be used on the base collection to avoid duplicate suffixing */
versions?: boolean
/** Adds the versions suffix to custom dbName only - this is used while creating blocks / selects / arrays / etc */
versionsCustomName?: boolean
}
/**
* Used to name database enums and tables
* Returns the table or enum name for a given entity
*/
export const getTableName = ({
export const createTableName = ({
adapter,
config: { name, slug },
config,
locales = false,
parentTableName,
prefix = '',
relationships = false,
target = 'dbName',
throwValidationError = false,
versions = false,
versionsCustomName = false,
}: Args): string => {
let result: string
let custom = config[target]
let customNameDefinition = config[target]
if (!custom && target === 'enumName') {
custom = config['dbName']
let defaultTableName = `${prefix}${toSnakeCase(name ?? slug)}`
if (versions) defaultTableName = `_${defaultTableName}${adapter.versionsSuffix}`
let customTableNameResult: string
if (!customNameDefinition && target === 'enumName') {
customNameDefinition = config['dbName']
}
if (custom) {
result = typeof custom === 'function' ? custom({ tableName: parentTableName }) : custom
} else {
result = `${prefix}${toSnakeCase(name ?? slug)}`
if (customNameDefinition) {
customTableNameResult =
typeof customNameDefinition === 'function'
? customNameDefinition({ tableName: parentTableName })
: customNameDefinition
if (versionsCustomName)
customTableNameResult = `_${customTableNameResult}${adapter.versionsSuffix}`
}
if (locales) result = `${result}${adapter.localesSuffix}`
if (versions) result = `_${result}${adapter.versionsSuffix}`
if (relationships) result = `${result}${adapter.relationshipsSuffix}`
const result = customTableNameResult || defaultTableName
adapter.tableNameMap.set(defaultTableName, result)
if (!throwValidationError) {
return result
@@ -71,5 +77,6 @@ export const getTableName = ({
`Exceeded max identifier length for table or enum name of 63 characters. Invalid name: ${result}`,
)
}
return result
}

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-param-reassign */
import type { Relation } from 'drizzle-orm'
import type { IndexBuilder, PgColumnBuilder, UniqueConstraintBuilder } from 'drizzle-orm/pg-core'
import type { IndexBuilder, PgColumnBuilder } from 'drizzle-orm/pg-core'
import type { Field, TabAsField } from 'payload/types'
import { relations } from 'drizzle-orm'
@@ -9,6 +9,7 @@ import {
PgUUIDBuilder,
PgVarcharBuilder,
boolean,
foreignKey,
index,
integer,
jsonb,
@@ -23,11 +24,12 @@ import { fieldAffectsData, optionIsObject } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { GenericColumns, IDType, PostgresAdapter } from '../types'
import type { BaseExtraConfig } from './build'
import { hasLocalesTable } from '../utilities/hasLocalesTable'
import { buildTable } from './build'
import { createIndex } from './createIndex'
import { getTableName } from './getTableName'
import { createTableName } from './createTableName'
import { idToUUID } from './idToUUID'
import { parentIDColumnMap } from './parentIDColumnMap'
import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical'
@@ -221,7 +223,7 @@ export const traverseFields = ({
case 'radio':
case 'select': {
const enumName = getTableName({
const enumName = createTableName({
adapter,
config: field,
parentTableName: newTableName,
@@ -242,7 +244,7 @@ export const traverseFields = ({
)
if (field.type === 'select' && field.hasMany) {
const selectTableName = getTableName({
const selectTableName = createTableName({
adapter,
config: field,
parentTableName: newTableName,
@@ -251,17 +253,18 @@ export const traverseFields = ({
})
const baseColumns: Record<string, PgColumnBuilder> = {
order: integer('order').notNull(),
parent: parentIDColumnMap[parentIDColType]('parent_id')
.references(() => adapter.tables[parentTableName].id, { onDelete: 'cascade' })
.notNull(),
parent: parentIDColumnMap[parentIDColType]('parent_id').notNull(),
value: adapter.enums[enumName]('value'),
}
const baseExtraConfig: Record<
string,
(cols: GenericColumns) => IndexBuilder | UniqueConstraintBuilder
> = {
const baseExtraConfig: BaseExtraConfig = {
orderIdx: (cols) => index(`${selectTableName}_order_idx`).on(cols.order),
parentFk: (cols) =>
foreignKey({
name: `${selectTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [adapter.tables[parentTableName].id],
}),
parentIdx: (cols) => index(`${selectTableName}_parent_idx`).on(cols.parent),
}
@@ -314,25 +317,28 @@ export const traverseFields = ({
case 'array': {
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
const arrayTableName = getTableName({
const arrayTableName = createTableName({
adapter,
config: field,
parentTableName: newTableName,
prefix: `${newTableName}_`,
throwValidationError,
versionsCustomName: versions,
})
const baseColumns: Record<string, PgColumnBuilder> = {
_order: integer('_order').notNull(),
_parentID: parentIDColumnMap[parentIDColType]('_parent_id')
.references(() => adapter.tables[parentTableName].id, { onDelete: 'cascade' })
.notNull(),
_parentID: parentIDColumnMap[parentIDColType]('_parent_id').notNull(),
}
const baseExtraConfig: Record<
string,
(cols: GenericColumns) => IndexBuilder | UniqueConstraintBuilder
> = {
const baseExtraConfig: BaseExtraConfig = {
_orderIdx: (cols) => index(`${arrayTableName}_order_idx`).on(cols._order),
_parentIDFk: (cols) =>
foreignKey({
name: `${arrayTableName}_parent_id_fk`,
columns: [cols['_parentID']],
foreignColumns: [adapter.tables[parentTableName].id],
}).onDelete('cascade'),
_parentIDIdx: (cols) => index(`${arrayTableName}_parent_id_idx`).on(cols._parentID),
}
@@ -400,28 +406,30 @@ export const traverseFields = ({
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
field.blocks.forEach((block) => {
const blockTableName = getTableName({
const blockTableName = createTableName({
adapter,
config: block,
parentTableName: rootTableName,
prefix: `${rootTableName}_blocks_`,
throwValidationError,
versionsCustomName: versions,
})
if (!adapter.tables[blockTableName]) {
const baseColumns: Record<string, PgColumnBuilder> = {
_order: integer('_order').notNull(),
_parentID: parentIDColumnMap[rootTableIDColType]('_parent_id')
.references(() => adapter.tables[rootTableName].id, { onDelete: 'cascade' })
.notNull(),
_parentID: parentIDColumnMap[rootTableIDColType]('_parent_id').notNull(),
_path: text('_path').notNull(),
}
const baseExtraConfig: Record<
string,
(cols: GenericColumns) => IndexBuilder | UniqueConstraintBuilder
> = {
const baseExtraConfig: BaseExtraConfig = {
_orderIdx: (cols) => index(`${blockTableName}_order_idx`).on(cols._order),
_parentIDIdx: (cols) => index(`${blockTableName}_parent_id_idx`).on(cols._parentID),
_parentIdFk: (cols) =>
foreignKey({
name: `${blockTableName}_parent_id_fk`,
columns: [cols._parentID],
foreignColumns: [adapter.tables[rootTableName].id],
}).onDelete('cascade'),
_pathIdx: (cols) => index(`${blockTableName}_path_idx`).on(cols._path),
}
@@ -494,7 +502,6 @@ export const traverseFields = ({
tableLocales: adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
})
}
adapter.blockTableNames[`${rootTableName}.${toSnakeCase(block.slug)}`] = blockTableName
rootRelationsToBuild.set(`_blocks_${block.slug}`, blockTableName)
})

View File

@@ -69,6 +69,7 @@ export const validateExistingBlockIsIdentical = ({
tableLocales,
}: Args): void => {
const fieldNames = getFlattenedFieldNames(block.fields)
const missingField =
// ensure every field from the config is in the matching table
fieldNames.find(({ name, localized }) => {

View File

@@ -4,11 +4,11 @@ import type { TextField } from 'payload/types'
type Args = {
field: TextField
locale?: string
textRows: Record<string, unknown>[]
ref: Record<string, unknown>
textRows: Record<string, unknown>[]
}
export const transformHasManyText = ({ field, locale, textRows, ref }: Args) => {
export const transformHasManyText = ({ field, locale, ref, textRows }: Args) => {
const result = textRows.map(({ text }) => text)
if (locale) {

View File

@@ -48,11 +48,11 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
deletions,
fieldPrefix: '',
fields,
texts,
numbers,
path: '',
relationships,
table: data,
texts,
})
deletions.forEach((deletion) => deletion())

View File

@@ -18,7 +18,6 @@ type Args = {
data: unknown
field: ArrayField
locale?: string
texts: Record<string, unknown>[]
numbers: Record<string, unknown>[]
path: string
relationships: Record<string, unknown>[]
@@ -26,6 +25,7 @@ type Args = {
selects: {
[tableName: string]: Record<string, unknown>[]
}
texts: Record<string, unknown>[]
}
export const transformArray = ({
@@ -37,14 +37,15 @@ export const transformArray = ({
data,
field,
locale,
texts,
numbers,
path,
relationships,
relationshipsToDelete,
selects,
texts,
}: Args) => {
const newRows: ArrayRowToInsert[] = []
const hasUUID = adapter.tables[arrayTableName]._uuid
if (isArrayOfRows(data)) {
@@ -88,7 +89,6 @@ export const transformArray = ({
fieldPrefix: '',
fields: field.fields,
locales: newRow.locales,
texts,
numbers,
parentTableName: arrayTableName,
path: `${path || ''}${field.name}.${i}.`,
@@ -96,6 +96,7 @@ export const transformArray = ({
relationshipsToDelete,
row: newRow.row,
selects,
texts,
})
newRows.push(newRow)

View File

@@ -61,7 +61,7 @@ export const transformBlocks = ({
if (field.localized && locale) newRow.row._locale = locale
const blockTableName = `${baseTableName}_blocks_${blockType}`
const blockTableName = adapter.tableNameMap.get(`${baseTableName}_blocks_${blockType}`)
const hasUUID = adapter.tables[blockTableName]._uuid

View File

@@ -27,12 +27,12 @@ export const transformForWrite = ({
blocks: {},
blocksToDelete: new Set(),
locales: {},
texts: [],
numbers: [],
relationships: [],
relationshipsToDelete: [],
row: {},
selects: {},
texts: [],
}
// This function is responsible for building up the
@@ -48,7 +48,6 @@ export const transformForWrite = ({
fieldPrefix: '',
fields,
locales: rowToInsert.locales,
texts: rowToInsert.texts,
numbers: rowToInsert.numbers,
parentTableName: tableName,
path,
@@ -56,6 +55,7 @@ export const transformForWrite = ({
relationshipsToDelete: rowToInsert.relationshipsToDelete,
row: rowToInsert.row,
selects: rowToInsert.selects,
texts: rowToInsert.texts,
})
return rowToInsert

View File

@@ -8,8 +8,8 @@ export const transformTexts = ({ baseRow, data, texts }: Args) => {
data.forEach((val, i) => {
texts.push({
...baseRow,
text: val,
order: i + 1,
text: val,
})
})
}

View File

@@ -7,7 +7,6 @@ import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../../types'
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types'
import { getTableName } from '../../schema/getTableName'
import { isArrayOfRows } from '../../utilities/isArrayOfRows'
import { transformArray } from './array'
import { transformBlocks } from './blocks'
@@ -89,18 +88,13 @@ export const traverseFields = ({
let fieldData: unknown
if (fieldAffectsData(field)) {
columnName = `${columnPrefix || ''}${getTableName({
adapter,
config: field,
// do not pass columnPrefix here because it is required and custom dbNames also need it
prefix: '',
})}`
columnName = `${columnPrefix || ''}${toSnakeCase(field.name)}`
fieldName = `${fieldPrefix || ''}${field.name}`
fieldData = data[field.name]
}
if (field.type === 'array') {
const arrayTableName = `${parentTableName}_${columnName}`
const arrayTableName = adapter.tableNameMap.get(`${parentTableName}_${columnName}`)
if (!arrays[arrayTableName]) arrays[arrayTableName] = []
@@ -153,8 +147,8 @@ export const traverseFields = ({
}
if (field.type === 'blocks') {
field.blocks.forEach((block) => {
blocksToDelete.add(getTableName({ adapter, config: block }))
field.blocks.forEach(({ slug }) => {
blocksToDelete.add(toSnakeCase(slug))
})
if (field.localized) {
@@ -464,7 +458,7 @@ export const traverseFields = ({
}
if (field.type === 'select' && field.hasMany) {
const selectTableName = `${parentTableName}_${columnName}`
const selectTableName = adapter.tableNameMap.get(`${parentTableName}_${columnName}`)
if (!selects[selectTableName]) selects[selectTableName] = []
if (field.localized) {
@@ -494,11 +488,7 @@ export const traverseFields = ({
}
if (fieldAffectsData(field)) {
const valuesToTransform: {
localeKey?: string
ref: unknown
value: unknown
}[] = []
const valuesToTransform: { localeKey?: string; ref: unknown; value: unknown }[] = []
if (field.localized) {
if (typeof fieldData === 'object' && fieldData !== null) {

View File

@@ -34,7 +34,6 @@ export type RowToInsert = {
locales: {
[locale: string]: Record<string, unknown>
}
texts: Record<string, unknown>[]
numbers: Record<string, unknown>[]
relationships: Record<string, unknown>[]
relationshipsToDelete: RelationshipToDelete[]
@@ -42,4 +41,5 @@ export type RowToInsert = {
selects: {
[tableName: string]: Record<string, unknown>[]
}
texts: Record<string, unknown>[]
}

View File

@@ -23,14 +23,14 @@ import type { Pool, PoolConfig } from 'pg'
export type DrizzleDB = NodePgDatabase<Record<string, unknown>>
export type Args = {
localesSuffix?: string
idType?: 'serial' | 'uuid'
localesSuffix?: string
logger?: DrizzleConfig['logger']
migrationDir?: string
pool: PoolConfig
push?: boolean
schemaName?: string
relationshipsSuffix?: string
schemaName?: string
versionsSuffix?: string
}
@@ -61,10 +61,6 @@ export type DrizzleTransaction = PgTransaction<
>
export type PostgresAdapter = BaseDatabaseAdapter & {
/**
* Used internally to map the block name to the table name
*/
blockTableNames: Record<string, string>
drizzle: DrizzleDB
enums: Record<string, GenericEnum>
/**
@@ -90,6 +86,7 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
resolve: () => Promise<void>
}
}
tableNameMap: Map<string, string>
tables: Record<string, GenericTable | PgTableWithColumns<any>>
versionsSuffix?: string
}

View File

@@ -2,12 +2,10 @@ import type { UpdateOne } from 'payload/database'
import toSnakeCase from 'to-snake-case'
import type { ChainedMethods } from './find/chainMethods'
import type { PostgresAdapter } from './types'
import buildQuery from './queries/buildQuery'
import { selectDistinct } from './queries/selectDistinct'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export const updateOne: UpdateOne = async function updateOne(
@@ -16,10 +14,7 @@ export const updateOne: UpdateOne = async function updateOne(
) {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collection = this.payload.collections[collectionSlug].config
const tableName = getTableName({
adapter: this,
config: collection,
})
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
const whereToUse = whereArg || { id: { equals: id } }
let idToUpdate = id

View File

@@ -1,9 +1,10 @@
import type { UpdateGlobalArgs } from 'payload/database'
import type { PayloadRequest, TypeWithID } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export async function updateGlobal<T extends TypeWithID>(
@@ -12,10 +13,7 @@ export async function updateGlobal<T extends TypeWithID>(
): Promise<T> {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
const tableName = getTableName({
adapter: this,
config: globalConfig,
})
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
const existingGlobal = await db.query[tableName].findFirst({})

View File

@@ -2,11 +2,11 @@ import type { TypeWithVersion, UpdateGlobalVersionArgs } from 'payload/database'
import type { PayloadRequest, SanitizedGlobalConfig, TypeWithID } from 'payload/types'
import { buildVersionGlobalFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import buildQuery from './queries/buildQuery'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export async function updateGlobalVersion<T extends TypeWithID>(
@@ -25,11 +25,11 @@ export async function updateGlobalVersion<T extends TypeWithID>(
({ slug }) => slug === global,
)
const whereToUse = whereArg || { id: { equals: id } }
const tableName = getTableName({
adapter: this,
config: globalConfig,
versions: true,
})
const tableName = this.tableNameMap.get(
`_${toSnakeCase(globalConfig.slug)}${this.versionsSuffix}`,
)
const fields = buildVersionGlobalFields(globalConfig)
const { where } = await buildQuery({

View File

@@ -2,11 +2,11 @@ import type { TypeWithVersion, UpdateVersionArgs } from 'payload/database'
import type { PayloadRequest, SanitizedCollectionConfig, TypeWithID } from 'payload/types'
import { buildVersionCollectionFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types'
import buildQuery from './queries/buildQuery'
import { getTableName } from './schema/getTableName'
import { upsertRow } from './upsertRow'
export async function updateVersion<T extends TypeWithID>(
@@ -23,11 +23,10 @@ export async function updateVersion<T extends TypeWithID>(
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const whereToUse = whereArg || { id: { equals: id } }
const tableName = getTableName({
adapter: this,
config: collectionConfig,
versions: true,
})
const tableName = this.tableNameMap.get(
`_${toSnakeCase(collectionConfig.slug)}${this.versionsSuffix}`,
)
const fields = buildVersionCollectionFields(collectionConfig)
const { where } = await buildQuery({

View File

@@ -224,14 +224,14 @@ export const upsertRow = async <T extends TypeWithID>({
if (operation === 'update') {
for (const blockName of rowToInsert.blocksToDelete) {
const blockTableName = adapter.blockTableNames[`${tableName}.${blockName}`]
const blockTableName = adapter.tableNameMap.get(`${tableName}_blocks_${blockName}`)
const blockTable = adapter.tables[blockTableName]
await db.delete(blockTable).where(eq(blockTable._parentID, insertedRow.id))
}
}
for (const [blockName, blockRows] of Object.entries(blocksToInsert)) {
const blockTableName = adapter.blockTableNames[`${tableName}.${blockName}`]
const blockTableName = adapter.tableNameMap.get(`${tableName}_blocks_${blockName}`)
insertedBlockRows[blockName] = await db
.insert(adapter.tables[blockTableName])
.values(blockRows.map(({ row }) => row))

View File

@@ -73,7 +73,7 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
// Insert locale rows
if (adapter.tables[`${tableName}${adapter.localesSuffix}`] && row.locales.length > 0) {
if (!row.locales[0]._parentID) {
row.locales = row.locales.map((localeRow, i) => {
row.locales = row.locales.map((localeRow) => {
if (typeof localeRow._getParentID === 'function') {
localeRow._parentID = localeRow._getParentID(insertedRows)
delete localeRow._getParentID

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "2.14.0",
"version": "2.16.1",
"description": "Node, React and MongoDB Headless CMS and Application Framework",
"license": "MIT",
"main": "./dist/index.js",
@@ -118,11 +118,11 @@
"process": "0.11.10",
"qs": "6.11.2",
"qs-middleware": "1.0.3",
"react": "18.2.0",
"react": "^18.0.0",
"react-animate-height": "2.1.2",
"react-datepicker": "4.16.0",
"react-diff-viewer-continued": "3.2.6",
"react-dom": "18.2.0",
"react-dom": "^18.0.0",
"react-helmet": "6.1.0",
"react-i18next": "11.18.6",
"react-image-crop": "10.1.8",

View File

@@ -19,6 +19,7 @@ export const ArrayAction: React.FC<Props> = ({
duplicateRow,
hasMaxRows,
index,
isSortable,
moveRow,
removeRow,
rowCount,
@@ -33,7 +34,7 @@ export const ArrayAction: React.FC<Props> = ({
render={({ close }) => {
return (
<PopupList.ButtonGroup buttonSize="small">
{index !== 0 && (
{isSortable && index !== 0 && (
<PopupList.Button
className={`${baseClass}__action ${baseClass}__move-up`}
onClick={() => {
@@ -47,7 +48,7 @@ export const ArrayAction: React.FC<Props> = ({
{t('moveUp')}
</PopupList.Button>
)}
{index < rowCount - 1 && (
{isSortable && index < rowCount - 1 && (
<PopupList.Button
className={`${baseClass}__action`}
onClick={() => {

View File

@@ -3,6 +3,7 @@ export type Props = {
duplicateRow: (current: number) => void
hasMaxRows: boolean
index: number
isSortable: boolean
moveRow: (from: number, to: number) => void
removeRow: (index: number) => void
rowCount: number

View File

@@ -15,7 +15,7 @@ import './index.scss'
const baseClass = 'column-selector'
const ColumnSelector: React.FC<Props> = (props) => {
const { collection } = props
const { slug } = props
const { columns, moveColumn, toggleColumn } = useTableColumns()
@@ -53,7 +53,7 @@ const ColumnSelector: React.FC<Props> = (props) => {
draggable
icon={active ? <X /> : <Plus />}
id={accessor}
key={`${collection.slug}-${col.name || i}${editDepth ? `-${editDepth}-` : ''}${uuid}`}
key={`${slug}-${col.name || i}${editDepth ? `-${editDepth}-` : ''}${uuid}`}
onClick={() => {
toggleColumn(accessor)
}}

View File

@@ -1,5 +1,5 @@
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
export type Props = {
collection: SanitizedCollectionConfig
slug: SanitizedCollectionConfig['slug']
}

View File

@@ -139,7 +139,7 @@ const Duplicate: React.FC<Props> = ({ id, slug, collection }) => {
if (localeErrors.length > 0) {
toast.error(
`
${t('error:localesNotSaved', { count: localeErrors.length })}
${t('error:localesNotSaved_other', { count: localeErrors.length })}
${localeErrors.join(', ')}
`,
{ autoClose: 5000 },

View File

@@ -9,7 +9,6 @@ import { fieldAffectsData } from '../../../../fields/config/types'
import { getTranslation } from '../../../../utilities/getTranslation'
import Chevron from '../../icons/Chevron'
import { useSearchParams } from '../../utilities/SearchParams'
import Button from '../Button'
import ColumnSelector from '../ColumnSelector'
import DeleteMany from '../DeleteMany'
import EditMany from '../EditMany'
@@ -50,6 +49,8 @@ export const ListControls: React.FC<Props> = (props) => {
const params = useSearchParams()
const shouldInitializeWhereOpened = validateWhereQuery(params?.where)
const hasWhereParam = React.useRef(Boolean(params?.where))
const [textFieldsToBeSearched, setFieldsToBeSearched] = useState(
getTextFieldsToBeSearched(listSearchableFields, fields),
)
@@ -65,6 +66,15 @@ export const ListControls: React.FC<Props> = (props) => {
setFieldsToBeSearched(getTextFieldsToBeSearched(listSearchableFields, fields))
}, [listSearchableFields, fields])
React.useEffect(() => {
if (hasWhereParam.current && !params?.where) {
setVisibleDrawer(undefined)
hasWhereParam.current = false
} else if (params?.where) {
hasWhereParam.current = true
}
}, [setVisibleDrawer, params?.where])
return (
<div className={baseClass}>
<div className={`${baseClass}__wrap`}>
@@ -136,7 +146,7 @@ export const ListControls: React.FC<Props> = (props) => {
height={visibleDrawer === 'columns' ? 'auto' : 0}
id={`${baseClass}-columns`}
>
<ColumnSelector collection={collection} />
<ColumnSelector slug={collection.slug} />
</AnimateHeight>
)}
<AnimateHeight
@@ -147,6 +157,7 @@ export const ListControls: React.FC<Props> = (props) => {
<WhereBuilder
collection={collection}
handleChange={handleWhereChange}
key={String(hasWhereParam.current && !params?.where)}
modifySearchQuery={modifySearchQuery}
/>
</AnimateHeight>

View File

@@ -18,7 +18,7 @@ import './index.scss'
const baseClass = 'publish-many'
const PublishMany: React.FC<Props> = (props) => {
const { collection: { labels: { plural }, slug, versions } = {}, resetParams } = props
const { collection: { slug, labels: { plural }, versions } = {}, resetParams } = props
const {
routes: { api },
@@ -27,7 +27,7 @@ const PublishMany: React.FC<Props> = (props) => {
const { permissions } = useAuth()
const { toggleModal } = useModal()
const { i18n, t } = useTranslation('version')
const { count, getQueryParams, selectAll } = useSelection()
const { getQueryParams, selectAll } = useSelection()
const [submitted, setSubmitted] = useState(false)
const collectionPermissions = permissions?.collections?.[slug]
@@ -41,9 +41,11 @@ const PublishMany: React.FC<Props> = (props) => {
const handlePublish = useCallback(() => {
setSubmitted(true)
requests
void requests
.patch(
`${serverURL}${api}/${slug}${getQueryParams({ _status: { not_equals: 'published' } })}`,
`${serverURL}${api}/${slug}${getQueryParams({
_status: { not_equals: 'published' },
})}&draft=true`,
{
body: JSON.stringify({
_status: 'published',

View File

@@ -33,6 +33,13 @@ export const TableColumnContext = createContext<ITableColumns>({} as ITableColum
export const useTableColumns = (): ITableColumns => useContext(TableColumnContext)
const filterTableFields = (fields: Field[]): Field[] => {
return fields.reduce((acc, field) => {
if (!field.admin?.disableListColumn) acc.push(field)
return acc
}, [])
}
export const TableColumnsProvider: React.FC<{
cellProps?: Partial<CellProps>[]
children: React.ReactNode
@@ -50,9 +57,10 @@ export const TableColumnsProvider: React.FC<{
const hasInitialized = useRef(false)
const { getPreference, setPreference } = usePreferences()
const [formattedFields] = useState<Field[]>(() => formatFields(collection))
const filteredFields = filterTableFields(formattedFields)
const [tableColumns, dispatchTableColumns] = useReducer(columnReducer, {}, () => {
const initialColumns = getInitialColumnState(formattedFields, useAsTitle, defaultColumns)
const initialColumns = getInitialColumnState(filteredFields, useAsTitle, defaultColumns)
return buildColumns({
cellProps,
@@ -77,13 +85,14 @@ export const TableColumnsProvider: React.FC<{
const currentPreferences = await getPreference<ListPreferences>(preferenceKey)
prevCollection.current = collection.slug
const initialColumns = getInitialColumnState(formattedFields, useAsTitle, defaultColumns)
const initialColumns = getInitialColumnState(filteredFields, useAsTitle, defaultColumns)
const newCols = currentPreferences?.columns || initialColumns
dispatchTableColumns({
type: 'set',
payload: {
cellProps,
collection: { ...collection, fields: formatFields(collection) },
collection: { ...collection, fields: filteredFields },
columns: newCols.map((column) => {
// 'string' is for backwards compatibility
// the preference used to be stored as an array of strings
@@ -96,14 +105,13 @@ export const TableColumnsProvider: React.FC<{
return column
}),
},
type: 'set',
})
hasInitialized.current = true
}
}
sync()
void sync()
}, [
preferenceKey,
setPreference,
@@ -113,7 +121,7 @@ export const TableColumnsProvider: React.FC<{
defaultColumns,
collection,
cellProps,
formattedFields,
filteredFields,
])
// /////////////////////////////////////
@@ -133,6 +141,7 @@ export const TableColumnsProvider: React.FC<{
const setActiveColumns = useCallback(
(columns: string[]) => {
dispatchTableColumns({
type: 'set',
payload: {
// onSelect,
cellProps,
@@ -142,7 +151,6 @@ export const TableColumnsProvider: React.FC<{
active: true,
})),
},
type: 'set',
})
},
[collection, cellProps],
@@ -153,13 +161,13 @@ export const TableColumnsProvider: React.FC<{
const { fromIndex, toIndex } = args
dispatchTableColumns({
type: 'move',
payload: {
cellProps,
collection: { ...collection, fields: formatFields(collection) },
fromIndex,
toIndex,
},
type: 'move',
})
},
[collection, cellProps],
@@ -168,12 +176,12 @@ export const TableColumnsProvider: React.FC<{
const toggleColumn = useCallback(
(column: string) => {
dispatchTableColumns({
type: 'toggle',
payload: {
cellProps,
collection: { ...collection, fields: formatFields(collection) },
column,
},
type: 'toggle',
})
},
[collection, cellProps],

View File

@@ -1,3 +1,6 @@
import type { Where } from 'payload/types'
import qs from 'qs'
import React, { useCallback, useEffect, useReducer, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -6,6 +9,7 @@ import type { Option } from '../../../ReactSelect/types'
import type { GetResults, Props, ValueWithRelation } from './types'
import useDebounce from '../../../../../hooks/useDebounce'
import { useAuth } from '../../../../utilities/Auth'
import { useConfig } from '../../../../utilities/Config'
import ReactSelect from '../../../ReactSelect'
import './index.scss'
@@ -16,7 +20,15 @@ const baseClass = 'condition-value-relationship'
const maxResultsPerRequest = 10
const RelationshipField: React.FC<Props> = (props) => {
const { admin: { isSortable } = {}, disabled, hasMany, onChange, relationTo, value } = props
const {
admin: { isSortable } = {},
disabled,
filterOptions,
hasMany,
onChange,
relationTo,
value,
} = props
const {
collections,
@@ -33,11 +45,12 @@ const RelationshipField: React.FC<Props> = (props) => {
const [hasLoadedFirstOptions, setHasLoadedFirstOptions] = useState(false)
const debouncedSearch = useDebounce(search, 300)
const { i18n, t } = useTranslation('general')
const { user } = useAuth()
const addOptions = useCallback(
(data, relation) => {
const collection = collections.find((coll) => coll.slug === relation)
dispatchOptions({ collection, data, hasMultipleRelations, i18n, relation, type: 'ADD' })
dispatchOptions({ type: 'ADD', collection, data, hasMultipleRelations, i18n, relation })
},
[collections, hasMultipleRelations, i18n],
)
@@ -61,23 +74,66 @@ const RelationshipField: React.FC<Props> = (props) => {
let resultsFetched = 0
if (!errorLoading) {
relationsToFetch.reduce(async (priorRelation, relation) => {
void relationsToFetch.reduce(async (priorRelation, relation) => {
await priorRelation
if (resultsFetched < 10) {
const search: Record<string, unknown> & { where: Where } = {
depth: 0,
limit: maxResultsPerRequest,
page: lastLoadedPageToUse,
where: { and: [] },
}
const collection = collections.find((coll) => coll.slug === relation)
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
const searchParam = searchArg ? `&where[${fieldToSearch}][like]=${searchArg}` : ''
const response = await fetch(
`${serverURL}${api}/${relation}?limit=${maxResultsPerRequest}&page=${lastLoadedPageToUse}&depth=0${searchParam}`,
{
credentials: 'include',
headers: {
'Accept-Language': i18n.language,
// add search arg to where object
if (searchArg) {
search.where.and.push({
[fieldToSearch]: {
like: searchArg,
},
})
}
// call the filterOptions function if it exists passing in the collection
if (filterOptions) {
const optionFilter =
typeof filterOptions === 'function'
? await filterOptions({
// data and siblingData are empty since we cannot fetch with the values covering the
// entire list this limitation means that filterOptions functions using a document's
// data are unsupported in the whereBuilder
id: undefined,
data: {},
relationTo: collection.slug,
siblingData: {},
user,
})
: filterOptions
if (typeof optionFilter === 'object') {
search.where.and.push(optionFilter)
}
if (optionFilter === false) {
// no options will be returned
setLastFullyLoadedRelation(relations.indexOf(relation))
// If there are more relations to search, need to reset lastLoadedPage to 1
// both locally within function and state
if (relations.indexOf(relation) + 1 < relations.length) {
lastLoadedPageToUse = 1
}
return
}
}
if (search.where.and.length === 0) {
delete search.where
}
const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(search)}`, {
credentials: 'include',
headers: {
'Accept-Language': i18n.language,
},
)
})
if (response.ok) {
const data: PaginatedDocs = await response.json()
@@ -103,7 +159,18 @@ const RelationshipField: React.FC<Props> = (props) => {
}, Promise.resolve())
}
},
[i18n, relationTo, errorLoading, collections, serverURL, api, addOptions, t],
[
relationTo,
errorLoading,
collections,
filterOptions,
serverURL,
api,
i18n.language,
user,
addOptions,
t,
],
)
const findOptionsByValue = useCallback((): Option | Option[] => {

View File

@@ -52,9 +52,9 @@ const Condition: React.FC<Props> = (props) => {
useEffect(() => {
dispatch({
type: 'update',
andIndex,
orIndex,
type: 'update',
value: debouncedValue || '',
})
}, [debouncedValue, dispatch, orIndex, andIndex])
@@ -80,10 +80,10 @@ const Condition: React.FC<Props> = (props) => {
isClearable={false}
onChange={(field) => {
dispatch({
andIndex: andIndex,
field: field?.value,
orIndex: orIndex,
type: 'update',
andIndex,
field: field?.value,
orIndex,
})
}}
options={fields}
@@ -96,10 +96,10 @@ const Condition: React.FC<Props> = (props) => {
isClearable={false}
onChange={(operator) => {
dispatch({
type: 'update',
andIndex,
operator: operator.value,
orIndex,
type: 'update',
})
setInternalOperatorField(operator.value)
}}
@@ -134,9 +134,9 @@ const Condition: React.FC<Props> = (props) => {
iconStyle="with-border"
onClick={() =>
dispatch({
type: 'remove',
andIndex,
orIndex,
type: 'remove',
})
}
round
@@ -148,11 +148,11 @@ const Condition: React.FC<Props> = (props) => {
iconStyle="with-border"
onClick={() =>
dispatch({
type: 'add',
andIndex: andIndex + 1,
field: fields[0].value,
orIndex,
relation: 'and',
type: 'add',
})
}
round

View File

@@ -32,34 +32,38 @@ const reduceFields = (fields, i18n) =>
} else {
operators = fieldTypes[field.type].operators
}
}
const operatorKeys = new Set()
const filteredOperators = operators.reduce((acc, operator) => {
if (!operatorKeys.has(operator.value)) {
operatorKeys.add(operator.value)
return [
...acc,
{
...operator,
label: i18n.t(`operators:${operator.label}`),
},
]
const operatorKeys = new Set()
const filteredOperators = operators.reduce((acc, operator) => {
if (!operatorKeys.has(operator.value)) {
operatorKeys.add(operator.value)
return [
...acc,
{
...operator,
label: i18n.t(`operators:${operator.label}`),
},
]
}
return acc
}, [])
const formattedField = {
label: getTranslation(field.label || field.name, i18n),
value: field.name,
...fieldTypes[field.type],
operators: filteredOperators,
props: {
...field,
},
}
return acc
}, [])
const formattedField = {
label: getTranslation(field.label || field.name, i18n),
value: field.name,
...fieldTypes[field.type],
operators: filteredOperators,
props: {
...field,
},
if (field.admin?.disableListFilter) return reduced
return [...reduced, formattedField]
}
return [...reduced, formattedField]
return reduced
}, [])
/**

View File

@@ -26,6 +26,7 @@ type ArrayRowProps = UseDraggableSortableReturn &
duplicateRow: (rowIndex: number) => void
forceRender?: boolean
hasMaxRows?: boolean
isSortable: boolean
moveRow: (fromIndex: number, toIndex: number) => void
readOnly?: boolean
removeRow: (rowIndex: number) => void
@@ -44,6 +45,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
forceRender = false,
hasMaxRows,
indexPath,
isSortable,
labels,
listeners,
moveRow,
@@ -94,6 +96,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
duplicateRow={duplicateRow}
hasMaxRows={hasMaxRows}
index={rowIndex}
isSortable={isSortable}
moveRow={moveRow}
removeRow={removeRow}
rowCount={rowCount}
@@ -103,11 +106,15 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
className={classNames}
collapsed={row.collapsed}
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
dragHandleProps={{
id: row.id,
attributes,
listeners,
}}
dragHandleProps={
isSortable
? {
id: row.id,
attributes,
listeners,
}
: undefined
}
header={
<div className={`${baseClass}__row-header`}>
<RowLabel

View File

@@ -29,7 +29,7 @@ const baseClass = 'array-field'
const ArrayFieldType: React.FC<Props> = (props) => {
const {
name,
admin: { className, components, condition, description, readOnly },
admin: { className, components, condition, description, isSortable = true, readOnly },
fieldTypes,
fields,
forceRender = false,
@@ -113,7 +113,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
const duplicateRow = useCallback(
(rowIndex: number) => {
dispatchFields({ path, rowIndex, type: 'DUPLICATE_ROW' })
dispatchFields({ type: 'DUPLICATE_ROW', path, rowIndex })
setModified(true)
setTimeout(() => {
@@ -133,7 +133,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
const moveRow = useCallback(
(moveFromIndex: number, moveToIndex: number) => {
dispatchFields({ moveFromIndex, moveToIndex, path, type: 'MOVE_ROW' })
dispatchFields({ type: 'MOVE_ROW', moveFromIndex, moveToIndex, path })
setModified(true)
},
[dispatchFields, path, setModified],
@@ -141,14 +141,14 @@ const ArrayFieldType: React.FC<Props> = (props) => {
const toggleCollapseAll = useCallback(
(collapsed: boolean) => {
dispatchFields({ collapsed, path, setDocFieldPreferences, type: 'SET_ALL_ROWS_COLLAPSED' })
dispatchFields({ type: 'SET_ALL_ROWS_COLLAPSED', collapsed, path, setDocFieldPreferences })
},
[dispatchFields, path, setDocFieldPreferences],
)
const setCollapse = useCallback(
(rowID: string, collapsed: boolean) => {
dispatchFields({ collapsed, path, rowID, setDocFieldPreferences, type: 'SET_ROW_COLLAPSED' })
dispatchFields({ type: 'SET_ROW_COLLAPSED', collapsed, path, rowID, setDocFieldPreferences })
},
[dispatchFields, path, setDocFieldPreferences],
)
@@ -227,7 +227,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
onDragEnd={({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex)}
>
{rows.map((row, i) => (
<DraggableSortableItem disabled={readOnly} id={row.id} key={row.id}>
<DraggableSortableItem disabled={readOnly || !isSortable} id={row.id} key={row.id}>
{(draggableSortableItemProps) => (
<ArrayRow
{...draggableSortableItemProps}
@@ -239,6 +239,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
forceRender={forceRender}
hasMaxRows={hasMaxRows}
indexPath={indexPath}
isSortable={isSortable}
labels={labels}
moveRow={moveRow}
path={path}

View File

@@ -26,6 +26,7 @@ type BlockFieldProps = UseDraggableSortableReturn &
duplicateRow: (rowIndex: number) => void
forceRender?: boolean
hasMaxRows?: boolean
isSortable?: boolean
moveRow: (fromIndex: number, toIndex: number) => void
readOnly: boolean
removeRow: (rowIndex: number) => void
@@ -44,6 +45,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
forceRender,
hasMaxRows,
indexPath,
isSortable,
labels,
listeners,
moveRow,
@@ -90,6 +92,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
blocks={blocks}
duplicateRow={duplicateRow}
hasMaxRows={hasMaxRows}
isSortable={isSortable}
labels={labels}
moveRow={moveRow}
removeRow={removeRow}
@@ -101,11 +104,15 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
className={classNames}
collapsed={row.collapsed}
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
dragHandleProps={{
id: row.id,
attributes,
listeners,
}}
dragHandleProps={
isSortable
? {
id: row.id,
attributes,
listeners,
}
: undefined
}
header={
<div className={`${baseClass}__block-header`}>
<span className={`${baseClass}__block-number`}>

View File

@@ -13,6 +13,7 @@ export const RowActions: React.FC<{
blocks: Block[]
duplicateRow: (rowIndex: number, blockType: string) => void
hasMaxRows?: boolean
isSortable?: boolean
labels: Labels
moveRow: (fromIndex: number, toIndex: number) => void
removeRow: (rowIndex: number) => void
@@ -25,6 +26,7 @@ export const RowActions: React.FC<{
blocks,
duplicateRow,
hasMaxRows,
isSortable,
labels,
moveRow,
removeRow,
@@ -59,6 +61,7 @@ export const RowActions: React.FC<{
duplicateRow={() => duplicateRow(rowIndex, blockType)}
hasMaxRows={hasMaxRows}
index={rowIndex}
isSortable={isSortable}
moveRow={moveRow}
removeRow={removeRow}
rowCount={rowCount}

View File

@@ -34,7 +34,7 @@ const BlocksField: React.FC<Props> = (props) => {
const {
name,
admin: { className, condition, description, readOnly },
admin: { className, condition, description, isSortable = true, readOnly },
blocks,
fieldTypes,
forceRender = false,
@@ -230,7 +230,7 @@ const BlocksField: React.FC<Props> = (props) => {
if (blockToRender) {
return (
<DraggableSortableItem disabled={readOnly} id={row.id} key={row.id}>
<DraggableSortableItem disabled={readOnly || !isSortable} id={row.id} key={row.id}>
{(draggableSortableItemProps) => (
<BlockRow
{...draggableSortableItemProps}
@@ -242,6 +242,7 @@ const BlocksField: React.FC<Props> = (props) => {
forceRender={forceRender}
hasMaxRows={hasMaxRows}
indexPath={indexPath}
isSortable={isSortable}
labels={labels}
moveRow={moveRow}
path={path}

View File

@@ -98,6 +98,7 @@ const RadioGroupInput: React.FC<RadioGroupInputProps> = (props) => {
onChange={readOnly ? undefined : onChange}
option={optionIsObject(option) ? option : { label: option, value: option }}
path={path}
readOnly={readOnly}
/>
</li>
)

View File

@@ -70,31 +70,3 @@
}
}
}
.radio-group--read-only {
.radio-input {
cursor: default;
&__label {
color: var(--theme-elevation-800);
}
&--is-selected {
.radio-input__styled-radio {
&:before {
background-color: var(--theme-elevation-800);
}
}
}
&:not(.radio-input--is-selected) {
&:hover {
.radio-input__styled-radio {
&:before {
opacity: 0;
}
}
}
}
}
}

View File

@@ -9,7 +9,7 @@ import './index.scss'
const baseClass = 'radio-input'
const RadioInput: React.FC<Props> = (props) => {
const { isSelected, onChange, option, path } = props
const { isSelected, onChange, option, path, readOnly } = props
const { i18n } = useTranslation()
const classes = [baseClass, isSelected && `${baseClass}--is-selected`].filter(Boolean).join(' ')
@@ -21,6 +21,7 @@ const RadioInput: React.FC<Props> = (props) => {
<div className={classes}>
<input
checked={isSelected}
disabled={readOnly}
id={id}
onChange={() => (typeof onChange === 'function' ? onChange(option.value) : null)}
type="radio"

View File

@@ -8,4 +8,5 @@ export type Props = {
value: string
}
path: string
readOnly?: boolean
}

View File

@@ -29,6 +29,34 @@
}
}
.radio-group--read-only {
.radio-input {
cursor: default;
&__label {
color: var(--theme-elevation-400);
}
&--is-selected {
.radio-input__styled-radio {
&:before {
background-color: var(--theme-elevation-100);
}
}
}
&:not(.radio-input--is-selected) {
&:hover {
.radio-input__styled-radio {
&:before {
opacity: 0;
}
}
}
}
}
}
html[data-theme='light'] {
.radio-group {
&.error {

View File

@@ -55,6 +55,7 @@ const RadioGroup: React.FC<Props> = (props) => {
onChange={readOnly ? undefined : setValue}
options={options}
path={path}
readOnly={readOnly}
required={required}
showError={showError}
style={style}

View File

@@ -51,6 +51,7 @@ type RichTextAdapterBase<
context: RequestContext
currentDepth?: number
depth: number
draft: boolean
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
findMany: boolean
flattenLocales: boolean

View File

@@ -66,7 +66,9 @@ const Restore: React.FC<Props> = ({
if (res.status === 200) {
const json = await res.json()
toast.success(json.message)
history.push(redirectURL)
history.push(redirectURL, {
refetchDocumentData: true,
})
} else {
toast.error(t('problemRestoringVersion'))
}

View File

@@ -7,14 +7,14 @@ import CopyToClipboard from '../../../../elements/CopyToClipboard'
import GenerateConfirmation from '../../../../elements/GenerateConfirmation'
import { useFormFields } from '../../../../forms/Form/context'
import Label from '../../../../forms/Label'
import useField from '../../../../forms/useField'
import { fieldBaseClass } from '../../../../forms/field-types/shared'
import useField from '../../../../forms/useField'
const path = 'apiKey'
const baseClass = 'api-key'
const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
const [initialAPIKey, setInitialAPIKey] = useState(null)
const APIKey: React.FC<{ enabled: boolean; readOnly?: boolean }> = ({ enabled, readOnly }) => {
const [initialAPIKey] = useState(uuidv4())
const [highlightedField, setHighlightedField] = useState(false)
const { t } = useTranslation()
@@ -51,14 +51,13 @@ const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
const { setValue, value } = fieldType
useEffect(() => {
setInitialAPIKey(uuidv4())
}, [])
useEffect(() => {
if (!apiKeyValue) {
if (!apiKeyValue && enabled) {
setValue(initialAPIKey)
}
}, [apiKeyValue, setValue, initialAPIKey])
if (!enabled) {
setValue(null)
}
}, [apiKeyValue, enabled, setValue, initialAPIKey])
useEffect(() => {
if (highlightedField) {
@@ -68,6 +67,10 @@ const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
}
}, [highlightedField])
if (!enabled) {
return null
}
return (
<React.Fragment>
<div className={[fieldBaseClass, 'api-key', 'read-only'].filter(Boolean).join(' ')}>

View File

@@ -145,7 +145,7 @@ const Auth: React.FC<Props> = (props) => {
{useAPIKey && (
<div className={`${baseClass}__api-key`}>
<Checkbox admin={{ readOnly }} label={t('enableAPIKey')} name="enableAPIKey" />
{enableAPIKey?.value && <APIKey readOnly={readOnly} />}
<APIKey enabled={!!enableAPIKey?.value} readOnly={readOnly} />
</div>
)}
{verify && <Checkbox admin={{ readOnly }} label={t('verified')} name="_verified" />}

View File

@@ -1,4 +1,4 @@
import React, { Fragment } from 'react'
import React, { Fragment, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { FieldTypes } from '../../../../forms/field-types'
@@ -12,6 +12,7 @@ import Meta from '../../../../utilities/Meta'
import Auth from '../Auth'
import { SetStepNav } from '../SetStepNav'
import { Upload } from '../Upload'
import formatFields from '../formatFields'
import './index.scss'
const baseClass = 'collection-default-edit'
@@ -37,7 +38,9 @@ export const DefaultCollectionEdit: React.FC<
permissions,
} = props
const { auth, fields, upload } = collection
const { auth, upload } = collection
const [fields] = useState(() => formatFields(collection, isEditing))
const operation = isEditing ? 'update' : 'create'

View File

@@ -50,7 +50,8 @@ const EditView: React.FC<IndexProps> = (props) => {
} = config
const { params: { id } = {} } = useRouteMatch<Record<string, string>>()
const history = useHistory()
const history = useHistory<{ refetchDocumentData?: boolean }>()
const [internalState, setInternalState] = useState<Fields>()
const [updatedAt, setUpdatedAt] = useState<string>()
const { permissions, user } = useAuth()
@@ -58,7 +59,7 @@ const EditView: React.FC<IndexProps> = (props) => {
const { docPermissions, getDocPermissions, getDocPreferences, getVersions } = useDocumentInfo()
const { t } = useTranslation('general')
const [{ data, isError, isLoading: isLoadingData }] = usePayloadAPI(
const [{ data, isError, isLoading: isLoadingData }, { refetchData }] = usePayloadAPI(
isEditing ? `${serverURL}${api}/${collectionSlug}/${id}` : '',
{ initialData: null, initialParams: { depth: 0, draft: 'true', 'fallback-locale': 'null' } },
)
@@ -128,10 +129,16 @@ const EditView: React.FC<IndexProps> = (props) => {
useEffect(() => {
setFormQueryParams((params) => ({
...params,
locale: locale,
locale,
}))
}, [locale])
useEffect(() => {
if (history.location.state?.refetchDocumentData) {
void refetchData()
}
}, [history.location.state?.refetchDocumentData, refetchData])
if (isError) {
return <NotFound marginTop="large" />
}

View File

@@ -9,11 +9,11 @@ const formatFields = (config: SanitizedCollectionConfig): Field[] => {
const defaultIDField: Field = {
name: 'id',
type: 'text',
admin: {
disableBulkEdit: true,
},
label: 'ID',
type: 'text',
}
const shouldSkipField = (field: Field): boolean =>

View File

@@ -47,13 +47,13 @@ const ListView: React.FC<ListIndexProps> = (props) => {
const {
collection,
collection: {
slug,
admin: {
components: { views: { List: CustomList } = {} } = {},
listSearchableFields,
pagination: { defaultLimit },
},
labels: { plural },
slug,
},
} = props

View File

@@ -1,5 +1,5 @@
import queryString from 'qs'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { requests } from '../api'
@@ -12,6 +12,7 @@ type Result = [
isLoading: boolean
},
{
refetchData: (abortController?: AbortController) => Promise<void>
setParams: React.Dispatch<unknown>
},
]
@@ -43,49 +44,53 @@ const usePayloadAPI: UsePayloadAPI = (url, options = {}) => {
},
)
const fetchData = useCallback(
async (abortController?: AbortController) => {
if (url) {
setIsError(false)
setIsLoading(true)
try {
const response = await requests.get(`${url}${search}`, {
headers: {
'Accept-Language': i18n.language,
},
signal: abortController ? abortController.signal : undefined,
})
if (response.status > 201) {
setIsError(true)
}
const json = await response.json()
setData(json)
setIsLoading(false)
} catch (error) {
if (!abortController || !abortController.signal.aborted) {
setIsError(true)
setIsLoading(false)
}
}
} else {
setIsError(false)
setIsLoading(false)
}
},
[url, search, i18n.language],
)
useEffect(() => {
const abortController = new AbortController()
const fetchData = async () => {
setIsError(false)
setIsLoading(true)
try {
const response = await requests.get(`${url}${search}`, {
headers: {
'Accept-Language': i18n.language,
},
signal: abortController.signal,
})
if (response.status > 201) {
setIsError(true)
}
const json = await response.json()
setData(json)
setIsLoading(false)
} catch (error) {
if (!abortController.signal.aborted) {
setIsError(true)
setIsLoading(false)
}
}
}
if (url) {
fetchData()
} else {
setIsError(false)
setIsLoading(false)
}
void fetchData(abortController)
return () => {
abortController.abort()
}
}, [url, locale, search, i18n.language])
}, [url, search, fetchData])
return [{ data, isError, isLoading }, { setParams }]
return [
{ data, isError, isLoading },
{ refetchData: fetchData, setParams },
]
}
export default usePayloadAPI

View File

@@ -20,7 +20,6 @@ export default [
Field: () => null,
},
},
defaultValue: false,
label: labels['authentication:enableAPIKey'],
},
{
@@ -46,16 +45,19 @@ export default [
hidden: true,
hooks: {
beforeValidate: [
async ({ data, req, value }) => {
({ data, req, value }) => {
if (data.apiKey === false || data.apiKey === null) {
return null
}
if (data.enableAPIKey === false || data.enableAPIKey === null) {
return null
}
if (data.apiKey) {
return crypto
.createHmac('sha1', req.payload.secret)
.update(data.apiKey as string)
.digest('hex')
}
if (data.enableAPIKey === false) {
return null
}
return value
},
],

View File

@@ -205,6 +205,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
context: req.context,
depth,
doc: user,
draft: undefined,
fallbackLocale,
global: null,
locale,

View File

@@ -54,6 +54,7 @@ const batchAndLoadDocs =
fallbackLocale,
overrideAccess,
showHiddenFields,
draft,
] = JSON.parse(key)
const batchKeyArray = [
@@ -65,6 +66,7 @@ const batchAndLoadDocs =
fallbackLocale,
overrideAccess,
showHiddenFields,
draft,
]
const batchKey = JSON.stringify(batchKeyArray)
@@ -100,6 +102,7 @@ const batchAndLoadDocs =
fallbackLocale,
overrideAccess,
showHiddenFields,
draft,
] = JSON.parse(batchKey)
req.transactionID = transactionID
@@ -109,6 +112,7 @@ const batchAndLoadDocs =
currentDepth,
depth,
disableErrors: true,
draft,
fallbackLocale,
locale,
overrideAccess: Boolean(overrideAccess),
@@ -136,6 +140,7 @@ const batchAndLoadDocs =
fallbackLocale,
overrideAccess,
showHiddenFields,
draft,
])
const docsIndex = keys.findIndex((key) => key === docKey)

View File

@@ -54,6 +54,8 @@ function initCollectionsGraphQL(payload: Payload): void {
if (!graphQL) return
const draftsEnabled = collection.config.versions?.drafts
let singularName
let pluralName
const fromSlug = formatNames(collection.config.slug)
@@ -150,7 +152,11 @@ function initCollectionsGraphQL(payload: Payload): void {
type: collection.graphQL.type,
args: {
id: { type: new GraphQLNonNull(idType) },
draft: { type: GraphQLBoolean },
...(draftsEnabled
? {
draft: { type: GraphQLBoolean },
}
: {}),
...(payload.config.localization
? {
fallbackLocale: { type: payload.types.fallbackLocaleInputType },
@@ -164,7 +170,11 @@ function initCollectionsGraphQL(payload: Payload): void {
payload.Query.fields[pluralName] = {
type: buildPaginatedListType(pluralName, collection.graphQL.type),
args: {
draft: { type: GraphQLBoolean },
...(draftsEnabled
? {
draft: { type: GraphQLBoolean },
}
: {}),
where: { type: collection.graphQL.whereInputType },
...(payload.config.localization
? {
@@ -187,7 +197,11 @@ function initCollectionsGraphQL(payload: Payload): void {
},
}),
args: {
draft: { type: GraphQLBoolean },
...(draftsEnabled
? {
draft: { type: GraphQLBoolean },
}
: {}),
where: { type: collection.graphQL.whereInputType },
...(payload.config.localization
? {
@@ -217,7 +231,11 @@ function initCollectionsGraphQL(payload: Payload): void {
...(createMutationInputType
? { data: { type: collection.graphQL.mutationInputType } }
: {}),
draft: { type: GraphQLBoolean },
...(draftsEnabled
? {
draft: { type: GraphQLBoolean },
}
: {}),
...(payload.config.localization
? {
locale: { type: payload.types.localeInputType },
@@ -235,7 +253,11 @@ function initCollectionsGraphQL(payload: Payload): void {
...(updateMutationInputType
? { data: { type: collection.graphQL.updateMutationInputType } }
: {}),
draft: { type: GraphQLBoolean },
...(draftsEnabled
? {
draft: { type: GraphQLBoolean },
}
: {}),
...(payload.config.localization
? {
locale: { type: payload.types.localeInputType },

View File

@@ -28,6 +28,8 @@ export default function findResolver(collection: Collection): Resolver {
req.locale = args.locale || locale
req.fallbackLocale = fallbackLocale
context.req = req
const options = {
collection,
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),

View File

@@ -33,6 +33,17 @@ export default function createResolver<TSlug extends keyof GeneratedTypes['colle
const locale = req.locale
req = isolateObjectProperty(req, 'locale')
req.locale = args.locale || locale
if (!req.query) req.query = {}
const draft: boolean =
args.draft ?? req.query?.draft === 'false'
? false
: req.query?.draft === 'true'
? true
: undefined
if (typeof draft === 'boolean') req.query.draft = String(draft)
context.req = req
const options = {
collection,

View File

@@ -31,6 +31,17 @@ export default function getDeleteResolver<TSlug extends keyof GeneratedTypes['co
req = isolateObjectProperty(req, 'fallbackLocale')
req.locale = args.locale || locale
req.fallbackLocale = args.fallbackLocale || fallbackLocale
if (!req.query) req.query = {}
const draft: boolean =
args.draft ?? req.query?.draft === 'false'
? false
: req.query?.draft === 'true'
? true
: undefined
if (typeof draft === 'boolean') req.query.draft = String(draft)
context.req = req
const options = {
id: args.id,

View File

@@ -35,6 +35,17 @@ export default function findResolver(collection: Collection): Resolver {
req = isolateObjectProperty(req, 'fallbackLocale')
req.locale = args.locale || locale
req.fallbackLocale = args.fallbackLocale || fallbackLocale
if (!req.query) req.query = {}
const draft: boolean =
args.draft ?? req.query?.draft === 'false'
? false
: req.query?.draft === 'true'
? true
: undefined
if (typeof draft === 'boolean') req.query.draft = String(draft)
context.req = req
const options = {
collection,

View File

@@ -30,6 +30,17 @@ export default function findByIDResolver<T extends keyof GeneratedTypes['collect
req = isolateObjectProperty(req, 'fallbackLocale')
req.locale = args.locale || locale
req.fallbackLocale = args.fallbackLocale || fallbackLocale
if (!req.query) req.query = {}
const draft: boolean =
args.draft ?? req.query?.draft === 'false'
? false
: req.query?.draft === 'true'
? true
: undefined
if (typeof draft === 'boolean') req.query.draft = String(draft)
context.req = req
const options = {
id: args.id,

View File

@@ -11,7 +11,6 @@ import findVersionByID from '../../operations/findVersionByID'
export type Resolver<T extends TypeWithID = any> = (
_: unknown,
args: {
draft: boolean
fallbackLocale?: string
id: number | string
locale?: string
@@ -32,11 +31,12 @@ export default function findVersionByIDResolver(collection: Collection): Resolve
req.locale = args.locale || locale
req.fallbackLocale = args.fallbackLocale || fallbackLocale
context.req = req
const options = {
id: args.id,
collection,
depth: 0,
draft: args.draft,
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
}

View File

@@ -35,6 +35,17 @@ export default function findVersionsResolver(collection: Collection): Resolver {
req = isolateObjectProperty(req, 'fallbackLocale')
req.locale = args.locale || locale
req.fallbackLocale = args.fallbackLocale || fallbackLocale
if (!req.query) req.query = {}
const draft: boolean =
args.draft ?? req.query?.draft === 'false'
? false
: req.query?.draft === 'true'
? true
: undefined
if (typeof draft === 'boolean') req.query.draft = String(draft)
context.req = req
const options = {
collection,

View File

@@ -34,6 +34,17 @@ export default function updateResolver<TSlug extends keyof GeneratedTypes['colle
req = isolateObjectProperty(req, 'fallbackLocale')
req.locale = args.locale || locale
req.fallbackLocale = args.fallbackLocale || fallbackLocale
if (!req.query) req.query = {}
const draft: boolean =
args.draft ?? req.query?.draft === 'false'
? false
: req.query?.draft === 'true'
? true
: undefined
if (typeof draft === 'boolean') req.query.draft = String(draft)
context.req = req
const options = {
id: args.id,

View File

@@ -290,6 +290,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
context: req.context,
depth,
doc: result,
draft,
fallbackLocale,
global: null,
locale,

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