Compare commits
60 Commits
fix/recurs
...
db-postgre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bca0b0b86 | ||
|
|
87a1d698b2 | ||
|
|
c11600aac3 | ||
|
|
ad01c6784d | ||
|
|
62601c54a7 | ||
|
|
4a144ddc44 | ||
|
|
9152a238d2 | ||
|
|
fc8b835264 | ||
|
|
28ee5e34c3 | ||
|
|
e25886649f | ||
|
|
985796be54 | ||
|
|
bd8b5123b0 | ||
|
|
c380deee4a | ||
|
|
0b12aac895 | ||
|
|
90d3f178ab | ||
|
|
a8c9625cde | ||
|
|
938d069523 | ||
|
|
1a337ec223 | ||
|
|
08f372e6c2 | ||
|
|
2994269f22 | ||
|
|
8d1a706928 | ||
|
|
fcb29bb1c6 | ||
|
|
2c402cc65c | ||
|
|
ad38f76011 | ||
|
|
b974a2c042 | ||
|
|
cb8d562132 | ||
|
|
1f0036054a | ||
|
|
aaa4397351 | ||
|
|
6185f8a5d8 | ||
|
|
e47e544364 | ||
|
|
2e80350a5a | ||
|
|
4c4f924e90 | ||
|
|
170957c380 | ||
|
|
9061ae05e7 | ||
|
|
fe0028c899 | ||
|
|
ec1ad0b662 | ||
|
|
e5f32562a3 | ||
|
|
ba6100cbe8 | ||
|
|
c08c8b5628 | ||
|
|
9e918831d1 | ||
|
|
25c9a145be | ||
|
|
3b1d331316 | ||
|
|
5aa68b5e5d | ||
|
|
d8e9084db2 | ||
|
|
65690a675c | ||
|
|
9530d28a67 | ||
|
|
509ec677c4 | ||
|
|
a00439ea89 | ||
|
|
0055a8eb36 | ||
|
|
de5d6cc4bd | ||
|
|
51f84a4fcf | ||
|
|
c0ba6cc19a | ||
|
|
5fa99fb060 | ||
|
|
e3c3ddac34 | ||
|
|
6186493246 | ||
|
|
9b44296092 | ||
|
|
cbd03ed2f8 | ||
|
|
cf135fd1e4 | ||
|
|
e7608f5507 | ||
|
|
608d6d0a87 |
46
.github/CODEOWNERS
vendored
46
.github/CODEOWNERS
vendored
@@ -1,50 +1,32 @@
|
|||||||
# Order matters. The last matching pattern takes precedence.
|
# Order matters. The last matching pattern takes precedence.
|
||||||
|
|
||||||
### Catch-all ###
|
|
||||||
* @denolfe @jmikrut @DanRibbens
|
|
||||||
.* @denolfe @jmikrut @DanRibbens
|
|
||||||
|
|
||||||
### Core ###
|
### Core ###
|
||||||
/packages/payload/ @denolfe @jmikrut @DanRibbens
|
|
||||||
/packages/payload/src/uploads/ @denolfe
|
|
||||||
/packages/payload/src/admin/ @jmikrut @jacobsfletch @JarrodMFlesch
|
|
||||||
|
|
||||||
### Adapters ###
|
### Adapters ###
|
||||||
/packages/bundler-*/ @denolfe @jmikrut @DanRibbens @JarrodMFlesch
|
/packages/richtext-*/ @AlessioGr
|
||||||
/packages/db-*/ @denolfe @jmikrut @DanRibbens
|
|
||||||
/packages/richtext-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
|
|
||||||
|
|
||||||
### Plugins ###
|
### Plugins ###
|
||||||
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens @jacobsfletch @JarrodMFlesch @AlessioGr
|
|
||||||
/packages/plugin-cloud*/ @denolfe
|
/packages/plugin-cloud*/ @denolfe
|
||||||
/packages/plugin-form-builder/ @jacobsfletch
|
|
||||||
/packages/plugin-live-preview*/ @jacobsfletch
|
|
||||||
/packages/plugin-nested-docs/ @jacobsfletch
|
|
||||||
/packages/plugin-password-protection/ @jmikrut
|
|
||||||
/packages/plugin-redirects/ @jacobsfletch
|
|
||||||
/packages/plugin-search/ @jacobsfletch
|
|
||||||
/packages/plugin-sentry/ @JessChowdhury
|
|
||||||
/packages/plugin-seo/ @jacobsfletch
|
|
||||||
/packages/plugin-stripe/ @jacobsfletch
|
|
||||||
/packages/plugin-zapier/ @JarrodMFlesch
|
|
||||||
|
|
||||||
### Examples ###
|
|
||||||
/examples/ @jacobsfletch
|
|
||||||
/examples/testing/ @JarrodMFlesch
|
|
||||||
/examples/email/ @JessChowdhury
|
|
||||||
/examples/whitelabel/ @JessChowdhury
|
|
||||||
|
|
||||||
### Templates ###
|
### Templates ###
|
||||||
/templates/ @jacobsfletch
|
/templates/ @jacobsfletch @denolfe
|
||||||
/templates/blank/ @denolfe
|
|
||||||
|
|
||||||
### Misc ###
|
### Misc ###
|
||||||
/packages/create-payload-app/ @denolfe
|
/packages/create-payload-app/ @denolfe
|
||||||
/packages/eslint-config-payload/ @denolfe
|
/packages/eslint-*/ @denolfe
|
||||||
/packages/payload-admin-bar/ @jacobsfletch
|
|
||||||
|
### Build Files ###
|
||||||
|
/**/package.json @denolfe
|
||||||
|
|
||||||
|
/tsconfig.json @denolfe
|
||||||
|
/**/tsconfig*.json @denolfe
|
||||||
|
|
||||||
|
/jest.config.js @denolfe
|
||||||
|
/**/jest.config.js @denolfe
|
||||||
|
|
||||||
### Root ###
|
### Root ###
|
||||||
/package.json @denolfe
|
/package.json @denolfe
|
||||||
/scripts/ @denolfe
|
/scripts/ @denolfe
|
||||||
|
/.husky/ @denolfe
|
||||||
|
/.vscode/ @denolfe
|
||||||
/.github/ @denolfe
|
/.github/ @denolfe
|
||||||
/.github/CODEOWNERS @denolfe
|
|
||||||
|
|||||||
100
.github/workflows/main.yml
vendored
100
.github/workflows/main.yml
vendored
@@ -15,6 +15,10 @@ jobs:
|
|||||||
needs_build: ${{ steps.filter.outputs.needs_build }}
|
needs_build: ${{ steps.filter.outputs.needs_build }}
|
||||||
templates: ${{ steps.filter.outputs.templates }}
|
templates: ${{ steps.filter.outputs.templates }}
|
||||||
steps:
|
steps:
|
||||||
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 25
|
fetch-depth: 25
|
||||||
@@ -45,13 +49,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 25
|
fetch-depth: 25
|
||||||
|
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
@@ -61,7 +69,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
name: Setup pnpm cache
|
name: Setup pnpm cache
|
||||||
with:
|
with:
|
||||||
path: ${{ env.STORE_PATH }}
|
path: ${{ env.STORE_PATH }}
|
||||||
@@ -74,7 +82,7 @@ jobs:
|
|||||||
- run: pnpm run build
|
- run: pnpm run build
|
||||||
|
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -96,19 +104,23 @@ jobs:
|
|||||||
AWS_REGION: us-east-1
|
AWS_REGION: us-east-1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -177,19 +189,23 @@ jobs:
|
|||||||
part: [ 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8 ]
|
part: [ 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8 ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -214,19 +230,23 @@ jobs:
|
|||||||
needs: core-build
|
needs: core-build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -254,19 +274,23 @@ jobs:
|
|||||||
- live-preview-react
|
- live-preview-react
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -291,19 +315,23 @@ jobs:
|
|||||||
- plugin-seo
|
- plugin-seo
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -329,10 +357,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 25
|
fetch-depth: 25
|
||||||
|
|
||||||
- name: Use Node.js 18
|
# https://github.com/actions/virtual-environments/issues/1187
|
||||||
uses: actions/setup-node@v3
|
- name: tune linux network
|
||||||
|
run: sudo ethtool -K eth0 tx off rx off
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Start MongoDB
|
- name: Start MongoDB
|
||||||
uses: supercharge/mongodb-github-action@1.10.0
|
uses: supercharge/mongodb-github-action@1.10.0
|
||||||
|
|||||||
11
.github/workflows/pr-title.yml
vendored
Normal file
11
.github/workflows/pr-title.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
name: pr-title
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Echo
|
||||||
|
run: echo "Register pr-title workflow"
|
||||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -5,21 +5,21 @@
|
|||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
|
|||||||
65
CHANGELOG.md
65
CHANGELOG.md
@@ -1,3 +1,68 @@
|
|||||||
|
## [2.14.0](https://github.com/payloadcms/payload/compare/v2.13.0...v2.14.0) (2024-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add count operation to collections ([#5936](https://github.com/payloadcms/payload/issues/5936)) ([c380dee](https://github.com/payloadcms/payload/commit/c380deee4a1db82bce9fea264060000957a53eee))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bulk publish ([#6006](https://github.com/payloadcms/payload/issues/6006)) ([c11600a](https://github.com/payloadcms/payload/commit/c11600aac38cd67019765faf2a41e62df13e50cc))
|
||||||
|
* **db-postgres:** extra version suffix added to table names ([#5939](https://github.com/payloadcms/payload/issues/5939)) ([bd8b512](https://github.com/payloadcms/payload/commit/bd8b5123b0991e53eb209315897dbca10d14d45e))
|
||||||
|
* **db-postgres:** Fixes nested groups inside nested blocks ([#5882](https://github.com/payloadcms/payload/issues/5882)) ([e258866](https://github.com/payloadcms/payload/commit/e25886649fce414d5d47918f35ba2d4d2ba59174))
|
||||||
|
* **db-postgres:** row table names were not being built properly - v2 ([#5961](https://github.com/payloadcms/payload/issues/5961)) ([9152a23](https://github.com/payloadcms/payload/commit/9152a238d2982503e7f509350651b0ba3f83b1ec))
|
||||||
|
* header filters ([#5997](https://github.com/payloadcms/payload/issues/5997)) ([ad01c67](https://github.com/payloadcms/payload/commit/ad01c6784d283386dc819dfcd47455cad5accfaa))
|
||||||
|
* min/max attributes missing from number input ([#5779](https://github.com/payloadcms/payload/issues/5779)) ([985796b](https://github.com/payloadcms/payload/commit/985796be54b593af0a4934685ab8621b9badda10))
|
||||||
|
* removes `equals` & `not_equals` operators from fields with `hasMany` ([#5885](https://github.com/payloadcms/payload/issues/5885)) ([a8c9625](https://github.com/payloadcms/payload/commit/a8c9625cdec33476a5da87bcd9f010f9d7fb9a94))
|
||||||
|
|
||||||
|
## [2.13.0](https://github.com/payloadcms/payload/compare/v2.12.1...v2.13.0) (2024-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow configuration for setting headers on external file fetch ([ec1ad0b](https://github.com/payloadcms/payload/commit/ec1ad0b6628d400d7435821c8a72b6746bf87577))
|
||||||
|
* **db-\*:** custom db table and enum names ([#5045](https://github.com/payloadcms/payload/issues/5045)) ([9bbacc4](https://github.com/payloadcms/payload/commit/9bbacc4fb1ad247634f394e95c42ee3adade8048))
|
||||||
|
* json field schemas ([#5726](https://github.com/payloadcms/payload/issues/5726)) ([2c402cc](https://github.com/payloadcms/payload/commit/2c402cc65c9e8f7f33e2fb0ce5e1a8ceff52af1b))
|
||||||
|
* **plugin-seo:** add Chinese translation ([#5429](https://github.com/payloadcms/payload/issues/5429)) ([fcb29bb](https://github.com/payloadcms/payload/commit/fcb29bb1c637867301bbc1070b4a84383bf0e90a))
|
||||||
|
* **richtext-lexical:** add HorizontalRuleFeature ([d8e9084](https://github.com/payloadcms/payload/commit/d8e9084db21828968046ab59775633e409ce5c2a))
|
||||||
|
* **richtext-lexical:** improve floating handle y-positioning by positioning it in the center for smaller elements. ([0055a8e](https://github.com/payloadcms/payload/commit/0055a8eb36b95722cccdc5eb3101a79d3e764f8b))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* adds type error validations for `email` and `password` in login operation ([#4852](https://github.com/payloadcms/payload/issues/4852)) ([1f00360](https://github.com/payloadcms/payload/commit/1f0036054a9461535b0992f2449e91e4eaf97d4e))
|
||||||
|
* avoids getting and setting doc preferences when creating new ([#5757](https://github.com/payloadcms/payload/issues/5757)) ([e3c3dda](https://github.com/payloadcms/payload/commit/e3c3ddac34dff8fa085f5b702be2838d513be300))
|
||||||
|
* block field type missing dbName ([#5695](https://github.com/payloadcms/payload/issues/5695)) ([e7608f5](https://github.com/payloadcms/payload/commit/e7608f5507d3b85ea3f44b5cb1f43edf67608b1b))
|
||||||
|
* **db-mongodb:** failing `contains` query with special chars ([#5774](https://github.com/payloadcms/payload/issues/5774)) ([5fa99fb](https://github.com/payloadcms/payload/commit/5fa99fb060cabbb69b5d6688748260e562e6bea3))
|
||||||
|
* **db-mongodb:** ignore end session errors ([#5904](https://github.com/payloadcms/payload/issues/5904)) ([cb8d562](https://github.com/payloadcms/payload/commit/cb8d562132bee437798880e1d7f64dbfdee36949))
|
||||||
|
* **db-mongodb:** version fields indexSortableFields ([#5863](https://github.com/payloadcms/payload/issues/5863)) ([fe0028c](https://github.com/payloadcms/payload/commit/fe0028c89945303a431b48efdae7b6e22304c8a3))
|
||||||
|
* **db-postgres:** hasMany relationship query contains operator ([#4212](https://github.com/payloadcms/payload/issues/4212)) ([608d6d0](https://github.com/payloadcms/payload/commit/608d6d0a872af224ea42c3e6c8a3b4f21678f550))
|
||||||
|
* **db-postgres:** issue querying by localised relationship not respecting locale as constraint ([#5666](https://github.com/payloadcms/payload/issues/5666)) ([44599cb](https://github.com/payloadcms/payload/commit/44599cbc7b8f23d6d8c7a3e05466237406812a6d))
|
||||||
|
* **db-postgres:** query hasMany fields with in ([#5881](https://github.com/payloadcms/payload/issues/5881)) ([6185f8a](https://github.com/payloadcms/payload/commit/6185f8a5d845d12651f5a3ee128eb43d3b9d2449))
|
||||||
|
* **db-postgres:** relationship query pagination ([#5802](https://github.com/payloadcms/payload/issues/5802)) ([65690a6](https://github.com/payloadcms/payload/commit/65690a675c17cfacebe775a327a57741ac09416a))
|
||||||
|
* **db-postgres:** validateExistingBlockIsIdentical localized ([#5839](https://github.com/payloadcms/payload/issues/5839)) ([4c4f924](https://github.com/payloadcms/payload/commit/4c4f924e90ee23a73c9a7cc7e69bbc2caf902b92))
|
||||||
|
* duplicate document multiple times in quick succession ([#5642](https://github.com/payloadcms/payload/issues/5642)) ([373787d](https://github.com/payloadcms/payload/commit/373787de31cbbd33b587aa4be6344948f082f5bb))
|
||||||
|
* missing date locales ([#5656](https://github.com/payloadcms/payload/issues/5656)) ([c1c8600](https://github.com/payloadcms/payload/commit/c1c86009a5e9aad401a05f7c63ad37bd3f88dc84))
|
||||||
|
* number ids were not sanitized to number in rest api ([51f84a4](https://github.com/payloadcms/payload/commit/51f84a4fcfd437eb73c7d83205b66e3620085909))
|
||||||
|
* passes parent id instead of incoming id to saveVersion ([#5831](https://github.com/payloadcms/payload/issues/5831)) ([25c9a14](https://github.com/payloadcms/payload/commit/25c9a145bec9e9566d2bbcba59d5b34394e10bbd))
|
||||||
|
* **plugin-seo:** uses correct key for ukrainian translation ([#5873](https://github.com/payloadcms/payload/issues/5873)) ([e47e544](https://github.com/payloadcms/payload/commit/e47e544364031ac834565a4d86ef6ec9c04e63c0))
|
||||||
|
* properly handle drafts in bulk update ([#5872](https://github.com/payloadcms/payload/issues/5872)) ([ad38f76](https://github.com/payloadcms/payload/commit/ad38f760111abf947c6b0ee4b983ee1224a9bf1b))
|
||||||
|
* req.collection being lost when querying a global inside a collection ([#5727](https://github.com/payloadcms/payload/issues/5727)) ([cbd03ed](https://github.com/payloadcms/payload/commit/cbd03ed2f8819ee8ac20e8739cc03e88ff4caa25))
|
||||||
|
* **richtext-lexical:** catch errors that may occur during HTML generation ([#5754](https://github.com/payloadcms/payload/issues/5754)) ([9b44296](https://github.com/payloadcms/payload/commit/9b442960929d00faa07f1383b1267f71e6f44efe))
|
||||||
|
* **richtext-lexical:** do not allow omitting editor prop for sub-richtext fields within lexical defined in the payload config ([#5766](https://github.com/payloadcms/payload/issues/5766)) ([6186493](https://github.com/payloadcms/payload/commit/6186493246157b4d4b33c8c47378f08581315942))
|
||||||
|
* **richtext-lexical:** incorrect floating handle y-position calculation next to certain kinds of HTML elements like HR ([de5d6cc](https://github.com/payloadcms/payload/commit/de5d6cc4bd591745156f0b8c56795b7bd2eaad7e))
|
||||||
|
* **richtext-lexical:** limit unnecessary floating handle positioning updates ([a00439e](https://github.com/payloadcms/payload/commit/a00439ea893e074d64be83ee6af1e780178a7ee3))
|
||||||
|
* **richtext-lexical:** pass through config for schema generation. Makes it more robust ([#5700](https://github.com/payloadcms/payload/issues/5700)) ([cf135fd](https://github.com/payloadcms/payload/commit/cf135fd1e4aeb30121281399e26be901393ada6d))
|
||||||
|
* **richtext-lexical:** use correct nodeType on HorizontalRule feature HTML converter ([#5805](https://github.com/payloadcms/payload/issues/5805)) ([3b1d331](https://github.com/payloadcms/payload/commit/3b1d3313165499616673f6d363c90ef884994525))
|
||||||
|
* updates type name of `CustomPublishButtonProps` to `CustomPublishButtonType` ([#5644](https://github.com/payloadcms/payload/issues/5644)) ([7df7bf4](https://github.com/payloadcms/payload/commit/7df7bf448bd26e870a1fde8aaa47430904d68366))
|
||||||
|
* updates var ([9530d28](https://github.com/payloadcms/payload/commit/9530d28a6760a667b718027a49ea43ba1accd546))
|
||||||
|
* use isolateObjectProperty function in createLocalReq ([#5748](https://github.com/payloadcms/payload/issues/5748)) ([c0ba6cc](https://github.com/payloadcms/payload/commit/c0ba6cc19a20c043a08ca77caacd47ef7cfb48f4))
|
||||||
|
* uses find instead of fieldIndex for custom ID check ([509ec67](https://github.com/payloadcms/payload/commit/509ec677c42993d9c08facf6928a5ef1e9767508))
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* **richtext-lexical:** do not allow omitting editor prop for sub-richtext fields within lexical defined in the payload config ([#5766](https://github.com/payloadcms/payload/issues/5766))
|
||||||
|
|
||||||
## [2.12.1](https://github.com/payloadcms/payload/compare/v2.12.0...v2.12.1) (2024-04-03)
|
## [2.12.1](https://github.com/payloadcms/payload/compare/v2.12.0...v2.12.1) (2024-04-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> 🎉 <strong>Payload 2.0 is now available!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>.
|
> 🎉 <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>.
|
||||||
|
|
||||||
<h3>Benefits over a regular CMS</h3>
|
<h3>Benefits over a regular CMS</h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -657,7 +657,7 @@ As your admin customizations gets more complex you may want to share state betwe
|
|||||||
|
|
||||||
### Styling Custom Components
|
### Styling Custom Components
|
||||||
|
|
||||||
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling, so it blends more thoroughly into the existing admin UI.
|
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-in styling, so it blends more thoroughly into the existing admin UI.
|
||||||
|
|
||||||
To make use of Payload SCSS variables / mixins to use directly in your own components, you can import them as follows:
|
To make use of Payload SCSS variables / mixins to use directly in your own components, you can import them as follows:
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ label: JSON
|
|||||||
order: 50
|
order: 50
|
||||||
desc: The JSON field type will store any string in the Database. Learn how to use JSON fields, see examples and options.
|
desc: The JSON field type will store any string in the Database. Learn how to use JSON fields, see examples and options.
|
||||||
|
|
||||||
keywords: json, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
keywords: json, jsonSchema, schema, validation, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||||
---
|
---
|
||||||
|
|
||||||
<Banner>
|
<Banner>
|
||||||
@@ -30,6 +30,7 @@ This field uses the `monaco-react` editor syntax highlighting.
|
|||||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
|
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
|
||||||
|
| **`jsonSchema`** | Provide a JSON schema that will be used for validation. [JSON schemas](https://json-schema.org/learn/getting-started-step-by-step) |
|
||||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
|
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
|
||||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||||
@@ -52,7 +53,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
`collections/ExampleCollection.ts
|
`collections/ExampleCollection.ts`
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { CollectionConfig } from 'payload/types'
|
import { CollectionConfig } from 'payload/types'
|
||||||
@@ -68,3 +69,68 @@ export const ExampleCollection: CollectionConfig = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### JSON Schema Validation
|
||||||
|
|
||||||
|
Payload JSON fields fully support the [JSON schema](https://json-schema.org/) standard. By providing a schema in your field config, the editor will be guided in the admin UI, getting typeahead for properties and their formats automatically. When the document is saved, the default validation will prevent saving any invalid data in the field according to the schema in your config.
|
||||||
|
|
||||||
|
If you only provide a URL to a schema, Payload will fetch the desired schema if it is publicly available. If not, it is recommended to add the schema directly to your config or import it from another file so that it can be implemented consistently in your project.
|
||||||
|
|
||||||
|
|
||||||
|
#### Local JSON Schema
|
||||||
|
|
||||||
|
`collections/ExampleCollection.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { CollectionConfig } from 'payload/types'
|
||||||
|
|
||||||
|
export const ExampleCollection: CollectionConfig = {
|
||||||
|
slug: 'example-collection',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'customerJSON', // required
|
||||||
|
type: 'json', // required
|
||||||
|
jsonSchema: {
|
||||||
|
uri: 'a://b/foo.json', // required
|
||||||
|
fileMatch: ['a://b/foo.json'], // required
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
foo: {
|
||||||
|
enum: ['bar', 'foobar'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
// {"foo": "bar"} or {"foo": "foobar"} - ok
|
||||||
|
// Attempting to create {"foo": "not-bar"} will throw an error
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Remote JSON Schema
|
||||||
|
|
||||||
|
`collections/ExampleCollection.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { CollectionConfig } from 'payload/types'
|
||||||
|
|
||||||
|
export const ExampleCollection: CollectionConfig = {
|
||||||
|
slug: 'example-collection',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'customerJSON', // required
|
||||||
|
type: 'json', // required
|
||||||
|
jsonSchema: {
|
||||||
|
uri: 'https://example.com/customer.schema.json', // required
|
||||||
|
fileMatch: ['https://example.com/customer.schema.json'], // required
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
// If 'https://example.com/customer.schema.json' has a JSON schema
|
||||||
|
// {"foo": "bar"} or {"foo": "foobar"} - ok
|
||||||
|
// Attempting to create {"foo": "not-bar"} will throw an error
|
||||||
|
```
|
||||||
|
|||||||
@@ -43,11 +43,12 @@ export const PublicUser: CollectionConfig = {
|
|||||||
|
|
||||||
**Payload will automatically open up the following queries:**
|
**Payload will automatically open up the following queries:**
|
||||||
|
|
||||||
| Query Name | Operation |
|
| Query Name | Operation |
|
||||||
| ------------------ | ------------------- |
|
| ------------------ | ------------------- |
|
||||||
| **`PublicUser`** | `findByID` |
|
| **`PublicUser`** | `findByID` |
|
||||||
| **`PublicUsers`** | `find` |
|
| **`PublicUsers`** | `find` |
|
||||||
| **`mePublicUser`** | `me` auth operation |
|
| **`countPublicUsers`** | `count` |
|
||||||
|
| **`mePublicUser`** | `me` auth operation |
|
||||||
|
|
||||||
**And the following mutations:**
|
**And the following mutations:**
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ keywords: live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, us
|
|||||||
|
|
||||||
While using Live Preview, the Admin panel emits a new `window.postMessage` event every time a change is made to the document. Your front-end application can listen for these events and re-render accordingly.
|
While using Live Preview, the Admin panel emits a new `window.postMessage` event every time a change is made to the document. Your front-end application can listen for these events and re-render accordingly.
|
||||||
|
|
||||||
Wiring your front-end into Live Preview is easy. If your front-end application is built with React or Next.js, use the [`useLivePreview`](#react) React hook that Payload provides. In the future, all other major frameworks like Vue, Svelte, etc will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.
|
Wiring your front-end into Live Preview is easy. If your front-end application is built with React, Next.js, Vue or Nuxt.js, use the `useLivePreview` hook that Payload provides. In the future, all other major frameworks like Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.
|
||||||
|
|
||||||
By default, all hooks accept the following args:
|
By default, all hooks accept the following args:
|
||||||
|
|
||||||
@@ -32,6 +32,10 @@ And return the following values:
|
|||||||
If your front-end is tightly coupled to required fields, you should ensure that your UI does not break when these fields are removed. For example, if you are rendering something like `data.relatedPosts[0].title`, your page will break once you remove the first related post. To get around this, use conditional logic, optional chaining, or default values in your UI where needed. For example, `data?.relatedPosts?.[0]?.title`.
|
If your front-end is tightly coupled to required fields, you should ensure that your UI does not break when these fields are removed. For example, if you are rendering something like `data.relatedPosts[0].title`, your page will break once you remove the first related post. To get around this, use conditional logic, optional chaining, or default values in your UI where needed. For example, `data?.relatedPosts?.[0]?.title`.
|
||||||
</Banner>
|
</Banner>
|
||||||
|
|
||||||
|
<Banner type="info">
|
||||||
|
If is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../getting-started/concepts#depth) for more information.
|
||||||
|
</Banner>
|
||||||
|
|
||||||
### React
|
### React
|
||||||
|
|
||||||
If your front-end application is built with React or Next.js, you can use the `useLivePreview` hook that Payload provides.
|
If your front-end application is built with React or Next.js, you can use the `useLivePreview` hook that Payload provides.
|
||||||
@@ -69,9 +73,40 @@ export const PageClient: React.FC<{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<Banner type="info">
|
### Vue
|
||||||
If is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../getting-started/concepts#depth) for more information.
|
|
||||||
</Banner>
|
If your front-end application is built with Vue 3 or Nuxt 3, you can use the `useLivePreview` composable that Payload provides.
|
||||||
|
|
||||||
|
First, install the `@payloadcms/live-preview-vue` package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @payloadcms/live-preview-vue
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, use the `useLivePreview` hook in your Vue component:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { PageData } from '~/types';
|
||||||
|
import { defineProps } from 'vue';
|
||||||
|
import { useLivePreview } from '@payloadcms/live-preview-vue';
|
||||||
|
|
||||||
|
// Fetch the initial data on the parent component or using async state
|
||||||
|
const props = defineProps<{ initialData: PageData }>();
|
||||||
|
|
||||||
|
// The hook will take over from here and keep the preview in sync with the changes you make.
|
||||||
|
// The `data` property will contain the live data of the document only when viewed from the Preview view of the Admin UI.
|
||||||
|
const { data } = useLivePreview<PageData>({
|
||||||
|
initialData: props.initialData,
|
||||||
|
serverURL: "<PAYLOAD_SERVER_URL>",
|
||||||
|
depth: 2,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>{{ data.title }}</h1>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
## Building your own hook
|
## Building your own hook
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,22 @@ const result = await payload.findByID({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Count
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Result will be an object with:
|
||||||
|
// {
|
||||||
|
// totalDocs: 10, // count of the documents satisfies query
|
||||||
|
// }
|
||||||
|
const result = await payload.count({
|
||||||
|
collection: 'posts', // required
|
||||||
|
locale: 'en',
|
||||||
|
where: {}, // pass a `where` query here
|
||||||
|
user: dummyUser,
|
||||||
|
overrideAccess: false,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
#### Update by ID
|
#### Update by ID
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|||||||
@@ -90,6 +90,19 @@ Note: Collection slugs must be formatted in kebab-case
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
operation: "Count",
|
||||||
|
method: "GET",
|
||||||
|
path: "/api/{collection-slug}/count",
|
||||||
|
description: "Count the documents",
|
||||||
|
example: {
|
||||||
|
slug: "count",
|
||||||
|
req: true,
|
||||||
|
res: {
|
||||||
|
totalDocs: 10
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
operation: "Create",
|
operation: "Create",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -153,13 +153,14 @@ Here's an overview of all the included features:
|
|||||||
| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |
|
| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |
|
||||||
| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |
|
| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |
|
||||||
| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |
|
| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |
|
||||||
| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |
|
| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |
|
||||||
| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |
|
| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |
|
||||||
| **`CheckListFeature`** | Yes | Adds checklists |
|
| **`CheckListFeature`** | Yes | Adds checklists |
|
||||||
| **`LinkFeature`** | Yes | Allows you to create internal and external links |
|
| **`LinkFeature`** | Yes | Allows you to create internal and external links |
|
||||||
| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |
|
| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |
|
||||||
| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |
|
| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |
|
||||||
| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |
|
| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |
|
||||||
|
| **`HorizontalRuleFeature`** | Yes | Horizontal rules / separators. Basically displays an `<hr>` element |
|
||||||
| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](/docs/fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |
|
| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](/docs/fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |
|
||||||
| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |
|
| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |
|
||||||
|
|
||||||
|
|||||||
@@ -40,21 +40,22 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
|
|||||||
|
|
||||||
### Collection Upload Options
|
### Collection Upload Options
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **`staticURL`** \* | The URL path to use to access your uploads. Relative path like `/media` will be served by payload. Full path like `https://example.com/media` needs to be served by another web server. |
|
| **`staticURL`** \* | The URL path to use to access your uploads. Relative path like `/media` will be served by payload. Full path like `https://example.com/media` needs to be served by another web server. |
|
||||||
| **`staticDir`** \* | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. |
|
| **`staticDir`** \* | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. |
|
||||||
| **`adminThumbnail`** | Set the way that the Admin panel will display thumbnails for this Collection. [More](#admin-thumbnails) |
|
| **`adminThumbnail`** | Set the way that the Admin panel will display thumbnails for this Collection. [More](#admin-thumbnails) |
|
||||||
| **`crop`** | Set to `false` to disable the cropping tool in the Admin panel. Crop is enabled by default. [More](#crop-and-focal-point-selector) |
|
| **`crop`** | Set to `false` to disable the cropping tool in the Admin panel. Crop is enabled by default. [More](#crop-and-focal-point-selector) |
|
||||||
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
|
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
|
||||||
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the Admin panel. The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
|
| **`externalFileHeaderFilter`** | Accepts existing headers and can filter/modify them. |
|
||||||
| **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) |
|
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the Admin panel. The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
|
||||||
| **`handlers`** | Array of Express request handlers to execute before the built-in Payload static middleware executes. |
|
| **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) |
|
||||||
| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |
|
| **`handlers`** | Array of Express request handlers to execute before the built-in Payload static middleware executes. |
|
||||||
| **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) |
|
| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |
|
||||||
| **`staticOptions`** | Set options for `express.static` to use while serving your static files. [More](http://expressjs.com/en/resources/middleware/serve-static.html) |
|
| **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) |
|
||||||
| **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) |
|
| **`staticOptions`** | Set options for `express.static` to use while serving your static files. [More](http://expressjs.com/en/resources/middleware/serve-static.html) |
|
||||||
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
|
| **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) |
|
||||||
|
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
|
||||||
|
|
||||||
_An asterisk denotes that a property above is required._
|
_An asterisk denotes that a property above is required._
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/react": "^7.77.0"
|
"@sentry/react": "^7.77.0",
|
||||||
|
"ajv": "^8.12.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@payloadcms/db-mongodb",
|
"name": "@payloadcms/db-mongodb",
|
||||||
"version": "1.4.4",
|
"version": "1.5.0",
|
||||||
"description": "The officially supported MongoDB database adapter for Payload",
|
"description": "The officially supported MongoDB database adapter for Payload",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
49
packages/db-mongodb/src/count.ts
Normal file
49
packages/db-mongodb/src/count.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type { QueryOptions } from 'mongoose'
|
||||||
|
import type { Count } from 'payload/database'
|
||||||
|
import type { PayloadRequest } from 'payload/types'
|
||||||
|
|
||||||
|
import { flattenWhereToOperators } from 'payload/database'
|
||||||
|
|
||||||
|
import type { MongooseAdapter } from '.'
|
||||||
|
|
||||||
|
import { withSession } from './withSession'
|
||||||
|
|
||||||
|
export const count: Count = async function count(
|
||||||
|
this: MongooseAdapter,
|
||||||
|
{ collection, locale, req = {} as PayloadRequest, where },
|
||||||
|
) {
|
||||||
|
const Model = this.collections[collection]
|
||||||
|
const options: QueryOptions = withSession(this, req.transactionID)
|
||||||
|
|
||||||
|
let hasNearConstraint = false
|
||||||
|
|
||||||
|
if (where) {
|
||||||
|
const constraints = flattenWhereToOperators(where)
|
||||||
|
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = await Model.buildQuery({
|
||||||
|
locale,
|
||||||
|
payload: this.payload,
|
||||||
|
where,
|
||||||
|
})
|
||||||
|
|
||||||
|
// useEstimatedCount is faster, but not accurate, as it ignores any filters. It is thus set to true if there are no filters.
|
||||||
|
const useEstimatedCount = hasNearConstraint || !query || Object.keys(query).length === 0
|
||||||
|
|
||||||
|
if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) {
|
||||||
|
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
|
||||||
|
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
|
||||||
|
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
|
||||||
|
// the correct indexed field
|
||||||
|
options.hint = {
|
||||||
|
_id: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await Model.countDocuments(query, options)
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalDocs: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import { createDatabaseAdapter } from 'payload/database'
|
|||||||
import type { CollectionModel, GlobalModel } from './types'
|
import type { CollectionModel, GlobalModel } from './types'
|
||||||
|
|
||||||
import { connect } from './connect'
|
import { connect } from './connect'
|
||||||
|
import { count } from './count'
|
||||||
import { create } from './create'
|
import { create } from './create'
|
||||||
import { createGlobal } from './createGlobal'
|
import { createGlobal } from './createGlobal'
|
||||||
import { createGlobalVersion } from './createGlobalVersion'
|
import { createGlobalVersion } from './createGlobalVersion'
|
||||||
@@ -108,6 +109,7 @@ export function mongooseAdapter({
|
|||||||
collections: {},
|
collections: {},
|
||||||
connectOptions: connectOptions || {},
|
connectOptions: connectOptions || {},
|
||||||
connection: undefined,
|
connection: undefined,
|
||||||
|
count,
|
||||||
disableIndexHints,
|
disableIndexHints,
|
||||||
globals: undefined,
|
globals: undefined,
|
||||||
mongoMemoryServer: undefined,
|
mongoMemoryServer: undefined,
|
||||||
@@ -115,7 +117,6 @@ export function mongooseAdapter({
|
|||||||
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
||||||
url,
|
url,
|
||||||
versions: {},
|
versions: {},
|
||||||
|
|
||||||
// DatabaseAdapter
|
// DatabaseAdapter
|
||||||
beginTransaction: transactionOptions ? beginTransaction : undefined,
|
beginTransaction: transactionOptions ? beginTransaction : undefined,
|
||||||
commitTransaction,
|
commitTransaction,
|
||||||
|
|||||||
@@ -6,11 +6,7 @@ import type { SanitizedCollectionConfig } from 'payload/types'
|
|||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
import mongooseAggregatePaginate from 'mongoose-aggregate-paginate-v2'
|
import mongooseAggregatePaginate from 'mongoose-aggregate-paginate-v2'
|
||||||
import paginate from 'mongoose-paginate-v2'
|
import paginate from 'mongoose-paginate-v2'
|
||||||
import {
|
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
|
||||||
buildVersionCollectionFields,
|
|
||||||
buildVersionGlobalFields,
|
|
||||||
getVersionsModelName,
|
|
||||||
} from 'payload/versions'
|
|
||||||
|
|
||||||
import type { MongooseAdapter } from '.'
|
import type { MongooseAdapter } from '.'
|
||||||
import type { CollectionModel } from './types'
|
import type { CollectionModel } from './types'
|
||||||
@@ -33,6 +29,7 @@ export const init: Init = async function init(this: MongooseAdapter) {
|
|||||||
const versionSchema = buildSchema(this.payload.config, versionCollectionFields, {
|
const versionSchema = buildSchema(this.payload.config, versionCollectionFields, {
|
||||||
disableUnique: true,
|
disableUnique: true,
|
||||||
draftsEnabled: true,
|
draftsEnabled: true,
|
||||||
|
indexSortableFields: this.payload.config.indexSortableFields,
|
||||||
options: {
|
options: {
|
||||||
minimize: false,
|
minimize: false,
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
|
|||||||
@@ -142,7 +142,10 @@ export const sanitizeQueryValue = ({
|
|||||||
|
|
||||||
if (path !== '_id' || (path === '_id' && hasCustomID && field.type === 'text')) {
|
if (path !== '_id' || (path === '_id' && hasCustomID && field.type === 'text')) {
|
||||||
if (operator === 'contains') {
|
if (operator === 'contains') {
|
||||||
formattedValue = { $options: 'i', $regex: formattedValue }
|
formattedValue = {
|
||||||
|
$options: 'i',
|
||||||
|
$regex: formattedValue.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ export const commitTransaction: CommitTransaction = async function commitTransac
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.sessions[id].commitTransaction()
|
await this.sessions[id].commitTransaction()
|
||||||
await this.sessions[id].endSession()
|
try {
|
||||||
|
await this.sessions[id].endSession()
|
||||||
|
} catch (error) {
|
||||||
|
// ending sessions is only best effort and won't impact anything if it fails since the transaction was committed
|
||||||
|
}
|
||||||
delete this.sessions[id]
|
delete this.sessions[id]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@payloadcms/db-postgres",
|
"name": "@payloadcms/db-postgres",
|
||||||
"version": "0.7.1",
|
"version": "0.8.1",
|
||||||
"description": "The officially supported Postgres database adapter for Payload",
|
"description": "The officially supported Postgres database adapter for Payload",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
65
packages/db-postgres/src/count.ts
Normal file
65
packages/db-postgres/src/count.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import type { Count } from 'payload/database'
|
||||||
|
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||||
|
|
||||||
|
import { sql } from 'drizzle-orm'
|
||||||
|
|
||||||
|
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,
|
||||||
|
{ collection, locale, req, where: whereArg },
|
||||||
|
) {
|
||||||
|
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||||
|
|
||||||
|
const tableName = getTableName({
|
||||||
|
adapter: this,
|
||||||
|
config: collectionConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||||
|
const table = this.tables[tableName]
|
||||||
|
|
||||||
|
const { joinAliases, joins, where } = await buildQuery({
|
||||||
|
adapter: this,
|
||||||
|
fields: collectionConfig.fields,
|
||||||
|
locale,
|
||||||
|
tableName,
|
||||||
|
where: whereArg,
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectCountMethods: ChainedMethods = []
|
||||||
|
|
||||||
|
joinAliases.forEach(({ condition, table }) => {
|
||||||
|
selectCountMethods.push({
|
||||||
|
args: [table, condition],
|
||||||
|
method: 'leftJoin',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.entries(joins).forEach(([joinTable, condition]) => {
|
||||||
|
if (joinTable) {
|
||||||
|
selectCountMethods.push({
|
||||||
|
args: [this.tables[joinTable], condition],
|
||||||
|
method: 'leftJoin',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const countResult = await chainMethods({
|
||||||
|
methods: selectCountMethods,
|
||||||
|
query: db
|
||||||
|
.select({
|
||||||
|
count: sql<number>`count
|
||||||
|
(DISTINCT ${this.tables[tableName].id})`,
|
||||||
|
})
|
||||||
|
.from(table)
|
||||||
|
.where(where),
|
||||||
|
})
|
||||||
|
|
||||||
|
return { totalDocs: Number(countResult[0].count) }
|
||||||
|
}
|
||||||
@@ -120,7 +120,7 @@ export const findMany = async function find({
|
|||||||
|
|
||||||
const findPromise = db.query[tableName].findMany(findManyArgs)
|
const findPromise = db.query[tableName].findMany(findManyArgs)
|
||||||
|
|
||||||
if (pagination !== false && (orderedIDs ? orderedIDs?.length >= limit : true)) {
|
if (pagination !== false && (orderedIDs ? orderedIDs?.length <= limit : true)) {
|
||||||
const selectCountMethods: ChainedMethods = []
|
const selectCountMethods: ChainedMethods = []
|
||||||
|
|
||||||
joinAliases.forEach(({ condition, table }) => {
|
joinAliases.forEach(({ condition, table }) => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { createDatabaseAdapter } from 'payload/database'
|
|||||||
import type { Args, PostgresAdapter, PostgresAdapterResult } from './types'
|
import type { Args, PostgresAdapter, PostgresAdapterResult } from './types'
|
||||||
|
|
||||||
import { connect } from './connect'
|
import { connect } from './connect'
|
||||||
|
import { count } from './count'
|
||||||
import { create } from './create'
|
import { create } from './create'
|
||||||
import { createGlobal } from './createGlobal'
|
import { createGlobal } from './createGlobal'
|
||||||
import { createGlobalVersion } from './createGlobalVersion'
|
import { createGlobalVersion } from './createGlobalVersion'
|
||||||
@@ -70,6 +71,7 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
|
|||||||
beginTransaction,
|
beginTransaction,
|
||||||
commitTransaction,
|
commitTransaction,
|
||||||
connect,
|
connect,
|
||||||
|
count,
|
||||||
create,
|
create,
|
||||||
createGlobal,
|
createGlobal,
|
||||||
createGlobalVersion,
|
createGlobalVersion,
|
||||||
|
|||||||
@@ -225,6 +225,85 @@ export const getTableColumnFromPath = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'select': {
|
||||||
|
if (field.hasMany) {
|
||||||
|
newTableName = getTableName({
|
||||||
|
adapter,
|
||||||
|
config: field,
|
||||||
|
parentTableName: `${tableName}_${tableNameSuffix}`,
|
||||||
|
prefix: `${tableName}_${tableNameSuffix}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (locale && field.localized && adapter.payload.config.localization) {
|
||||||
|
joins[newTableName] = and(
|
||||||
|
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
|
||||||
|
eq(adapter.tables[newTableName]._locale, locale),
|
||||||
|
)
|
||||||
|
if (locale !== 'all') {
|
||||||
|
constraints.push({
|
||||||
|
columnName: '_locale',
|
||||||
|
table: adapter.tables[newTableName],
|
||||||
|
value: locale,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
joins[newTableName] = eq(
|
||||||
|
adapter.tables[tableName].id,
|
||||||
|
adapter.tables[newTableName].parent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
columnName: 'value',
|
||||||
|
constraints,
|
||||||
|
field,
|
||||||
|
table: adapter.tables[newTableName],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'text':
|
||||||
|
case 'number': {
|
||||||
|
if (field.hasMany) {
|
||||||
|
let tableType = 'texts'
|
||||||
|
let columnName = 'text'
|
||||||
|
if (field.type === 'number') {
|
||||||
|
tableType = 'numbers'
|
||||||
|
columnName = 'number'
|
||||||
|
}
|
||||||
|
newTableName = `${tableName}_${tableType}`
|
||||||
|
const joinConstraints = [
|
||||||
|
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
|
||||||
|
eq(adapter.tables[newTableName].path, `${constraintPath}${field.name}`),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (locale && field.localized && adapter.payload.config.localization) {
|
||||||
|
joins[newTableName] = and(
|
||||||
|
...joinConstraints,
|
||||||
|
eq(adapter.tables[newTableName]._locale, locale),
|
||||||
|
)
|
||||||
|
if (locale !== 'all') {
|
||||||
|
constraints.push({
|
||||||
|
columnName: 'locale',
|
||||||
|
table: adapter.tables[newTableName],
|
||||||
|
value: locale,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
joins[newTableName] = and(...joinConstraints)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
columnName,
|
||||||
|
constraints,
|
||||||
|
field,
|
||||||
|
table: adapter.tables[newTableName],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case 'array': {
|
case 'array': {
|
||||||
newTableName = getTableName({
|
newTableName = getTableName({
|
||||||
adapter,
|
adapter,
|
||||||
@@ -484,43 +563,41 @@ export const getTableColumnFromPath = ({
|
|||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
if (fieldAffectsData(field)) {
|
||||||
if (fieldAffectsData(field)) {
|
if (field.localized && adapter.payload.config.localization) {
|
||||||
if (field.localized && adapter.payload.config.localization) {
|
// If localized, we go to localized table and set aliasTable to undefined
|
||||||
// If localized, we go to localized table and set aliasTable to undefined
|
// so it is not picked up below to be used as targetTable
|
||||||
// so it is not picked up below to be used as targetTable
|
newTableName = `${tableName}${adapter.localesSuffix}`
|
||||||
newTableName = `${tableName}${adapter.localesSuffix}`
|
|
||||||
|
|
||||||
const parentTable = aliasTable || adapter.tables[tableName]
|
const parentTable = aliasTable || adapter.tables[tableName]
|
||||||
|
|
||||||
joins[newTableName] = eq(parentTable.id, adapter.tables[newTableName]._parentID)
|
joins[newTableName] = eq(parentTable.id, adapter.tables[newTableName]._parentID)
|
||||||
|
|
||||||
aliasTable = undefined
|
aliasTable = undefined
|
||||||
|
|
||||||
if (locale !== 'all') {
|
if (locale !== 'all') {
|
||||||
constraints.push({
|
constraints.push({
|
||||||
columnName: '_locale',
|
columnName: '_locale',
|
||||||
table: adapter.tables[newTableName],
|
table: adapter.tables[newTableName],
|
||||||
value: locale,
|
value: locale,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetTable = aliasTable || adapter.tables[newTableName]
|
|
||||||
|
|
||||||
selectFields[`${newTableName}.${columnPrefix}${field.name}`] =
|
|
||||||
targetTable[`${columnPrefix}${field.name}`]
|
|
||||||
|
|
||||||
return {
|
|
||||||
columnName: `${columnPrefix}${field.name}`,
|
|
||||||
constraints,
|
|
||||||
field,
|
|
||||||
pathSegments,
|
|
||||||
table: targetTable,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const targetTable = aliasTable || adapter.tables[newTableName]
|
||||||
|
|
||||||
|
selectFields[`${newTableName}.${columnPrefix}${field.name}`] =
|
||||||
|
targetTable[`${columnPrefix}${field.name}`]
|
||||||
|
|
||||||
|
return {
|
||||||
|
columnName: `${columnPrefix}${field.name}`,
|
||||||
|
constraints,
|
||||||
|
field,
|
||||||
|
pathSegments,
|
||||||
|
table: targetTable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,10 @@ export const sanitizeQueryValue = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('hasMany' in field && field.hasMany && operator === 'contains') {
|
||||||
|
operator = 'equals'
|
||||||
|
}
|
||||||
|
|
||||||
if (operator === 'near' || operator === 'within' || operator === 'intersects') {
|
if (operator === 'near' || operator === 'within' || operator === 'intersects') {
|
||||||
throw new APIError(
|
throw new APIError(
|
||||||
`Querying with '${operator}' is not supported with the postgres database adapter.`,
|
`Querying with '${operator}' is not supported with the postgres database adapter.`,
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ export const traverseFields = ({
|
|||||||
prefix: `enum_${newTableName}_`,
|
prefix: `enum_${newTableName}_`,
|
||||||
target: 'enumName',
|
target: 'enumName',
|
||||||
throwValidationError,
|
throwValidationError,
|
||||||
versions,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
adapter.enums[enumName] = pgEnum(
|
adapter.enums[enumName] = pgEnum(
|
||||||
@@ -249,7 +248,6 @@ export const traverseFields = ({
|
|||||||
parentTableName: newTableName,
|
parentTableName: newTableName,
|
||||||
prefix: `${newTableName}_`,
|
prefix: `${newTableName}_`,
|
||||||
throwValidationError,
|
throwValidationError,
|
||||||
versions,
|
|
||||||
})
|
})
|
||||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||||
order: integer('order').notNull(),
|
order: integer('order').notNull(),
|
||||||
@@ -493,6 +491,7 @@ export const traverseFields = ({
|
|||||||
localized: field.localized,
|
localized: field.localized,
|
||||||
rootTableName,
|
rootTableName,
|
||||||
table: adapter.tables[blockTableName],
|
table: adapter.tables[blockTableName],
|
||||||
|
tableLocales: adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
adapter.blockTableNames[`${rootTableName}.${toSnakeCase(block.slug)}`] = blockTableName
|
adapter.blockTableNames[`${rootTableName}.${toSnakeCase(block.slug)}`] = blockTableName
|
||||||
@@ -658,7 +657,7 @@ export const traverseFields = ({
|
|||||||
indexes,
|
indexes,
|
||||||
localesColumns,
|
localesColumns,
|
||||||
localesIndexes,
|
localesIndexes,
|
||||||
newTableName: parentTableName,
|
newTableName,
|
||||||
parentTableName,
|
parentTableName,
|
||||||
relationsToBuild,
|
relationsToBuild,
|
||||||
relationships,
|
relationships,
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ type Args = {
|
|||||||
localized: boolean
|
localized: boolean
|
||||||
rootTableName: string
|
rootTableName: string
|
||||||
table: GenericTable
|
table: GenericTable
|
||||||
|
tableLocales?: GenericTable
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFlattenedFieldNames = (fields: Field[], prefix: string = ''): string[] => {
|
const getFlattenedFieldNames = (
|
||||||
|
fields: Field[],
|
||||||
|
prefix: string = '',
|
||||||
|
): { localized?: boolean; name: string }[] => {
|
||||||
return fields.reduce((fieldsToUse, field) => {
|
return fields.reduce((fieldsToUse, field) => {
|
||||||
let fieldPrefix = prefix
|
let fieldPrefix = prefix
|
||||||
|
|
||||||
@@ -24,7 +28,7 @@ const getFlattenedFieldNames = (fields: Field[], prefix: string = ''): string[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fieldHasSubFields(field)) {
|
if (fieldHasSubFields(field)) {
|
||||||
fieldPrefix = 'name' in field ? `${prefix}${field.name}.` : prefix
|
fieldPrefix = 'name' in field ? `${prefix}${field.name}_` : prefix
|
||||||
return [...fieldsToUse, ...getFlattenedFieldNames(field.fields, fieldPrefix)]
|
return [...fieldsToUse, ...getFlattenedFieldNames(field.fields, fieldPrefix)]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@ const getFlattenedFieldNames = (fields: Field[], prefix: string = ''): string[]
|
|||||||
return [
|
return [
|
||||||
...fieldsToUse,
|
...fieldsToUse,
|
||||||
...field.tabs.reduce((tabFields, tab) => {
|
...field.tabs.reduce((tabFields, tab) => {
|
||||||
fieldPrefix = 'name' in tab ? `${prefix}.${tab.name}` : prefix
|
fieldPrefix = 'name' in tab ? `${prefix}_${tab.name}` : prefix
|
||||||
return [
|
return [
|
||||||
...tabFields,
|
...tabFields,
|
||||||
...(tabHasName(tab)
|
...(tabHasName(tab)
|
||||||
@@ -44,7 +48,13 @@ const getFlattenedFieldNames = (fields: Field[], prefix: string = ''): string[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fieldAffectsData(field)) {
|
if (fieldAffectsData(field)) {
|
||||||
return [...fieldsToUse, `${fieldPrefix?.replace('.', '_') || ''}${field.name}`]
|
return [
|
||||||
|
...fieldsToUse,
|
||||||
|
{
|
||||||
|
name: `${fieldPrefix}${field.name}`,
|
||||||
|
localized: field.localized,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return fieldsToUse
|
return fieldsToUse
|
||||||
@@ -56,22 +66,29 @@ export const validateExistingBlockIsIdentical = ({
|
|||||||
localized,
|
localized,
|
||||||
rootTableName,
|
rootTableName,
|
||||||
table,
|
table,
|
||||||
|
tableLocales,
|
||||||
}: Args): void => {
|
}: Args): void => {
|
||||||
const fieldNames = getFlattenedFieldNames(block.fields)
|
const fieldNames = getFlattenedFieldNames(block.fields)
|
||||||
|
|
||||||
const missingField =
|
const missingField =
|
||||||
// ensure every field from the config is in the matching table
|
// ensure every field from the config is in the matching table
|
||||||
fieldNames.find((name) => Object.keys(table).indexOf(name) === -1) ||
|
fieldNames.find(({ name, localized }) => {
|
||||||
|
const fieldTable = localized && tableLocales ? tableLocales : table
|
||||||
|
return Object.keys(fieldTable).indexOf(name) === -1
|
||||||
|
}) ||
|
||||||
// ensure every table column is matched for every field from the config
|
// ensure every table column is matched for every field from the config
|
||||||
Object.keys(table).find((fieldName) => {
|
Object.keys(table).find((fieldName) => {
|
||||||
if (!['_locale', '_order', '_parentID', '_path', '_uuid'].includes(fieldName)) {
|
if (!['_locale', '_order', '_parentID', '_path', '_uuid'].includes(fieldName)) {
|
||||||
return fieldNames.indexOf(fieldName) === -1
|
return fieldNames.findIndex((field) => field.name) === -1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (missingField) {
|
if (missingField) {
|
||||||
throw new InvalidConfiguration(
|
throw new InvalidConfiguration(
|
||||||
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${missingField}, while the other block does not.`,
|
`The table ${rootTableName} has multiple blocks with slug ${
|
||||||
|
block.slug
|
||||||
|
}, but the schemas do not match. One block includes the field ${
|
||||||
|
typeof missingField === 'string' ? missingField : missingField.name
|
||||||
|
}, while the other block does not.`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
packages/live-preview-vue/.eslintignore
Normal file
10
packages/live-preview-vue/.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.tmp
|
||||||
|
**/.git
|
||||||
|
**/.hg
|
||||||
|
**/.pnp.*
|
||||||
|
**/.svn
|
||||||
|
**/.yarn/**
|
||||||
|
**/build
|
||||||
|
**/dist/**
|
||||||
|
**/node_modules
|
||||||
|
**/temp
|
||||||
37
packages/live-preview-vue/.eslintrc.js
Normal file
37
packages/live-preview-vue/.eslintrc.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/** @type {import('prettier').Config} */
|
||||||
|
module.exports = {
|
||||||
|
extends: ['@payloadcms'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||||
|
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['package.json', 'tsconfig.json'],
|
||||||
|
rules: {
|
||||||
|
'perfectionist/sort-array-includes': 'off',
|
||||||
|
'perfectionist/sort-astro-attributes': 'off',
|
||||||
|
'perfectionist/sort-classes': 'off',
|
||||||
|
'perfectionist/sort-enums': 'off',
|
||||||
|
'perfectionist/sort-exports': 'off',
|
||||||
|
'perfectionist/sort-imports': 'off',
|
||||||
|
'perfectionist/sort-interfaces': 'off',
|
||||||
|
'perfectionist/sort-jsx-props': 'off',
|
||||||
|
'perfectionist/sort-keys': 'off',
|
||||||
|
'perfectionist/sort-maps': 'off',
|
||||||
|
'perfectionist/sort-named-exports': 'off',
|
||||||
|
'perfectionist/sort-named-imports': 'off',
|
||||||
|
'perfectionist/sort-object-types': 'off',
|
||||||
|
'perfectionist/sort-objects': 'off',
|
||||||
|
'perfectionist/sort-svelte-attributes': 'off',
|
||||||
|
'perfectionist/sort-union-types': 'off',
|
||||||
|
'perfectionist/sort-vue-attributes': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
root: true,
|
||||||
|
}
|
||||||
10
packages/live-preview-vue/.prettierignore
Normal file
10
packages/live-preview-vue/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.tmp
|
||||||
|
**/.git
|
||||||
|
**/.hg
|
||||||
|
**/.pnp.*
|
||||||
|
**/.svn
|
||||||
|
**/.yarn/**
|
||||||
|
**/build
|
||||||
|
**/dist/**
|
||||||
|
**/node_modules
|
||||||
|
**/temp
|
||||||
15
packages/live-preview-vue/.swcrc
Normal file
15
packages/live-preview-vue/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/swcrc",
|
||||||
|
"sourceMaps": "inline",
|
||||||
|
"jsc": {
|
||||||
|
"target": "esnext",
|
||||||
|
"parser": {
|
||||||
|
"syntax": "typescript",
|
||||||
|
"tsx": true,
|
||||||
|
"dts": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"module": {
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
49
packages/live-preview-vue/package.json
Normal file
49
packages/live-preview-vue/package.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "@payloadcms/live-preview-vue",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "The official live preview Vue SDK for Payload",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/payloadcms/payload.git",
|
||||||
|
"directory": "packages/live-preview-vue"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://payloadcms.com",
|
||||||
|
"author": "Payload CMS, Inc.",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
||||||
|
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||||
|
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||||
|
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||||
|
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||||
|
"prepublishOnly": "pnpm clean && pnpm build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@payloadcms/live-preview": "workspace:^0.x"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@payloadcms/eslint-config": "workspace:*",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"payload": "workspace:*"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"exports": null,
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"registry": "https://registry.npmjs.org/",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
||||||
58
packages/live-preview-vue/src/index.ts
Normal file
58
packages/live-preview-vue/src/index.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
|
import { ready, subscribe, unsubscribe } from '@payloadcms/live-preview'
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue composable to implement Payload CMS Live Preview.
|
||||||
|
*
|
||||||
|
* {@link https://payloadcms.com/docs/live-preview/frontend View the documentation}
|
||||||
|
*/
|
||||||
|
export const useLivePreview = <T>(props: {
|
||||||
|
apiRoute?: string
|
||||||
|
depth?: number
|
||||||
|
initialData: T
|
||||||
|
serverURL: string
|
||||||
|
}): {
|
||||||
|
data: Ref<T>
|
||||||
|
isLoading: Ref<boolean>
|
||||||
|
} => {
|
||||||
|
const { apiRoute, depth, initialData, serverURL } = props
|
||||||
|
const data = ref(initialData) as Ref<T>
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const hasSentReadyMessage = ref(false)
|
||||||
|
|
||||||
|
const onChange = (mergedData: T) => {
|
||||||
|
data.value = mergedData
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
let subscription: (event: MessageEvent) => void
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
subscription = subscribe({
|
||||||
|
apiRoute,
|
||||||
|
callback: onChange,
|
||||||
|
depth,
|
||||||
|
initialData,
|
||||||
|
serverURL,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!hasSentReadyMessage.value) {
|
||||||
|
hasSentReadyMessage.value = true
|
||||||
|
|
||||||
|
ready({
|
||||||
|
serverURL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
unsubscribe(subscription)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
isLoading,
|
||||||
|
}
|
||||||
|
}
|
||||||
25
packages/live-preview-vue/tsconfig.json
Normal file
25
packages/live-preview-vue/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||||
|
"noEmit": false /* Do not emit outputs. */,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||||
|
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"build",
|
||||||
|
"tests",
|
||||||
|
"test",
|
||||||
|
"node_modules",
|
||||||
|
".eslintrc.js",
|
||||||
|
"src/**/*.spec.js",
|
||||||
|
"src/**/*.spec.jsx",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.spec.tsx"
|
||||||
|
],
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||||
|
"references": [{ "path": "../payload" }] // db-mongodb depends on payload
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "payload",
|
"name": "payload",
|
||||||
"version": "2.12.1",
|
"version": "2.14.0",
|
||||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ const DeleteDocument: React.FC<Props> = (props) => {
|
|||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
setDeleting(false)
|
setDeleting(false)
|
||||||
toggleModal(modalSlug)
|
toggleModal(modalSlug)
|
||||||
toast.success(json.message || t('titleDeleted', { label: getTranslation(singular, i18n), title }))
|
toast.success(
|
||||||
|
json.message || t('titleDeleted', { label: getTranslation(singular, i18n), title }),
|
||||||
|
)
|
||||||
return history.push(`${admin}/collections/${slug}`)
|
return history.push(`${admin}/collections/${slug}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const DocumentControls: React.FC<{
|
|||||||
label:
|
label:
|
||||||
typeof collection?.labels?.singular === 'string'
|
typeof collection?.labels?.singular === 'string'
|
||||||
? collection.labels.singular
|
? collection.labels.singular
|
||||||
: 'document',
|
: t('document'),
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const SaveDraft: React.FC<{ action: string; disabled: boolean }> = ({ action, di
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
const EditMany: React.FC<Props> = (props) => {
|
const EditMany: React.FC<Props> = (props) => {
|
||||||
const { collection: { fields, labels: { plural }, slug } = {}, collection, resetParams } = props
|
const { collection: { slug, fields, labels: { plural } } = {}, collection, resetParams } = props
|
||||||
|
|
||||||
const { permissions } = useAuth()
|
const { permissions } = useAuth()
|
||||||
const { closeModal } = useModal()
|
const { closeModal } = useModal()
|
||||||
@@ -148,11 +148,11 @@ const EditMany: React.FC<Props> = (props) => {
|
|||||||
{collection.versions ? (
|
{collection.versions ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Publish
|
<Publish
|
||||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
action={`${serverURL}${api}/${slug}${getQueryParams()}&draft=true`}
|
||||||
disabled={selected.length === 0}
|
disabled={selected.length === 0}
|
||||||
/>
|
/>
|
||||||
<SaveDraft
|
<SaveDraft
|
||||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
action={`${serverURL}${api}/${slug}${getQueryParams()}&draft=true`}
|
||||||
disabled={selected.length === 0}
|
disabled={selected.length === 0}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -77,6 +77,15 @@ const contains = {
|
|||||||
value: 'contains',
|
value: 'contains',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filterOperators = (operators, hasMany = false) => {
|
||||||
|
if (hasMany) {
|
||||||
|
return operators.filter(
|
||||||
|
(operator) => operator.value !== 'equals' && operator.value !== 'not_equals',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return operators
|
||||||
|
}
|
||||||
|
|
||||||
const fieldTypeConditions = {
|
const fieldTypeConditions = {
|
||||||
checkbox: {
|
checkbox: {
|
||||||
component: 'Text',
|
component: 'Text',
|
||||||
@@ -100,7 +109,7 @@ const fieldTypeConditions = {
|
|||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
component: 'Number',
|
component: 'Number',
|
||||||
operators: [...base, ...numeric],
|
operators: (hasMany) => filterOperators([...base, ...numeric], hasMany),
|
||||||
},
|
},
|
||||||
point: {
|
point: {
|
||||||
component: 'Point',
|
component: 'Point',
|
||||||
@@ -120,11 +129,11 @@ const fieldTypeConditions = {
|
|||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
operators: [...base],
|
operators: (hasMany) => filterOperators([...base], hasMany),
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
component: 'Text',
|
component: 'Text',
|
||||||
operators: [...base, like, contains],
|
operators: (hasMany) => filterOperators([...base, like, contains], hasMany),
|
||||||
},
|
},
|
||||||
textarea: {
|
textarea: {
|
||||||
component: 'Text',
|
component: 'Text',
|
||||||
|
|||||||
@@ -22,36 +22,44 @@ const baseClass = 'where-builder'
|
|||||||
|
|
||||||
const reduceFields = (fields, i18n) =>
|
const reduceFields = (fields, i18n) =>
|
||||||
flattenTopLevelFields(fields).reduce((reduced, field) => {
|
flattenTopLevelFields(fields).reduce((reduced, field) => {
|
||||||
|
let operators = []
|
||||||
|
|
||||||
if (typeof fieldTypes[field.type] === 'object') {
|
if (typeof fieldTypes[field.type] === 'object') {
|
||||||
const operatorKeys = new Set()
|
if (typeof fieldTypes[field.type].operators === 'function') {
|
||||||
const operators = fieldTypes[field.type].operators.reduce((acc, operator) => {
|
operators = fieldTypes[field.type].operators(
|
||||||
if (!operatorKeys.has(operator.value)) {
|
'hasMany' in field && field.hasMany ? true : false,
|
||||||
operatorKeys.add(operator.value)
|
)
|
||||||
return [
|
} else {
|
||||||
...acc,
|
operators = fieldTypes[field.type].operators
|
||||||
{
|
|
||||||
...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,
|
|
||||||
props: {
|
|
||||||
...field,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...reduced, formattedField]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return reduced
|
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 [...reduced, formattedField]
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,7 +193,7 @@ const WhereBuilder: React.FC<Props> = (props) => {
|
|||||||
iconStyle="with-border"
|
iconStyle="with-border"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (reducedFields.length > 0)
|
if (reducedFields.length > 0)
|
||||||
dispatchConditions({ field: reducedFields[0].value, type: 'add' })
|
dispatchConditions({ type: 'add', field: reducedFields[0].value })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('or')}
|
{t('or')}
|
||||||
@@ -203,7 +211,7 @@ const WhereBuilder: React.FC<Props> = (props) => {
|
|||||||
iconStyle="with-border"
|
iconStyle="with-border"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (reducedFields.length > 0)
|
if (reducedFields.length > 0)
|
||||||
dispatchConditions({ field: reducedFields[0].value, type: 'add' })
|
dispatchConditions({ type: 'add', field: reducedFields[0].value })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('addFilter')}
|
{t('addFilter')}
|
||||||
|
|||||||
@@ -41,46 +41,53 @@ const CollapsibleField: React.FC<Props> = (props) => {
|
|||||||
async (newCollapsedState: boolean) => {
|
async (newCollapsedState: boolean) => {
|
||||||
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
|
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
|
||||||
|
|
||||||
setPreference(preferencesKey, {
|
if (preferencesKey) {
|
||||||
...existingPreferences,
|
await setPreference(preferencesKey, {
|
||||||
...(path
|
...existingPreferences,
|
||||||
? {
|
...(path
|
||||||
fields: {
|
? {
|
||||||
...(existingPreferences?.fields || {}),
|
fields: {
|
||||||
[path]: {
|
...(existingPreferences?.fields || {}),
|
||||||
...existingPreferences?.fields?.[path],
|
[path]: {
|
||||||
collapsed: newCollapsedState,
|
...existingPreferences?.fields?.[path],
|
||||||
|
collapsed: newCollapsedState,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
: {
|
||||||
: {
|
fields: {
|
||||||
fields: {
|
...(existingPreferences?.fields || {}),
|
||||||
...(existingPreferences?.fields || {}),
|
[fieldPreferencesKey]: {
|
||||||
[fieldPreferencesKey]: {
|
...existingPreferences?.fields?.[fieldPreferencesKey],
|
||||||
...existingPreferences?.fields?.[fieldPreferencesKey],
|
collapsed: newCollapsedState,
|
||||||
collapsed: newCollapsedState,
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
})
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
[preferencesKey, fieldPreferencesKey, getPreference, setPreference, path],
|
[preferencesKey, fieldPreferencesKey, getPreference, setPreference, path],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchInitialState = async () => {
|
const fetchInitialState = async () => {
|
||||||
const preferences = await getPreference(preferencesKey)
|
if (preferencesKey) {
|
||||||
if (preferences) {
|
const preferences = await getPreference(preferencesKey)
|
||||||
const initCollapsedFromPref = path
|
const specificPreference = path
|
||||||
? preferences?.fields?.[path]?.collapsed
|
? preferences?.fields?.[path]?.collapsed
|
||||||
: preferences?.fields?.[fieldPreferencesKey]?.collapsed
|
: preferences?.fields?.[fieldPreferencesKey]?.collapsed
|
||||||
setCollapsedOnMount(Boolean(initCollapsedFromPref))
|
|
||||||
|
if (specificPreference !== undefined) {
|
||||||
|
setCollapsedOnMount(Boolean(specificPreference))
|
||||||
|
} else {
|
||||||
|
setCollapsedOnMount(typeof initCollapsed === 'boolean' ? initCollapsed : false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setCollapsedOnMount(typeof initCollapsed === 'boolean' ? initCollapsed : false)
|
setCollapsedOnMount(typeof initCollapsed === 'boolean' ? initCollapsed : false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchInitialState()
|
void fetchInitialState()
|
||||||
}, [getPreference, preferencesKey, fieldPreferencesKey, initCollapsed, path])
|
}, [getPreference, preferencesKey, fieldPreferencesKey, initCollapsed, path])
|
||||||
|
|
||||||
if (typeof collapsedOnMount !== 'boolean') return null
|
if (typeof collapsedOnMount !== 'boolean') return null
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const JSONField: React.FC<Props> = (props) => {
|
|||||||
style,
|
style,
|
||||||
width,
|
width,
|
||||||
} = {},
|
} = {},
|
||||||
|
jsonSchema,
|
||||||
label,
|
label,
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
required,
|
required,
|
||||||
@@ -54,6 +55,25 @@ const JSONField: React.FC<Props> = (props) => {
|
|||||||
validate: memoizedValidate,
|
validate: memoizedValidate,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleMount = useCallback(
|
||||||
|
(editor, monaco) => {
|
||||||
|
if (!jsonSchema) return
|
||||||
|
|
||||||
|
const existingSchemas = monaco.languages.json.jsonDefaults.diagnosticsOptions.schemas || []
|
||||||
|
const modelUri = monaco.Uri.parse(jsonSchema.uri)
|
||||||
|
|
||||||
|
const model = monaco.editor.createModel(JSON.stringify(value, null, 2), 'json', modelUri)
|
||||||
|
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||||
|
enableSchemaRequest: true,
|
||||||
|
schemas: [...existingSchemas, jsonSchema],
|
||||||
|
validate: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
editor.setModel(model)
|
||||||
|
},
|
||||||
|
[value, jsonSchema],
|
||||||
|
)
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(val) => {
|
(val) => {
|
||||||
try {
|
try {
|
||||||
@@ -104,6 +124,7 @@ const JSONField: React.FC<Props> = (props) => {
|
|||||||
<CodeEditor
|
<CodeEditor
|
||||||
defaultLanguage="json"
|
defaultLanguage="json"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onMount={handleMount}
|
||||||
options={editorOptions}
|
options={editorOptions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
value={stringValue}
|
value={stringValue}
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ const NumberField: React.FC<Props> = (props) => {
|
|||||||
<input
|
<input
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
id={`field-${path.replace(/\./g, '__')}`}
|
id={`field-${path.replace(/\./g, '__')}`}
|
||||||
|
max={max}
|
||||||
|
min={min}
|
||||||
name={path}
|
name={path}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onWheel={(e) => {
|
onWheel={(e) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { JSONSchema4 } from 'json-schema'
|
import type { JSONSchema4 } from 'json-schema'
|
||||||
|
import type { SanitizedConfig } from 'payload/config'
|
||||||
|
|
||||||
import type { PayloadRequest } from '../../../../../express/types'
|
import type { PayloadRequest } from '../../../../../express/types'
|
||||||
import type { RequestContext } from '../../../../../express/types'
|
import type { RequestContext } from '../../../../../express/types'
|
||||||
@@ -30,12 +31,14 @@ type RichTextAdapterBase<
|
|||||||
}) => Promise<void> | null
|
}) => Promise<void> | null
|
||||||
outputSchema?: ({
|
outputSchema?: ({
|
||||||
collectionIDFieldTypes,
|
collectionIDFieldTypes,
|
||||||
|
config,
|
||||||
field,
|
field,
|
||||||
interfaceNameDefinitions,
|
interfaceNameDefinitions,
|
||||||
isRequired,
|
isRequired,
|
||||||
payload,
|
payload,
|
||||||
}: {
|
}: {
|
||||||
collectionIDFieldTypes: { [key: string]: 'number' | 'string' }
|
collectionIDFieldTypes: { [key: string]: 'number' | 'string' }
|
||||||
|
config?: SanitizedConfig
|
||||||
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
||||||
/**
|
/**
|
||||||
* Allows you to define new top-level interfaces that can be re-used in the output schema.
|
* Allows you to define new top-level interfaces that can be re-used in the output schema.
|
||||||
|
|||||||
@@ -88,14 +88,16 @@ const TabsField: React.FC<Props> = (props) => {
|
|||||||
const tabsPrefKey = `tabs-${indexPath}`
|
const tabsPrefKey = `tabs-${indexPath}`
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getInitialPref = async () => {
|
if (preferencesKey) {
|
||||||
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
|
const getInitialPref = async () => {
|
||||||
const initialIndex = path
|
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
|
||||||
? existingPreferences?.fields?.[path]?.tabIndex
|
const initialIndex = path
|
||||||
: existingPreferences?.fields?.[tabsPrefKey]?.tabIndex
|
? existingPreferences?.fields?.[path]?.tabIndex
|
||||||
setActiveTabIndex(initialIndex || 0)
|
: existingPreferences?.fields?.[tabsPrefKey]?.tabIndex
|
||||||
|
setActiveTabIndex(initialIndex || 0)
|
||||||
|
}
|
||||||
|
void getInitialPref()
|
||||||
}
|
}
|
||||||
void getInitialPref()
|
|
||||||
}, [path, indexPath, getPreference, preferencesKey, tabsPrefKey])
|
}, [path, indexPath, getPreference, preferencesKey, tabsPrefKey])
|
||||||
|
|
||||||
const handleTabChange = useCallback(
|
const handleTabChange = useCallback(
|
||||||
@@ -104,28 +106,30 @@ const TabsField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
|
const existingPreferences: DocumentPreferences = await getPreference(preferencesKey)
|
||||||
|
|
||||||
setPreference(preferencesKey, {
|
if (preferencesKey) {
|
||||||
...existingPreferences,
|
await setPreference(preferencesKey, {
|
||||||
...(path
|
...existingPreferences,
|
||||||
? {
|
...(path
|
||||||
fields: {
|
? {
|
||||||
...(existingPreferences?.fields || {}),
|
fields: {
|
||||||
[path]: {
|
...(existingPreferences?.fields || {}),
|
||||||
...existingPreferences?.fields?.[path],
|
[path]: {
|
||||||
tabIndex: incomingTabIndex,
|
...existingPreferences?.fields?.[path],
|
||||||
|
tabIndex: incomingTabIndex,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
: {
|
||||||
: {
|
fields: {
|
||||||
fields: {
|
...existingPreferences?.fields,
|
||||||
...existingPreferences?.fields,
|
[tabsPrefKey]: {
|
||||||
[tabsPrefKey]: {
|
...existingPreferences?.fields?.[tabsPrefKey],
|
||||||
...existingPreferences?.fields?.[tabsPrefKey],
|
tabIndex: incomingTabIndex,
|
||||||
tabIndex: incomingTabIndex,
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
})
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
[preferencesKey, getPreference, setPreference, path, tabsPrefKey],
|
[preferencesKey, getPreference, setPreference, path, tabsPrefKey],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export type DefaultEditViewProps = CollectionEditViewProps & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DefaultEditView: React.FC<DefaultEditViewProps> = (props) => {
|
const DefaultEditView: React.FC<DefaultEditViewProps> = (props) => {
|
||||||
const { i18n } = useTranslation('general')
|
const { i18n, t } = useTranslation('general')
|
||||||
const { refreshCookieAsync, user } = useAuth()
|
const { refreshCookieAsync, user } = useAuth()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -115,7 +115,7 @@ const DefaultEditView: React.FC<DefaultEditViewProps> = (props) => {
|
|||||||
name={`collection-edit--${
|
name={`collection-edit--${
|
||||||
typeof collection?.labels?.singular === 'string'
|
typeof collection?.labels?.singular === 'string'
|
||||||
? collection.labels.singular
|
? collection.labels.singular
|
||||||
: 'document'
|
: t('document')
|
||||||
}`}
|
}`}
|
||||||
type="withoutNav"
|
type="withoutNav"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { PayloadRequest } from '../../express/types'
|
|||||||
import type { User } from '../types'
|
import type { User } from '../types'
|
||||||
|
|
||||||
import { buildAfterOperation } from '../../collections/operations/utils'
|
import { buildAfterOperation } from '../../collections/operations/utils'
|
||||||
import { AuthenticationError, LockedAuth } from '../../errors'
|
import { AuthenticationError, LockedAuth, ValidationError } from '../../errors'
|
||||||
import { afterRead } from '../../fields/hooks/afterRead'
|
import { afterRead } from '../../fields/hooks/afterRead'
|
||||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||||
import getCookieExpiration from '../../utilities/getCookieExpiration'
|
import getCookieExpiration from '../../utilities/getCookieExpiration'
|
||||||
@@ -86,6 +86,13 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
|
|
||||||
const { email: unsanitizedEmail, password } = data
|
const { email: unsanitizedEmail, password } = data
|
||||||
|
|
||||||
|
if (typeof unsanitizedEmail !== 'string' || unsanitizedEmail.trim() === '') {
|
||||||
|
throw new ValidationError([{ field: 'email', message: req.i18n.t('validation:required') }])
|
||||||
|
}
|
||||||
|
if (typeof password !== 'string' || password.trim() === '') {
|
||||||
|
throw new ValidationError([{ field: 'password', message: req.i18n.t('validation:required') }])
|
||||||
|
}
|
||||||
|
|
||||||
const email = unsanitizedEmail ? unsanitizedEmail.toLowerCase().trim() : null
|
const email = unsanitizedEmail ? unsanitizedEmail.toLowerCase().trim() : null
|
||||||
|
|
||||||
let user = await payload.db.findOne<any>({
|
let user = await payload.db.findOne<any>({
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import registerFirstUserHandler from '../auth/requestHandlers/registerFirstUser'
|
|||||||
import resetPassword from '../auth/requestHandlers/resetPassword'
|
import resetPassword from '../auth/requestHandlers/resetPassword'
|
||||||
import unlock from '../auth/requestHandlers/unlock'
|
import unlock from '../auth/requestHandlers/unlock'
|
||||||
import verifyEmail from '../auth/requestHandlers/verifyEmail'
|
import verifyEmail from '../auth/requestHandlers/verifyEmail'
|
||||||
|
import count from './requestHandlers/count'
|
||||||
import create from './requestHandlers/create'
|
import create from './requestHandlers/create'
|
||||||
import deleteHandler from './requestHandlers/delete'
|
import deleteHandler from './requestHandlers/delete'
|
||||||
import deleteByID from './requestHandlers/deleteByID'
|
import deleteByID from './requestHandlers/deleteByID'
|
||||||
@@ -124,6 +125,11 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
path: '/',
|
path: '/',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
handler: count,
|
||||||
|
method: 'get',
|
||||||
|
path: '/count',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
handler: docAccessRequestHandler,
|
handler: docAccessRequestHandler,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ const collectionSchema = joi.object().keys({
|
|||||||
adminThumbnail: joi.alternatives().try(joi.string(), joi.func()),
|
adminThumbnail: joi.alternatives().try(joi.string(), joi.func()),
|
||||||
crop: joi.bool(),
|
crop: joi.bool(),
|
||||||
disableLocalStorage: joi.bool(),
|
disableLocalStorage: joi.bool(),
|
||||||
|
externalFileHeaderFilter: joi.func(),
|
||||||
filesRequiredOnCreate: joi.bool(),
|
filesRequiredOnCreate: joi.bool(),
|
||||||
focalPoint: joi.bool(),
|
focalPoint: joi.bool(),
|
||||||
formatOptions: joi.object().keys({
|
formatOptions: joi.object().keys({
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import type { AfterOperationArg, AfterOperationMap } from '../operations/utils'
|
|||||||
|
|
||||||
export type HookOperationType =
|
export type HookOperationType =
|
||||||
| 'autosave'
|
| 'autosave'
|
||||||
|
| 'count'
|
||||||
| 'create'
|
| 'create'
|
||||||
| 'delete'
|
| 'delete'
|
||||||
| 'forgotPassword'
|
| 'forgotPassword'
|
||||||
@@ -341,7 +342,8 @@ export type CollectionAdminOptions = {
|
|||||||
useAsTitle?: string
|
useAsTitle?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BaseCollectionConfig = {
|
/** Manage all aspects of a data collection */
|
||||||
|
export type CollectionConfig = {
|
||||||
/**
|
/**
|
||||||
* Access control
|
* Access control
|
||||||
*/
|
*/
|
||||||
@@ -448,9 +450,6 @@ export type BaseCollectionConfig = {
|
|||||||
versions?: IncomingCollectionVersions | boolean
|
versions?: IncomingCollectionVersions | boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Manage all aspects of a data collection */
|
|
||||||
export type CollectionConfig = BaseCollectionConfig
|
|
||||||
|
|
||||||
export interface SanitizedCollectionConfig
|
export interface SanitizedCollectionConfig
|
||||||
extends Omit<
|
extends Omit<
|
||||||
DeepRequired<CollectionConfig>,
|
DeepRequired<CollectionConfig>,
|
||||||
@@ -467,6 +466,7 @@ export type Collection = {
|
|||||||
config: SanitizedCollectionConfig
|
config: SanitizedCollectionConfig
|
||||||
graphQL?: {
|
graphQL?: {
|
||||||
JWT: GraphQLObjectType
|
JWT: GraphQLObjectType
|
||||||
|
countType: GraphQLObjectType
|
||||||
mutationInputType: GraphQLNonNull<any>
|
mutationInputType: GraphQLNonNull<any>
|
||||||
paginatedType: GraphQLObjectType
|
paginatedType: GraphQLObjectType
|
||||||
type: GraphQLObjectType
|
type: GraphQLObjectType
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ import buildPaginatedListType from '../../graphql/schema/buildPaginatedListType'
|
|||||||
import { buildPolicyType } from '../../graphql/schema/buildPoliciesType'
|
import { buildPolicyType } from '../../graphql/schema/buildPoliciesType'
|
||||||
import buildWhereInputType from '../../graphql/schema/buildWhereInputType'
|
import buildWhereInputType from '../../graphql/schema/buildWhereInputType'
|
||||||
import formatName from '../../graphql/utilities/formatName'
|
import formatName from '../../graphql/utilities/formatName'
|
||||||
|
import flattenFields from '../../utilities/flattenTopLevelFields'
|
||||||
import { formatNames, toWords } from '../../utilities/formatLabels'
|
import { formatNames, toWords } from '../../utilities/formatLabels'
|
||||||
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields'
|
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields'
|
||||||
|
import countResolver from './resolvers/count'
|
||||||
import createResolver from './resolvers/create'
|
import createResolver from './resolvers/create'
|
||||||
import getDeleteResolver from './resolvers/delete'
|
import getDeleteResolver from './resolvers/delete'
|
||||||
import { docAccessResolver } from './resolvers/docAccess'
|
import { docAccessResolver } from './resolvers/docAccess'
|
||||||
@@ -41,7 +43,6 @@ import findVersionByIDResolver from './resolvers/findVersionByID'
|
|||||||
import findVersionsResolver from './resolvers/findVersions'
|
import findVersionsResolver from './resolvers/findVersions'
|
||||||
import restoreVersionResolver from './resolvers/restoreVersion'
|
import restoreVersionResolver from './resolvers/restoreVersion'
|
||||||
import updateResolver from './resolvers/update'
|
import updateResolver from './resolvers/update'
|
||||||
import flattenFields from '../../utilities/flattenTopLevelFields'
|
|
||||||
|
|
||||||
function initCollectionsGraphQL(payload: Payload): void {
|
function initCollectionsGraphQL(payload: Payload): void {
|
||||||
Object.keys(payload.collections).forEach((slug) => {
|
Object.keys(payload.collections).forEach((slug) => {
|
||||||
@@ -118,9 +119,9 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
if (config.auth && !config.auth.disableLocalStrategy) {
|
if (config.auth && !config.auth.disableLocalStrategy) {
|
||||||
fields.push({
|
fields.push({
|
||||||
name: 'password',
|
name: 'password',
|
||||||
|
type: 'text',
|
||||||
label: 'Password',
|
label: 'Password',
|
||||||
required: true,
|
required: true,
|
||||||
type: 'text',
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +147,7 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload.Query.fields[singularName] = {
|
payload.Query.fields[singularName] = {
|
||||||
|
type: collection.graphQL.type,
|
||||||
args: {
|
args: {
|
||||||
id: { type: new GraphQLNonNull(idType) },
|
id: { type: new GraphQLNonNull(idType) },
|
||||||
draft: { type: GraphQLBoolean },
|
draft: { type: GraphQLBoolean },
|
||||||
@@ -157,10 +159,10 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
resolve: findByIDResolver(collection),
|
resolve: findByIDResolver(collection),
|
||||||
type: collection.graphQL.type,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Query.fields[pluralName] = {
|
payload.Query.fields[pluralName] = {
|
||||||
|
type: buildPaginatedListType(pluralName, collection.graphQL.type),
|
||||||
args: {
|
args: {
|
||||||
draft: { type: GraphQLBoolean },
|
draft: { type: GraphQLBoolean },
|
||||||
where: { type: collection.graphQL.whereInputType },
|
where: { type: collection.graphQL.whereInputType },
|
||||||
@@ -175,23 +177,42 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
sort: { type: GraphQLString },
|
sort: { type: GraphQLString },
|
||||||
},
|
},
|
||||||
resolve: findResolver(collection),
|
resolve: findResolver(collection),
|
||||||
type: buildPaginatedListType(pluralName, collection.graphQL.type),
|
}
|
||||||
|
|
||||||
|
payload.Query.fields[`count${pluralName}`] = {
|
||||||
|
type: new GraphQLObjectType({
|
||||||
|
name: `count${pluralName}`,
|
||||||
|
fields: {
|
||||||
|
totalDocs: { type: GraphQLInt },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
args: {
|
||||||
|
draft: { type: GraphQLBoolean },
|
||||||
|
where: { type: collection.graphQL.whereInputType },
|
||||||
|
...(payload.config.localization
|
||||||
|
? {
|
||||||
|
locale: { type: payload.types.localeInputType },
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
resolve: countResolver(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Query.fields[`docAccess${singularName}`] = {
|
payload.Query.fields[`docAccess${singularName}`] = {
|
||||||
|
type: buildPolicyType({
|
||||||
|
type: 'collection',
|
||||||
|
entity: config,
|
||||||
|
scope: 'docAccess',
|
||||||
|
typeSuffix: 'DocAccess',
|
||||||
|
}),
|
||||||
args: {
|
args: {
|
||||||
id: { type: new GraphQLNonNull(idType) },
|
id: { type: new GraphQLNonNull(idType) },
|
||||||
},
|
},
|
||||||
resolve: docAccessResolver(),
|
resolve: docAccessResolver(),
|
||||||
type: buildPolicyType({
|
|
||||||
entity: config,
|
|
||||||
scope: 'docAccess',
|
|
||||||
type: 'collection',
|
|
||||||
typeSuffix: 'DocAccess',
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`create${singularName}`] = {
|
payload.Mutation.fields[`create${singularName}`] = {
|
||||||
|
type: collection.graphQL.type,
|
||||||
args: {
|
args: {
|
||||||
...(createMutationInputType
|
...(createMutationInputType
|
||||||
? { data: { type: collection.graphQL.mutationInputType } }
|
? { data: { type: collection.graphQL.mutationInputType } }
|
||||||
@@ -204,10 +225,10 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
resolve: createResolver(collection),
|
resolve: createResolver(collection),
|
||||||
type: collection.graphQL.type,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`update${singularName}`] = {
|
payload.Mutation.fields[`update${singularName}`] = {
|
||||||
|
type: collection.graphQL.type,
|
||||||
args: {
|
args: {
|
||||||
id: { type: new GraphQLNonNull(idType) },
|
id: { type: new GraphQLNonNull(idType) },
|
||||||
autosave: { type: GraphQLBoolean },
|
autosave: { type: GraphQLBoolean },
|
||||||
@@ -222,15 +243,14 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
resolve: updateResolver(collection),
|
resolve: updateResolver(collection),
|
||||||
type: collection.graphQL.type,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`delete${singularName}`] = {
|
payload.Mutation.fields[`delete${singularName}`] = {
|
||||||
|
type: collection.graphQL.type,
|
||||||
args: {
|
args: {
|
||||||
id: { type: new GraphQLNonNull(idType) },
|
id: { type: new GraphQLNonNull(idType) },
|
||||||
},
|
},
|
||||||
resolve: getDeleteResolver(collection),
|
resolve: getDeleteResolver(collection),
|
||||||
type: collection.graphQL.type,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.versions) {
|
if (config.versions) {
|
||||||
@@ -243,13 +263,13 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'createdAt',
|
name: 'createdAt',
|
||||||
label: 'Created At',
|
|
||||||
type: 'date',
|
type: 'date',
|
||||||
|
label: 'Created At',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'updatedAt',
|
name: 'updatedAt',
|
||||||
label: 'Updated At',
|
|
||||||
type: 'date',
|
type: 'date',
|
||||||
|
label: 'Updated At',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -262,6 +282,7 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
payload.Query.fields[`version${formatName(singularName)}`] = {
|
payload.Query.fields[`version${formatName(singularName)}`] = {
|
||||||
|
type: collection.graphQL.versionType,
|
||||||
args: {
|
args: {
|
||||||
id: { type: versionIDType },
|
id: { type: versionIDType },
|
||||||
...(payload.config.localization
|
...(payload.config.localization
|
||||||
@@ -272,9 +293,12 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
resolve: findVersionByIDResolver(collection),
|
resolve: findVersionByIDResolver(collection),
|
||||||
type: collection.graphQL.versionType,
|
|
||||||
}
|
}
|
||||||
payload.Query.fields[`versions${pluralName}`] = {
|
payload.Query.fields[`versions${pluralName}`] = {
|
||||||
|
type: buildPaginatedListType(
|
||||||
|
`versions${formatName(pluralName)}`,
|
||||||
|
collection.graphQL.versionType,
|
||||||
|
),
|
||||||
args: {
|
args: {
|
||||||
where: {
|
where: {
|
||||||
type: buildWhereInputType({
|
type: buildWhereInputType({
|
||||||
@@ -295,17 +319,13 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
sort: { type: GraphQLString },
|
sort: { type: GraphQLString },
|
||||||
},
|
},
|
||||||
resolve: findVersionsResolver(collection),
|
resolve: findVersionsResolver(collection),
|
||||||
type: buildPaginatedListType(
|
|
||||||
`versions${formatName(pluralName)}`,
|
|
||||||
collection.graphQL.versionType,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
payload.Mutation.fields[`restoreVersion${formatName(singularName)}`] = {
|
payload.Mutation.fields[`restoreVersion${formatName(singularName)}`] = {
|
||||||
|
type: collection.graphQL.type,
|
||||||
args: {
|
args: {
|
||||||
id: { type: versionIDType },
|
id: { type: versionIDType },
|
||||||
},
|
},
|
||||||
resolve: restoreVersionResolver(collection),
|
resolve: restoreVersionResolver(collection),
|
||||||
type: collection.graphQL.type,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,8 +335,8 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
name: 'email',
|
name: 'email',
|
||||||
required: true,
|
|
||||||
type: 'email',
|
type: 'email',
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
collection.graphQL.JWT = buildObjectType({
|
collection.graphQL.JWT = buildObjectType({
|
||||||
@@ -326,8 +346,8 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
...authFields,
|
...authFields,
|
||||||
{
|
{
|
||||||
name: 'collection',
|
name: 'collection',
|
||||||
required: true,
|
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
parentName: formatName(`${slug}JWT`),
|
parentName: formatName(`${slug}JWT`),
|
||||||
@@ -335,7 +355,6 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
payload.Query.fields[`me${singularName}`] = {
|
payload.Query.fields[`me${singularName}`] = {
|
||||||
resolve: me(collection),
|
|
||||||
type: new GraphQLObjectType({
|
type: new GraphQLObjectType({
|
||||||
name: formatName(`${slug}Me`),
|
name: formatName(`${slug}Me`),
|
||||||
fields: {
|
fields: {
|
||||||
@@ -353,18 +372,15 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
resolve: me(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Query.fields[`initialized${singularName}`] = {
|
payload.Query.fields[`initialized${singularName}`] = {
|
||||||
resolve: init(collection.config.slug),
|
|
||||||
type: GraphQLBoolean,
|
type: GraphQLBoolean,
|
||||||
|
resolve: init(collection.config.slug),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`refreshToken${singularName}`] = {
|
payload.Mutation.fields[`refreshToken${singularName}`] = {
|
||||||
args: {
|
|
||||||
token: { type: GraphQLString },
|
|
||||||
},
|
|
||||||
resolve: refresh(collection),
|
|
||||||
type: new GraphQLObjectType({
|
type: new GraphQLObjectType({
|
||||||
name: formatName(`${slug}Refreshed${singularName}`),
|
name: formatName(`${slug}Refreshed${singularName}`),
|
||||||
fields: {
|
fields: {
|
||||||
@@ -379,30 +395,29 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
args: {
|
||||||
|
token: { type: GraphQLString },
|
||||||
|
},
|
||||||
|
resolve: refresh(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`logout${singularName}`] = {
|
payload.Mutation.fields[`logout${singularName}`] = {
|
||||||
resolve: logout(collection),
|
|
||||||
type: GraphQLString,
|
type: GraphQLString,
|
||||||
|
resolve: logout(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.auth.disableLocalStrategy) {
|
if (!config.auth.disableLocalStrategy) {
|
||||||
if (config.auth.maxLoginAttempts > 0) {
|
if (config.auth.maxLoginAttempts > 0) {
|
||||||
payload.Mutation.fields[`unlock${singularName}`] = {
|
payload.Mutation.fields[`unlock${singularName}`] = {
|
||||||
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
args: {
|
args: {
|
||||||
email: { type: new GraphQLNonNull(GraphQLString) },
|
email: { type: new GraphQLNonNull(GraphQLString) },
|
||||||
},
|
},
|
||||||
resolve: unlock(collection),
|
resolve: unlock(collection),
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`login${singularName}`] = {
|
payload.Mutation.fields[`login${singularName}`] = {
|
||||||
args: {
|
|
||||||
email: { type: GraphQLString },
|
|
||||||
password: { type: GraphQLString },
|
|
||||||
},
|
|
||||||
resolve: login(collection),
|
|
||||||
type: new GraphQLObjectType({
|
type: new GraphQLObjectType({
|
||||||
name: formatName(`${slug}LoginResult`),
|
name: formatName(`${slug}LoginResult`),
|
||||||
fields: {
|
fields: {
|
||||||
@@ -417,24 +432,24 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
args: {
|
||||||
|
email: { type: GraphQLString },
|
||||||
|
password: { type: GraphQLString },
|
||||||
|
},
|
||||||
|
resolve: login(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`forgotPassword${singularName}`] = {
|
payload.Mutation.fields[`forgotPassword${singularName}`] = {
|
||||||
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
args: {
|
args: {
|
||||||
disableEmail: { type: GraphQLBoolean },
|
disableEmail: { type: GraphQLBoolean },
|
||||||
email: { type: new GraphQLNonNull(GraphQLString) },
|
email: { type: new GraphQLNonNull(GraphQLString) },
|
||||||
expiration: { type: GraphQLInt },
|
expiration: { type: GraphQLInt },
|
||||||
},
|
},
|
||||||
resolve: forgotPassword(collection),
|
resolve: forgotPassword(collection),
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`resetPassword${singularName}`] = {
|
payload.Mutation.fields[`resetPassword${singularName}`] = {
|
||||||
args: {
|
|
||||||
password: { type: GraphQLString },
|
|
||||||
token: { type: GraphQLString },
|
|
||||||
},
|
|
||||||
resolve: resetPassword(collection),
|
|
||||||
type: new GraphQLObjectType({
|
type: new GraphQLObjectType({
|
||||||
name: formatName(`${slug}ResetPassword`),
|
name: formatName(`${slug}ResetPassword`),
|
||||||
fields: {
|
fields: {
|
||||||
@@ -442,14 +457,19 @@ function initCollectionsGraphQL(payload: Payload): void {
|
|||||||
user: { type: collection.graphQL.type },
|
user: { type: collection.graphQL.type },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
args: {
|
||||||
|
password: { type: GraphQLString },
|
||||||
|
token: { type: GraphQLString },
|
||||||
|
},
|
||||||
|
resolve: resetPassword(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.Mutation.fields[`verifyEmail${singularName}`] = {
|
payload.Mutation.fields[`verifyEmail${singularName}`] = {
|
||||||
|
type: GraphQLBoolean,
|
||||||
args: {
|
args: {
|
||||||
token: { type: GraphQLString },
|
token: { type: GraphQLString },
|
||||||
},
|
},
|
||||||
resolve: verifyEmail(collection),
|
resolve: verifyEmail(collection),
|
||||||
type: GraphQLBoolean,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
packages/payload/src/collections/graphql/resolvers/count.ts
Normal file
40
packages/payload/src/collections/graphql/resolvers/count.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { PayloadRequest } from '../../../express/types'
|
||||||
|
import type { Where } from '../../../types'
|
||||||
|
import type { Collection } from '../../config/types'
|
||||||
|
|
||||||
|
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||||
|
import count from '../../operations/count'
|
||||||
|
|
||||||
|
export type Resolver = (
|
||||||
|
_: unknown,
|
||||||
|
args: {
|
||||||
|
data: Record<string, unknown>
|
||||||
|
locale?: string
|
||||||
|
where?: Where
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
req: PayloadRequest
|
||||||
|
res: Response
|
||||||
|
},
|
||||||
|
) => Promise<{ totalDocs: number }>
|
||||||
|
|
||||||
|
export default function findResolver(collection: Collection): Resolver {
|
||||||
|
return async function resolver(_, args, context) {
|
||||||
|
let { req } = context
|
||||||
|
const locale = req.locale
|
||||||
|
const fallbackLocale = req.fallbackLocale
|
||||||
|
req = isolateObjectProperty(req, 'locale')
|
||||||
|
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||||
|
req.locale = args.locale || locale
|
||||||
|
req.fallbackLocale = fallbackLocale
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
collection,
|
||||||
|
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||||
|
where: args.where,
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await count(options)
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
}
|
||||||
113
packages/payload/src/collections/operations/count.ts
Normal file
113
packages/payload/src/collections/operations/count.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import type { AccessResult } from '../../config/types'
|
||||||
|
import type { PayloadRequest, Where } from '../../types/index'
|
||||||
|
import type { Collection, TypeWithID } from '../config/types'
|
||||||
|
|
||||||
|
import executeAccess from '../../auth/executeAccess'
|
||||||
|
import { combineQueries } from '../../database/combineQueries'
|
||||||
|
import { validateQueryPaths } from '../../database/queryValidation/validateQueryPaths'
|
||||||
|
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||||
|
import { initTransaction } from '../../utilities/initTransaction'
|
||||||
|
import { killTransaction } from '../../utilities/killTransaction'
|
||||||
|
import { buildAfterOperation } from './utils'
|
||||||
|
|
||||||
|
export type Arguments = {
|
||||||
|
collection: Collection
|
||||||
|
disableErrors?: boolean
|
||||||
|
overrideAccess?: boolean
|
||||||
|
req?: PayloadRequest
|
||||||
|
where?: Where
|
||||||
|
}
|
||||||
|
|
||||||
|
async function count<T extends TypeWithID & Record<string, unknown>>(
|
||||||
|
incomingArgs: Arguments,
|
||||||
|
): Promise<{ totalDocs: number }> {
|
||||||
|
let args = incomingArgs
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shouldCommit = await initTransaction(args.req)
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// beforeOperation - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
args =
|
||||||
|
(await hook({
|
||||||
|
args,
|
||||||
|
collection: args.collection.config,
|
||||||
|
context: args.req.context,
|
||||||
|
operation: 'count',
|
||||||
|
req: args.req,
|
||||||
|
})) || args
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
const {
|
||||||
|
collection: { config: collectionConfig },
|
||||||
|
disableErrors,
|
||||||
|
overrideAccess,
|
||||||
|
req: { payload },
|
||||||
|
req,
|
||||||
|
where,
|
||||||
|
} = args
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Access
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
let accessResult: AccessResult
|
||||||
|
|
||||||
|
if (!overrideAccess) {
|
||||||
|
accessResult = await executeAccess({ disableErrors, req }, collectionConfig.access.read)
|
||||||
|
|
||||||
|
// If errors are disabled, and access returns false, return empty results
|
||||||
|
if (accessResult === false) {
|
||||||
|
return {
|
||||||
|
totalDocs: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: { totalDocs: number }
|
||||||
|
|
||||||
|
const fullWhere = combineQueries(where, accessResult)
|
||||||
|
|
||||||
|
await validateQueryPaths({
|
||||||
|
collectionConfig,
|
||||||
|
overrideAccess,
|
||||||
|
req,
|
||||||
|
where,
|
||||||
|
})
|
||||||
|
|
||||||
|
result = await payload.db.count({
|
||||||
|
collection: collectionConfig.slug,
|
||||||
|
req,
|
||||||
|
where: fullWhere,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// afterOperation - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
result = await buildAfterOperation<T>({
|
||||||
|
args,
|
||||||
|
collection: collectionConfig,
|
||||||
|
operation: 'count',
|
||||||
|
result,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Return results
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
if (shouldCommit) await commitTransaction(req)
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (error: unknown) {
|
||||||
|
await killTransaction(args.req)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default count
|
||||||
47
packages/payload/src/collections/operations/local/count.ts
Normal file
47
packages/payload/src/collections/operations/local/count.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import type { GeneratedTypes } from '../../../'
|
||||||
|
import type { PayloadRequest, RequestContext } from '../../../express/types'
|
||||||
|
import type { Payload } from '../../../payload'
|
||||||
|
import type { Document, Where } from '../../../types'
|
||||||
|
|
||||||
|
import { APIError } from '../../../errors'
|
||||||
|
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||||
|
import count from '../count'
|
||||||
|
|
||||||
|
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||||
|
collection: T
|
||||||
|
/**
|
||||||
|
* context, which will then be passed to req.context, which can be read by hooks
|
||||||
|
*/
|
||||||
|
context?: RequestContext
|
||||||
|
disableErrors?: boolean
|
||||||
|
locale?: string
|
||||||
|
overrideAccess?: boolean
|
||||||
|
req?: PayloadRequest
|
||||||
|
user?: Document
|
||||||
|
where?: Where
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function countLocal<T extends keyof GeneratedTypes['collections']>(
|
||||||
|
payload: Payload,
|
||||||
|
options: Options<T>,
|
||||||
|
): Promise<{ totalDocs: number }> {
|
||||||
|
const { collection: collectionSlug, disableErrors, overrideAccess = true, where } = options
|
||||||
|
|
||||||
|
const collection = payload.collections[collectionSlug]
|
||||||
|
|
||||||
|
if (!collection) {
|
||||||
|
throw new APIError(
|
||||||
|
`The collection with slug ${String(collectionSlug)} can't be found. Find Operation.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = createLocalReq(options, payload)
|
||||||
|
|
||||||
|
return count<GeneratedTypes['collections'][T]>({
|
||||||
|
collection,
|
||||||
|
disableErrors,
|
||||||
|
overrideAccess,
|
||||||
|
req,
|
||||||
|
where,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import auth from '../../../auth/operations/local'
|
import auth from '../../../auth/operations/local'
|
||||||
|
import count from './count'
|
||||||
import create from './create'
|
import create from './create'
|
||||||
import deleteLocal from './delete'
|
import deleteLocal from './delete'
|
||||||
import find from './find'
|
import find from './find'
|
||||||
@@ -10,6 +11,7 @@ import update from './update'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
auth,
|
auth,
|
||||||
|
count,
|
||||||
create,
|
create,
|
||||||
deleteLocal,
|
deleteLocal,
|
||||||
find,
|
find,
|
||||||
|
|||||||
@@ -268,14 +268,15 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
global: null,
|
global: null,
|
||||||
operation: 'update',
|
operation: 'update',
|
||||||
req,
|
req,
|
||||||
skipValidation: shouldSaveDraft || data._status === 'draft',
|
skipValidation:
|
||||||
|
Boolean(collectionConfig.versions?.drafts) && data._status !== 'published',
|
||||||
})
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Update
|
// Update
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
if (!shouldSaveDraft) {
|
if (!shouldSaveDraft || data._status === 'published') {
|
||||||
result = await req.payload.db.updateOne({
|
result = await req.payload.db.updateOne({
|
||||||
id,
|
id,
|
||||||
collection: collectionConfig.slug,
|
collection: collectionConfig.slug,
|
||||||
@@ -297,7 +298,6 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
...result,
|
...result,
|
||||||
createdAt: doc.createdAt,
|
createdAt: doc.createdAt,
|
||||||
},
|
},
|
||||||
draft: shouldSaveDraft,
|
|
||||||
payload,
|
payload,
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
global: null,
|
global: null,
|
||||||
operation: 'update',
|
operation: 'update',
|
||||||
req,
|
req,
|
||||||
skipValidation: shouldSaveDraft || data._status === 'draft',
|
skipValidation: Boolean(collectionConfig.versions?.drafts) && data._status !== 'published',
|
||||||
})
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
@@ -262,7 +262,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Update
|
// Update
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
if (!shouldSaveDraft) {
|
if (!shouldSaveDraft || data._status === 'published') {
|
||||||
result = await req.payload.db.updateOne({
|
result = await req.payload.db.updateOne({
|
||||||
id,
|
id,
|
||||||
collection: collectionConfig.slug,
|
collection: collectionConfig.slug,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type login from '../../auth/operations/login'
|
|||||||
import type refresh from '../../auth/operations/refresh'
|
import type refresh from '../../auth/operations/refresh'
|
||||||
import type { PayloadRequest } from '../../express/types'
|
import type { PayloadRequest } from '../../express/types'
|
||||||
import type { AfterOperationHook, SanitizedCollectionConfig, TypeWithID } from '../config/types'
|
import type { AfterOperationHook, SanitizedCollectionConfig, TypeWithID } from '../config/types'
|
||||||
|
import type countOperation from './count'
|
||||||
import type create from './create'
|
import type create from './create'
|
||||||
import type deleteOperation from './delete'
|
import type deleteOperation from './delete'
|
||||||
import type deleteByID from './deleteByID'
|
import type deleteByID from './deleteByID'
|
||||||
@@ -12,6 +13,7 @@ import type update from './update'
|
|||||||
import type updateByID from './updateByID'
|
import type updateByID from './updateByID'
|
||||||
|
|
||||||
export type AfterOperationMap<T extends TypeWithID> = {
|
export type AfterOperationMap<T extends TypeWithID> = {
|
||||||
|
count: typeof countOperation
|
||||||
create: typeof create // todo: pass correct generic
|
create: typeof create // todo: pass correct generic
|
||||||
delete: typeof deleteOperation // todo: pass correct generic
|
delete: typeof deleteOperation // todo: pass correct generic
|
||||||
deleteByID: typeof deleteByID // todo: pass correct generic
|
deleteByID: typeof deleteByID // todo: pass correct generic
|
||||||
@@ -28,6 +30,11 @@ export type AfterOperationArg<T extends TypeWithID> = {
|
|||||||
collection: SanitizedCollectionConfig
|
collection: SanitizedCollectionConfig
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
} & (
|
} & (
|
||||||
|
| {
|
||||||
|
args: Parameters<AfterOperationMap<T>['count']>[0]
|
||||||
|
operation: 'count'
|
||||||
|
result: Awaited<ReturnType<AfterOperationMap<T>['count']>>
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
args: Parameters<AfterOperationMap<T>['create']>[0]
|
args: Parameters<AfterOperationMap<T>['create']>[0]
|
||||||
operation: 'create'
|
operation: 'create'
|
||||||
|
|||||||
26
packages/payload/src/collections/requestHandlers/count.ts
Normal file
26
packages/payload/src/collections/requestHandlers/count.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import type { NextFunction, Response } from 'express'
|
||||||
|
|
||||||
|
import httpStatus from 'http-status'
|
||||||
|
|
||||||
|
import type { PayloadRequest } from '../../express/types'
|
||||||
|
import type { Where } from '../../types'
|
||||||
|
|
||||||
|
import count from '../operations/count'
|
||||||
|
|
||||||
|
export default async function countHandler(
|
||||||
|
req: PayloadRequest,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction,
|
||||||
|
): Promise<Response<{ totalDocs: number }> | void> {
|
||||||
|
try {
|
||||||
|
const result = await count({
|
||||||
|
collection: req.collection,
|
||||||
|
req,
|
||||||
|
where: req.query.where as Where, // This is a little shady
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.status(httpStatus.OK).json(result)
|
||||||
|
} catch (error) {
|
||||||
|
return next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import type { PayloadRequest } from '../../express/types'
|
|||||||
import type { Document } from '../../types'
|
import type { Document } from '../../types'
|
||||||
|
|
||||||
import { NotFound } from '../../errors'
|
import { NotFound } from '../../errors'
|
||||||
|
import { sanitizeCollectionID } from '../../utilities/sanitizeCollectionID'
|
||||||
import deleteByID from '../operations/deleteByID'
|
import deleteByID from '../operations/deleteByID'
|
||||||
|
|
||||||
export type DeleteResult = {
|
export type DeleteResult = {
|
||||||
@@ -18,9 +19,15 @@ export default async function deleteByIDHandler(
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
): Promise<Response<DeleteResult> | void> {
|
): Promise<Response<DeleteResult> | void> {
|
||||||
|
const id = sanitizeCollectionID({
|
||||||
|
id: req.params.id,
|
||||||
|
collectionSlug: req.collection.config.slug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const doc = await deleteByID({
|
const doc = await deleteByID({
|
||||||
id: req.params.id,
|
id,
|
||||||
collection: req.collection,
|
collection: req.collection,
|
||||||
depth: parseInt(String(req.query.depth), 10),
|
depth: parseInt(String(req.query.depth), 10),
|
||||||
req,
|
req,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { NextFunction, Response } from 'express'
|
|||||||
import type { PayloadRequest } from '../../express/types'
|
import type { PayloadRequest } from '../../express/types'
|
||||||
import type { Document } from '../../types'
|
import type { Document } from '../../types'
|
||||||
|
|
||||||
|
import { sanitizeCollectionID } from '../../utilities/sanitizeCollectionID'
|
||||||
import findByID from '../operations/findByID'
|
import findByID from '../operations/findByID'
|
||||||
|
|
||||||
export type FindByIDResult = {
|
export type FindByIDResult = {
|
||||||
@@ -15,9 +16,15 @@ export default async function findByIDHandler(
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
): Promise<Response<FindByIDResult> | void> {
|
): Promise<Response<FindByIDResult> | void> {
|
||||||
|
const id = sanitizeCollectionID({
|
||||||
|
id: req.params.id,
|
||||||
|
collectionSlug: req.collection.config.slug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const doc = await findByID({
|
const doc = await findByID({
|
||||||
id: req.params.id,
|
id,
|
||||||
collection: req.collection,
|
collection: req.collection,
|
||||||
depth: Number(req.query.depth),
|
depth: Number(req.query.depth),
|
||||||
draft: req.query.draft === 'true',
|
draft: req.query.draft === 'true',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { NextFunction, Response } from 'express'
|
|||||||
import type { PayloadRequest } from '../../express/types'
|
import type { PayloadRequest } from '../../express/types'
|
||||||
import type { Document } from '../../types'
|
import type { Document } from '../../types'
|
||||||
|
|
||||||
|
import { sanitizeCollectionID } from '../../utilities/sanitizeCollectionID'
|
||||||
import findVersionByID from '../operations/findVersionByID'
|
import findVersionByID from '../operations/findVersionByID'
|
||||||
|
|
||||||
export type FindByIDResult = {
|
export type FindByIDResult = {
|
||||||
@@ -15,8 +16,14 @@ export default async function findVersionByIDHandler(
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
): Promise<Response<FindByIDResult> | void> {
|
): Promise<Response<FindByIDResult> | void> {
|
||||||
const options = {
|
const id = sanitizeCollectionID({
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
|
collectionSlug: req.collection.config.slug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
id,
|
||||||
collection: req.collection,
|
collection: req.collection,
|
||||||
depth: parseInt(String(req.query.depth), 10),
|
depth: parseInt(String(req.query.depth), 10),
|
||||||
payload: req.payload,
|
payload: req.payload,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { PayloadRequest } from '../../express/types'
|
|||||||
import type { Document } from '../../types'
|
import type { Document } from '../../types'
|
||||||
|
|
||||||
import formatSuccessResponse from '../../express/responses/formatSuccess'
|
import formatSuccessResponse from '../../express/responses/formatSuccess'
|
||||||
|
import { sanitizeCollectionID } from '../../utilities/sanitizeCollectionID'
|
||||||
import restoreVersion from '../operations/restoreVersion'
|
import restoreVersion from '../operations/restoreVersion'
|
||||||
|
|
||||||
export type RestoreResult = {
|
export type RestoreResult = {
|
||||||
@@ -18,8 +19,14 @@ export default async function restoreVersionHandler(
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
): Promise<Response<RestoreResult> | void> {
|
): Promise<Response<RestoreResult> | void> {
|
||||||
const options = {
|
const id = sanitizeCollectionID({
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
|
collectionSlug: req.collection.config.slug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
id,
|
||||||
collection: req.collection,
|
collection: req.collection,
|
||||||
depth: Number(req.query.depth),
|
depth: Number(req.query.depth),
|
||||||
payload: req.payload,
|
payload: req.payload,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import httpStatus from 'http-status'
|
|||||||
import type { PayloadRequest } from '../../express/types'
|
import type { PayloadRequest } from '../../express/types'
|
||||||
|
|
||||||
import formatSuccessResponse from '../../express/responses/formatSuccess'
|
import formatSuccessResponse from '../../express/responses/formatSuccess'
|
||||||
|
import { sanitizeCollectionID } from '../../utilities/sanitizeCollectionID'
|
||||||
import updateByID from '../operations/updateByID'
|
import updateByID from '../operations/updateByID'
|
||||||
|
|
||||||
export type UpdateResult = {
|
export type UpdateResult = {
|
||||||
@@ -29,12 +30,18 @@ export default async function updateByIDHandler(
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
): Promise<Response<UpdateResult> | void> {
|
): Promise<Response<UpdateResult> | void> {
|
||||||
|
const id = sanitizeCollectionID({
|
||||||
|
id: req.params.id,
|
||||||
|
collectionSlug: req.collection.config.slug,
|
||||||
|
payload: req.payload,
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const draft = req.query.draft === 'true'
|
const draft = req.query.draft === 'true'
|
||||||
const autosave = req.query.autosave === 'true'
|
const autosave = req.query.autosave === 'true'
|
||||||
|
|
||||||
const doc = await updateByID({
|
const doc = await updateByID({
|
||||||
id: req.params.id,
|
id,
|
||||||
autosave,
|
autosave,
|
||||||
collection: req.collection,
|
collection: req.collection,
|
||||||
data: req.body,
|
data: req.body,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export interface BaseDatabaseAdapter {
|
|||||||
*/
|
*/
|
||||||
connect?: Connect
|
connect?: Connect
|
||||||
|
|
||||||
|
count: Count
|
||||||
|
|
||||||
create: Create
|
create: Create
|
||||||
|
|
||||||
createGlobal: CreateGlobal
|
createGlobal: CreateGlobal
|
||||||
@@ -197,6 +199,15 @@ export type FindArgs = {
|
|||||||
|
|
||||||
export type Find = <T = TypeWithID>(args: FindArgs) => Promise<PaginatedDocs<T>>
|
export type Find = <T = TypeWithID>(args: FindArgs) => Promise<PaginatedDocs<T>>
|
||||||
|
|
||||||
|
export type CountArgs = {
|
||||||
|
collection: string
|
||||||
|
locale?: string
|
||||||
|
req: PayloadRequest
|
||||||
|
where?: Where
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Count = (args: CountArgs) => Promise<{ totalDocs: number }>
|
||||||
|
|
||||||
type BaseVersionArgs = {
|
type BaseVersionArgs = {
|
||||||
limit?: number
|
limit?: number
|
||||||
locale?: string
|
locale?: string
|
||||||
|
|||||||
18
packages/payload/src/errors/FileRetrievalError.ts
Normal file
18
packages/payload/src/errors/FileRetrievalError.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { TFunction } from 'i18next'
|
||||||
|
|
||||||
|
import httpStatus from 'http-status'
|
||||||
|
|
||||||
|
import APIError from './APIError'
|
||||||
|
|
||||||
|
class FileRetrievalError extends APIError {
|
||||||
|
constructor(t?: TFunction, message?: string) {
|
||||||
|
let msg = t ? t('error:problemUploadingFile') : 'There was a problem while retrieving the file.'
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
msg += ` ${message}`
|
||||||
|
}
|
||||||
|
super(msg, httpStatus.BAD_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileRetrievalError
|
||||||
16
packages/payload/src/errors/MissingEditorProps.ts
Normal file
16
packages/payload/src/errors/MissingEditorProps.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Field } from '../fields/config/types'
|
||||||
|
|
||||||
|
import { fieldAffectsData } from '../fields/config/types'
|
||||||
|
import APIError from './APIError'
|
||||||
|
|
||||||
|
class MissingEditorProp extends APIError {
|
||||||
|
constructor(field: Field) {
|
||||||
|
super(
|
||||||
|
`RichText field${
|
||||||
|
fieldAffectsData(field) ? ` "${field.name}"` : ''
|
||||||
|
} is missing the editor prop`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MissingEditorProp
|
||||||
@@ -3,6 +3,8 @@ export {
|
|||||||
BeginTransaction,
|
BeginTransaction,
|
||||||
CommitTransaction,
|
CommitTransaction,
|
||||||
Connect,
|
Connect,
|
||||||
|
Count,
|
||||||
|
CountArgs,
|
||||||
Create,
|
Create,
|
||||||
CreateArgs,
|
CreateArgs,
|
||||||
CreateGlobal,
|
CreateGlobal,
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export type {
|
|||||||
FieldWithMany,
|
FieldWithMany,
|
||||||
FieldWithMaxDepth,
|
FieldWithMaxDepth,
|
||||||
FieldWithPath,
|
FieldWithPath,
|
||||||
|
FieldWithRichTextRequiredEditor,
|
||||||
FieldWithSubFields,
|
FieldWithSubFields,
|
||||||
FilterOptions,
|
FilterOptions,
|
||||||
FilterOptionsProps,
|
FilterOptionsProps,
|
||||||
@@ -86,6 +87,7 @@ export type {
|
|||||||
RelationshipField,
|
RelationshipField,
|
||||||
RelationshipValue,
|
RelationshipValue,
|
||||||
RichTextField,
|
RichTextField,
|
||||||
|
RichTextFieldRequiredEditor,
|
||||||
RowAdmin,
|
RowAdmin,
|
||||||
RowField,
|
RowField,
|
||||||
SelectField,
|
SelectField,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
InvalidFieldRelationship,
|
InvalidFieldRelationship,
|
||||||
MissingFieldType,
|
MissingFieldType,
|
||||||
} from '../../errors'
|
} from '../../errors'
|
||||||
|
import MissingEditorProp from '../../errors/MissingEditorProps'
|
||||||
import { formatLabels, toWords } from '../../utilities/formatLabels'
|
import { formatLabels, toWords } from '../../utilities/formatLabels'
|
||||||
import { baseBlockFields } from '../baseFields/baseBlockFields'
|
import { baseBlockFields } from '../baseFields/baseBlockFields'
|
||||||
import { baseIDField } from '../baseFields/baseIDField'
|
import { baseIDField } from '../baseFields/baseIDField'
|
||||||
@@ -18,6 +19,12 @@ type Args = {
|
|||||||
config: Config
|
config: Config
|
||||||
existingFieldNames?: Set<string>
|
existingFieldNames?: Set<string>
|
||||||
fields: Field[]
|
fields: Field[]
|
||||||
|
/**
|
||||||
|
* If true, a richText field will require an editor property to be set, as the sanitizeFields function will not add it from the payload config if not present.
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
requireFieldLevelRichTextEditor?: boolean
|
||||||
/**
|
/**
|
||||||
* If not null, will validate that upload and relationship fields do not relate to a collection that is not in this array.
|
* If not null, will validate that upload and relationship fields do not relate to a collection that is not in this array.
|
||||||
* This validation will be skipped if validRelationships is null.
|
* This validation will be skipped if validRelationships is null.
|
||||||
@@ -29,6 +36,7 @@ export const sanitizeFields = ({
|
|||||||
config,
|
config,
|
||||||
existingFieldNames = new Set(),
|
existingFieldNames = new Set(),
|
||||||
fields,
|
fields,
|
||||||
|
requireFieldLevelRichTextEditor = false,
|
||||||
validRelationships,
|
validRelationships,
|
||||||
}: Args): Field[] => {
|
}: Args): Field[] => {
|
||||||
if (!fields) return []
|
if (!fields) return []
|
||||||
@@ -44,8 +52,12 @@ export const sanitizeFields = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the richText field has an editor
|
// Make sure that the richText field has an editor
|
||||||
if (field.type === 'richText' && !field.editor && config.editor) {
|
if (field.type === 'richText' && !field.editor) {
|
||||||
field.editor = config.editor
|
if (config.editor && !requireFieldLevelRichTextEditor) {
|
||||||
|
field.editor = config.editor
|
||||||
|
} else {
|
||||||
|
throw new MissingEditorProp(field)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-label
|
// Auto-label
|
||||||
@@ -113,7 +125,7 @@ export const sanitizeFields = ({
|
|||||||
if (fieldAffectsData(field)) {
|
if (fieldAffectsData(field)) {
|
||||||
if (existingFieldNames.has(field.name)) {
|
if (existingFieldNames.has(field.name)) {
|
||||||
throw new DuplicateFieldName(field.name)
|
throw new DuplicateFieldName(field.name)
|
||||||
} else if (!['id', 'blockName'].includes(field.name)) {
|
} else if (!['blockName', 'id'].includes(field.name)) {
|
||||||
existingFieldNames.add(field.name)
|
existingFieldNames.add(field.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +157,7 @@ export const sanitizeFields = ({
|
|||||||
config,
|
config,
|
||||||
existingFieldNames: fieldAffectsData(field) ? new Set() : existingFieldNames,
|
existingFieldNames: fieldAffectsData(field) ? new Set() : existingFieldNames,
|
||||||
fields: field.fields,
|
fields: field.fields,
|
||||||
|
requireFieldLevelRichTextEditor,
|
||||||
validRelationships,
|
validRelationships,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -160,6 +173,7 @@ export const sanitizeFields = ({
|
|||||||
config,
|
config,
|
||||||
existingFieldNames: tabHasName(tab) ? new Set() : existingFieldNames,
|
existingFieldNames: tabHasName(tab) ? new Set() : existingFieldNames,
|
||||||
fields: tab.fields,
|
fields: tab.fields,
|
||||||
|
requireFieldLevelRichTextEditor,
|
||||||
validRelationships,
|
validRelationships,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -176,9 +190,10 @@ export const sanitizeFields = ({
|
|||||||
|
|
||||||
unsanitizedBlock.fields = sanitizeFields({
|
unsanitizedBlock.fields = sanitizeFields({
|
||||||
config,
|
config,
|
||||||
fields: block.fields,
|
|
||||||
validRelationships,
|
|
||||||
existingFieldNames: new Set(),
|
existingFieldNames: new Set(),
|
||||||
|
fields: block.fields,
|
||||||
|
requireFieldLevelRichTextEditor,
|
||||||
|
validRelationships,
|
||||||
})
|
})
|
||||||
|
|
||||||
return unsanitizedBlock
|
return unsanitizedBlock
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ export const json = baseField.keys({
|
|||||||
editorOptions: joi.object().unknown(), // Editor['options'] @monaco-editor/react
|
editorOptions: joi.object().unknown(), // Editor['options'] @monaco-editor/react
|
||||||
}),
|
}),
|
||||||
defaultValue: joi.alternatives().try(joi.array(), joi.object()),
|
defaultValue: joi.alternatives().try(joi.array(), joi.object()),
|
||||||
|
jsonSchema: joi.object().unknown(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const select = baseField.keys({
|
export const select = baseField.keys({
|
||||||
|
|||||||
@@ -436,6 +436,7 @@ type JSONAdmin = Admin & {
|
|||||||
|
|
||||||
export type JSONField = Omit<FieldBase, 'admin'> & {
|
export type JSONField = Omit<FieldBase, 'admin'> & {
|
||||||
admin?: JSONAdmin
|
admin?: JSONAdmin
|
||||||
|
jsonSchema?: Record<string, unknown>
|
||||||
type: 'json'
|
type: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,6 +550,14 @@ export type RichTextField<
|
|||||||
type: 'richText'
|
type: 'richText'
|
||||||
} & ExtraProperties
|
} & ExtraProperties
|
||||||
|
|
||||||
|
export type RichTextFieldRequiredEditor<
|
||||||
|
Value extends object = any,
|
||||||
|
AdapterProps = any,
|
||||||
|
ExtraProperties = object,
|
||||||
|
> = Omit<RichTextField<Value, AdapterProps, ExtraProperties>, 'editor'> & {
|
||||||
|
editor: RichTextAdapter<Value, AdapterProps, ExtraProperties>
|
||||||
|
}
|
||||||
|
|
||||||
export type ArrayField = FieldBase & {
|
export type ArrayField = FieldBase & {
|
||||||
admin?: Admin & {
|
admin?: Admin & {
|
||||||
components?: {
|
components?: {
|
||||||
@@ -597,6 +606,10 @@ export type RadioField = FieldBase & {
|
|||||||
export type Block = {
|
export type Block = {
|
||||||
/** Extension point to add your custom data. */
|
/** Extension point to add your custom data. */
|
||||||
custom?: Record<string, any>
|
custom?: Record<string, any>
|
||||||
|
/**
|
||||||
|
* Customize the SQL table name
|
||||||
|
*/
|
||||||
|
dbName?: DBIdentifierName
|
||||||
fields: Field[]
|
fields: Field[]
|
||||||
/** @deprecated - please migrate to the interfaceName property instead. */
|
/** @deprecated - please migrate to the interfaceName property instead. */
|
||||||
graphQL?: {
|
graphQL?: {
|
||||||
@@ -654,6 +667,10 @@ export type Field =
|
|||||||
| UIField
|
| UIField
|
||||||
| UploadField
|
| UploadField
|
||||||
|
|
||||||
|
export type FieldWithRichTextRequiredEditor =
|
||||||
|
| Exclude<Field, RichTextField>
|
||||||
|
| RichTextFieldRequiredEditor
|
||||||
|
|
||||||
export type FieldAffectingData =
|
export type FieldAffectingData =
|
||||||
| ArrayField
|
| ArrayField
|
||||||
| BlockField
|
| BlockField
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Ajv from 'ajv'
|
||||||
import type { RichTextAdapter } from '../exports/types'
|
import type { RichTextAdapter } from '../exports/types'
|
||||||
import type {
|
import type {
|
||||||
ArrayField,
|
ArrayField,
|
||||||
@@ -130,9 +131,9 @@ export const code: Validate<unknown, unknown, CodeField> = (value: string, { req
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export const json: Validate<unknown, unknown, JSONField & { jsonError?: string }> = (
|
export const json: Validate<unknown, unknown, JSONField & { jsonError?: string }> = async (
|
||||||
value: string,
|
value: string,
|
||||||
{ jsonError, required, t },
|
{ jsonError, jsonSchema, required, t },
|
||||||
) => {
|
) => {
|
||||||
if (required && !value) {
|
if (required && !value) {
|
||||||
return t('validation:required')
|
return t('validation:required')
|
||||||
@@ -142,6 +143,55 @@ export const json: Validate<unknown, unknown, JSONField & { jsonError?: string }
|
|||||||
return t('validation:invalidInput')
|
return t('validation:invalidInput')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isNotEmpty = (value) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value) && value.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object' && Object.keys(value).length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchSchema = ({ uri, schema }) => {
|
||||||
|
if (uri && schema) return schema
|
||||||
|
return fetch(uri)
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((json) => {
|
||||||
|
const jsonSchemaSanitizations = {
|
||||||
|
id: undefined,
|
||||||
|
$id: json.id,
|
||||||
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||||
|
}
|
||||||
|
return Object.assign(json, jsonSchemaSanitizations)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canUseDOM && jsonSchema && isNotEmpty(value)) {
|
||||||
|
try {
|
||||||
|
jsonSchema.schema = await fetchSchema(jsonSchema)
|
||||||
|
const { schema } = jsonSchema
|
||||||
|
const ajv = new Ajv()
|
||||||
|
|
||||||
|
if (!ajv.validate(schema, value)) {
|
||||||
|
return t(ajv.errorsText())
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return t(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import type { Options as VerifyEmailOptions } from './auth/operations/local/veri
|
|||||||
import type { Result as LoginResult } from './auth/operations/login'
|
import type { Result as LoginResult } from './auth/operations/login'
|
||||||
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
|
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
|
||||||
import type { BulkOperationResult, Collection } from './collections/config/types'
|
import type { BulkOperationResult, Collection } from './collections/config/types'
|
||||||
|
import type { Options as CountOptions } from './collections/operations/local/count'
|
||||||
import type { Options as CreateOptions } from './collections/operations/local/create'
|
import type { Options as CreateOptions } from './collections/operations/local/create'
|
||||||
import type {
|
import type {
|
||||||
ByIDOptions as DeleteByIDOptions,
|
ByIDOptions as DeleteByIDOptions,
|
||||||
@@ -74,6 +75,18 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
|||||||
|
|
||||||
config: SanitizedConfig
|
config: SanitizedConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Performs count operation
|
||||||
|
* @param options
|
||||||
|
* @returns count of documents satisfying query
|
||||||
|
*/
|
||||||
|
count = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: CountOptions<T>,
|
||||||
|
): Promise<{ totalDocs: number }> => {
|
||||||
|
const { count } = localOperations
|
||||||
|
return count(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Performs create operation
|
* @description Performs create operation
|
||||||
* @param options
|
* @param options
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "نوع ملف غير صالح",
|
"invalidFileType": "نوع ملف غير صالح",
|
||||||
"invalidFileTypeValue": "نوع ملف غير صالح: {{value}}",
|
"invalidFileTypeValue": "نوع ملف غير صالح: {{value}}",
|
||||||
"loadingDocument": "حدثت مشكلة أثناء تحميل المستند برقم التعريف {{id}}.",
|
"loadingDocument": "حدثت مشكلة أثناء تحميل المستند برقم التعريف {{id}}.",
|
||||||
|
"localesNotSaved_one": "لم يتم حفظ اللغة التالية:",
|
||||||
|
"localesNotSaved_other": "لم يتم حفظ اللغات التالية:",
|
||||||
"missingEmail": "البريد الإلكتروني مفقود.",
|
"missingEmail": "البريد الإلكتروني مفقود.",
|
||||||
"missingIDOfDocument": "معرّف المستند المراد تحديثه مفقود.",
|
"missingIDOfDocument": "معرّف المستند المراد تحديثه مفقود.",
|
||||||
"missingIDOfVersion": "معرّف النسخة مفقود.",
|
"missingIDOfVersion": "معرّف النسخة مفقود.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "يتمّ الحذف...",
|
"deleting": "يتمّ الحذف...",
|
||||||
"descending": "تنازلي",
|
"descending": "تنازلي",
|
||||||
"deselectAllRows": "إلغاء تحديد جميع الصفوف",
|
"deselectAllRows": "إلغاء تحديد جميع الصفوف",
|
||||||
|
"document": "وثيقة",
|
||||||
|
"documents": "وثائق",
|
||||||
"duplicate": "استنساخ",
|
"duplicate": "استنساخ",
|
||||||
"duplicateWithoutSaving": "استنساخ بدون حفظ التغييرات",
|
"duplicateWithoutSaving": "استنساخ بدون حفظ التغييرات",
|
||||||
"edit": "تعديل",
|
"edit": "تعديل",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "يتمّ استعراض النُّسَخ ل {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "يتمّ استعراض النُّسَخ ل {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "يتمّ استعراض النُّسَخ للاعداد العامّ {{entityLabel}}"
|
"viewingVersionsGlobal": "يتمّ استعراض النُّسَخ للاعداد العامّ {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,13 +63,14 @@
|
|||||||
"deletingFile": "Faylın silinməsində xəta baş verdi.",
|
"deletingFile": "Faylın silinməsində xəta baş verdi.",
|
||||||
"deletingTitle": "{{title}} silinərkən xəta baş verdi. Zəhmət olmasa, bağlantınızı yoxlayın və yenidən cəhd edin.",
|
"deletingTitle": "{{title}} silinərkən xəta baş verdi. Zəhmət olmasa, bağlantınızı yoxlayın və yenidən cəhd edin.",
|
||||||
"emailOrPasswordIncorrect": "Təqdim olunan e-poçt və ya şifrə yanlışdır.",
|
"emailOrPasswordIncorrect": "Təqdim olunan e-poçt və ya şifrə yanlışdır.",
|
||||||
"followingFieldsInvalid_many": "Aşağıdakı sahələr yanlışdır:",
|
|
||||||
"followingFieldsInvalid_one": "Aşağıdakı sahə yanlışdır:",
|
"followingFieldsInvalid_one": "Aşağıdakı sahə yanlışdır:",
|
||||||
"followingFieldsInvalid_other": "Aşağıdaki sahələr yanlışdır:",
|
"followingFieldsInvalid_other": "Aşağıdaki sahələr yanlışdır:",
|
||||||
"incorrectCollection": "Yanlış Kolleksiya",
|
"incorrectCollection": "Yanlış Kolleksiya",
|
||||||
"invalidFileType": "Yanlış fayl növü",
|
"invalidFileType": "Yanlış fayl növü",
|
||||||
"invalidFileTypeValue": "Yanlış fayl növü: {{value}}",
|
"invalidFileTypeValue": "Yanlış fayl növü: {{value}}",
|
||||||
"loadingDocument": "{{id}} ID-li sənədin yüklənməsində problem baş verdi.",
|
"loadingDocument": "{{id}} ID-li sənədin yüklənməsində problem baş verdi.",
|
||||||
|
"localesNotSaved_one": "Aşağıdakı yerləşdirmə saxlanıla bilmədi:",
|
||||||
|
"localesNotSaved_other": "Aşağıdakı yerləşdirmələr saxlanıla bilmədi:",
|
||||||
"missingEmail": "E-poçt adresi çatışmır.",
|
"missingEmail": "E-poçt adresi çatışmır.",
|
||||||
"missingIDOfDocument": "Yeniləmək üçün sənədin ID-si çatışmır.",
|
"missingIDOfDocument": "Yeniləmək üçün sənədin ID-si çatışmır.",
|
||||||
"missingIDOfVersion": "Versiyanın ID-si çatışmır.",
|
"missingIDOfVersion": "Versiyanın ID-si çatışmır.",
|
||||||
@@ -178,6 +179,8 @@
|
|||||||
"deleting": "Silinir...",
|
"deleting": "Silinir...",
|
||||||
"descending": "Azalan",
|
"descending": "Azalan",
|
||||||
"deselectAllRows": "Bütün sıraları seçimi ləğv edin",
|
"deselectAllRows": "Bütün sıraları seçimi ləğv edin",
|
||||||
|
"document": "Sənəd",
|
||||||
|
"documents": "Sənədlər",
|
||||||
"duplicate": "Dublikat",
|
"duplicate": "Dublikat",
|
||||||
"duplicateWithoutSaving": "Dəyişiklikləri saxlamadan dublikatla",
|
"duplicateWithoutSaving": "Dəyişiklikləri saxlamadan dublikatla",
|
||||||
"edit": "Redaktə et",
|
"edit": "Redaktə et",
|
||||||
@@ -370,4 +373,4 @@
|
|||||||
"viewingVersions": "{{entityLabel}} {{documentTitle}} üçün versiyaları göstərir",
|
"viewingVersions": "{{entityLabel}} {{documentTitle}} üçün versiyaları göstərir",
|
||||||
"viewingVersionsGlobal": "Qlobal {{entityLabel}} üçün versiyaları göstərir"
|
"viewingVersionsGlobal": "Qlobal {{entityLabel}} üçün versiyaları göstərir"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Невалиден тип на файл",
|
"invalidFileType": "Невалиден тип на файл",
|
||||||
"invalidFileTypeValue": "Невалиден тип на файл: {{value}}",
|
"invalidFileTypeValue": "Невалиден тип на файл: {{value}}",
|
||||||
"loadingDocument": "Имаше проблем при зареждането на документа с идентификатор {{id}}.",
|
"loadingDocument": "Имаше проблем при зареждането на документа с идентификатор {{id}}.",
|
||||||
|
"localesNotSaved_one": "Следният локал не може да бъде запазен:",
|
||||||
|
"localesNotSaved_other": "Следните локали не може да бъдат запазени:",
|
||||||
"missingEmail": "Липсващ имейл.",
|
"missingEmail": "Липсващ имейл.",
|
||||||
"missingIDOfDocument": "Липсващ идентификатор на документа за обновяване.",
|
"missingIDOfDocument": "Липсващ идентификатор на документа за обновяване.",
|
||||||
"missingIDOfVersion": "Липсващ идентификатор на версия.",
|
"missingIDOfVersion": "Липсващ идентификатор на версия.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Изтриване...",
|
"deleting": "Изтриване...",
|
||||||
"descending": "Низходящо",
|
"descending": "Низходящо",
|
||||||
"deselectAllRows": "Деселектирай всички редове",
|
"deselectAllRows": "Деселектирай всички редове",
|
||||||
|
"document": "Документ",
|
||||||
|
"documents": "Документи",
|
||||||
"duplicate": "Дупликирай",
|
"duplicate": "Дупликирай",
|
||||||
"duplicateWithoutSaving": "Дупликирай без да запазваш промените",
|
"duplicateWithoutSaving": "Дупликирай без да запазваш промените",
|
||||||
"edit": "Редактирай",
|
"edit": "Редактирай",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Гледане на версии за {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Гледане на версии за {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Гледане на версии за глобалния документ {{entityLabel}}"
|
"viewingVersionsGlobal": "Гледане на версии за глобалния документ {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Neplatný typ souboru",
|
"invalidFileType": "Neplatný typ souboru",
|
||||||
"invalidFileTypeValue": "Neplatný typ souboru: {{value}}",
|
"invalidFileTypeValue": "Neplatný typ souboru: {{value}}",
|
||||||
"loadingDocument": "Při načítání dokumentu s ID {{id}} došlo k chybě.",
|
"loadingDocument": "Při načítání dokumentu s ID {{id}} došlo k chybě.",
|
||||||
|
"localesNotSaved_one": "Následující lokalitu se nepodařilo uložit:",
|
||||||
|
"localesNotSaved_other": "Následující lokality se nepodařilo uložit:",
|
||||||
"missingEmail": "Chybějící email.",
|
"missingEmail": "Chybějící email.",
|
||||||
"missingIDOfDocument": "Chybějící ID dokumentu pro aktualizaci.",
|
"missingIDOfDocument": "Chybějící ID dokumentu pro aktualizaci.",
|
||||||
"missingIDOfVersion": "Chybějící ID verze.",
|
"missingIDOfVersion": "Chybějící ID verze.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Odstraňování...",
|
"deleting": "Odstraňování...",
|
||||||
"descending": "Sestupně",
|
"descending": "Sestupně",
|
||||||
"deselectAllRows": "Zrušte výběr všech řádků",
|
"deselectAllRows": "Zrušte výběr všech řádků",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumenty",
|
||||||
"duplicate": "Duplikovat",
|
"duplicate": "Duplikovat",
|
||||||
"duplicateWithoutSaving": "Duplikovat bez uložení změn",
|
"duplicateWithoutSaving": "Duplikovat bez uložení změn",
|
||||||
"edit": "Upravit",
|
"edit": "Upravit",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Zobrazuji verze pro {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Zobrazuji verze pro {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Zobrazuji verze pro globální {{entityLabel}}"
|
"viewingVersionsGlobal": "Zobrazuji verze pro globální {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Ungültiger Datei-Typ",
|
"invalidFileType": "Ungültiger Datei-Typ",
|
||||||
"invalidFileTypeValue": "Ungültiger Datei-Typ: {{value}}",
|
"invalidFileTypeValue": "Ungültiger Datei-Typ: {{value}}",
|
||||||
"loadingDocument": "Es gab ein Problem, das Dokument mit der ID {{id}} zu laden.",
|
"loadingDocument": "Es gab ein Problem, das Dokument mit der ID {{id}} zu laden.",
|
||||||
|
"localesNotSaved_one": "Das folgende Gebietsschema konnte nicht gespeichert werden:",
|
||||||
|
"localesNotSaved_other": "Die folgenden Gebietsschemata konnten nicht gespeichert werden:",
|
||||||
"missingEmail": "E-Mail-Adresse fehlt.",
|
"missingEmail": "E-Mail-Adresse fehlt.",
|
||||||
"missingIDOfDocument": "ID des zu speichernden Dokuments fehlt.",
|
"missingIDOfDocument": "ID des zu speichernden Dokuments fehlt.",
|
||||||
"missingIDOfVersion": "ID der Version fehlt.",
|
"missingIDOfVersion": "ID der Version fehlt.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Lösche...",
|
"deleting": "Lösche...",
|
||||||
"descending": "Absteigend",
|
"descending": "Absteigend",
|
||||||
"deselectAllRows": "Alle Zeilen abwählen",
|
"deselectAllRows": "Alle Zeilen abwählen",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumente",
|
||||||
"duplicate": "Duplizieren",
|
"duplicate": "Duplizieren",
|
||||||
"duplicateWithoutSaving": "Dupliziere ohne Änderungen zu speichern",
|
"duplicateWithoutSaving": "Dupliziere ohne Änderungen zu speichern",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Betrachte Versionen für {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Betrachte Versionen für {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "`Betrachte Versionen für das Globale Dokument {{entityLabel}}"
|
"viewingVersionsGlobal": "`Betrachte Versionen für das Globale Dokument {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,8 @@
|
|||||||
"deleting": "Deleting...",
|
"deleting": "Deleting...",
|
||||||
"descending": "Descending",
|
"descending": "Descending",
|
||||||
"deselectAllRows": "Deselect all rows",
|
"deselectAllRows": "Deselect all rows",
|
||||||
|
"document": "Document",
|
||||||
|
"documents": "Documents",
|
||||||
"duplicate": "Duplicate",
|
"duplicate": "Duplicate",
|
||||||
"duplicateWithoutSaving": "Duplicate without saving changes",
|
"duplicateWithoutSaving": "Duplicate without saving changes",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Tipo de archivo inválido",
|
"invalidFileType": "Tipo de archivo inválido",
|
||||||
"invalidFileTypeValue": "Tipo de archivo inválido: {{value}}",
|
"invalidFileTypeValue": "Tipo de archivo inválido: {{value}}",
|
||||||
"loadingDocument": "Ocurrió un problema al cargar el documento con la ID {{id}}.",
|
"loadingDocument": "Ocurrió un problema al cargar el documento con la ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "No se pudo guardar la siguiente configuración regional:",
|
||||||
|
"localesNotSaved_other": "No se pudieron guardar las siguientes configuraciones regionales:",
|
||||||
"missingEmail": "Falta el correo.",
|
"missingEmail": "Falta el correo.",
|
||||||
"missingIDOfDocument": "Falta la ID del documento a actualizar.",
|
"missingIDOfDocument": "Falta la ID del documento a actualizar.",
|
||||||
"missingIDOfVersion": "Falta la ID de la versión.",
|
"missingIDOfVersion": "Falta la ID de la versión.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Eliminando...",
|
"deleting": "Eliminando...",
|
||||||
"descending": "Descendente",
|
"descending": "Descendente",
|
||||||
"deselectAllRows": "Deselecciona todas las filas",
|
"deselectAllRows": "Deselecciona todas las filas",
|
||||||
|
"document": "Documento",
|
||||||
|
"documents": "Documentos",
|
||||||
"duplicate": "Duplicar",
|
"duplicate": "Duplicar",
|
||||||
"duplicateWithoutSaving": "Duplicar sin guardar cambios",
|
"duplicateWithoutSaving": "Duplicar sin guardar cambios",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Viendo versiones para {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Viendo versiones para {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Viendo versiones para el global {{entityLabel}}"
|
"viewingVersionsGlobal": "Viendo versiones para el global {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "نوع رسانه نامعتبر است",
|
"invalidFileType": "نوع رسانه نامعتبر است",
|
||||||
"invalidFileTypeValue": "نوع رسانه نامعتبر: {{value}}",
|
"invalidFileTypeValue": "نوع رسانه نامعتبر: {{value}}",
|
||||||
"loadingDocument": "مشکلی در بارگیری رسانه با شناسه {{id}} پیش آمد.",
|
"loadingDocument": "مشکلی در بارگیری رسانه با شناسه {{id}} پیش آمد.",
|
||||||
|
"localesNotSaved_one": "امکان ذخیرهسازی تنظیمات محلی زیر وجود ندارد:",
|
||||||
|
"localesNotSaved_other": "امکان ذخیرهسازی تنظیمات محلی زیر وجود ندارد:",
|
||||||
"missingEmail": "رایانامه وارد نشده.",
|
"missingEmail": "رایانامه وارد نشده.",
|
||||||
"missingIDOfDocument": "شناسه سند جهت بروزرسانی نامعتبر است.",
|
"missingIDOfDocument": "شناسه سند جهت بروزرسانی نامعتبر است.",
|
||||||
"missingIDOfVersion": "شناسه نگارش وارد نشده.",
|
"missingIDOfVersion": "شناسه نگارش وارد نشده.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "در حال حذف...",
|
"deleting": "در حال حذف...",
|
||||||
"descending": "رو به پایین",
|
"descending": "رو به پایین",
|
||||||
"deselectAllRows": "تمام سطرها را از انتخاب خارج کنید",
|
"deselectAllRows": "تمام سطرها را از انتخاب خارج کنید",
|
||||||
|
"document": "سند",
|
||||||
|
"documents": "اسناد",
|
||||||
"duplicate": "تکراری",
|
"duplicate": "تکراری",
|
||||||
"duplicateWithoutSaving": "رونوشت بدون ذخیره کردن تغییرات",
|
"duplicateWithoutSaving": "رونوشت بدون ذخیره کردن تغییرات",
|
||||||
"edit": "نگارش",
|
"edit": "نگارش",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "مشاهده نگارشها برای {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "مشاهده نگارشها برای {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "مشاهده نگارشهای کلی {{entityLabel}}"
|
"viewingVersionsGlobal": "مشاهده نگارشهای کلی {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Type de fichier invalide",
|
"invalidFileType": "Type de fichier invalide",
|
||||||
"invalidFileTypeValue": "Type de fichier invalide : {{value}}",
|
"invalidFileTypeValue": "Type de fichier invalide : {{value}}",
|
||||||
"loadingDocument": "Un problème est survenu lors du chargement du document qui a pour identifiant {{id}}.",
|
"loadingDocument": "Un problème est survenu lors du chargement du document qui a pour identifiant {{id}}.",
|
||||||
|
"localesNotSaved_one": "Le paramètre régional suivant n'a pas pu être enregistré :",
|
||||||
|
"localesNotSaved_other": "Les paramètres régionaux suivants n'ont pas pu être enregistrés :",
|
||||||
"missingEmail": "E-mail manquant.",
|
"missingEmail": "E-mail manquant.",
|
||||||
"missingIDOfDocument": "Il manque l'identifiant du document à mettre à jour.",
|
"missingIDOfDocument": "Il manque l'identifiant du document à mettre à jour.",
|
||||||
"missingIDOfVersion": "Il manque l'identifiant de la version.",
|
"missingIDOfVersion": "Il manque l'identifiant de la version.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Suppression en cours...",
|
"deleting": "Suppression en cours...",
|
||||||
"descending": "Descendant(e)",
|
"descending": "Descendant(e)",
|
||||||
"deselectAllRows": "Désélectionner toutes les lignes",
|
"deselectAllRows": "Désélectionner toutes les lignes",
|
||||||
|
"document": "Document",
|
||||||
|
"documents": "Documents",
|
||||||
"duplicate": "Dupliquer",
|
"duplicate": "Dupliquer",
|
||||||
"duplicateWithoutSaving": "Dupliquer sans enregistrer les modifications",
|
"duplicateWithoutSaving": "Dupliquer sans enregistrer les modifications",
|
||||||
"edit": "Éditer",
|
"edit": "Éditer",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Affichage des versions de ou du {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Affichage des versions de ou du {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Affichage des versions globales de ou du {{entityLabel}}"
|
"viewingVersionsGlobal": "Affichage des versions globales de ou du {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Nevaljan tip datoteke",
|
"invalidFileType": "Nevaljan tip datoteke",
|
||||||
"invalidFileTypeValue": "Nevaljan tip datoteke: {{value}}",
|
"invalidFileTypeValue": "Nevaljan tip datoteke: {{value}}",
|
||||||
"loadingDocument": "Pojavio se problem pri učitavanju dokumenta čiji je ID {{id}}.",
|
"loadingDocument": "Pojavio se problem pri učitavanju dokumenta čiji je ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Sljedeću lokalnu postavku nije bilo moguće spremiti:",
|
||||||
|
"localesNotSaved_other": "Sljedeće lokalne postavke nije bilo moguće spremiti:",
|
||||||
"missingEmail": "Nedostaje email.",
|
"missingEmail": "Nedostaje email.",
|
||||||
"missingIDOfDocument": "Nedostaje ID dokumenta da bi se ažurirao.",
|
"missingIDOfDocument": "Nedostaje ID dokumenta da bi se ažurirao.",
|
||||||
"missingIDOfVersion": "Nedostaje ID verzije.",
|
"missingIDOfVersion": "Nedostaje ID verzije.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Brisanje...",
|
"deleting": "Brisanje...",
|
||||||
"descending": "Silazno",
|
"descending": "Silazno",
|
||||||
"deselectAllRows": "Odznači sve redove",
|
"deselectAllRows": "Odznači sve redove",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumenti",
|
||||||
"duplicate": "Duplikat",
|
"duplicate": "Duplikat",
|
||||||
"duplicateWithoutSaving": "Dupliciraj bez spremanja promjena",
|
"duplicateWithoutSaving": "Dupliciraj bez spremanja promjena",
|
||||||
"edit": "Uredi",
|
"edit": "Uredi",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Pregled verzija za {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Pregled verzija za {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Pregled verzije za globalni {{entityLabel}}"
|
"viewingVersionsGlobal": "Pregled verzije za globalni {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Érvénytelen fájltípus",
|
"invalidFileType": "Érvénytelen fájltípus",
|
||||||
"invalidFileTypeValue": "Érvénytelen fájltípus: {{value}}",
|
"invalidFileTypeValue": "Érvénytelen fájltípus: {{value}}",
|
||||||
"loadingDocument": "Hiba történt a {{id}} azonosítójú dokumentum betöltésekor.",
|
"loadingDocument": "Hiba történt a {{id}} azonosítójú dokumentum betöltésekor.",
|
||||||
|
"localesNotSaved_one": "Az alábbi helyi beállítást nem sikerült menteni:",
|
||||||
|
"localesNotSaved_other": "Az alábbi helyi beállításokat nem sikerült menteni:",
|
||||||
"missingEmail": "Hiányzó e-mail.",
|
"missingEmail": "Hiányzó e-mail.",
|
||||||
"missingIDOfDocument": "Hiányzik a frissítendő dokumentum azonosítója.",
|
"missingIDOfDocument": "Hiányzik a frissítendő dokumentum azonosítója.",
|
||||||
"missingIDOfVersion": "A verzió azonosítója hiányzik.",
|
"missingIDOfVersion": "A verzió azonosítója hiányzik.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Törlés...",
|
"deleting": "Törlés...",
|
||||||
"descending": "Csökkenő",
|
"descending": "Csökkenő",
|
||||||
"deselectAllRows": "Jelölje ki az összes sort",
|
"deselectAllRows": "Jelölje ki az összes sort",
|
||||||
|
"document": "Dokumentum",
|
||||||
|
"documents": "Dokumentumok",
|
||||||
"duplicate": "Duplikálás",
|
"duplicate": "Duplikálás",
|
||||||
"duplicateWithoutSaving": "Duplikálás a módosítások mentése nélkül",
|
"duplicateWithoutSaving": "Duplikálás a módosítások mentése nélkül",
|
||||||
"edit": "Szerkesztés",
|
"edit": "Szerkesztés",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "A {{entityLabel}} {{documentTitle}} verzióinak megtekintése",
|
"viewingVersions": "A {{entityLabel}} {{documentTitle}} verzióinak megtekintése",
|
||||||
"viewingVersionsGlobal": "A globális {{entityLabel}} verzióinak megtekintése"
|
"viewingVersionsGlobal": "A globális {{entityLabel}} verzióinak megtekintése"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Tipo di file non valido",
|
"invalidFileType": "Tipo di file non valido",
|
||||||
"invalidFileTypeValue": "Tipo di file non valido: {{value}}",
|
"invalidFileTypeValue": "Tipo di file non valido: {{value}}",
|
||||||
"loadingDocument": "Si è verificato un problema durante il caricamento del documento con ID {{id}}.",
|
"loadingDocument": "Si è verificato un problema durante il caricamento del documento con ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Non è stato possibile salvare la seguente impostazione locale:",
|
||||||
|
"localesNotSaved_other": "Non è stato possibile salvare le seguenti impostazioni locali:",
|
||||||
"missingEmail": "Email mancante.",
|
"missingEmail": "Email mancante.",
|
||||||
"missingIDOfDocument": "ID del documento da aggiornare mancante.",
|
"missingIDOfDocument": "ID del documento da aggiornare mancante.",
|
||||||
"missingIDOfVersion": "ID della versione mancante.",
|
"missingIDOfVersion": "ID della versione mancante.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Sto eliminando...",
|
"deleting": "Sto eliminando...",
|
||||||
"descending": "Decrescente",
|
"descending": "Decrescente",
|
||||||
"deselectAllRows": "Deseleziona tutte le righe",
|
"deselectAllRows": "Deseleziona tutte le righe",
|
||||||
|
"document": "Documento",
|
||||||
|
"documents": "Documenti",
|
||||||
"duplicate": "Duplica",
|
"duplicate": "Duplica",
|
||||||
"duplicateWithoutSaving": "Duplica senza salvare le modifiche",
|
"duplicateWithoutSaving": "Duplica senza salvare le modifiche",
|
||||||
"edit": "Modificare",
|
"edit": "Modificare",
|
||||||
@@ -370,4 +374,4 @@
|
|||||||
"viewingVersions": "Visualizzazione delle versioni per {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Visualizzazione delle versioni per {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "`Visualizzazione delle versioni per {{entityLabel}}"
|
"viewingVersionsGlobal": "`Visualizzazione delle versioni per {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "無効なファイル形式",
|
"invalidFileType": "無効なファイル形式",
|
||||||
"invalidFileTypeValue": "無効なファイル形式: {{value}}",
|
"invalidFileTypeValue": "無効なファイル形式: {{value}}",
|
||||||
"loadingDocument": "IDが {{id}} のデータを読み込む際に問題が発生しました。",
|
"loadingDocument": "IDが {{id}} のデータを読み込む際に問題が発生しました。",
|
||||||
|
"localesNotSaved_one": "次のロケールを保存できませんでした:",
|
||||||
|
"localesNotSaved_other": "次のロケールを保存できませんでした:",
|
||||||
"missingEmail": "メールアドレスが不足しています。",
|
"missingEmail": "メールアドレスが不足しています。",
|
||||||
"missingIDOfDocument": "更新するデータのIDが不足しています。",
|
"missingIDOfDocument": "更新するデータのIDが不足しています。",
|
||||||
"missingIDOfVersion": "バージョンIDが不足しています。",
|
"missingIDOfVersion": "バージョンIDが不足しています。",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "削除しています...",
|
"deleting": "削除しています...",
|
||||||
"descending": "降順",
|
"descending": "降順",
|
||||||
"deselectAllRows": "すべての行の選択を解除します",
|
"deselectAllRows": "すべての行の選択を解除します",
|
||||||
|
"document": "ドキュメント",
|
||||||
|
"documents": "ドキュメント",
|
||||||
"duplicate": "複製",
|
"duplicate": "複製",
|
||||||
"duplicateWithoutSaving": "変更を保存せずに複製",
|
"duplicateWithoutSaving": "変更を保存せずに複製",
|
||||||
"edit": "編集",
|
"edit": "編集",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "表示バージョン: {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "表示バージョン: {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "表示バージョン: グローバルな {{entityLabel}}"
|
"viewingVersionsGlobal": "表示バージョン: グローバルな {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,8 @@
|
|||||||
"invalidFileType": "잘못된 파일 형식",
|
"invalidFileType": "잘못된 파일 형식",
|
||||||
"invalidFileTypeValue": "잘못된 파일 형식: {{value}}",
|
"invalidFileTypeValue": "잘못된 파일 형식: {{value}}",
|
||||||
"loadingDocument": "ID가 {{id}}인 문서를 불러오는 중에 문제가 발생했습니다.",
|
"loadingDocument": "ID가 {{id}}인 문서를 불러오는 중에 문제가 발생했습니다.",
|
||||||
|
"localesNotSaved_one": "다음 로케일을 저장할 수 없습니다:",
|
||||||
|
"localesNotSaved_other": "다음 로케일들을 저장할 수 없습니다:",
|
||||||
"missingEmail": "이메일이 누락되었습니다.",
|
"missingEmail": "이메일이 누락되었습니다.",
|
||||||
"missingIDOfDocument": "업데이트할 문서의 ID가 누락되었습니다.",
|
"missingIDOfDocument": "업데이트할 문서의 ID가 누락되었습니다.",
|
||||||
"missingIDOfVersion": "버전의 ID가 누락되었습니다.",
|
"missingIDOfVersion": "버전의 ID가 누락되었습니다.",
|
||||||
@@ -176,6 +178,8 @@
|
|||||||
"deleting": "삭제 중...",
|
"deleting": "삭제 중...",
|
||||||
"descending": "내림차순",
|
"descending": "내림차순",
|
||||||
"deselectAllRows": "모든 행 선택 해제",
|
"deselectAllRows": "모든 행 선택 해제",
|
||||||
|
"document": "문서",
|
||||||
|
"documents": "문서들",
|
||||||
"duplicate": "복제",
|
"duplicate": "복제",
|
||||||
"duplicateWithoutSaving": "변경 사항 저장 없이 복제",
|
"duplicateWithoutSaving": "변경 사항 저장 없이 복제",
|
||||||
"edit": "수정",
|
"edit": "수정",
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "မမှန်ကန်သော ဖိုင်အမျိုးအစား",
|
"invalidFileType": "မမှန်ကန်သော ဖိုင်အမျိုးအစား",
|
||||||
"invalidFileTypeValue": "မမှန်ကန်သော ဖိုင်အမျိုးအစား: {{value}}",
|
"invalidFileTypeValue": "မမှန်ကန်သော ဖိုင်အမျိုးအစား: {{value}}",
|
||||||
"loadingDocument": "{{id}} ID ဖြင့် ဖိုင်ကို ဖွင့်ရာတွင် ပြဿနာရှိနေသည်။",
|
"loadingDocument": "{{id}} ID ဖြင့် ဖိုင်ကို ဖွင့်ရာတွင် ပြဿနာရှိနေသည်။",
|
||||||
|
"localesNotSaved_one": "အောက်ပါ ဒေသသတ်မှတ်ချက်ကို သိမ်းဆည်း၍ မရပါ။",
|
||||||
|
"localesNotSaved_other": "အောက်ပါ ဒေသသတ်မှတ်ချက်များကို သိမ်းဆည်း၍ မရပါ။",
|
||||||
"missingEmail": "အီးမေးလ်ပျောက်ဆုံး",
|
"missingEmail": "အီးမေးလ်ပျောက်ဆုံး",
|
||||||
"missingIDOfDocument": "ပြင်ဆင်ရန် ဖိုင် ID ပျောက်နေပါသည်။",
|
"missingIDOfDocument": "ပြင်ဆင်ရန် ဖိုင် ID ပျောက်နေပါသည်။",
|
||||||
"missingIDOfVersion": "ပျောက်ဆုံး ဗားရှင်း ID",
|
"missingIDOfVersion": "ပျောက်ဆုံး ဗားရှင်း ID",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "ဖျက်နေဆဲ ...",
|
"deleting": "ဖျက်နေဆဲ ...",
|
||||||
"descending": "ဆင်းသက်လာသည်။",
|
"descending": "ဆင်းသက်လာသည်။",
|
||||||
"deselectAllRows": "အားလုံးကို မရွေးနိုင်ပါ",
|
"deselectAllRows": "အားလုံးကို မရွေးနိုင်ပါ",
|
||||||
|
"document": "စာရွက်စာတမ်း",
|
||||||
|
"documents": "စာရွက်စာတမ်းများ",
|
||||||
"duplicate": "ပုံတူပွားမည်။",
|
"duplicate": "ပုံတူပွားမည်။",
|
||||||
"duplicateWithoutSaving": "သေချာပါပြီ။",
|
"duplicateWithoutSaving": "သေချာပါပြီ။",
|
||||||
"edit": "တည်းဖြတ်ပါ။",
|
"edit": "တည်းဖြတ်ပါ။",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "{{entityLabel}} {{documentTitle}} အတွက် ဗားရှင်းများကို ကြည့်ရှုခြင်း",
|
"viewingVersions": "{{entityLabel}} {{documentTitle}} အတွက် ဗားရှင်းများကို ကြည့်ရှုခြင်း",
|
||||||
"viewingVersionsGlobal": "`ဂလိုဘယ်ဆိုင်ရာ {{entityLabel}} အတွက် ဗားရှင်းများကို ကြည့်ရှုနေသည်"
|
"viewingVersionsGlobal": "`ဂလိုဘယ်ဆိုင်ရာ {{entityLabel}} အတွက် ဗားရှင်းများကို ကြည့်ရှုနေသည်"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Ugyldig filtype",
|
"invalidFileType": "Ugyldig filtype",
|
||||||
"invalidFileTypeValue": "Ugyldig filtype: {{value}}",
|
"invalidFileTypeValue": "Ugyldig filtype: {{value}}",
|
||||||
"loadingDocument": "Det oppstod et problem under lasting av dokumentet med ID {{id}}.",
|
"loadingDocument": "Det oppstod et problem under lasting av dokumentet med ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Følgende lokalisering kunne ikke lagres:",
|
||||||
|
"localesNotSaved_other": "Følgende lokaliseringer kunne ikke lagres:",
|
||||||
"missingEmail": "Mangler e-postadresse.",
|
"missingEmail": "Mangler e-postadresse.",
|
||||||
"missingIDOfDocument": "Mangler ID for dokumentet som skal oppdateres.",
|
"missingIDOfDocument": "Mangler ID for dokumentet som skal oppdateres.",
|
||||||
"missingIDOfVersion": "Mangler ID for versjonen.",
|
"missingIDOfVersion": "Mangler ID for versjonen.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Sletter...",
|
"deleting": "Sletter...",
|
||||||
"descending": "Synkende",
|
"descending": "Synkende",
|
||||||
"deselectAllRows": "Fjern markeringen fra alle rader",
|
"deselectAllRows": "Fjern markeringen fra alle rader",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumenter",
|
||||||
"duplicate": "Dupliser",
|
"duplicate": "Dupliser",
|
||||||
"duplicateWithoutSaving": "Dupliser uten å lagre endringer",
|
"duplicateWithoutSaving": "Dupliser uten å lagre endringer",
|
||||||
"edit": "Redigere",
|
"edit": "Redigere",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Viser versjoner for {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Viser versjoner for {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Viser versjoner for den globale variabelen {{entityLabel}}"
|
"viewingVersionsGlobal": "Viser versjoner for den globale variabelen {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Ongeldig bestandstype",
|
"invalidFileType": "Ongeldig bestandstype",
|
||||||
"invalidFileTypeValue": "Ongeldig bestandstype: {{value}}",
|
"invalidFileTypeValue": "Ongeldig bestandstype: {{value}}",
|
||||||
"loadingDocument": "Er was een probleem met het laden van het document met ID {{id}}.",
|
"loadingDocument": "Er was een probleem met het laden van het document met ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "De volgende taalinstelling kon niet worden opgeslagen:",
|
||||||
|
"localesNotSaved_other": "De volgende taalinstellingen konden niet worden opgeslagen:",
|
||||||
"missingEmail": "E-mailadres ontbreekt.",
|
"missingEmail": "E-mailadres ontbreekt.",
|
||||||
"missingIDOfDocument": "ID ontbreekt van het aan te passen document.",
|
"missingIDOfDocument": "ID ontbreekt van het aan te passen document.",
|
||||||
"missingIDOfVersion": "ID van versie ontbreekt.",
|
"missingIDOfVersion": "ID van versie ontbreekt.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Verwijderen...",
|
"deleting": "Verwijderen...",
|
||||||
"descending": "Aflopend",
|
"descending": "Aflopend",
|
||||||
"deselectAllRows": "Deselecteer alle rijen",
|
"deselectAllRows": "Deselecteer alle rijen",
|
||||||
|
"document": "Document",
|
||||||
|
"documents": "Documenten",
|
||||||
"duplicate": "Dupliceren",
|
"duplicate": "Dupliceren",
|
||||||
"duplicateWithoutSaving": "Dupliceren zonder wijzigingen te bewaren",
|
"duplicateWithoutSaving": "Dupliceren zonder wijzigingen te bewaren",
|
||||||
"edit": "Bewerk",
|
"edit": "Bewerk",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Bekijk versies voor {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Bekijk versies voor {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "`Bekijk versies voor global {{entityLabel}}"
|
"viewingVersionsGlobal": "`Bekijk versies voor global {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Nieprawidłowy typ pliku",
|
"invalidFileType": "Nieprawidłowy typ pliku",
|
||||||
"invalidFileTypeValue": "Nieprawidłowy typ pliku: {{value}}",
|
"invalidFileTypeValue": "Nieprawidłowy typ pliku: {{value}}",
|
||||||
"loadingDocument": "Wystapił problem podczas ładowania dokumentu o ID {{id}}.",
|
"loadingDocument": "Wystapił problem podczas ładowania dokumentu o ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Następującej lokalizacji nie można było zapisać:",
|
||||||
|
"localesNotSaved_other": "Następujących lokalizacji nie można było zapisać:",
|
||||||
"missingEmail": "Brak adresu email.",
|
"missingEmail": "Brak adresu email.",
|
||||||
"missingIDOfDocument": "Brak ID dokumentu do aktualizacji.",
|
"missingIDOfDocument": "Brak ID dokumentu do aktualizacji.",
|
||||||
"missingIDOfVersion": "Brak ID wersji",
|
"missingIDOfVersion": "Brak ID wersji",
|
||||||
@@ -178,6 +180,8 @@
|
|||||||
"deleting": "Usuwanie...",
|
"deleting": "Usuwanie...",
|
||||||
"descending": "Malejąco",
|
"descending": "Malejąco",
|
||||||
"deselectAllRows": "Odznacz wszystkie wiersze",
|
"deselectAllRows": "Odznacz wszystkie wiersze",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumenty",
|
||||||
"duplicate": "Zduplikuj",
|
"duplicate": "Zduplikuj",
|
||||||
"duplicateWithoutSaving": "Zduplikuj bez zapisywania zmian",
|
"duplicateWithoutSaving": "Zduplikuj bez zapisywania zmian",
|
||||||
"edit": "Edytuj",
|
"edit": "Edytuj",
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Tipo de arquivo inválido",
|
"invalidFileType": "Tipo de arquivo inválido",
|
||||||
"invalidFileTypeValue": "Tipo de arquivo inválido: {{value}}",
|
"invalidFileTypeValue": "Tipo de arquivo inválido: {{value}}",
|
||||||
"loadingDocument": "Ocorreu um problema ao carregar o documento com ID {{id}}.",
|
"loadingDocument": "Ocorreu um problema ao carregar o documento com ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "A seguinte configuração regional não pôde ser salva:",
|
||||||
|
"localesNotSaved_other": "As seguintes configurações regionais não puderam ser salvas:",
|
||||||
"missingEmail": "Email ausente.",
|
"missingEmail": "Email ausente.",
|
||||||
"missingIDOfDocument": "ID do documento a ser atualizado ausente.",
|
"missingIDOfDocument": "ID do documento a ser atualizado ausente.",
|
||||||
"missingIDOfVersion": "ID da versão ausente.",
|
"missingIDOfVersion": "ID da versão ausente.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Excluindo...",
|
"deleting": "Excluindo...",
|
||||||
"descending": "Decrescente",
|
"descending": "Decrescente",
|
||||||
"deselectAllRows": "Desmarcar todas as linhas",
|
"deselectAllRows": "Desmarcar todas as linhas",
|
||||||
|
"document": "Documento",
|
||||||
|
"documents": "Documentos",
|
||||||
"duplicate": "Duplicar",
|
"duplicate": "Duplicar",
|
||||||
"duplicateWithoutSaving": "Duplicar sem salvar alterações",
|
"duplicateWithoutSaving": "Duplicar sem salvar alterações",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Visualizando versões para o/a {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Visualizando versões para o/a {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "`Visualizando versões para o global {{entityLabel}}"
|
"viewingVersionsGlobal": "`Visualizando versões para o global {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Tip de fișier invalid",
|
"invalidFileType": "Tip de fișier invalid",
|
||||||
"invalidFileTypeValue": "Tip de fișier invalid: {{value}}",
|
"invalidFileTypeValue": "Tip de fișier invalid: {{value}}",
|
||||||
"loadingDocument": "A existat o problemă la încărcarea documentului cu ID-ul de {{id}}.",
|
"loadingDocument": "A existat o problemă la încărcarea documentului cu ID-ul de {{id}}.",
|
||||||
|
"localesNotSaved_one": "Următoarea localizare nu a putut fi salvată:",
|
||||||
|
"localesNotSaved_other": "Următoarele localizări nu au putut fi salvate:",
|
||||||
"missingEmail": "Lipsește emailul.",
|
"missingEmail": "Lipsește emailul.",
|
||||||
"missingIDOfDocument": "Lipsește ID-ul documentului care trebuie actualizat.",
|
"missingIDOfDocument": "Lipsește ID-ul documentului care trebuie actualizat.",
|
||||||
"missingIDOfVersion": "Lipsește ID-ul versiunii.",
|
"missingIDOfVersion": "Lipsește ID-ul versiunii.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Deleting...",
|
"deleting": "Deleting...",
|
||||||
"descending": "Descendentă",
|
"descending": "Descendentă",
|
||||||
"deselectAllRows": "Deselectează toate rândurile",
|
"deselectAllRows": "Deselectează toate rândurile",
|
||||||
|
"document": "Document",
|
||||||
|
"documents": "Documente",
|
||||||
"duplicate": "Duplicați",
|
"duplicate": "Duplicați",
|
||||||
"duplicateWithoutSaving": "Duplicați fără salvarea modificărilor",
|
"duplicateWithoutSaving": "Duplicați fără salvarea modificărilor",
|
||||||
"edit": "Editează",
|
"edit": "Editează",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Vizualizarea versiunilor pentru {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Vizualizarea versiunilor pentru {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Vizualizarea versiunilor pentru globala {{entityLabel}}"
|
"viewingVersionsGlobal": "Vizualizarea versiunilor pentru globala {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Nevalidan tip datoteke",
|
"invalidFileType": "Nevalidan tip datoteke",
|
||||||
"invalidFileTypeValue": "Nevalidan tip datoteke: {{value}}",
|
"invalidFileTypeValue": "Nevalidan tip datoteke: {{value}}",
|
||||||
"loadingDocument": "Postoji problem pri učitavanju dokumenta čiji je ID {{id}}.",
|
"loadingDocument": "Postoji problem pri učitavanju dokumenta čiji je ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Nije moglo da se sačuva sledeće lokalno podešavanje:",
|
||||||
|
"localesNotSaved_other": "Nisu mogla da se sačuvaju sledeća lokalna podešavanja:",
|
||||||
"missingEmail": "Nedostaje adresa e-pošte.",
|
"missingEmail": "Nedostaje adresa e-pošte.",
|
||||||
"missingIDOfDocument": "Nedostaje ID dokumenta da bi se ažurirao.",
|
"missingIDOfDocument": "Nedostaje ID dokumenta da bi se ažurirao.",
|
||||||
"missingIDOfVersion": "Nedostaje ID verzije.",
|
"missingIDOfVersion": "Nedostaje ID verzije.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Brisanje...",
|
"deleting": "Brisanje...",
|
||||||
"descending": "Opadajuće",
|
"descending": "Opadajuće",
|
||||||
"deselectAllRows": "Deselektujte sve redove",
|
"deselectAllRows": "Deselektujte sve redove",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumenti",
|
||||||
"duplicate": "Duplikat",
|
"duplicate": "Duplikat",
|
||||||
"duplicateWithoutSaving": "Ponovi bez čuvanja promena",
|
"duplicateWithoutSaving": "Ponovi bez čuvanja promena",
|
||||||
"edit": "Uredi",
|
"edit": "Uredi",
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Невалидан тип датотеке",
|
"invalidFileType": "Невалидан тип датотеке",
|
||||||
"invalidFileTypeValue": "Невалидан тип датотеке: {{value}}",
|
"invalidFileTypeValue": "Невалидан тип датотеке: {{value}}",
|
||||||
"loadingDocument": "Постоји проблем при учитавању документа чији је ИД {{id}}.",
|
"loadingDocument": "Постоји проблем при учитавању документа чији је ИД {{id}}.",
|
||||||
|
"localesNotSaved_one": "Следеће локалне поставке није могло бити сачувано:",
|
||||||
|
"localesNotSaved_other": "Следеће локалне поставке нису могле бити сачуване:",
|
||||||
"missingEmail": "Недостаје емаил.",
|
"missingEmail": "Недостаје емаил.",
|
||||||
"missingIDOfDocument": "Недостаје ИД документа да би се ажурирао.",
|
"missingIDOfDocument": "Недостаје ИД документа да би се ажурирао.",
|
||||||
"missingIDOfVersion": "Недостаје ИД верзије.",
|
"missingIDOfVersion": "Недостаје ИД верзије.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Брисање...",
|
"deleting": "Брисање...",
|
||||||
"descending": "Опадајуће",
|
"descending": "Опадајуће",
|
||||||
"deselectAllRows": "Деселектујте све редове",
|
"deselectAllRows": "Деселектујте све редове",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokumenti",
|
||||||
"duplicate": "Дупликат",
|
"duplicate": "Дупликат",
|
||||||
"duplicateWithoutSaving": "Понови без чувања промена",
|
"duplicateWithoutSaving": "Понови без чувања промена",
|
||||||
"edit": "Уреди",
|
"edit": "Уреди",
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Недопустимый тип файла",
|
"invalidFileType": "Недопустимый тип файла",
|
||||||
"invalidFileTypeValue": "Недопустимый тип файла: {{value}}",
|
"invalidFileTypeValue": "Недопустимый тип файла: {{value}}",
|
||||||
"loadingDocument": "Возникла проблема при загрузке документа с ID {{id}}.",
|
"loadingDocument": "Возникла проблема при загрузке документа с ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Следующую локализацию не удалось сохранить:",
|
||||||
|
"localesNotSaved_other": "Следующие локализации не удалось сохранить:",
|
||||||
"missingEmail": "Отсутствует email.",
|
"missingEmail": "Отсутствует email.",
|
||||||
"missingIDOfDocument": "Отсутствующий ID документа для обновления.",
|
"missingIDOfDocument": "Отсутствующий ID документа для обновления.",
|
||||||
"missingIDOfVersion": "Отсутствует ID версии.",
|
"missingIDOfVersion": "Отсутствует ID версии.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Удаление...",
|
"deleting": "Удаление...",
|
||||||
"descending": "Уменьшение",
|
"descending": "Уменьшение",
|
||||||
"deselectAllRows": "Снять выделение со всех строк",
|
"deselectAllRows": "Снять выделение со всех строк",
|
||||||
|
"document": "Документ",
|
||||||
|
"documents": "Документы",
|
||||||
"duplicate": "Дублировать",
|
"duplicate": "Дублировать",
|
||||||
"duplicateWithoutSaving": "Дублирование без сохранения изменений",
|
"duplicateWithoutSaving": "Дублирование без сохранения изменений",
|
||||||
"edit": "Редактировать",
|
"edit": "Редактировать",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Просмотр версий для {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Просмотр версий для {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "`Просмотр версии для глобальной Коллекции {{entityLabel}}"
|
"viewingVersionsGlobal": "`Просмотр версии для глобальной Коллекции {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@
|
|||||||
"invalidFileType": "Ogiltig filtyp",
|
"invalidFileType": "Ogiltig filtyp",
|
||||||
"invalidFileTypeValue": "Ogiltig filtyp: {{value}}",
|
"invalidFileTypeValue": "Ogiltig filtyp: {{value}}",
|
||||||
"loadingDocument": "Det gick inte att läsa in dokumentet med ID {{id}}.",
|
"loadingDocument": "Det gick inte att läsa in dokumentet med ID {{id}}.",
|
||||||
|
"localesNotSaved_one": "Följande lokal kunde inte sparas:",
|
||||||
|
"localesNotSaved_other": "Följande lokaler kunde inte sparas:",
|
||||||
"missingEmail": "E-postadress saknas.",
|
"missingEmail": "E-postadress saknas.",
|
||||||
"missingIDOfDocument": "Saknar ID för dokumentet som ska uppdateras.",
|
"missingIDOfDocument": "Saknar ID för dokumentet som ska uppdateras.",
|
||||||
"missingIDOfVersion": "ID för versionen saknas.",
|
"missingIDOfVersion": "ID för versionen saknas.",
|
||||||
@@ -177,6 +179,8 @@
|
|||||||
"deleting": "Tar bort...",
|
"deleting": "Tar bort...",
|
||||||
"descending": "Fallande",
|
"descending": "Fallande",
|
||||||
"deselectAllRows": "Avmarkera alla rader",
|
"deselectAllRows": "Avmarkera alla rader",
|
||||||
|
"document": "Dokument",
|
||||||
|
"documents": "Dokument",
|
||||||
"duplicate": "Duplicera",
|
"duplicate": "Duplicera",
|
||||||
"duplicateWithoutSaving": "Duplicera utan att spara ändringar",
|
"duplicateWithoutSaving": "Duplicera utan att spara ändringar",
|
||||||
"edit": "Redigera",
|
"edit": "Redigera",
|
||||||
@@ -369,4 +373,4 @@
|
|||||||
"viewingVersions": "Visar versioner för {{entityLabel}} {{documentTitle}}",
|
"viewingVersions": "Visar versioner för {{entityLabel}} {{documentTitle}}",
|
||||||
"viewingVersionsGlobal": "Visa versioner för den globala {{entityLabel}}"
|
"viewingVersionsGlobal": "Visa versioner för den globala {{entityLabel}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user