Compare commits

...

64 Commits

Author SHA1 Message Date
Alessio Gravili
4a54aa7776 fix: pin ajv to 8.14.0, as 8.15.0 is broken (#6607) 2024-06-03 20:22:24 -04:00
hasanbeder
4fddea86eb feat(translations): update Turkish translations (#5738)
chore(i18n): This commit enriches the application by integrating Turkish
language support, enhancing accessibility and user experience for
Turkish-speaking audiences. 🇹🇷

Co-authored-by: Elliot DeNolf <denolfe@users.noreply.github.com>
2024-05-30 12:36:26 -04:00
Jan Jakub Sasinka
547acfe876 fix: pagination on polymorphic relationship field requesting entries with page parameter set to NaN (#5366)
## Description
Fixes [5362](https://github.com/payloadcms/payload/issues/5362)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-05-30 11:51:56 -04:00
Patrik
56c6700cf2 fix: adds new userEmailAlreadyRegistered translations (#6549)
## Description

Fixes #6535 

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-30 09:36:18 -04:00
Patrik
a352ebc552 fix: adjusts sizing of remove/add buttons to be same size (#6527)
## Description

Fixes #6098 

`Remove` / `Add` buttons are a bit different in size and different
viewpoints.

Before:
![Screenshot 2024-05-28 at 9 06
44 AM](https://github.com/payloadcms/payload/assets/35232443/483b6773-5877-4da2-96d7-43ba701cc138)

![Screenshot 2024-05-28 at 9 06
33 AM](https://github.com/payloadcms/payload/assets/35232443/3a0f55ed-dd83-4ba9-8dc3-58d7378eb7f5)


After:
![Screenshot 2024-05-28 at 9 07
20 AM](https://github.com/payloadcms/payload/assets/35232443/3986544d-ab4c-405c-871a-1fa94b5a49ae)



- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-30 09:35:20 -04:00
Jessica Chowdhury
312b4a94c3 docs: adds sentry to plugin docs 2024-05-29 15:11:36 -04:00
Jacob Fletcher
cb3dbed127 docs: renames vercel visual editing to vercel content link (#6556) 2024-05-29 13:34:29 -04:00
Elliot DeNolf
3a73a9696f ci: stale workflow evals assigned [skip ci] 2024-05-29 13:14:26 -04:00
Elliot DeNolf
d587441e80 ci: update create by with usernames 2024-05-29 13:13:59 -04:00
Elliot DeNolf
fcfc3c593f fix: focalPoint undefined handling (#6552)
Better undefined handling on incoming focalPoint
2024-05-29 12:44:58 -04:00
Elliot DeNolf
baf945b1ea ci: remove needs-triage on issue assigned 2024-05-28 15:20:16 -04:00
Patrik
4f9d78df5e fix: safely evaluates field.admin in WhereBuilder (#6534)
## Description

Safely evaluates `field.admin` in WhereBuilder

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-28 11:18:54 -04:00
Dan Ribbens
f07783a279 chore: move db-example to examples (#6532)
- Moved to examples
- Removed unnecessary dependencies
- Improved the readme with instructions for writing the db adapter
2024-05-28 10:28:18 -04:00
Patrik
eeddeceda9 fix: ui field validation error with admin.disableListColumn property (#6530)
## Description

Fixes #6521 

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update

## Checklist:

- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-05-28 10:25:40 -04:00
Elliot DeNolf
4c832ad984 ci: update status labels on issue change 2024-05-26 22:00:26 -04:00
Elliot DeNolf
81bc777dfe chore: update v2 issue template to use new tag 2024-05-26 08:04:20 -04:00
Patrik
a757635bc7 chore: db-example (#6495)
## Description

Adds `db-example` repo

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Chore (non-breaking change which does not add functionality)

## Checklist:

- [x] Existing test suite passes locally with my changes

---------

Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-05-24 12:16:34 -04:00
Elliot DeNolf
80bf0a3067 chore: label updates [skip ci] 2024-05-24 09:31:51 -04:00
Elliot DeNolf
a06c06415c ci: increase stale operations-per-run 2024-05-23 09:09:50 -04:00
Elliot DeNolf
2dd7e82fdc ci: add stale workflow in debug mode 2024-05-22 16:43:17 -04:00
Donovan Glover
63e04e2ae0 fix(editorconfig): use off for max_line_length (#4713)
Fixes "invalid value for option max_line_length: null"

See:
https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#max_line_length
2024-05-22 15:28:14 -04:00
Elliot DeNolf
e71888a625 chore: add label-author.yml 2024-05-21 23:03:14 -04:00
Patrik
c5514f1441 chore(bundler-webpack): updates webpack-dev-middleware dependency (#6446)
## Description

`pnpm audit` of `bundler-webpack` showed a high severity security issue
with the `webpack-dev-middleware` package

Updated package to latest to resolve.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-21 16:08:00 -04:00
Elliot DeNolf
23e2fe643e chore: v3 bug template ids update 2024-05-21 11:56:17 -04:00
Elliot DeNolf
bdef2f1bc7 chore: add v3 bug template 2024-05-21 11:54:26 -04:00
Jessica Chowdhury
8f03cd7c78 fix(ui): blocks browser save dialog from opening when hotkey used with no changes (#6365)
## Description

Related issue
[#214](https://github.com/payloadcms/payload-3.0-demo/issues/214) (3.0).

Using the `cmd+s` hotkey in the Edit Document view was opening the
_browser_ save dialogue when no changes had been made.

This change triggers a toast info banner with `No changes to save` and
adds translated string for other languages.

PR for 3.0 is [here](https://github.com/payloadcms/payload/pull/6366).

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] Chore (non-breaking change which does not add functionality)

## Checklist:

- [X] Existing test suite passes locally with my changes
2024-05-21 14:07:47 +01:00
Anders Semb Hermansen
c0092191a6 fix: separate sort and search fields when looking up relationship. (#5964)
## Description

Default sort is used as searching field which is causing unexpected
behaviour described in #4815 and #5222 This bugfix separates which field
is used for sorting and which is used for searching.

Fixes: #4815 #5222

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-05-20 15:59:54 -04:00
Elliot DeNolf
b9854ed60a chore(release): db-postgres/0.8.4 [skip ci] 2024-05-17 14:36:02 -04:00
Elliot DeNolf
576ee14976 chore(release): payload/2.18.3 [skip ci] 2024-05-17 14:33:14 -04:00
Dan Ribbens
bf77cec7e9 fix(db-postgres): query with like on id columns (#6416)
copy of https://github.com/payloadcms/payload/pull/6414 to main
2024-05-17 14:07:57 -04:00
Patrik
ab8b2f3fb8 fix: nested disableListColumn in rows (#6412)
## Description

Fixes #6407 

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-05-17 14:01:08 -04:00
Patrik
db5f3f3ccd fix(db-postgres): uuid custom db name (#6409)
## Description

Fixes an issue with creating versions when using custom DB names,
`uuid`, and drafts.

v3 PR [here](https://github.com/payloadcms/payload/pull/6408)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-17 13:58:08 -04:00
Francis Turmel
cece39957f chore(i18n): French translation improvements (#6035)
## Description

see #6406 for the changes against `beta`

* The apostrophe character `’` should be used instead of the single
quote `'`
* Gender corrections: "L’adresse e-mail fourni**e**", "Vérification
échoué**e**"
* Lowercase: "Supprimer le **té**léversement"
* Dark and light theme: I think it makes more sense to use "Sombre" and
"Clair" here to identify the theme. Day/Night modes imply a hue/warmth
correction and are different features altogether. Reference:
https://fr.wikipedia.org/wiki/Mode_sombre#Mode_sombre_et_mode_nuit_ou_chaud
* Fix accent: "Mis à jour avec succ**è**s"
* "Bienvenue" I think would be the correct standalone greeting form.
Reference:
https://www.projet-voltaire.fr/question-orthographe/orthographe-bienvenu-bienvenue-chez-moi/
* "Recadrer" is the correct word for "crop". "Récolte" means "crop" in
the sense of "harvest", so this was probably a bad literal Google
Translate that slipped through.
* Correct all "Es-tu sûr ?" to the proper formal "Êtes-vous sûr ?" for
consistency
* Use _article défini_ since we will enumerate the values: "Ce champ
contient **les** sélections invalides suivantes :"
* Space before question marks

---

<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] Chore (non-breaking change which does not add functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-05-17 13:51:47 -03:00
Elliot DeNolf
157fff0417 chore(release): payload/2.18.2 [skip ci] 2024-05-17 10:09:35 -04:00
Elliot DeNolf
88e113a545 fix: allow focal point when no sizes defined (#6397)
Allow focal points to be saved even when no imageSizes are defined.
Expands upon #6364 .
2024-05-17 09:06:42 -04:00
Elliot DeNolf
d33afe48fe chore(release): payload/2.18.1 [skip ci] 2024-05-16 15:46:23 -04:00
Elliot DeNolf
e76df32f09 fix: add back explicit crop x and y values (#6391)
Cropping x and y values pulling from the UI were unintentionally removed
in #6364
2024-05-16 15:32:36 -04:00
Elliot DeNolf
b068f30f51 chore(release): richtext-lexical/0.11.1 [skip ci] 2024-05-16 14:05:28 -04:00
Elliot DeNolf
82bd5c656f chore(release): db-postgres/0.8.3 [skip ci] 2024-05-16 14:05:16 -04:00
Elliot DeNolf
afe8992ca6 chore(release): payload/2.18.0 [skip ci] 2024-05-16 14:04:05 -04:00
Alessio Gravili
48a410e294 fix(richtext-lexical): upload, relationship and block node insertion fails sometimes (#6390)
Backport of https://github.com/payloadcms/payload/pull/6389
2024-05-16 13:34:43 -04:00
Elliot DeNolf
82b88a315f feat: store focal point on uploads (#6364)
Store focal point data on uploads as `focalX` and `focalY`

Addresses https://github.com/payloadcms/payload/discussions/4082
2024-05-16 11:58:49 -04:00
Patrik
cc94078607 fix: filter with ID not_in AND queries - postgres (#6358)
## Description

Fixes #5151 

`Issue`: With `Postgres`, when filtering by two queries with `AND`, if
the first query involved `ID` and the `not_in` operator, the second
query in the filter would never be evaluated.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-05-15 15:09:53 -04:00
Elliot DeNolf
1c30ad73b6 chore(release): richtext-lexical/0.11.0 [skip ci] 2024-05-15 11:35:05 -04:00
Elliot DeNolf
d9442dcce3 chore(release): payload/2.17.0 [skip ci] 2024-05-15 11:33:53 -04:00
Patrik
de92c50847 fix: safely access cookie header for uploads (#6367)
## Description

Issue with editing and changing the crop or focal point of an image

`Error`:

```
ERROR (payload): FileRetrievalError: There was a problem while uploading the file. Cannot read properties of undefined (reading 'cookie')
    at generateFileData (/node_modules/payload/src/uploads/generateFileData.ts:86:15)
```
(`payload v2.16.1` and `plugin-cloud v3.0.1`)

Fix: add optional chaing to safely access `cookie` header when fetching
image

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-15 10:50:14 -04:00
Jacob Fletcher
b7f5f932f6 fix(examples/live-preview): regenerates yarn lockfiles (#6356) 2024-05-14 15:27:51 -04:00
Jessica Chowdhury
bc9e591e37 chore: removes letter spacing in loading overlay for script languages (#4769) 2024-05-14 16:51:34 +01:00
Jessica Chowdhury
ad38011348 chore: fix api preview indentation (#6152) 2024-05-14 16:09:17 +01:00
Patrik
d02b1fb084 fix: step-nav breadcrumbs ellipsis (#6345) 2024-05-14 11:08:59 -04:00
Jessica Chowdhury
51efe4f39b fix: collection labels with locales not working when creating new doc (#5995) 2024-05-14 10:45:39 -04:00
Jessica Chowdhury
30e535b5b9 feat: adds misc translations to API view and react select (#6138) 2024-05-13 08:52:12 -04:00
Jessica Chowdhury
4bd3bb9400 chore: adds translations for checkbox field in collection view (#6167) 2024-05-13 12:31:36 +01:00
Patrik
69c93d3c62 fix: appends editDepth value to radio & checkbox IDs when inside drawer (#6181) 2024-05-10 11:24:21 -04:00
Elliot DeNolf
78aa957043 fix(plugin-object-ids): add webpack to dev deps 2024-05-10 11:02:29 -04:00
Patrik
cd06c022c0 feat(plugin-relationship-object-ids): adds plugin-relationship-object-ids package (#6044) 2024-05-10 10:39:47 -04:00
Jacob Fletcher
8299e9fc33 docs(examples): adds examples docs (#6312) 2024-05-10 10:22:22 -04:00
Alessio Gravili
9df5ab8a10 feat(richtext-lexical)!: remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types (#6279)
BREAKING:

`LexicalBlock`, `RichTextFieldRequiredEditor` and `FieldWithRichTextRequiredEditor` types have been removed. Instead of `LexicalBlock`, use `Block`. Instead of `RichTextFieldRequiredEditor`, use `RichTextField`. And instead of `FieldWithRichTextRequiredEditor`, use `Field`.
2024-05-09 09:46:03 -04:00
Elliot DeNolf
6979f5a1b1 ci: register release-canary workflow 2024-05-09 09:22:18 -04:00
Elliot DeNolf
723791b94f chore(release): richtext-lexical/0.10.0 [skip ci] 2024-05-07 15:53:11 -04:00
Elliot DeNolf
aa3833ec83 chore(release): payload/2.16.1 [skip ci] 2024-05-07 15:52:06 -04:00
Dan Ribbens
2972af2af1 chore: add index to version status field (#6256) 2024-05-07 15:47:55 -04:00
Alessio Gravili
857b9a4ac3 feat(richtext-lexical): add maxDepth property to various lexical features (#6250)
Backports #6242
2024-05-07 09:39:48 -04:00
Alessio Gravili
f829b084ba fix(richtext-lexical): export missing HorizontalRuleFeature (#6236) 2024-05-06 13:54:49 -04:00
173 changed files with 9842 additions and 7829 deletions

View File

@@ -0,0 +1,57 @@
name: Bug Report v3
description: Create a bug report for Payload v3 (beta)
labels: ['status: needs-triage', 'v3']
body:
- type: input
id: reproduction-link
attributes:
label: Link to reproduction
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: false
- type: input
id: version
attributes:
label: Payload Version
description: What version of Payload are you running?
validations:
required: true
- type: input
id: node-version
attributes:
label: Node Version
description: What version of Node are you running?
validations:
required: true
- type: input
id: nextjs-version
attributes:
label: Next.js Version
description: What version of Next.js are you running?
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: textarea
attributes:
label: Reproduction Steps
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations:
required: true
- type: input
id: adapters-plugins
attributes:
label: Adapters and Plugins
description: What adapters and plugins are you using if relevant? ie. db-mongodb, db-postgres, storage-vercel-blob, etc.
- type: markdown
attributes:
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
- type: markdown
attributes:
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
- type: markdown
attributes:
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!

View File

@@ -1,6 +1,6 @@
name: Bug Report
description: Create a bug report for Payload
labels: ['[possible-bug]']
labels: ['status: needs-triage']
body:
- type: markdown
attributes:

80
.github/workflows/label-author.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: label-author
on:
pull_request:
types: [opened]
issues:
types: [opened]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
debug-context:
runs-on: ubuntu-latest
steps:
- name: View context attributes
uses: actions/github-script@v7
with:
script: console.log(context)
label-created-by:
name: Label pr/issue on opening
runs-on: ubuntu-latest
steps:
- name: Tag with 'created-by'
uses: actions/github-script@v7
if: github.event.action == 'opened'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const payloadTeamUsernames = [
'denolfe',
'jmikrut',
'DanRibbens',
'jacobsfletch',
'JarrodMFlesch',
'AlessioGr',
'JessChowdhury',
'kendelljoseph',
'PatrikKozak',
'tylandavis',
'paulpopus',
];
const type = context.payload.pull_request ? 'pull_request' : 'issue';
const isTeamMember = payloadTeamUsernames
.map(n => n.toLowerCase())
.includes(context.payload[type].user.login.toLowerCase());
if (isTeamMember) {
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['created-by: Payload team'],
});
console.log(`Added 'created-by: Payload team' label`);
return;
}
const association = context.payload[type].author_association;
let label = ''
if (association === 'MEMBER' || association === 'OWNER') {
label = 'created-by: Payload team';
} else if (association === 'CONTRIBUTOR') {
label = 'created-by: Contributor';
}
if (!label) return;
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [label],
});
console.log(`Added '${label}' label.`);

116
.github/workflows/label-on-change.yml vendored Normal file
View File

@@ -0,0 +1,116 @@
name: label-on-change
on:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
issues:
types:
- assigned
- closed
- labeled
- reopened
# TODO: Handle labeling on comment
jobs:
on-labeled-ensure-one-status:
runs-on: ubuntu-latest
permissions:
issues: write
# Only run on issue labeled and if label starts with 'status:'
if: github.event.action == 'labeled' && startsWith(github.event.label.name, 'status:')
steps:
- name: Ensure only one status label
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Get all labels that start with 'status:' and are not the incoming label
const incomingLabelName = context.payload.label.name;
const labelNamesToRemove = context.payload.issue.labels
.filter(label => label.name.startsWith('status:') && label.name !== incomingLabelName)
.map(label => label.name);
if (!labelNamesToRemove.length) {
console.log('No labels to remove');
return;
}
console.log(`Labels to remove: '${labelNamesToRemove}'`);
// If there is more than one status label, remove all but the incoming label
for (const labelName of labelNamesToRemove) {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
name: labelName,
owner: context.repo.owner,
repo: context.repo.repo,
});
console.log(`Removed '${labelName}' label`);
}
on-issue-close:
runs-on: ubuntu-latest
permissions:
issues: write
if: github.event.action == 'closed'
steps:
- name: Remove all labels on issue close
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Get all labels that start with 'status:' and 'stale'
const labelNamesToRemove = context.payload.issue.labels
.filter(label => label.name.startsWith('status:') || label.name === 'stale')
.map(label => label.name);
if (!labelNamesToRemove.length) {
console.log('No labels to remove');
return;
}
console.log(`Labels to remove: '${labelNamesToRemove}'`);
for (const labelName of labelNamesToRemove) {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
name: labelName,
owner: context.repo.owner,
repo: context.repo.repo,
});
console.log(`Removed '${labelName}' label`);
}
on-issue-reopen:
runs-on: ubuntu-latest
permissions:
issues: write
if: github.event.action == 'reopened'
steps:
- name: Add needs-triage label on issue reopen
uses: actions-ecosystem/action-add-labels@v1
with:
labels: 'status: needs-triage'
on-issue-assigned:
runs-on: ubuntu-latest
permissions:
issues: write
if: >
github.event.action == 'assigned' &&
contains(github.event.issue.labels.*.name, 'status: needs-triage')
steps:
- name: Remove needs-triage label on issue assign
uses: actions-ecosystem/action-remove-labels@v1
with:
labels: 'status: needs-triage'
# on-pr-merge:
# runs-on: ubuntu-latest
# if: github.event.pull_request.merged == true
# steps:
# on-pr-close:
# runs-on: ubuntu-latest
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
# steps:

View File

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

42
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: stale
on:
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
id: stale
with:
debug-only: true
days-before-stale: 90
days-before-close: 7
ascending: true
operations-per-run: 300
# Ignore all assigned
exempt-all-assignees: false
# Issues
stale-issue-label: 'stale'
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
stale-issue-message: >
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
close-issue-message: >
This issue was automatically closed due to lack of activity.
# Pull Requests
stale-pr-label: 'stale'
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
stale-pr-message: >
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
close-pr-message: >
This pull request was automatically closed due to lack of activity.
- name: Print outputs
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}

View File

@@ -1,3 +1,71 @@
## [2.18.3](https://github.com/payloadcms/payload/compare/v2.18.2...v2.18.3) (2024-05-17)
### Bug Fixes
* **db-postgres:** query with like on id columns ([#6416](https://github.com/payloadcms/payload/issues/6416)) ([bf77cec](https://github.com/payloadcms/payload/commit/bf77cec7e9e7db4988e481d464178636203fca32))
* **db-postgres:** uuid custom db name ([#6409](https://github.com/payloadcms/payload/issues/6409)) ([db5f3f3](https://github.com/payloadcms/payload/commit/db5f3f3ccdaedd9e8036c3e39fc20650a309a151))
* nested `disableListColumn` in rows ([#6412](https://github.com/payloadcms/payload/issues/6412)) ([ab8b2f3](https://github.com/payloadcms/payload/commit/ab8b2f3fb87864484582b7d819ca307888a9449b)), closes [#6407](https://github.com/payloadcms/payload/issues/6407)
## [2.18.2](https://github.com/payloadcms/payload/compare/v2.18.1...v2.18.2) (2024-05-17)
### Bug Fixes
* allow focal point when no sizes defined ([#6397](https://github.com/payloadcms/payload/issues/6397)) ([88e113a](https://github.com/payloadcms/payload/commit/88e113a5452300434f690186d10ea02ab159ffc3))
## [2.18.1](https://github.com/payloadcms/payload/compare/v2.18.0...v2.18.1) (2024-05-16)
### Bug Fixes
* add back explicit crop x and y values ([#6391](https://github.com/payloadcms/payload/issues/6391)) ([e76df32](https://github.com/payloadcms/payload/commit/e76df32f0987cc92dc8d9c693950e650c52576bf))
## [2.18.0](https://github.com/payloadcms/payload/compare/v2.17.0...v2.18.0) (2024-05-16)
### Features
* store focal point on uploads ([#6364](https://github.com/payloadcms/payload/issues/6364)) ([82b88a3](https://github.com/payloadcms/payload/commit/82b88a315ff1d52f0b19a70224d5c600a3a97eb5))
### Bug Fixes
* **db-postgres:** filter with ID not_in AND queries - postgres ([#6358](https://github.com/payloadcms/payload/issues/6358)) ([cc94078](https://github.com/payloadcms/payload/commit/cc940786072c0065f10fdd2893050bddc4595a21)), closes [#5151](https://github.com/payloadcms/payload/issues/5151)
* **richtext-lexical:** upload, relationship and block node insertion fails sometimes ([#6390](https://github.com/payloadcms/payload/issues/6390)) ([48a410e](https://github.com/payloadcms/payload/commit/48a410e294598af9c73577a04f86466248f93da0))
## [2.17.0](https://github.com/payloadcms/payload/compare/v2.16.1...v2.17.0) (2024-05-15)
### Features
* adds misc translations to API view and react select ([#6138](https://github.com/payloadcms/payload/issues/6138)) ([30e535b](https://github.com/payloadcms/payload/commit/30e535b5b929dddead007d8a9adca62808595e2c))
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types ([#6279](https://github.com/payloadcms/payload/issues/6279)) ([9df5ab8](https://github.com/payloadcms/payload/commit/9df5ab8a10a35ad34615d7e4da024f59ff037e0e))
### Bug Fixes
* appends `editDepth` value to `radio` & `checkbox` IDs when inside drawer ([#6181](https://github.com/payloadcms/payload/issues/6181)) ([69c93d3](https://github.com/payloadcms/payload/commit/69c93d3c62394a5cf995a2eaec9a3ab30e0f77af))
* collection labels with locales not working when creating new doc ([#5995](https://github.com/payloadcms/payload/issues/5995)) ([51efe4f](https://github.com/payloadcms/payload/commit/51efe4f39bcaadccb109a2a02a690ca65041ee57))
* safely access cookie header for uploads ([#6367](https://github.com/payloadcms/payload/issues/6367)) ([de92c50](https://github.com/payloadcms/payload/commit/de92c50847640661f915455f8db0029873ddc7ab))
* step-nav breadcrumbs ellipsis ([#6345](https://github.com/payloadcms/payload/issues/6345)) ([d02b1fb](https://github.com/payloadcms/payload/commit/d02b1fb084e636e49122ad55b25b9c49eb761f1c))
*
### ⚠ BREAKING CHANGES
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types (#6279)
## [2.16.1](https://github.com/payloadcms/payload/compare/v2.16.0...v2.16.1) (2024-05-07)
### Features
* **richtext-lexical:** add maxDepth property to various lexical features ([#6250](https://github.com/payloadcms/payload/issues/6250)) ([857b9a4](https://github.com/payloadcms/payload/commit/857b9a4ac3236c740458750f156a3a4274eda210)), closes [#6242](https://github.com/payloadcms/payload/issues/6242)
### Bug Fixes
* **richtext-lexical:** export missing HorizontalRuleFeature ([#6236](https://github.com/payloadcms/payload/issues/6236)) ([f829b08](https://github.com/payloadcms/payload/commit/f829b084ba9649ef596cce4a7bf6ae8c7ccf57e3))
## [2.16.0](https://github.com/payloadcms/payload/compare/v2.15.0...v2.16.0) (2024-05-06)

View File

@@ -0,0 +1,39 @@
---
title: Examples
label: Overview
order: 10
desc:
keywords: example, examples, starter, boilerplate, template, templates
---
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated, so you can easily decipher what is going on.
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
- [Custom Server](https://github.com/payloadcms/payload/tree/main/examples/custom-server)
- [Draft Preview](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)
- [Email](https://github.com/payloadcms/payload/tree/main/examples/email)
- [Form Builder](https://github.com/payloadcms/payload/tree/main/examples/form-builder)
- [Hierarchy](https://github.com/payloadcms/payload/tree/main/examples/hierarchy)
- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)
- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)
- [Nested Docs](https://github.com/payloadcms/payload/tree/main/examples/nested-docs)
- [Redirects](https://github.com/payloadcms/payload/tree/main/examples/redirects)
- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)
- [Virtual Fields](https://github.com/payloadcms/payload/tree/main/examples/virtual-fields)
- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)
Where necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
```plaintext
example/
├── payload/
├── next-app/
├── next-pages/
├── react-router/
├── vue/
├── svelte/
```
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.

View File

@@ -31,6 +31,7 @@ With this field, you can also inject custom `Cell` components that appear as add
| **`label`** | Human-readable label for this UI field. |
| **`admin.components.Field`** \* | React component to be rendered for this field within the Edit view. [More](/docs/admin/components/#field-component) |
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](/docs/admin/components/#field-component) |
| **`admin.disableListColumn`** | Set `disableListColumn` to `true` to prevent the UI field from appearing in the list view column selector. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._

View File

@@ -1,30 +1,30 @@
---
title: Vercel Visual Editing
label: Vercel Visual Editing
title: Vercel Content Link
label: Vercel Content Link
order: 10
desc: Payload + Vercel Visual Editing allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.
keywords: vercel, vercel visual editing, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, express
desc: Payload + Vercel Content Link allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.
keywords: vercel, vercel content link, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, express
---
[Vercel Visual Editing](https://vercel.com/docs/workflow-collaboration/visual-editing) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload config.
[Vercel Content Link](https://vercel.com/docs/workflow-collaboration/edit-mode#content-link) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload config.
![Versions](/images/docs/vercel-visual-editing.jpg)
<Banner type="warning">
Vercel Visual Editing is an enterprise-only feature and only available for deployments hosted on
Vercel Content Link is an enterprise-only feature and only available for deployments hosted on
Vercel. If you are an existing enterprise customer, [contact our sales
team](https://payloadcms.com/for-enterprise) for help with your integration.
</Banner>
### How it works
To power Vercel Visual Editing, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Visual Editing interface.
To power Vercel Content Link, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Content Link interface.
For full details on how the encoding and decoding algorithm works, check out [`@vercel/stega`](https://www.npmjs.com/package/@vercel/stega).
### Getting Started
Setting up Payload with Vercel Visual Editing is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
Setting up Payload with Vercel Content Link is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
```bash
npm i @payloadcms/plugin-csm
@@ -76,7 +76,7 @@ And that's it! You are now ready to enter Edit Mode and begin visually editing y
##### Edit Mode
To see Visual Editing on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
To see Content Link on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
![Versions](/images/docs/vercel-toolbar.jpg)
@@ -93,7 +93,7 @@ const { cleaned, encoded } = vercelStegaSplit(text)
##### Blocks
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Visual Editing on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Content Link on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
```ts
<div data-vercel-edit-target>

133
docs/plugins/sentry.mdx Normal file
View File

@@ -0,0 +1,133 @@
---
title: Sentry Plugin
label: Sentry
order: 20
desc: Integrate Sentry error tracking into your Payload application
keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-sentry)](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
This plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.
### What is Sentry?
Sentry is a powerful error tracking and performance monitoring tool that helps developers identify, diagnose, and resolve issues in their applications.
<Banner type="success">
Sentry does smart stuff with error data to make bugs easier to find and fix. - [sentry.io](https://sentry.io/)
</Banner>
This multi-faceted software offers a range of features that will help you manage errors with greater ease and ultimately ensure your application is running smoothly:
#### Core Features
- **Error Tracking**: Instantly captures and logs errors as they occur in your application
- **Performance Monitoring**: Tracks application performance to identify slowdowns and bottlenecks
- **Detailed Reports**: Provides comprehensive insights into errors, including stack traces and context
- **Alerts and Notifications**: Send and customize event-triggered notifications
- **Issue Grouping, Filtering and Search**: Automatically groups similar errors, and allows filtering and searching issues by custom criteria
- **Breadcrumbs**: Records user actions and events leading up to an error
- **Integrations**: Connects with various tools and services for enhanced workflow and issue management
<Banner type="info">
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-seo%3A) with as much detail as possible.
</Banner>
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
yarn add @payloadcms/plugin-sentry
```
## Basic Usage
In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin and pass in your Sentry DSN as an option.
```ts
import { buildConfig } from 'payload/config'
import { sentry } from '@payloadcms/plugin-sentry'
import { Pages, Media } from './collections'
const config = buildConfig({
collections: [Pages, Media],
plugins: [
sentry({
dsn: 'https://61edebas776889984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
}),
],
})
export default config
```
## Options
- `dsn` : string | **required**
Sentry automatically assigns a DSN when you create a project, the unique DSN informs Sentry where to send events so they are associated with the correct project.
<Banner type="warning">
You can find your project DSN (Data Source Name) by visiting [sentry.io](sentry.io) and navigating to your [Project] > Settings > Client Keys (DSN).
</Banner>
- `enabled`: boolean | optional
Set to false to disable the plugin. Defaults to true.
- `init` : ClientOptions | optional
Sentry allows a variety of options to be passed into the Sentry.init() function, see the full list of options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
- `requestHandler` : RequestHandlerOptions | optional
Accepts options that let you decide what data should be included in the event sent to Sentry, checkout the options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
- `captureErrors`: number[] | optional
By default, `Sentry.errorHandler` will capture only errors with a status code of 500 or higher. To capture additional error codes, pass the values as numbers in an array.
To see all options available, visit the [Sentry Docs](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
### Example
Configure any of these options by passing them to the plugin:
```ts
import { buildConfig } from 'payload/config'
import { sentry } from '@payloadcms/plugin-sentry'
import { Pages, Media } from './collections'
const config = buildConfig({
collections: [Pages, Media],
plugins: [
sentry({
dsn: 'https://61edebas777689984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
options: {
init: {
debug: true,
environment: 'development',
tracesSampleRate: 1.0,
},
requestHandler: {
serverName: false,
user: ['email'],
},
captureErrors: [400, 403, 404],
},
}),
],
})
export default config
```
## TypeScript
All types can be directly imported:
```ts
import { PluginOptions } from '@payloadcms/plugin-sentry/types'
```

View File

@@ -0,0 +1,10 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp

View 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,
}

1
examples/db-example/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/migrations

View File

@@ -0,0 +1,10 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp

View 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"
}
}

View File

@@ -0,0 +1,32 @@
# Example Database Adapter for Payload
- [Main Repository](https://github.com/payloadcms/payload)
- [Payload Docs](https://payloadcms.com/docs)
To build a fully working database adapter for Payload you must implement the database adapter interface. There is a mix
of required and not required methods depending on the areas you need to support. Payload will call the adapter's `init`
function followed by the `connect` method if it exists. The adapter must create any schema necessary on the target
database in the init function. The adapter can be extended as needed to keep track of models for collections and other
artifacts necessary to perform all the needed operations.
## Installation
```bash
npm install @payloadcms/db-example
```
## Usage
```ts
import { buildConfig } from 'payload/config'
import { exampleAdapter } from '@payloadcms/db-example'
export default buildConfig({
db: exampleAdapter({
url: process.env.DATABASE_URI,
}),
// ...rest of config
})
```
More detailed usage can be found in the [Payload Docs](https://payloadcms.com/docs/configuration/overview).

View File

@@ -0,0 +1,40 @@
{
"name": "@payloadcms/db-example",
"private": true,
"version": "0.0.1",
"description": "A sample implementation of a Payload database adapter",
"repository": {
"type": "git",
"url": "https://github.com/payloadcms/payload.git",
"directory": "examples/db-example"
},
"license": "MIT",
"homepage": "https://payloadcms.com",
"author": {
"email": "info@payloadcms.com",
"name": "Payload",
"url": "https://payloadcms.com"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "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}",
"prepublishOnly": "pnpm clean && pnpm build"
},
"dependencies": {
},
"devDependencies": {
"@payloadcms/eslint-config": "^1.1.1",
"payload": "^2.18.3",
"rimraf": "^4.1.2"
},
"peerDependencies": {
"payload": "^2.0.0"
},
"files": [
"dist"
]
}

View File

@@ -0,0 +1,26 @@
import type { Connect } from 'payload/database'
import type { ExampleAdapter } from '.'
/**
* Implement the connect feature here. This will connect to the underlying resource and set the this.connection property.
*
* Optional - this method is not required
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {Payload} payload
* @returns {Promise<void>} A promise that resolves when the connection is established.
*
* @example
* ```ts
* this.connection = await myUnderlyingStore.connect(this.url)
* ```
*/
export const connect: Connect = async function connect(this: ExampleAdapter, payload) {
try {
// this.connection = await myUnderlyingStore.connect(this.url)
} catch (err) {
this.payload.logger.error(`Error: cannot connect to DB. Details: ${err.message}`, err)
process.exit(1)
}
}

View File

@@ -0,0 +1,26 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { Count } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Counts the total number of documents that match the query in the specified collection.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for counting documents.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the documents for counting.
* @returns {Promise<{ totalDocs: number }>} A promise resolving to an object containing the total number of documents.
*
* Implement the count function here for the specified collection and return the total number of documents that match the query.
*/
export const count: Count = async function count(
this: ExampleAdapter,
{ collection, locale, req = {} as PayloadRequest, where },
): Promise<{ totalDocs: number }> {
return {
totalDocs: 0,
}
}

View File

@@ -0,0 +1,51 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { Create } from 'payload/database'
import type { Document, PayloadRequest } from 'payload/types'
import type { ExampleAdapter } from '.'
import handleError from './errors/handleError'
/**
* Creates a new document in the specified collection.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for creating documents.
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
* @param {boolean} draft - Determine whether or not to create as a draft or not.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @returns {Promise<Document>} A promise resolving to the created document.
*/
export const create: Create = async function create(
this: ExampleAdapter,
{ collection, data, draft, locale, req = {} as PayloadRequest },
) {
let doc
try {
/**
* Here you would send your data to where ever it needs to go
*
* Implement the logic to create the document in your database.
*
* @example
* ```ts
* doc = await adapterSpecificModel.create(data, options)
* ```
*/
} catch (error) {
handleError(error, req)
}
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result: Document = doc
return result
}

View File

@@ -0,0 +1,40 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { CreateGlobal } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Creates a global document in the database.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} slug - The specified slug of the global.
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @returns {Promise<T>} A promise that resolves with the created global document.
*/
export const createGlobal: CreateGlobal = async function createGlobal(
this: ExampleAdapter,
{ slug, data, req = {} as PayloadRequest },
) {
let result
/**
* Implement the logic to create the global document in your database.
*
* @example
* ```ts
* result = await adapterSpecificModel.create(global, options)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
return result
}

View File

@@ -0,0 +1,53 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { CreateGlobalVersion } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import type { Document } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Creates a global version document in the database.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {boolean} autosave - Indicates if autosave is enabled.
* @param {Date} createdAt - Created-At date of the document.
* @param {string} globalSlug - The global slug of the document.
* @param {string} parent - ID of the parent document for which the version should be created for.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Date} updatedAt - Updated-At date of the document.
* @param {object} versionData - Full version data passed to create the global version.
* @returns {Promise<TypeWithVersion<T>>} A promise that resolves with the created global version document.
*/
export const createGlobalVersion: CreateGlobalVersion = async function createGlobalVersion(
this: ExampleAdapter,
{ autosave, createdAt, globalSlug, parent, req = {} as PayloadRequest, updatedAt, versionData },
) {
let doc
/**
* Implement the logic to create the global version document in your database.
*
* @example
* ```ts
* doc = await adapterSpecificVersionModel.create({
* autosave,
* createdAt,
* latest: true,
* parent,
* updatedAt,
* version: versionData,
* }, options, req)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result: Document = doc
return result
}

View File

@@ -0,0 +1,61 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { CreateVersion, TypeWithVersion } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import type { Document } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Creates a version document in the database.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {boolean} autosave - Indicates if autosave is enabled.
* @param {string} collectionSlug - The collection slug of the document.
* @param {Date} createdAt - Created-At date of the document.
* @param {string} parent - ID of the parent document for which the version should be created for.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Date} updatedAt - Updated-At date of the document.
* @param {object} versionData - Full version data passed to create the version.
* @returns {Promise<TypeWithVersion<T>>} A promise that resolves with the created version document.
*
*/
export const createVersion: CreateVersion = async function createVersion(
this: ExampleAdapter,
{
autosave,
collectionSlug,
createdAt,
parent,
req = {} as PayloadRequest,
updatedAt,
versionData,
},
) {
let doc
/**
* Implement the logic to create the version document in your database.
*
* @example
* ```ts
* doc = await adapterSpecificVersionModel.create({
* autosave,
* createdAt,
* latest: true,
* parent,
* updatedAt,
* version: versionData,
* }, options, req)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result: Document = doc
return result
}

View File

@@ -0,0 +1,28 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { DeleteMany } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Deletes multiple documents from the specified collection in the database.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for deleting documents.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the documents for deleting.
* @returns {Promise<void>} A promise that resolves when the documents are deleted.
*/
export const deleteMany: DeleteMany = async function deleteMany(
this: ExampleAdapter,
{ collection, req = {} as PayloadRequest, where },
) {
/**
* Implement the logic to delete many documents from your database.
*
* @example
* ```ts
* await adapterSpecificModel.deleteMany(query, options)
* ```
*/
}

View File

@@ -0,0 +1,43 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { DeleteOne } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { Document } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Deletes a single document from the specified collection in the database.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for deleting a document.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the document for deleting.
* @returns {Promise<Document>} A promise that resolves with the deleted document.
*/
export const deleteOne: DeleteOne = async function deleteOne(
this: ExampleAdapter,
{ collection, req = {} as PayloadRequest, where },
) {
let doc
/**
* Need to go delete your document through the API
*
* Implement the logic to delete the document from your database.
*
* @example
* ```ts
* doc = await adapterSpecificModel.delete(query, options)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result: Document = doc
return result
}

View File

@@ -0,0 +1,29 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { DeleteVersions } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Deletes many version documents from your database.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for deleting versions.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the documents for deleting versions.
* @returns {Promise<void>} A promise that resolves with the created global document.
*/
export const deleteVersions: DeleteVersions = async function deleteVersions(
this: ExampleAdapter,
{ collection, locale, req = {} as PayloadRequest, where },
) {
/**
* Implement the logic to delete many version documents from your database.
*
* @example
* ```ts
* await adapterSpecificVersionsModel.deleteMany(query, options)
* ```
*/
}

View File

@@ -0,0 +1,33 @@
import type { Destroy } from 'payload/database'
import type { ExampleAdapter } from './index'
/**
* Closes the database connection and cleans up resources used by the adapter.
*
* This function is typically used to gracefully shutdown the database connections
* when the application is closing or when the adapter is no longer needed.
*
* Optional - this method is not required
*
* @param {ExampleAdapter} - The ExampleAdapter instance.
* @returns {Promise<void>}
*/
export const destroy: Destroy = async function destroy(this: ExampleAdapter) {
/**
* If using an in-memory database or a similar service, add the specific steps to drop the database and stop the server.
*
* @example
* ```ts
* if (this.inMemoryDatabase) {
* await this.connection.dropDatabase()
*
* await this.connection.close()
*
* await this.inMemoryDatabase.stop()
* } else {
* await this.connection.close()
* }
* ```
*/
}

View File

@@ -0,0 +1,18 @@
/**
* Handle uniqueness error
*
* If the error is from a unique or required field - return the expected format using validation error
*/
const handleError = (error, req) => {
// throw new ValidationError(
// [
// {
// field: field.name,
// message: req.t('error:valueMustBeUnique'),
// },
// ],
// req.t,
// )
}
export default handleError

View File

@@ -0,0 +1,45 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { Find, PaginatedDocs } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Queries for documents in the specified collection based on the provided criteria using the incoming where,
* sort, page query and then only return the correct documents in the format Payload expects.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to query for documents.
* @param {number} limit - The maximum number of documents to return.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {number} page - The page number of the results to return.
* @param {boolean} pagination - Determines whether pagination is enabled.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {string} sort - The top-level field to sort the results by.
* @param {Where} where - The specific query used to filter documents.
* @returns {Promise<PaginatedDocs<T>>} A promise resolving to the paginated documents matching the query criteria.
*/
export const find: Find = async function find(
this: ExampleAdapter,
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
) {
let result
/**
* Implement the logic to paginate the query results according to your database's methods.
*
* @example
* ```ts
* const result = await adapterSpecificModel.paginate(query, paginationOptions)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
return result
}

View File

@@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { FindGlobal } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Finds a global document based on the specified criteria using the incoming slug, locale, and
* where query, then returns it in the format expected by Payload.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} slug - The specified slug of the global document.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the global document.
* @returns {Promise<T>} A promise resolving to the found global document or null if not found.
*/
export const findGlobal: FindGlobal = async function findGlobal(
this: ExampleAdapter,
{ slug, locale, req = {} as PayloadRequest, where },
) {
let doc
/**
* Implement the logic to find a global in your database.
*
* @example
* ```ts
* let doc = await adapterSpecificModel.findOne(query, {}, options)
* ```
*/
if (!doc) {
return null
}
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
*/
return doc
}

View File

@@ -0,0 +1,57 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { FindGlobalVersions, PaginatedDocs, TypeWithVersion } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Finds versions of a global document based on the specified criteria using the incoming global, limit, locale, page,
* pagination, req, skip, sort, and where parameters, and returns them in the format expected by Payload.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} global - The name of the global document to reference for finding versions.
* @param {number} limit - The maximum number of versions to return.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {number} page - The page number of the results to return.
* @param {boolean} pagination - Determines whether pagination is enabled.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {boolean} skip - Middleware function that can bypass the limit if it returns true.
* @param {string} sort - The top-level field to sort the results by.
* @param {Where} where - The specific query used to filter global versions.
* @returns {Promise<PaginatedDocs<TypeWithVersion<T>>>} A promise resolving to the paginated versions matching the query criteria.
*/
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
this: ExampleAdapter,
{
global,
limit,
locale,
page,
pagination,
req = {} as PayloadRequest,
skip,
sort: sortArg,
where,
},
) {
let result
/**
* Implement the logic to paginate the query results according to your database's methods.
*
* @example
* ```ts
* const result = await adapterSpecificModel.paginate(query, paginationOptions)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
return result
}

View File

@@ -0,0 +1,47 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { FindOne } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { Document } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Finds a single document in the specified collection based on the provided criteria using
* the incoming locale and where query, and returns it in the format expected by Payload.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for finding the document.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the document.
* @returns {Promise<T>} A promise resolving to the found document or null if not found.
*/
export const findOne: FindOne = async function findOne(
this: ExampleAdapter,
{ collection, locale, req = {} as PayloadRequest, where },
) {
let doc
/**
* Implement the logic to find one document in your database.
*
* @example
* ```ts
* const doc = await adapterSpecificModel.findOne(query, {}, options)
* ```
*/
if (!doc) {
return null
}
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result: Document = doc
return result
}

View File

@@ -0,0 +1,56 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { FindVersions, PaginatedDocs } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Queries for versions of documents in the specified collection based on the provided criteria using the incoming where,
* sort, page query and then only returns the correct document versions in the format Payload expects.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for finding versions.
* @param {number} limit - The maximum number of versions to return.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {number} page - The page number of the results to return.
* @param {boolean} pagination - Determines whether pagination is enabled.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {boolean} skip - Middleware function that can bypass the limit if it returns true.
* @param {string} sort - The top-level field to sort the results by.
* @param {Where} where - The specific query used to filter document versions.
* @returns {Promise<PaginatedDocs<TypeWithVersion<T>>>} A promise resolving to the paginated document versions matching the query criteria.
*/
export const findVersions: FindVersions = async function findVersions(
this: ExampleAdapter,
{
collection,
limit,
locale,
page,
pagination,
req = {} as PayloadRequest,
skip,
sort: sortArg,
where,
},
) {
let result
/**
* Implement the logic to paginate the query results according to your database's methods.
*
* @example
* ```ts
* const result = await adapterSpecificModel.paginate(query, paginationOptions)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*/
return result
}

View File

@@ -0,0 +1,130 @@
import type { Payload } from 'payload'
import type { BaseDatabaseAdapter } from 'payload/database'
import { createDatabaseAdapter } from 'payload/database'
import { connect } from './connect'
import { count } from './count'
import { create } from './create'
import { createGlobal } from './createGlobal'
import { createGlobalVersion } from './createGlobalVersion'
import { createVersion } from './createVersion'
import { deleteMany } from './deleteMany'
import { deleteOne } from './deleteOne'
import { deleteVersions } from './deleteVersions'
import { destroy } from './destroy'
import { find } from './find'
import { findGlobal } from './findGlobal'
import { findGlobalVersions } from './findGlobalVersions'
import { findOne } from './findOne'
import { findVersions } from './findVersions'
import { init } from './init'
import { queryDrafts } from './queryDrafts'
import { beginTransaction } from './transactions/beginTransaction'
import { commitTransaction } from './transactions/commitTransaction'
import { rollbackTransaction } from './transactions/rollbackTransaction'
import { updateGlobal } from './updateGlobal'
import { updateGlobalVersion } from './updateGlobalVersion'
import { updateOne } from './updateOne'
import { updateVersion } from './updateVersion'
/**
* Configure any adapter-specific options here
*
* For example: connection options, transaction options, etc.
*/
export interface Args {
url: string
}
/**
* The exported adapter Type
*
* _Optionally_, expose any adapter-specific methods here. How much you expose here relies on your underlying implementation.
*/
export type ExampleAdapter = BaseDatabaseAdapter &
Args & {
/**
* This will allow access to the underlying model via `payload.db.collections.<slug>.find()
*
* This optional
*/
collections: {
[slug: string]: unknown
}
/**
* Access to underlying global model via `payload.db.globals`
*/
globals: {
[slug: string]: unknown
}
}
type ExampleAdapterResult = (args: { payload: Payload }) => ExampleAdapter
/**
* This declaration injects the proper types for the DB Adapter into Payload when accessing the adapter in code
*
* Optional
*/
declare module 'payload' {
export interface DatabaseAdapter extends BaseDatabaseAdapter {
collections: {
[slug: string]: unknown
}
globals: {
[slug: string]: unknown
}
versions: {
[slug: string]: unknown
}
}
}
export function exampleAdapter({ url }: Args): ExampleAdapterResult {
function adapter({ payload }: { payload: Payload }) {
return createDatabaseAdapter<ExampleAdapter>({
name: 'example',
// Example adapter-specific
collections: {},
count,
globals: undefined,
url,
/**
* Configure DatabaseAdapter
*
* Many of these are optional and will fallback on some default functionality if not defined.
*/
beginTransaction,
commitTransaction,
connect,
create,
createGlobal,
createGlobalVersion,
createVersion,
defaultIDType: 'text',
deleteMany,
deleteOne,
deleteVersions,
destroy,
find,
findGlobal,
findGlobalVersions,
findOne,
findVersions,
init,
payload,
queryDrafts,
rollbackTransaction,
updateGlobal,
updateGlobalVersion,
updateOne,
updateVersion,
})
}
return adapter
}

View File

@@ -0,0 +1,40 @@
/* eslint-disable no-param-reassign */
import type { Init } from 'payload/database'
import type { ExampleAdapter } from '.'
/**
* Perform any initialization actions here.
* This will run immediately when the DB adapter is initialized and will take place before the connect method.
* If your implementation needs models created from Payload collections and globals, you would do this here.
* If your implementation needs to maintain a persistent connection, you should call this.connect here.
*
* Your DB adapter needs to be able to handle all the different Payload field types found in collections & globals:
*
* - Fields containing subfields:
* - 'array'
* - 'blocks'
* - 'collapsible'
* - 'group'
* - 'row'
* - 'tabs'
*
* - Other fields:
* - 'checkbox'
* - 'code'
* - 'date'
* - 'email'
* - 'json'
* - 'number'
* - 'point'
* - 'radio'
* - 'relationship'
* - 'richText'
* - 'select'
* - 'text'
* - 'textarea'
* - 'upload'
*
* @returns {Promise<void>}
*/
export const init: Init = async function init(this: ExampleAdapter) {}

View File

@@ -0,0 +1,46 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { PaginatedDocs, QueryDrafts } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Queries for drafts in the specified collection based on the provided criteria using
* the incoming where, sort, page query and then only returns the correct drafts in the format Payload expects.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} collection - The name of the collection to reference for querying drafts.
* @param {number} limit - The maximum number of drafts to return.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {number} page - The page number of the results to return.
* @param {boolean} pagination - Determines whether pagination is enabled.
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {string} sort - The top-level field to sort the results by.
* @param {Where} where - The specific query used to filter drafts.
* @returns {Promise<PaginatedDocs<T>>} A promise resolving to the paginated drafts matching the query criteria.
*/
export const queryDrafts: QueryDrafts = async function queryDrafts(
this: ExampleAdapter,
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
) {
let result
/**
* Implement the logic to paginate the query results according to your database's methods.
*
* @example
* ```ts
* const result = await adapterSpecificModel.paginate(versionQuery, paginationOptions)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
return result
}

View File

@@ -0,0 +1,24 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { TransactionOptions } from 'mongodb'
import type { BeginTransaction } from 'payload/database'
import type { ExampleAdapter } from '../index'
/**
* Begins a new transaction with the provided options.
*
* If you want to support database transactions, you would initialize them within this function.
* Alternatively, if transactions are not supported or not needed, this function can do nothing and return null.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {TransactionOptions} options - The options for the transaction.
* @returns {Promise<null>} A promise resolving to null.
*
* This function is optional and can be implemented as needed for database adapters that support transactions.
*/
export const beginTransaction: BeginTransaction = async function beginTransaction(
this: ExampleAdapter,
options: TransactionOptions,
) {
return null
}

View File

@@ -0,0 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { CommitTransaction } from 'payload/database'
/**
* Commits a transaction identified by its ID.
*
* Optional - this method is not required
*
* @param {string} id - The ID of the transaction to commit.
* @returns {Promise<void>}
*
* This function is optional and can be implemented as needed for database adapters that support transactions.
*/
export const commitTransaction: CommitTransaction = async function commitTransaction(id) {}

View File

@@ -0,0 +1,14 @@
import type { RollbackTransaction } from 'payload/database'
/**
* Rolls back a transaction identified by its ID.
*
* @param {string} id - The ID of the transaction to rollback.
* @returns {Promise<void>}
*
* This function is optional and can be implemented as needed for database adapters that support transactions.
*/
export const rollbackTransaction: RollbackTransaction = async function rollbackTransaction(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
id = '',
) {}

View File

@@ -0,0 +1,40 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { UpdateGlobal } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Updates a global document in the specified collection based on the provided criteria.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} slug - The specified slug of the global.
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @returns {Promise<T>} A promise resolving to the updated global document.
*/
export const updateGlobal: UpdateGlobal = async function updateGlobal(
this: ExampleAdapter,
{ slug, data, req = {} as PayloadRequest },
) {
/**
* Implement the logic to find one and update the document in your database.
*
* @example
* ```ts
* result = await adapterSpecificModel.findOneAndUpdate({ globalType: slug }, data, options)
* ```
*/
let result
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
return result
}

View File

@@ -0,0 +1,52 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { UpdateGlobalVersionArgs } from 'payload/database'
import type { PayloadRequest, TypeWithID, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Updates a global version document in the specified collection based on the provided criteria using
* the incoming ID, global, locale, and versionData, then returns the updated global version document in the format Payload expects.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} id - The ID of the global version.
* @param {string} global - The name of the global to reference for updating a global's version.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {object} versionData - Full version data passed to update the global version.
* @param {Where} where - The specific query for querying the global version documents in question to update.
* @returns {Promise<any>} A promise resolving to the updated global version document.
*/
export async function updateGlobalVersion<T extends TypeWithID>(
this: ExampleAdapter,
{
id,
global,
locale,
req = {} as PayloadRequest,
versionData,
where,
}: UpdateGlobalVersionArgs<T>,
) {
let doc
/**
* Implement the logic to find one and update the document in your database.
*
* @example
* ```ts
* const doc = await adapterSpecificModel.findOneAndUpdate(query, versionData, options)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result = doc
return result
}

View File

@@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { UpdateOne } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Updates a single document in the specified collection based on the provided criteria using
* the incoming ID, collection, locale, and data, then returns the updated document in the format Payload expects.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} id - The ID of the collection document.
* @param {string} collection - The name of the collection to reference for updating one.
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {Where} where - The specific query used to find the documents for updating.
* @returns {Promise<Document>} A promise resolving to the updated document.
*/
export const updateOne: UpdateOne = async function updateOne(
this: ExampleAdapter,
{ id, collection, data, locale, req = {} as PayloadRequest, where },
) {
let result
/**
* Implement the logic to update one document in your database.
*
* @example
* ```ts
* const result = await adapterSpecificModel.findOneAndUpdate(query, data, options)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
return result
}

View File

@@ -0,0 +1,45 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { UpdateVersion } from 'payload/database'
import type { PayloadRequest, Where } from 'payload/types'
import type { ExampleAdapter } from '.'
/**
* Updates a version of a document in the specified collection based on the provided criteria using
* the incoming ID, collection, locale, and versionData, then returns the updated version in the format Payload expects.
*
* @param {ExampleAdapter} this - The ExampleAdapter instance.
* @param {string} id - The ID of the collection document.
* @param {string} collection - The name of the collection to reference for updating a document's version.
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
* @param {object} versionData - Full version data passed to create the version.
* @param {Where} where - The specific query used to find the documents for updating its versions.
* @returns {Promise<TypeWithVersion<T>>} A promise resolving to the updated version document.
*/
export const updateVersion: UpdateVersion = async function updateVersion(
this: ExampleAdapter,
{ id, collection, locale, req = {} as PayloadRequest, versionData, where },
) {
let doc
/**
* Implement the logic to update a document's version in your database.
*
* @example
* ```ts
* const doc = await adapterSpecificModel.findOneAndUpdate(query, versionData, options)
* ```
*/
/**
* This should be the shape of the data that gets returned in Payload when you do:
*
* ?depth=0&locale=all&fallbackLocale=null
*
* The result of the outgoing data is always going to be the same shape that Payload expects
*
*/
const result = doc
return result
}

View File

@@ -0,0 +1,24 @@
{
"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. */
},
"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
}

View File

@@ -7,4 +7,4 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
max_line_length = null
max_line_length = off

View File

@@ -7,4 +7,4 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
max_line_length = null
max_line_length = off

File diff suppressed because it is too large Load Diff

View File

@@ -7,4 +7,4 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
max_line_length = null
max_line_length = off

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,4 +7,4 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
max_line_length = null
max_line_length = off

View File

@@ -7,4 +7,4 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
max_line_length = null
max_line_length = off

View File

@@ -129,6 +129,6 @@
},
"dependencies": {
"@sentry/react": "^7.77.0",
"ajv": "^8.12.0"
"ajv": "8.14.0"
}
}

View File

@@ -47,7 +47,7 @@
"webpack": "^5.78.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.10.0",
"webpack-dev-middleware": "6.0.1",
"webpack-dev-middleware": "6.1.2",
"webpack-hot-middleware": "^2.25.3"
},
"devDependencies": {

View File

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

View File

@@ -45,8 +45,7 @@ export async function createVersion<T extends TypeWithID>(
const table = this.tables[tableName]
const relationshipsTable =
this.tables[`_${defaultTableName}${this.versionsSuffix}${this.relationshipsSuffix}`]
const relationshipsTable = this.tables[`${tableName}${this.relationshipsSuffix}`]
if (collection.versions.drafts) {
await db.execute(sql`

View File

@@ -10,8 +10,8 @@ import type { GenericColumn, PostgresAdapter } from '../types'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
import { buildAndOrConditions } from './buildAndOrConditions'
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
import { createJSONQuery } from './createJSONQuery'
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
import { getTableColumnFromPath } from './getTableColumnFromPath'
import { operatorMap } from './operatorMap'
import { sanitizeQueryValue } from './sanitizeQueryValue'
@@ -71,7 +71,7 @@ export async function parseParams({
// So we need to loop on keys again here to handle each operator independently
const pathOperators = where[relationOrPath]
if (typeof pathOperators === 'object') {
for (const operator of Object.keys(pathOperators)) {
for (let operator of Object.keys(pathOperators)) {
if (validOperators.includes(operator as Operator)) {
const val = where[relationOrPath][operator]
const {
@@ -157,6 +157,13 @@ export async function parseParams({
break
}
if (
operator === 'like' &&
(field.type === 'number' || table[columnName].columnType === 'PgUUID')
) {
operator = 'equals'
}
if (operator === 'like') {
constraints.push(
and(...val.split(' ').map((word) => ilike(table[columnName], `%${word}%`))),
@@ -195,10 +202,10 @@ export async function parseParams({
operator === 'not_in'
) {
constraints.push(
sql`${notInArray(table[columnName], queryValue)} OR
sql`(${notInArray(table[columnName], queryValue)} OR
${table[columnName]}
IS
NULL`,
NULL)`,
)
break

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "2.16.0",
"version": "2.18.3",
"description": "Node, React and MongoDB Headless CMS and Application Framework",
"license": "MIT",
"main": "./dist/index.js",

View File

@@ -19,6 +19,7 @@ import { ConfigProvider } from './components/utilities/Config'
import { CustomProvider } from './components/utilities/CustomProvider'
import { DocumentEventsProvider } from './components/utilities/DocumentEvents'
import { I18n } from './components/utilities/I18n'
import { LanguageWrap } from './components/utilities/LanguageWrap'
import { LoadingOverlayProvider } from './components/utilities/LoadingOverlay'
import { LocaleProvider } from './components/utilities/Locale'
import { PreferencesProvider } from './components/utilities/Preferences'
@@ -46,21 +47,23 @@ const Root = ({ config: incomingConfig }: { config?: SanitizedConfig }) => {
<AuthProvider>
<PreferencesProvider>
<ThemeProvider>
<SearchParamsProvider>
<LocaleProvider>
<StepNavProvider>
<LoadingOverlayProvider>
<DocumentEventsProvider>
<NavProvider>
<CustomProvider>
<Routes />
</CustomProvider>
</NavProvider>
</DocumentEventsProvider>
</LoadingOverlayProvider>
</StepNavProvider>
</LocaleProvider>
</SearchParamsProvider>
<LanguageWrap>
<SearchParamsProvider>
<LocaleProvider>
<StepNavProvider>
<LoadingOverlayProvider>
<DocumentEventsProvider>
<NavProvider>
<CustomProvider>
<Routes />
</CustomProvider>
</NavProvider>
</DocumentEventsProvider>
</LoadingOverlayProvider>
</StepNavProvider>
</LocaleProvider>
</SearchParamsProvider>
</LanguageWrap>
</ThemeProvider>
<ModalContainer />
</PreferencesProvider>

View File

@@ -5,6 +5,7 @@ import type { CollectionPermission, GlobalPermission } from '../../../../auth'
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
import type { SanitizedGlobalConfig } from '../../../../globals/config/types'
import { getTranslation } from '../../../../utilities/getTranslation'
import { formatDate } from '../../../utilities/formatDate'
import { useConfig } from '../../utilities/Config'
import { useDocumentInfo } from '../../utilities/DocumentInfo'
@@ -63,6 +64,12 @@ export const DocumentControls: React.FC<{
collection && id && !disableActions && (hasCreatePermission || hasDeletePermission),
)
const collectionLabel = () => {
const label = collection?.labels?.singular
if (!label) return t('document')
return typeof label === 'string' ? label : getTranslation(label, i18n)
}
return (
<Gutter className={baseClass}>
<div className={`${baseClass}__wrapper`}>
@@ -71,12 +78,7 @@ export const DocumentControls: React.FC<{
{collection && !isEditing && !isAccountView && (
<li className={`${baseClass}__list-item`}>
<p className={`${baseClass}__value`}>
{t('creatingNewLabel', {
label:
typeof collection?.labels?.singular === 'string'
? collection.labels.singular
: t('document'),
})}
{t('creatingNewLabel', { label: collectionLabel() })}
</p>
</li>
)}

View File

@@ -32,7 +32,7 @@ export const EditUpload: React.FC<{
imageCacheTag?: string
showCrop?: boolean
showFocalPoint?: boolean
}> = ({ fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
}> = ({ doc, fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
const { closeModal } = useModal()
const { t } = useTranslation(['general', 'upload'])
const { formQueryParams, setFormQueryParams } = useFormQueryParams()
@@ -45,10 +45,11 @@ export const EditUpload: React.FC<{
y: uploadEdits?.crop?.y || 0,
})
const [pointPosition, setPointPosition] = useState<{ x: number; y: number }>({
x: uploadEdits?.focalPoint?.x || 50,
y: uploadEdits?.focalPoint?.y || 50,
const [focalPosition, setFocalPosition] = useState<{ x: number; y: number }>({
x: uploadEdits?.focalPoint?.x || doc.focalX || 50,
y: uploadEdits?.focalPoint?.y || doc.focalY || 50,
})
const [checkBounds, setCheckBounds] = useState<boolean>(false)
const [originalHeight, setOriginalHeight] = useState<number>(0)
const [originalWidth, setOriginalWidth] = useState<number>(0)
@@ -72,10 +73,16 @@ export const EditUpload: React.FC<{
})
}
const fineTuneFocalPoint = ({ coordinate, value }: { coordinate: 'x' | 'y'; value: string }) => {
const fineTuneFocalPosition = ({
coordinate,
value,
}: {
coordinate: 'x' | 'y'
value: string
}) => {
const intValue = parseInt(value)
if (intValue >= 0 && intValue <= 100) {
setPointPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue }))
setFocalPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue }))
}
}
@@ -84,14 +91,14 @@ export const EditUpload: React.FC<{
...formQueryParams,
uploadEdits: {
crop: crop || undefined,
focalPoint: pointPosition ? pointPosition : undefined,
focalPoint: focalPosition ? focalPosition : undefined,
},
})
closeModal(editDrawerSlug)
}
const onDragEnd = React.useCallback(({ x, y }) => {
setPointPosition({ x, y })
setFocalPosition({ x, y })
setCheckBounds(false)
}, [])
@@ -104,7 +111,7 @@ export const EditUpload: React.FC<{
((boundsRect.left - containerRect.left + boundsRect.width / 2) / containerRect.width) * 100
const yCenter =
((boundsRect.top - containerRect.top + boundsRect.height / 2) / containerRect.height) * 100
setPointPosition({ x: xCenter, y: yCenter })
setFocalPosition({ x: xCenter, y: yCenter })
}
const fileSrcToUse = imageCacheTag ? `${fileSrc}?${imageCacheTag}` : fileSrc
@@ -180,7 +187,7 @@ export const EditUpload: React.FC<{
checkBounds={showCrop ? checkBounds : false}
className={`${baseClass}__focalPoint`}
containerRef={focalWrapRef}
initialPosition={pointPosition}
initialPosition={focalPosition}
onDragEnd={onDragEnd}
setCheckBounds={showCrop ? setCheckBounds : false}
>
@@ -251,13 +258,13 @@ export const EditUpload: React.FC<{
<div className={`${baseClass}__inputsWrap`}>
<Input
name="X %"
onChange={(value) => fineTuneFocalPoint({ coordinate: 'x', value })}
value={pointPosition.x.toFixed(0)}
onChange={(value) => fineTuneFocalPosition({ coordinate: 'x', value })}
value={focalPosition.x.toFixed(0)}
/>
<Input
name="Y %"
onChange={(value) => fineTuneFocalPoint({ coordinate: 'y', value })}
value={pointPosition.y.toFixed(0)}
onChange={(value) => fineTuneFocalPosition({ coordinate: 'y', value })}
value={focalPosition.y.toFixed(0)}
/>
</div>
</div>

View File

@@ -24,7 +24,6 @@ export const LoadingOverlay: React.FC<Props> = ({
show = true,
}) => {
const { t } = useTranslation('general')
return (
<div
className={[
@@ -59,25 +58,25 @@ type UseLoadingOverlayToggleT = {
}
export const LoadingOverlayToggle: React.FC<UseLoadingOverlayToggleT> = ({
name: key,
type = 'fullscreen',
loadingText,
show,
type = 'fullscreen',
}) => {
const { toggleLoadingOverlay } = useLoadingOverlay()
React.useEffect(() => {
toggleLoadingOverlay({
type,
isLoading: show,
key,
loadingText: loadingText || undefined,
type,
})
return () => {
toggleLoadingOverlay({
type,
isLoading: false,
key,
type,
})
}
}, [show, toggleLoadingOverlay, key, type, loadingText])
@@ -94,10 +93,10 @@ type FormLoadingOverlayToggleT = {
}
export const FormLoadingOverlayToggle: React.FC<FormLoadingOverlayToggleT> = ({
name,
type = 'fullscreen',
action,
formIsLoading = false,
loadingSuffix,
type = 'fullscreen',
}) => {
const isProcessing = useFormProcessing()
const { i18n, t } = useTranslation('general')

View File

@@ -40,7 +40,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
isCreatable,
isLoading,
isSearchable = true,
noOptionsMessage,
noOptionsMessage = () => t('general:noOptions'),
numberOnly = false,
onChange,
onMenuOpen,
@@ -50,6 +50,8 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
value,
} = props
const loadingMessage = () => t('general:loading') + '...'
const classes = [className, 'react-select', showError && 'react-select--error']
.filter(Boolean)
.join(' ')
@@ -79,6 +81,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
isClearable={isClearable}
isDisabled={disabled}
isSearchable={isSearchable}
loadingMessage={loadingMessage}
menuPlacement="auto"
noOptionsMessage={noOptionsMessage}
onChange={onChange}
@@ -148,6 +151,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
isClearable={isClearable}
isDisabled={disabled}
isSearchable={isSearchable}
loadingMessage={loadingMessage}
menuPlacement="auto"
noOptionsMessage={noOptionsMessage}
onChange={onChange}

View File

@@ -2,9 +2,10 @@ import React, { useRef } from 'react'
import { useTranslation } from 'react-i18next'
import useHotkey from '../../../hooks/useHotkey'
import { useForm } from '../../forms/Form/context'
import { useForm, useFormModified } from '../../forms/Form/context'
import FormSubmit from '../../forms/Submit'
import { useEditDepth } from '../../utilities/EditDepth'
import { useOperation } from '../../utilities/OperationProvider'
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
export type CustomSaveButtonProps = React.ComponentType<
@@ -20,17 +21,26 @@ type DefaultSaveButtonProps = {
const DefaultSaveButton: React.FC<DefaultSaveButtonProps> = ({ label, save }) => {
const ref = useRef<HTMLButtonElement>(null)
const editDepth = useEditDepth()
const operation = useOperation()
const modified = useFormModified()
const forceDisable = operation === 'update' && !modified
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
e.preventDefault()
e.stopPropagation()
if (forceDisable) {
return
}
if (ref?.current) {
ref.current.click()
}
})
return (
<FormSubmit buttonId="action-save" onClick={save} ref={ref} type="button" size="small">
<FormSubmit buttonId="action-save" onClick={save} ref={ref} size="small" type="button">
{label}
</FormSubmit>
)

View File

@@ -8,6 +8,7 @@ import { useConfig } from '../../utilities/Config'
import { useDocumentInfo } from '../../utilities/DocumentInfo'
import { useEditDepth } from '../../utilities/EditDepth'
import { useLocale } from '../../utilities/Locale'
import { useOperation } from '../../utilities/OperationProvider'
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
const baseClass = 'save-draft'
@@ -31,12 +32,13 @@ const DefaultSaveDraftButton: React.FC<DefaultSaveDraftButtonProps> = ({
const editDepth = useEditDepth()
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
e.preventDefault()
e.stopPropagation()
if (disabled) {
return
}
e.preventDefault()
e.stopPropagation()
if (ref?.current) {
ref.current.click()
}
@@ -68,11 +70,13 @@ export const SaveDraft: React.FC<Props> = ({ CustomComponent }) => {
} = useConfig()
const { submit } = useForm()
const { id, collection, global } = useDocumentInfo()
const operation = useOperation()
const modified = useFormModified()
const { code: locale } = useLocale()
const { t } = useTranslation('version')
const canSaveDraft = modified
const canSaveDraft = operation === 'update' && modified
const saveDraft = useCallback(async () => {
const search = `?locale=${locale}&depth=0&fallback-locale=null&draft=true`

View File

@@ -2,6 +2,7 @@
.step-nav {
display: flex;
align-items: center;
gap: calc(var(--base) / 2);
&::after {
@@ -56,8 +57,6 @@
}
span {
display: flex;
align-items: center;
max-width: base(8);
text-overflow: ellipsis;
overflow: hidden;

View File

@@ -9,12 +9,12 @@ import React, {
} from 'react'
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
import type { Field } from '../../../../fields/config/types'
import type { Props as CellProps } from '../../views/collections/List/Cell/types'
import type { ListPreferences } from '../../views/collections/List/types'
import type { Column } from '../Table/types'
import type { Action } from './columnReducer'
import { type Field, fieldHasSubFields } from '../../../../fields/config/types'
import { usePreferences } from '../../utilities/Preferences'
import formatFields from '../../views/collections/List/formatFields'
import buildColumns from './buildColumns'
@@ -35,6 +35,12 @@ export const useTableColumns = (): ITableColumns => useContext(TableColumnContex
const filterTableFields = (fields: Field[]): Field[] => {
return fields.reduce((acc, field) => {
if (fieldHasSubFields(field)) {
field = {
...field,
fields: filterTableFields(field.fields),
}
}
if (!field.admin?.disableListColumn) acc.push(field)
return acc
}, [])

View File

@@ -58,7 +58,8 @@ const reduceFields = (fields, i18n) =>
},
}
if (field.admin?.disableListFilter) return reduced
if (field.admin && 'disableListFilter' in field.admin && field.admin?.disableListFilter)
return reduced
return [...reduced, formattedField]
}

View File

@@ -5,6 +5,7 @@ import type { Props } from './types'
import { checkbox } from '../../../../../fields/validations'
import { getTranslation } from '../../../../../utilities/getTranslation'
import { useEditDepth } from '../../../utilities/EditDepth'
import DefaultError from '../../Error'
import FieldDescription from '../../FieldDescription'
import useField from '../../useField'
@@ -41,6 +42,8 @@ const Checkbox: React.FC<Props> = (props) => {
const path = pathFromProps || name
const editDepth = useEditDepth()
const memoizedValidate = useCallback(
(value, options) => {
return validate(value, { ...options, required })
@@ -62,7 +65,7 @@ const Checkbox: React.FC<Props> = (props) => {
}
}, [onChange, readOnly, setValue, value])
const fieldID = `field-${path.replace(/\./g, '__')}`
const fieldID = `field-${path.replace(/\./g, '__')}${editDepth > 1 ? `-${editDepth}` : ''}`
return (
<div

View File

@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import type { Props } from './types'
import { getTranslation } from '../../../../../../utilities/getTranslation'
import { useEditDepth } from '../../../../utilities/EditDepth'
import './index.scss'
const baseClass = 'radio-input'
@@ -12,9 +13,11 @@ const RadioInput: React.FC<Props> = (props) => {
const { isSelected, onChange, option, path, readOnly } = props
const { i18n } = useTranslation()
const editDepth = useEditDepth()
const classes = [baseClass, isSelected && `${baseClass}--is-selected`].filter(Boolean).join(' ')
const id = `field-${path}-${option.value}`
const id = `field-${path}-${option.value}${editDepth > 1 ? `-${editDepth}` : ''}`
return (
<label htmlFor={id}>

View File

@@ -69,14 +69,24 @@ const Relationship: React.FC<Props> = (props) => {
serverURL,
} = config
const hasMultipleRelations = Array.isArray(relationTo)
const initialLoadedPageState = hasMultipleRelations
? relationTo.reduce((acc, relation) => {
return {
...acc,
[relation]: 0,
}
}, {})
: {}
const { i18n, t } = useTranslation('fields')
const { permissions } = useAuth()
const { code: locale } = useLocale()
const formProcessing = useFormProcessing()
const hasMultipleRelations = Array.isArray(relationTo)
const [options, dispatchOptions] = useReducer(optionsReducer, [])
const [lastFullyLoadedRelation, setLastFullyLoadedRelation] = useState(-1)
const [lastLoadedPage, setLastLoadedPage] = useState<Record<string, number>>({})
const [lastLoadedPage, setLastLoadedPage] =
useState<Record<string, number>>(initialLoadedPageState)
const [errorLoading, setErrorLoading] = useState('')
const [filterOptionsResult, setFilterOptionsResult] = useState<FilterOptionsResult>()
const [search, setSearch] = useState('')
@@ -146,13 +156,12 @@ const Relationship: React.FC<Props> = (props) => {
if (resultsFetched < 10) {
const collection = collections.find((coll) => coll.slug === relation)
let fieldToSearch = collection?.defaultSort || collection?.admin?.useAsTitle || 'id'
if (!searchArg) {
if (typeof sortOptions === 'string') {
fieldToSearch = sortOptions
} else if (sortOptions?.[relation]) {
fieldToSearch = sortOptions[relation]
}
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
let fieldToSort = collection?.defaultSort || 'id'
if (typeof sortOptions === 'string') {
fieldToSort = sortOptions
} else if (sortOptions?.[relation]) {
fieldToSort = sortOptions[relation]
}
const query: {
@@ -164,7 +173,7 @@ const Relationship: React.FC<Props> = (props) => {
limit: maxResultsPerRequest,
locale,
page: lastLoadedPageToUse,
sort: fieldToSearch,
sort: fieldToSort,
where: {
and: [
{
@@ -267,7 +276,7 @@ const Relationship: React.FC<Props> = (props) => {
const handleInputChange = useCallback(
(searchArg: string, valueArg: Value | Value[]) => {
if (search !== searchArg) {
setLastLoadedPage({})
setLastLoadedPage(initialLoadedPageState)
updateSearch(searchArg, valueArg, searchArg !== '')
}
},
@@ -374,7 +383,7 @@ const Relationship: React.FC<Props> = (props) => {
dispatchOptions({ type: 'CLEAR' })
setLastFullyLoadedRelation(-1)
setLastLoadedPage({})
setLastLoadedPage(initialLoadedPageState)
setHasLoadedFirstPage(false)
}, [relationTo, filterOptionsResult, locale])

View File

@@ -1,9 +1,6 @@
@import '../../../scss/styles';
.icon--plus {
width: $baseline;
height: $baseline;
.stroke {
stroke: var(--theme-elevation-800);
stroke-width: $style-stroke-width;

View File

@@ -3,7 +3,12 @@ import React from 'react'
import './index.scss'
const Plus: React.FC = () => (
<svg className="icon icon--plus" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
<svg
className="icon icon--plus"
viewBox="0 0 25 25"
width="25"
xmlns="http://www.w3.org/2000/svg"
>
<line className="stroke" x1="12.4589" x2="12.4589" y1="16.9175" y2="8.50115" />
<line className="stroke" x1="8.05164" x2="16.468" y1="12.594" y2="12.594" />
</svg>

View File

@@ -0,0 +1,14 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import '../../../scss/app.scss'
const scriptLanguages = ['ar', 'fa']
export const LanguageWrap: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
const { i18n } = useTranslation()
const currentLanguage = i18n?.language
const isScriptLanguage = currentLanguage && scriptLanguages.includes(currentLanguage)
return <div className={isScriptLanguage ? `script-language` : ''}>{children}</div>
}

View File

@@ -27,9 +27,9 @@ const chars = {
const baseClass = 'query-inspector'
const Bracket = ({
type,
comma = false,
position,
type,
}: {
comma?: boolean
position: 'end' | 'start'
@@ -64,9 +64,9 @@ const RecursivelyRenderObjectData = ({
const objectKeys = Object.keys(object)
const objectLength = objectKeys.length
const [isOpen, setIsOpen] = React.useState<boolean>(true)
const isNestedAndEmpty = isEmpty && (parentType === 'object' || parentType === 'array')
return (
<li>
<li className={isNestedAndEmpty ? `${baseClass}__row-line--nested` : ''}>
<button
aria-label="toggle"
className={`${baseClass}__list-toggle ${isEmpty ? `${baseClass}__list-toggle--empty` : ''}`}
@@ -173,7 +173,7 @@ function createURL(url: string) {
export const API: React.FC<EditViewProps> = (props) => {
const { apiURL } = props
const { i18n } = useTranslation()
const { i18n, t } = useTranslation()
const {
localization,
routes: { api },
@@ -262,14 +262,14 @@ export const API: React.FC<EditViewProps> = (props) => {
<CheckboxInput
checked={draft}
id="draft-checkbox"
label="Draft"
label={t('version:draft')}
onToggle={() => setDraft(!draft)}
/>
)}
<CheckboxInput
checked={authenticated}
id="auth-checkbox"
label="Authenticated"
label={t('authentication:authenticated')}
onToggle={() => setAuthenticated(!authenticated)}
/>
</div>
@@ -280,7 +280,7 @@ export const API: React.FC<EditViewProps> = (props) => {
label: locale,
value: locale,
}}
label="Locale"
label={t('general:locale')}
name="locale"
onChange={(e) => setLocale(e.value as string)}
options={localeOptions}
@@ -292,7 +292,7 @@ export const API: React.FC<EditViewProps> = (props) => {
label: depth,
value: depth,
}}
label="Depth"
label={t('general:depth')}
name="depth"
onChange={(e) => setDepth(e.value as string)}
options={[

View File

@@ -116,10 +116,12 @@ export const Upload: React.FC<Props> = (props) => {
const hasImageSizes = collection?.upload?.imageSizes?.length > 0
const hasResizeOptions = Boolean(collection?.upload?.resizeOptions)
// Explicitly check if set to true, default is undefined
const focalPointEnabled = collection?.upload?.focalPoint === true
const { collection: { upload: { crop: showCrop = true, focalPoint = true } } = {} } = props
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions)
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions || focalPointEnabled)
const lastSubmittedTime = submitted ? new Date().toISOString() : null

View File

@@ -1,4 +1,5 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import type { CheckboxField } from '../../../../../../../../exports/types'
import type { CellComponentProps } from '../../types'
@@ -6,9 +7,13 @@ import type { CellComponentProps } from '../../types'
import './index.scss'
// Handles boolean values
const Checkbox: React.FC<CellComponentProps<CheckboxField>> = ({ data }) => (
<code className="bool-cell">
<span>{JSON.stringify(data)}</span>
</code>
)
const Checkbox: React.FC<CellComponentProps<CheckboxField>> = ({ data }) => {
const { t } = useTranslation('general')
if (typeof data !== 'boolean') return null
return (
<code className="bool-cell">
<span>{t(`${data}`).toLowerCase()}</span>
</code>
)
}
export default Checkbox

View File

@@ -200,4 +200,11 @@ dialog {
z-index: var(--z-modal);
}
.script-language {
& > *,
& > * > * {
letter-spacing: 0 !important;
}
}
@import '~payload-user-css';

View File

@@ -32,7 +32,7 @@ export const registerLocalStrategy = async ({
if (existingUser.docs.length > 0) {
throw new ValidationError([
{ field: 'email', message: 'A user with the given email is already registered' },
{ field: 'email', message: req.t('error:userEmailAlreadyRegistered') },
])
}

View File

@@ -130,6 +130,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
collection,
config,
data,
operation: 'create',
overwriteExistingFiles,
req,
throwOnMissingFile:

View File

@@ -157,6 +157,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
collection,
config,
data: bulkUpdateData,
operation: 'update',
overwriteExistingFiles,
req,
throwOnMissingFile: false,

View File

@@ -148,6 +148,8 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
collection,
config,
data,
operation: 'update',
originalDoc,
overwriteExistingFiles,
req,
throwOnMissingFile: false,

View File

@@ -68,7 +68,6 @@ export type {
FieldWithMany,
FieldWithMaxDepth,
FieldWithPath,
FieldWithRichTextRequiredEditor,
FieldWithSubFields,
FilterOptions,
FilterOptionsProps,
@@ -87,7 +86,6 @@ export type {
RelationshipField,
RelationshipValue,
RichTextField,
RichTextFieldRequiredEditor,
RowAdmin,
RowField,
SelectField,

View File

@@ -477,6 +477,7 @@ export const richText = baseField.keys({
validate: joi.func().required(),
})
.unknown(),
maxDepth: joi.number(),
})
export const date = baseField.keys({
@@ -520,6 +521,7 @@ export const ui = joi.object().keys({
})
.default({}),
condition: joi.func(),
disableListColumn: joi.boolean().default(false),
position: joi.string().valid('sidebar'),
width: joi.string(),
})

View File

@@ -390,7 +390,6 @@ export type UIField = {
condition?: Condition
disableBulkEdit?: boolean
disableListColumn?: boolean
disableListFilter?: boolean
position?: string
width?: string
}
@@ -551,17 +550,13 @@ export type RichTextField<
}
}
editor?: RichTextAdapter<Value, AdapterProps, AdapterProps>
/**
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
*/
maxDepth?: number
type: 'richText'
} & 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 & {
admin?: Admin & {
components?: {
@@ -679,10 +674,6 @@ export type Field =
| UIField
| UploadField
export type FieldWithRichTextRequiredEditor =
| Exclude<Field, RichTextField>
| RichTextFieldRequiredEditor
export type FieldAffectingData =
| ArrayField
| BlockField

View File

@@ -143,10 +143,13 @@ export const promise = async ({
const editor: RichTextAdapter = field?.editor
// This is run here AND in the GraphQL Resolver
if (editor?.populationPromise) {
const populateDepth =
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
const populationPromise = editor.populationPromise({
context,
currentDepth,
depth,
depth: populateDepth,
draft,
field,
findMany,

View File

@@ -1,4 +1,6 @@
import Ajv from 'ajv'
import ObjectID from 'bson-objectid'
import type { RichTextAdapter } from '../exports/types'
import type {
ArrayField,
@@ -159,7 +161,7 @@ export const json: Validate<unknown, unknown, JSONField & { jsonError?: string }
return true
}
const fetchSchema = ({ uri, schema }) => {
const fetchSchema = ({ schema, uri }) => {
if (uri && schema) return schema
return fetch(uri)
.then((response) => {
@@ -344,8 +346,12 @@ const validateFilterOptions: Validate = async (
const valueIDs: (number | string)[] = []
values.forEach((val) => {
if (typeof val === 'object' && val?.value) {
valueIDs.push(val.value)
if (typeof val === 'object') {
if (val?.value) {
valueIDs.push(val.value)
} else if (ObjectID.isValid(val)) {
valueIDs.push(new ObjectID(val).toHexString())
}
}
if (typeof val === 'string' || typeof val === 'number') {
@@ -397,6 +403,10 @@ const validateFilterOptions: Validate = async (
if (typeof val === 'string' || typeof val === 'number') {
requestedID = val
}
if (typeof val === 'object' && ObjectID.isValid(val)) {
requestedID = new ObjectID(val).toHexString()
}
}
if (Array.isArray(relationTo) && typeof val === 'object' && val?.relationTo) {

View File

@@ -476,9 +476,12 @@ function buildObjectType({
// In the graphql find.ts resolver, the depth is then hard-coded to 0.
// Effectively, this means that the populationPromise for GraphQL is only run here, and not in the find.ts resolver / normal population promise.
if (editor?.populationPromise) {
const populateDepth =
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
await editor?.populationPromise({
context,
depth,
depth: populateDepth,
draft: args.draft,
field,
findMany: false,

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "تمّ التّفعيل بالفعل",
"alreadyLoggedIn": "تمّ تسجيل الدّخول بالفعل",
"apiKey": "مفتاح API",
"authenticated": "مصادق عليه",
"backToLogin": "العودة لتسجيل الدخول",
"beginCreateFirstUser": "للبدء, قم بإنشاء المستخدم الأوّل.",
"changePassword": "تغيير كلمة المرور",
@@ -90,6 +91,7 @@
"unauthorized": "غير مصرّح لك ، عليك أن تقوم بتسجيل الدّخول لتتمكّن من تقديم هذا الطّلب.",
"unknown": "حدث خطأ غير معروف.",
"unspecific": "حدث خطأ.",
"userEmailAlreadyRegistered": "يوجد مستخدم مسجل بالفعل بهذا البريد الإلكتروني.",
"userLocked": "تمّ قفل هذا المستخدم نظرًا لوجود عدد كبير من محاولات تسجيل الدّخول الغير ناجحة.",
"valueMustBeUnique": "على القيمة أن تكون فريدة",
"verificationTokenInvalid": "رمز التحقّق غير صالح."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "تمّ حذف {{count}} {{label}} بنجاح.",
"deletedSuccessfully": "تمّ الحذف بنجاح.",
"deleting": "يتمّ الحذف...",
"depth": "عمق",
"descending": "تنازلي",
"deselectAllRows": "إلغاء تحديد جميع الصفوف",
"document": "وثيقة",
@@ -195,6 +198,7 @@
"error": "خطأ",
"errors": "أخطاء",
"fallbackToDefaultLocale": "الرجوع إلى اللغة الافتراضية",
"false": "كاذب",
"filter": "تصفية",
"filterWhere": "تصفية {{label}} حيث",
"filters": "عوامل التصفية",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} تم استنساخها بنجاح.",
"thisLanguage": "العربية",
"titleDeleted": "تم حذف {{label}} \"{{title}}\" بنجاح.",
"true": "صحيح",
"unauthorized": "غير مصرح به",
"unsavedChangesDuplicate": "لديك تغييرات لم يتم حفظها. هل تريد الاستمرار في الاستنساخ؟",
"untitled": "بدون عنوان",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Artıq Aktivləşdirilib",
"alreadyLoggedIn": "Artıq daxil olunub",
"apiKey": "API Açarı",
"authenticated": "Doğrulandı",
"backToLogin": "Girişə qayıt",
"beginCreateFirstUser": "Başlamaq üçün ilk istifadəçinizi yaradın.",
"changePassword": "Parolu dəyişdir",
@@ -90,6 +91,7 @@
"unauthorized": "İcazəniz yoxdur, bu tələbi yerinə yetirmək üçün daxil olmalısınız.",
"unknown": "Naməlum bir xəta baş verdi.",
"unspecific": "Xəta baş verdi.",
"userEmailAlreadyRegistered": "Verilən e-poçt ünvanı ilə artıq istifadəçi qeydiyyatdan keçib.",
"userLocked": "Bu istifadəçi çoxsaylı uğursuz giriş cəhdləri səbəbindən kilidlənib.",
"valueMustBeUnique": "Dəyər təkrar olmamalıdır",
"verificationTokenInvalid": "Doğrulama tokenı yanlışdır."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "{{count}} {{label}} uğurla silindi.",
"deletedSuccessfully": "Uğurla silindi.",
"deleting": "Silinir...",
"depth": "Dərinlik",
"descending": "Azalan",
"deselectAllRows": "Bütün sıraları seçimi ləğv edin",
"document": "Sənəd",
@@ -195,6 +198,7 @@
"error": "Xəta",
"errors": "Xətalar",
"fallbackToDefaultLocale": "Standart lokalə keçid",
"false": "Yalan",
"filter": "Filter",
"filterWhere": "{{label}} filtrlə",
"filters": "Filtərlər",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} uğurla dublikatlandı.",
"thisLanguage": "Azərbaycan dili",
"titleDeleted": "{{label}} \"{{title}}\" uğurla silindi.",
"true": "Doğru",
"unauthorized": "İcazəsiz",
"unsavedChangesDuplicate": "Saxlanılmamış dəyişiklikləriniz var. Dublikatla davam etmək istəyirsiniz?",
"untitled": "Başlıqsız",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Вече активиран",
"alreadyLoggedIn": "Вече влязъл",
"apiKey": "API ключ",
"authenticated": "Аутентикиран",
"backToLogin": "Обратно към влизане",
"beginCreateFirstUser": "За да започнеш, създай първия си потребител",
"changePassword": "Промяна на паролата",
@@ -90,6 +91,7 @@
"unauthorized": "Неавторизиран, трябва да влезеш, за да извършиш тази заявка.",
"unknown": "Неизвестна грешка.",
"unspecific": "Грешка.",
"userEmailAlreadyRegistered": "Потребител с дадения имейл вече е регистриран.",
"userLocked": "Този потребител има прекалено много невалидни опити за влизане и е заключен.",
"valueMustBeUnique": "Стойността трябва да е уникална",
"verificationTokenInvalid": "Ключът за верификация е невалиден."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "Изтрити {{count}} {{label}} успешно.",
"deletedSuccessfully": "Изтрито успешно.",
"deleting": "Изтриване...",
"depth": "Дълбочина",
"descending": "Низходящо",
"deselectAllRows": "Деселектирай всички редове",
"document": "Документ",
@@ -195,6 +198,7 @@
"error": "Грешка",
"errors": "Грешки",
"fallbackToDefaultLocale": "Използвай локализация по подразбиране",
"false": "Ложно",
"filter": "Филтрирай",
"filterWhere": "Филтрирай {{label}} където",
"filters": "Филтри",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} успешно дупликиран.",
"thisLanguage": "Български",
"titleDeleted": "{{label}} \"{{title}}\" успешно изтрит.",
"true": "Истина",
"unauthorized": "Неавторизиран",
"unsavedChangesDuplicate": "Имаш незапазени промени. Искаш ли да продължиш да дупликираш?",
"untitled": "Неозаглавен",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Již aktivováno",
"alreadyLoggedIn": "Již přihlášen",
"apiKey": "API klíč",
"authenticated": "Ověřený",
"backToLogin": "Zpět na přihlášení",
"beginCreateFirstUser": "Začněte vytvořením svého prvního uživatele.",
"changePassword": "Změnit heslo",
@@ -90,6 +91,7 @@
"unauthorized": "Neautorizováno, pro zadání tohoto požadavku musíte být přihlášeni.",
"unknown": "Došlo k neznámé chybě.",
"unspecific": "Došlo k chybě.",
"userEmailAlreadyRegistered": "Uživatel s daným e-mailem je již zaregistrován.",
"userLocked": "Tento uživatel je uzamčen kvůli příliš mnoha neúspěšným pokusům o přihlášení.",
"valueMustBeUnique": "Hodnota musí být jedinečná",
"verificationTokenInvalid": "Ověřovací token je neplatný."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "Úspěšně smazáno {{count}} {{label}}.",
"deletedSuccessfully": "Úspěšně odstraněno.",
"deleting": "Odstraňování...",
"depth": "Hloubka",
"descending": "Sestupně",
"deselectAllRows": "Zrušte výběr všech řádků",
"document": "Dokument",
@@ -195,6 +198,7 @@
"error": "Chyba",
"errors": "Chyby",
"fallbackToDefaultLocale": "Zpětné přepnutí do výchozího locale",
"false": "Nepravda",
"filter": "Filtr",
"filterWhere": "Filtrovat {{label}} kde",
"filters": "Filtry",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} úspěšně duplikováno.",
"thisLanguage": "Čeština",
"titleDeleted": "{{label}} \"{{title}}\" úspěšně smazáno.",
"true": "Pravda",
"unauthorized": "Neoprávněný",
"unsavedChangesDuplicate": "Máte neuložené změny. Chtěli byste pokračovat v duplikování?",
"untitled": "Bez názvu",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Bereits aktiviert",
"alreadyLoggedIn": "Bereits angemeldet",
"apiKey": "API-Key",
"authenticated": "Authentifiziert",
"backToLogin": "Zurück zur Anmeldung",
"beginCreateFirstUser": "Erstelle deinen ersten Benutzer um zu beginnen",
"changePassword": "Passwort ändern",
@@ -90,6 +91,7 @@
"unauthorized": "Nicht autorisiert - du musst angemeldet sein, um diese Anfrage zu stellen.",
"unknown": "Ein unbekannter Fehler ist aufgetreten.",
"unspecific": "Ein Fehler ist aufgetreten.",
"userEmailAlreadyRegistered": "Ein Benutzer mit der angegebenen E-Mail ist bereits registriert.",
"userLocked": "Dieser Benutzer ist auf Grund zu vieler unerfolgreicher Anmelde-Versuche gesperrt.",
"valueMustBeUnique": "Wert muss einzigartig sein",
"verificationTokenInvalid": "Verifizierungs-Token ist nicht korrekt."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "{{count}} {{label}} erfolgreich gelöscht.",
"deletedSuccessfully": "Erfolgreich gelöscht.",
"deleting": "Lösche...",
"depth": "Tiefe",
"descending": "Absteigend",
"deselectAllRows": "Alle Zeilen abwählen",
"document": "Dokument",
@@ -195,6 +198,7 @@
"error": "Fehler",
"errors": "Fehler",
"fallbackToDefaultLocale": "Rückgriff auf das Standardgebietsschema",
"false": "Falsch",
"filter": "Filter",
"filterWhere": "Filter {{label}} wo",
"filters": "Filter",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} wurde erfolgreich dupliziert.",
"thisLanguage": "Deutsch",
"titleDeleted": "{{label}} {{title}} wurde erfolgreich gelöscht.",
"true": "Wahr",
"unauthorized": "Nicht autorisiert",
"unsavedChangesDuplicate": "Du hast ungespeicherte Änderungen, möchtest du mit dem Duplizieren fortfahren?",
"untitled": "ohne Titel",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Already Activated",
"alreadyLoggedIn": "Already logged in",
"apiKey": "API Key",
"authenticated": "Authenticated",
"backToLogin": "Back to login",
"beginCreateFirstUser": "To begin, create your first user.",
"changePassword": "Change Password",
@@ -90,6 +91,7 @@
"unauthorized": "Unauthorized, you must be logged in to make this request.",
"unknown": "An unknown error has occurred.",
"unspecific": "An error has occurred.",
"userEmailAlreadyRegistered": "A user with the given email is already registered.",
"userLocked": "This user is locked due to having too many failed login attempts.",
"valueMustBeUnique": "Value must be unique",
"verificationTokenInvalid": "Verification token is invalid."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "Deleted {{count}} {{label}} successfully.",
"deletedSuccessfully": "Deleted successfully.",
"deleting": "Deleting...",
"depth": "Depth",
"descending": "Descending",
"deselectAllRows": "Deselect all rows",
"document": "Document",
@@ -195,6 +198,7 @@
"error": "Error",
"errors": "Errors",
"fallbackToDefaultLocale": "Fallback to default locale",
"false": "False",
"filter": "Filter",
"filterWhere": "Filter {{label}} where",
"filters": "Filters",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} successfully duplicated.",
"thisLanguage": "English",
"titleDeleted": "{{label}} \"{{title}}\" successfully deleted.",
"true": "True",
"unauthorized": "Unauthorized",
"unsavedChangesDuplicate": "You have unsaved changes. Would you like to continue to duplicate?",
"untitled": "Untitled",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Ya Activado",
"alreadyLoggedIn": "Sesión iniciada",
"apiKey": "Clave API",
"authenticated": "Autenticado",
"backToLogin": "Regresar al inicio de sesión",
"beginCreateFirstUser": "Para empezar, crea tu primer usuario.",
"changePassword": "Cambiar contraseña",
@@ -90,6 +91,7 @@
"unauthorized": "No autorizado, debes iniciar sesión para realizar esta solicitud.",
"unknown": "Ocurrió un error desconocido.",
"unspecific": "Ocurrió un error.",
"userEmailAlreadyRegistered": "Ya hay un usuario registrado con el correo electrónico proporcionado.",
"userLocked": "Este usuario ha sido bloqueado debido a que tiene muchos intentos fallidos para iniciar sesión.",
"valueMustBeUnique": "El valor debe ser único",
"verificationTokenInvalid": "Token de verificación inválido."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "Se eliminó {{count}} {{label}} con éxito.",
"deletedSuccessfully": "Borrado exitosamente.",
"deleting": "Eliminando...",
"depth": "Profundidad",
"descending": "Descendente",
"deselectAllRows": "Deselecciona todas las filas",
"document": "Documento",
@@ -195,6 +198,7 @@
"error": "Error",
"errors": "Errores",
"fallbackToDefaultLocale": "Volver a la configuración regional por defecto",
"false": "Falso",
"filter": "Filtro",
"filterWhere": "Filtrar {{label}} donde",
"filters": "Filtros",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} duplicado correctamente.",
"thisLanguage": "Español",
"titleDeleted": "{{label}} {{title}} eliminado correctamente.",
"true": "Verdadero",
"unauthorized": "No autorizado",
"unsavedChangesDuplicate": "Tienes cambios sin guardar. ¿Deseas continuar para duplicar?",
"untitled": "Sin título",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "قبلاً فعال شده است",
"alreadyLoggedIn": "قبلاً وارد شده‌اید",
"apiKey": "کلید اِی‌پی‌آی",
"authenticated": "احراز هویت شده",
"backToLogin": "بازگشت به برگه ورود",
"beginCreateFirstUser": "برای آغاز، نخستین کاربر خود را بسازید.",
"changePassword": "تغییر گذرواژه",
@@ -90,6 +91,7 @@
"unauthorized": "درخواست نامعتبر، جهت فرستادن این درخواست باید وارد شوید.",
"unknown": "یک خطای ناشناخته رخ داد.",
"unspecific": "خطایی رخ داد.",
"userEmailAlreadyRegistered": "کاربری با ایمیل داده شده قبلاً ثبت نام کرده است.",
"userLocked": "این کاربر به دلیل تلاش های زیاد برای ورود ناموفق قفل شده است.",
"valueMustBeUnique": "مقدار باید منحصر به فرد باشد",
"verificationTokenInvalid": "ژتون تأیید نامعتبر است."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "تعداد {{count}} {{label}} با موفقیت پاک گردید.",
"deletedSuccessfully": "با موفقیت حذف شد.",
"deleting": "در حال حذف...",
"depth": "عمق",
"descending": "رو به پایین",
"deselectAllRows": "تمام سطرها را از انتخاب خارج کنید",
"document": "سند",
@@ -195,6 +198,7 @@
"error": "خطا",
"errors": "خطاها",
"fallbackToDefaultLocale": "بازگردان پیشفرض زبان",
"false": "غلط",
"filter": "علامت‌گذاری",
"filterWhere": "علامت گذاری کردن {{label}} جایی که",
"filters": "علامت‌گذاری‌ها",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} با موفقیت رونوشت شد.",
"thisLanguage": "فارسی",
"titleDeleted": "{{label}} \"{{title}}\" با موفقیت پاک شد.",
"true": "درست",
"unauthorized": "غیرمجاز",
"unsavedChangesDuplicate": "شما تغییرات ذخیره نشده دارید. مطمئنید میخواهید به رونوشت ادامه دهید؟",
"untitled": "بدون عنوان",

View File

@@ -2,10 +2,11 @@
"$schema": "./translation-schema.json",
"authentication": {
"account": "Compte",
"accountOfCurrentUser": "Compte de l'utilisateur actuel",
"accountOfCurrentUser": "Compte de lutilisateur actuel",
"alreadyActivated": "Déjà activé",
"alreadyLoggedIn": "Déjà connecté",
"apiKey": "Clé API",
"authenticated": "Authentifié",
"backToLogin": "Retour à la connexion",
"beginCreateFirstUser": "Pour commencer, créez votre premier utilisateur.",
"changePassword": "Changer le mot de passe",
@@ -13,7 +14,7 @@
"confirmGeneration": "Confirmer la génération",
"confirmPassword": "Confirmez le mot de passe",
"createFirstUser": "Créer le premier utilisateur",
"emailNotValid": "L'adresse e-mail fourni n'est pas valide",
"emailNotValid": "Ladresse e-mail fournie nest pas valide",
"emailSent": "E-mail envoyé",
"enableAPIKey": "Activer la clé API",
"failedToUnlock": "Déverrouillage échoué",
@@ -23,76 +24,77 @@
"forgotPasswordQuestion": "Mot de passe oublié ?",
"generate": "Générer",
"generateNewAPIKey": "Générer une nouvelle clé API",
"generatingNewAPIKeyWillInvalidate": "La génération d'une nouvelle clé API <1>invalidera</1> la clé précédente. Êtes-vous sûr de vouloir continuer ?",
"lockUntil": "Verrouiller jusqu'à",
"generatingNewAPIKeyWillInvalidate": "La génération dune nouvelle clé API <1>invalidera</1> la clé précédente. Êtes-vous sûr de vouloir continuer ?",
"lockUntil": "Verrouiller jusquà",
"logBackIn": "Se reconnecter",
"logOut": "Se déconnecter",
"loggedIn": "Pour vous connecter en tant qu'un autre utilisateur, vous devez d'abord vous <0>déconnecter</0>.",
"loggedIn": "Pour vous connecter en tant quun autre utilisateur, vous devez dabord vous <0>déconnecter</0>.",
"loggedInChangePassword": "Pour changer votre mot de passe, rendez-vous sur votre <0>compte</0> puis modifiez-y votre mot de passe.",
"loggedOutInactivity": "Vous avez été déconnecté pour cause d'inactivité.",
"loggedOutInactivity": "Vous avez été déconnecté pour cause dinactivité.",
"loggedOutSuccessfully": "Vous avez été déconnecté avec succès.",
"login": "Se connecter",
"loginAttempts": "Tentatives de connexion",
"loginUser": "Connecter l'utilisateur",
"loginWithAnotherUser": "Pour vous connecter en tant qu'un autre utilisateur, vous devez d'abord vous <0>déconnecter</0>.",
"loginUser": "Connecter lutilisateur",
"loginWithAnotherUser": "Pour vous connecter en tant quun autre utilisateur, vous devez dabord vous <0>déconnecter</0>.",
"logout": "Se déconnecter",
"logoutUser": "Déconnecter l'utilisateur",
"logoutUser": "Déconnecter lutilisateur",
"newAPIKeyGenerated": "Nouvelle clé API générée.",
"newAccountCreated": "Un nouveau compte vient d'être créé pour vous permettre d'accéder <a href=\"{{serverURL}}\">{{serverURL}}</a>. Veuillez cliquer sur le lien suivant ou collez l'URL ci-dessous dans votre navigateur pour vérifier votre adresse e-mail: <a href=\"{{verificationURL}}\">{{verificationURL}}</a><br>. Après avoir vérifié votre adresse e-mail, vous pourrez vous connecter avec succès.",
"newAccountCreated": "Un nouveau compte vient dêtre créé pour vous permettre daccéder <a href=\"{{serverURL}}\">{{serverURL}}</a>. Veuillez cliquer sur le lien suivant ou collez lURL ci-dessous dans votre navigateur pour vérifier votre adresse e-mail: <a href=\"{{verificationURL}}\">{{verificationURL}}</a><br>. Après avoir vérifié votre adresse e-mail, vous pourrez vous connecter avec succès.",
"newPassword": "Nouveau mot de passe",
"resetPassword": "Réinitialiser le mot de passe",
"resetPasswordExpiration": "Réinitialiser l'expiration du mot de passe",
"resetPasswordExpiration": "Réinitialiser lexpiration du mot de passe",
"resetPasswordToken": "Réinitialiser le jeton de mot de passe",
"resetYourPassword": "Réinitialisez votre mot de passe",
"stayLoggedIn": "Rester connecté",
"successfullyUnlocked": "Déverrouillé avec succès",
"unableToVerify": "Vérification échoué",
"unableToVerify": "Vérification échouée",
"verified": "Vérifié",
"verifiedSuccessfully": "Vérifié avec succès",
"verify": "Vérifier",
"verifyUser": "Vérifier l'utilisateur",
"verifyUser": "Vérifier lutilisateur",
"verifyYourEmail": "Vérifiez votre e-mail",
"youAreInactive": "Vous n'avez pas été actif depuis un moment alors vous serez bientôt automatiquement déconnecté pour votre propre sécurité. Souhaitez-vous rester connecté ?",
"youAreReceivingResetPassword": "Vous recevez ceci parce que vous (ou quelqu'un d'autre) avez demandé la réinitialisation du mot de passe de votre compte. Veuillez cliquer sur le lien suivant ou le coller dans votre navigateur pour terminer le processus :",
"youDidNotRequestPassword": "Si vous ne l'avez pas demandé, veuillez ignorer cet e-mail et votre mot de passe restera inchangé."
"youAreInactive": "Vous navez pas été actif depuis un moment alors vous serez bientôt automatiquement déconnecté pour votre propre sécurité. Souhaitez-vous rester connecté ?",
"youAreReceivingResetPassword": "Vous recevez ceci parce que vous (ou quelquun dautre) avez demandé la réinitialisation du mot de passe de votre compte. Veuillez cliquer sur le lien suivant ou le coller dans votre navigateur pour terminer le processus :",
"youDidNotRequestPassword": "Si vous ne lavez pas demandé, veuillez ignorer cet e-mail et votre mot de passe restera inchangé."
},
"error": {
"accountAlreadyActivated": "Ce compte a déjà été activé.",
"autosaving": "Un problème est survenu lors de l'enregistrement automatique de ce document.",
"autosaving": "Un problème est survenu lors de lenregistrement automatique de ce document.",
"correctInvalidFields": "Veuillez corriger les champs invalides.",
"deletingFile": "Une erreur s'est produite lors de la suppression du fichier.",
"deletingTitle": "Une erreur s'est produite lors de la suppression de {{title}}. Veuillez vérifier votre connexion puis réessayer.",
"emailOrPasswordIncorrect": "L'adresse e-mail ou le mot de passe fourni est incorrect.",
"followingFieldsInvalid_one": "Le champ suivant n'est pas valide :",
"deletingFile": "Une erreur sest produite lors de la suppression du fichier.",
"deletingTitle": "Une erreur sest produite lors de la suppression de {{title}}. Veuillez vérifier votre connexion puis réessayer.",
"emailOrPasswordIncorrect": "Ladresse e-mail ou le mot de passe fourni est incorrect.",
"followingFieldsInvalid_one": "Le champ suivant nest pas valide :",
"followingFieldsInvalid_other": "Les champs suivants ne sont pas valides :",
"incorrectCollection": "Collection incorrecte",
"invalidFileType": "Type de fichier invalide",
"invalidFileTypeValue": "Type de fichier invalide : {{value}}",
"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 :",
"localesNotSaved_one": "Le paramètre régional suivant na pas pu être enregistré :",
"localesNotSaved_other": "Les paramètres régionaux suivants nont pas pu être enregistrés :",
"missingEmail": "E-mail manquant.",
"missingIDOfDocument": "Il manque l'identifiant du document à mettre à jour.",
"missingIDOfVersion": "Il manque l'identifiant de la version.",
"missingIDOfDocument": "Il manque lidentifiant du document à mettre à jour.",
"missingIDOfVersion": "Il manque lidentifiant de la version.",
"missingRequiredData": "Données requises manquantes.",
"noFilesUploaded": "Aucun fichier n'a été téléversé.",
"noMatchedField": "Aucun champ correspondant n'a été trouvé pour \"{{label}}\"",
"noFilesUploaded": "Aucun fichier na été téléversé.",
"noMatchedField": "Aucun champ correspondant na été trouvé pour \"{{label}}\"",
"noUser": "Aucun utilisateur",
"notAllowedToAccessPage": "Vous n'êtes pas autorisé à accéder à cette page.",
"notAllowedToPerformAction": "Vous n'êtes pas autorisé à effectuer cette action.",
"notFound": "La ressource demandée n'a pas été trouvée.",
"previewing": "Un problème est survenu lors de l'aperçu de ce document.",
"notAllowedToAccessPage": "Vous nêtes pas autorisé à accéder à cette page.",
"notAllowedToPerformAction": "Vous nêtes pas autorisé à effectuer cette action.",
"notFound": "La ressource demandée na pas été trouvée.",
"previewing": "Un problème est survenu lors de laperçu de ce document.",
"problemUploadingFile": "Il y a eu un problème lors du téléversement du fichier.",
"tokenInvalidOrExpired": "Le jeton n'est soit pas valide ou a expiré.",
"unPublishingDocument": "Un problème est survenu lors de l'annulation de la publication de ce document.",
"tokenInvalidOrExpired": "Le jeton nest soit pas valide ou a expiré.",
"unPublishingDocument": "Un problème est survenu lors de lannulation de la publication de ce document.",
"unableToDeleteCount": "Impossible de supprimer {{count}} sur {{total}} {{label}}.",
"unableToUpdateCount": "Impossible de mettre à jour {{count}} sur {{total}} {{label}}.",
"unauthorized": "Non autorisé, vous devez être connecté pour effectuer cette demande.",
"unknown": "Une erreur inconnue s'est produite.",
"unknown": "Une erreur inconnue sest produite.",
"unspecific": "Une erreur est survenue.",
"userLocked": "Cet utilisateur est verrouillé en raison d'un trop grand nombre de tentatives de connexion infructueuses.",
"userEmailAlreadyRegistered": "Un utilisateur avec l'email donné est déjà enregistré.",
"userLocked": "Cet utilisateur est verrouillé en raison dun trop grand nombre de tentatives de connexion infructueuses.",
"valueMustBeUnique": "La valeur doit être unique",
"verificationTokenInvalid": "Le jeton de vérification n'est pas valide."
"verificationTokenInvalid": "Le jeton de vérification nest pas valide."
},
"fields": {
"addLabel": "Ajouter {{label}}",
@@ -127,7 +129,7 @@
"relatedDocument": "Document connexe",
"relationTo": "Lié à",
"removeRelationship": "Supprimer la relation",
"removeUpload": "Supprimer le Téléversement",
"removeUpload": "Supprimer le téléversement",
"saveChanges": "Sauvegarder les modifications",
"searchForBlock": "Rechercher un bloc",
"selectExistingLabel": "Sélectionnez {{label}} existant",
@@ -146,14 +148,14 @@
"aboutToDeleteCount_other": "Vous êtes sur le point de supprimer {{count}} {{label}}",
"addBelow": "Ajoutez ci-dessous",
"addFilter": "Ajouter un filtre",
"adminTheme": "Thème d'administration",
"adminTheme": "Thème dadministration",
"and": "Et",
"applyChanges": "Appliquer les modifications",
"ascending": "Ascendant",
"automatic": "Automatique",
"backToDashboard": "Retour au tableau de bord",
"cancel": "Annuler",
"changesNotSaved": "Vos modifications n'ont pas été enregistrées. Vous perdrez vos modifications si vous quittez maintenant.",
"changesNotSaved": "Vos modifications nont pas été enregistrées. Vous perdrez vos modifications si vous quittez maintenant.",
"close": "Fermer",
"collapse": "Réduire",
"collections": "Collections",
@@ -170,13 +172,14 @@
"created": "Créé(e)",
"createdAt": "Créé(e) à",
"creating": "création en cours",
"creatingNewLabel": "Création d'un(e) nouveau ou nouvelle {{label}}",
"dark": "Nuit",
"creatingNewLabel": "Création dun(e) nouveau ou nouvelle {{label}}",
"dark": "Sombre",
"dashboard": "Tableau de bord",
"delete": "Supprimer",
"deletedCountSuccessfully": "{{count}} {{label}} supprimé avec succès.",
"deletedSuccessfully": "Supprimé(e) avec succès.",
"deleting": "Suppression en cours...",
"depth": "Profondeur",
"descending": "Descendant(e)",
"deselectAllRows": "Désélectionner toutes les lignes",
"document": "Document",
@@ -195,6 +198,7 @@
"error": "Erreur",
"errors": "Erreurs",
"fallbackToDefaultLocale": "Retour à la locale par défaut",
"false": "Faux",
"filter": "Filtrer",
"filterWhere": "Filtrer {{label}} où",
"filters": "Filtres",
@@ -203,7 +207,7 @@
"lastModified": "Dernière modification",
"leaveAnyway": "Quitter quand même",
"leaveWithoutSaving": "Quitter sans sauvegarder",
"light": "Lumière ou Jour",
"light": "Clair",
"livePreview": "Aperçu",
"loading": "Chargement en cours",
"locale": "Paramètres régionaux",
@@ -215,11 +219,11 @@
"noFiltersSet": "Aucun filtre défini",
"noLabel": "<Pas de {{label}}>",
"noOptions": "Aucune option",
"noResults": "Aucun(e) {{label}} trouvé(e). Soit aucun(e) {{label}} n'existe encore, soit aucun(e) ne correspond aux filtres que vous avez spécifiés ci-dessus",
"noResults": "Aucun(e) {{label}} trouvé(e). Soit aucun(e) {{label}} nexiste encore, soit aucun(e) ne correspond aux filtres que vous avez spécifiés ci-dessus",
"noValue": "Aucune valeur",
"none": "Aucun(e)",
"notFound": "Pas trouvé",
"nothingFound": "Rien n'a été trouvé",
"nothingFound": "Rien na été trouvé",
"of": "de",
"open": "Ouvrir",
"or": "ou",
@@ -250,18 +254,19 @@
"successfullyDuplicated": "{{label}} dupliqué(e) avec succès.",
"thisLanguage": "Français",
"titleDeleted": "{{label}} \"{{title}}\" supprimé(e) avec succès.",
"true": "Vrai",
"unauthorized": "Non autorisé",
"unsavedChangesDuplicate": "Vous avez des changements non enregistrés. Souhaitez-vous continuer la duplication ?",
"untitled": "Sans titre",
"updatedAt": "Modifié le",
"updatedCountSuccessfully": "{{count}} {{label}} mis à jour avec succès.",
"updatedSuccessfully": "Mis à jour avec succés.",
"updatedSuccessfully": "Mis à jour avec succès.",
"updating": "Mise à jour",
"uploading": "Téléchargement",
"user": "Utilisateur",
"users": "Utilisateurs",
"value": "Valeur",
"welcome": "Bienvenu(e)"
"welcome": "Bienvenue"
},
"operators": {
"contains": "contient",
@@ -273,24 +278,24 @@
"isLessThan": "est inférieur à",
"isLessThanOrEqualTo": "est inférieur ou égal à",
"isLike": "est comme",
"isNotEqualTo": "n'est pas égal à",
"isNotIn": "n'est pas dans",
"isNotEqualTo": "nest pas égal à",
"isNotIn": "nest pas dans",
"near": "proche"
},
"upload": {
"crop": "Récolte",
"crop": "Recadrer",
"cropToolDescription": "Faites glisser les coins de la zone sélectionnée, dessinez une nouvelle zone ou ajustez les valeurs ci-dessous.",
"dragAndDrop": "Glisser-déposer un fichier",
"dragAndDropHere": "ou glissez-déposez un fichier ici",
"editImage": "Modifier l'image",
"editImage": "Modifier limage",
"fileName": "Nom du fichier",
"fileSize": "Taille du fichier",
"focalPoint": "Point focal",
"focalPointDescription": "Faites glisser le point focal directement sur l'aperçu ou ajustez les valeurs ci-dessous.",
"focalPointDescription": "Faites glisser le point focal directement sur laperçu ou ajustez les valeurs ci-dessous.",
"height": "Hauteur",
"lessInfo": "Moins d'infos",
"moreInfo": "Plus d'infos",
"previewSizes": "Tailles d'aperçu",
"lessInfo": "Moins dinfos",
"moreInfo": "Plus dinfos",
"previewSizes": "Tailles daperçu",
"selectCollectionToBrowse": "Sélectionnez une collection à parcourir",
"selectFile": "Sélectionnez un fichier",
"setCropArea": "Définir la zone de recadrage",
@@ -300,32 +305,32 @@
"width": "Largeur"
},
"validation": {
"emailAddress": "S'il vous plaît, veuillez entrer une adresse e-mail valide.",
"enterNumber": "S'il vous plait, veuillez entrer un nombre valide.",
"fieldHasNo": "Ce champ n'a pas de {{label}}",
"emailAddress": "Sil vous plaît, veuillez entrer une adresse e-mail valide.",
"enterNumber": "Sil vous plait, veuillez entrer un nombre valide.",
"fieldHasNo": "Ce champ na pas de {{label}}",
"greaterThanMax": "{{value}} est supérieur au max autorisé {{label}} de {{max}}.",
"invalidInput": "Ce champ a une entrée invalide.",
"invalidSelection": "Ce champ a une sélection invalide.",
"invalidSelections": "Ce champ contient des sélections invalides suivantes :",
"invalidSelections": "Ce champ contient les sélections invalides suivantes :",
"lessThanMin": "{{value}} est inférieur au min autorisé {{label}} de {{min}}.",
"limitReached": "Limite atteinte, seulement {{max}} éléments peuvent être ajoutés.",
"longerThanMin": "Cette valeur doit être supérieure à la longueur minimale de {{minLength}} caractères.",
"notValidDate": "\"{{value}}\" n'est pas une date valide.",
"notValidDate": "\"{{value}}\" nest pas une date valide.",
"required": "Ce champ est requis.",
"requiresAtLeast": "Ce champ doit avoir au moins {{count}} {{label}}.",
"requiresNoMoreThan": "Ce champ ne doit pas avoir plus de {{count}} {{label}}.",
"requiresTwoNumbers": "Ce champ doit avoir deux chiffres.",
"shorterThanMax": "Cette valeur doit être inférieure à la longueur maximale de {{maxLength}} caractères.",
"trueOrFalse": "Ce champ ne peut être égal qu'à vrai ou faux.",
"validUploadID": "Ce champ n'est pas un valide identifiant de fichier."
"trueOrFalse": "Ce champ ne peut être égal quà vrai ou faux.",
"validUploadID": "Ce champ nest pas un valide identifiant de fichier."
},
"version": {
"aboutToPublishSelection": "Vous êtes sur le point de publier tous les {{label}} de la sélection. Es-tu sûr?",
"aboutToRestore": "Vous êtes sur le point de restaurer le document {{label}} à l'état où il se trouvait le {{versionDate}}.",
"aboutToRestoreGlobal": "Vous êtes sur le point de restaurer le ou la {{label}} global(e) à l'état où il ou elle se trouvait le {{versionDate}}.",
"aboutToPublishSelection": "Vous êtes sur le point de publier tous les {{label}} de la sélection. Êtes-vous sûr ?",
"aboutToRestore": "Vous êtes sur le point de restaurer le document {{label}} à létat où il se trouvait le {{versionDate}}.",
"aboutToRestoreGlobal": "Vous êtes sur le point de restaurer le ou la {{label}} global(e) à létat où il ou elle se trouvait le {{versionDate}}.",
"aboutToRevertToPublished": "Vous êtes sur le point de rétablir les modifications apportées à ce document à la version publiée. Êtes-vous sûr ?",
"aboutToUnpublish": "Vous êtes sur le point d'annuler la publication de ce document. Êtes-vous sûr ?",
"aboutToUnpublishSelection": "Vous êtes sur le point de dépublier tous les {{label}} de la sélection. Es-tu sûr?",
"aboutToUnpublish": "Vous êtes sur le point dannuler la publication de ce document. Êtes-vous sûr ?",
"aboutToUnpublishSelection": "Vous êtes sur le point de dépublier tous les {{label}} de la sélection. Êtes-vous sûr ?",
"autosave": "Enregistrement automatique",
"autosavedSuccessfully": "Enregistrement automatique réussi.",
"autosavedVersion": "Version enregistrée automatiquement",
@@ -333,7 +338,7 @@
"compareVersion": "Comparez cette version à :",
"confirmPublish": "Confirmer la publication",
"confirmRevertToSaved": "Confirmer la restauration",
"confirmUnpublish": "Confirmer l'annulation",
"confirmUnpublish": "Confirmer lannulation",
"confirmVersionRestoration": "Confirmer la restauration de la version",
"currentDocumentStatus": "Document {{docStatus}} actuel",
"draft": "Brouillon",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Već aktivirano",
"alreadyLoggedIn": "Već prijavljen",
"apiKey": "API ključ",
"authenticated": "Autenticiran",
"backToLogin": "Nazad na prijavu",
"beginCreateFirstUser": "Za početak, kreiraj svog prvog korisnika.",
"changePassword": "Promjeni lozinku",
@@ -90,6 +91,7 @@
"unauthorized": "Neovlašten, morate biti prijavljeni da biste uputili ovaj zahtjev.",
"unknown": "Došlo je do nepoznate pogreške.",
"unspecific": "Došlo je do pogreške.",
"userEmailAlreadyRegistered": "Korisnik s navedenom e-poštom je već registriran.",
"userLocked": "Ovaj korisnik je zaključan zbog previše neuspješnih pokušaja prijave.",
"valueMustBeUnique": "Vrijednost mora biti jedinstvena.",
"verificationTokenInvalid": "Verifikacijski token je nevaljan."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "Uspješno izbrisano {{count}} {{label}}.",
"deletedSuccessfully": "Uspješno obrisano.",
"deleting": "Brisanje...",
"depth": "Dubina",
"descending": "Silazno",
"deselectAllRows": "Odznači sve redove",
"document": "Dokument",
@@ -195,6 +198,7 @@
"error": "Greška",
"errors": "Greške",
"fallbackToDefaultLocale": "Vraćanje na zadani jezik",
"false": "Netočno",
"filter": "Filter",
"filterWhere": "Filter {{label}} gdje",
"filters": "Filteri",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} uspješno duplicirano.",
"thisLanguage": "Hrvatski",
"titleDeleted": "{{label}} \"{{title}}\" uspješno obrisano.",
"true": "Istinito",
"unauthorized": "Neovlašteno",
"unsavedChangesDuplicate": "Imate nespremljene promjene. Želite li nastaviti s dupliciranjem?",
"untitled": "Bez naslova",

View File

@@ -6,6 +6,7 @@
"alreadyActivated": "Már aktiválva van",
"alreadyLoggedIn": "Már bejelentkezett",
"apiKey": "API-kulcs",
"authenticated": "Hitelesített",
"backToLogin": "Vissza a bejelentkezéshez",
"beginCreateFirstUser": "Kezdésként hozza létre az első felhasználót.",
"changePassword": "Jelszó módosítása",
@@ -90,6 +91,7 @@
"unauthorized": "Jogosulatlan, a kéréshez be kell jelentkeznie.",
"unknown": "Ismeretlen hiba történt.",
"unspecific": "Hiba történt.",
"userEmailAlreadyRegistered": "A megadott email címmel már regisztráltak egy felhasználót.",
"userLocked": "Ez a felhasználó túl sok sikertelen bejelentkezési kísérlet miatt zárolva van.",
"valueMustBeUnique": "Az értéknek egyedinek kell lennie",
"verificationTokenInvalid": "Az ellenőrző token érvénytelen."
@@ -177,6 +179,7 @@
"deletedCountSuccessfully": "{{count}} {{label}} sikeresen törölve.",
"deletedSuccessfully": "Sikeresen törölve.",
"deleting": "Törlés...",
"depth": "Mélység",
"descending": "Csökkenő",
"deselectAllRows": "Jelölje ki az összes sort",
"document": "Dokumentum",
@@ -195,6 +198,7 @@
"error": "Hiba",
"errors": "Hibák",
"fallbackToDefaultLocale": "Visszatérés az alapértelmezett nyelvhez",
"false": "Hamis",
"filter": "Szűrő",
"filterWhere": "Szűrő {{label}} ahol",
"filters": "Szűrők",
@@ -250,6 +254,7 @@
"successfullyDuplicated": "{{label}} sikeresen duplikálódott.",
"thisLanguage": "Magyar",
"titleDeleted": "{{label}} \"{{title}}\" sikeresen törölve.",
"true": "Igaz",
"unauthorized": "Jogosulatlan",
"unsavedChangesDuplicate": "Nem mentett módosításai vannak. Szeretné folytatni a duplikációt?",
"untitled": "Névtelen",

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