Compare commits

..

4 Commits

Author SHA1 Message Date
Jarrod Flesch
f8d01d64a5 chore: adds test from main for upload fields within globals 2024-06-07 11:03:42 -04:00
Alessio Gravili
11c3a65e63 feat(richtext-*): allow omitting the root editor property (#6660)
No need to add lexical/slate to the bundle if someone decides not to
make use of richText fields within payload at all
2024-06-06 17:57:03 +00:00
Paul
8dd5e4dc24 fix: max versions config not being respected on globals (#6654)
Closes https://github.com/payloadcms/payload/issues/6646

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-06-06 17:21:32 +00:00
Alessio Gravili
9bd9e7a986 feat!: upgrade minimum node 20 version from 20.6.0 to 20.9.0 (#6659)
**BREAKING**:
- This bumps the minimum required node version from node 20.6.0 to node
20.9.0. This is because 20.6.0 breaks type generation due to a CJS node
bug, and 20.9.0 is the next v20 LTS version. The minimum node 18 version
stays the same (18.20.2)
2024-06-06 17:15:21 +00:00
41 changed files with 425 additions and 45 deletions

View File

@@ -158,7 +158,7 @@
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2",
"node": "^18.20.2 || >=20.9.0",
"pnpm": "^8.15.7"
},
"pnpm": {

View File

@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -40,7 +40,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -41,6 +41,7 @@ import {
GraphQLUnionType,
} from 'graphql'
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars'
import { MissingEditorProp } from 'payload/errors'
import { tabHasName } from 'payload/types'
import { createDataloaderCacheKey, toWords } from 'payload/utilities'
@@ -476,6 +477,10 @@ function buildObjectType({
async resolve(parent, args, context: Context) {
let depth = config.defaultDepth
if (typeof args.depth !== 'undefined') depth = args.depth
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -84,7 +84,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -137,7 +137,7 @@
}
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -101,7 +101,7 @@ export default joi.object({
defaultMaxTextLength: joi.number(),
editor: joi
.object()
.required()
.optional()
.keys({
CellComponent: componentSchema.optional(),
FieldComponent: componentSchema.optional(),

View File

@@ -615,7 +615,7 @@ export type Config = {
*/
defaultMaxTextLength?: number
/** Default richtext editor to use for richText fields */
editor: RichTextAdapterProvider<any, any, any>
editor?: RichTextAdapterProvider<any, any, any>
/**
* Email Adapter
*
@@ -747,7 +747,7 @@ export type SanitizedConfig = Omit<
> & {
collections: SanitizedCollectionConfig[]
/** Default richtext editor to use for richText fields */
editor: RichTextAdapter<any, any, any>
editor?: RichTextAdapter<any, any, any>
endpoints: Endpoint[]
globals: SanitizedGlobalConfig[]
i18n: Required<I18nOptions>

View File

@@ -12,6 +12,7 @@ export { InvalidFieldName } from './InvalidFieldName.js'
export { InvalidFieldRelationship } from './InvalidFieldRelationship.js'
export { LockedAuth } from './LockedAuth.js'
export { MissingCollectionLabel } from './MissingCollectionLabel.js'
export { MissingEditorProp } from './MissingEditorProp.js'
export { MissingFieldInputOptions } from './MissingFieldInputOptions.js'
export { MissingFieldType } from './MissingFieldType.js'
export { MissingFile } from './MissingFile.js'

View File

@@ -2,8 +2,10 @@ export {
APIError,
AuthenticationError,
DuplicateCollection,
DuplicateFieldName,
DuplicateGlobal,
ErrorDeletingFile,
FileRetrievalError,
FileUploadError,
Forbidden,
InvalidConfiguration,
@@ -11,6 +13,7 @@ export {
InvalidFieldRelationship,
LockedAuth,
MissingCollectionLabel,
MissingEditorProp,
MissingFieldInputOptions,
MissingFieldType,
MissingFile,

View File

@@ -158,7 +158,7 @@ export const sanitizeFields = async ({
// config.editor should be sanitized at this point
field.editor = _config.editor
} else {
throw new MissingEditorProp(field)
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
}

View File

@@ -5,6 +5,7 @@ import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
import type { PayloadRequestWithData, RequestContext } from '../../../types/index.js'
import type { Field, TabAsField } from '../../config/types.js'
import { MissingEditorProp } from '../../../errors/index.js'
import { fieldAffectsData, tabHasName } from '../../config/types.js'
import getValueWithDefault from '../../getDefaultValue.js'
import { relationshipPopulationPromise } from './relationshipPopulationPromise.js'
@@ -143,6 +144,9 @@ export const promise = async ({
}
case 'richText': {
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -268,6 +268,9 @@ export const richText: Validate<object, unknown, unknown, RichTextField> = async
value,
options,
) => {
if (!options?.editor) {
throw new Error('richText field has no editor property.')
}
if (typeof options?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -8,6 +8,7 @@ import type { SanitizedConfig } from '../config/types.js'
import type { Field, FieldAffectingData, Option } from '../fields/config/types.js'
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
import { MissingEditorProp } from '../errors/MissingEditorProp.js'
import { fieldAffectsData, tabHasName } from '../fields/config/types.js'
import { deepCopyObject } from './deepCopyObject.js'
import { toWords } from './formatLabels.js'
@@ -195,6 +196,9 @@ export function fieldsToJSONSchema(
}
case 'richText': {
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -68,7 +68,7 @@ export const enforceMaxVersions = async ({
}
await payload.db.deleteVersions({
collection: collection?.slug,
collection: slug,
req,
where: deleteQuery,
})

View File

@@ -95,7 +95,7 @@
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -55,7 +55,7 @@
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -45,7 +45,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -43,7 +43,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -42,7 +42,7 @@
"payload": "workspace:*"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -153,7 +153,7 @@
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
},
"engines": {
"node": ">=18.20.2"
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {

View File

@@ -10,6 +10,7 @@ import type {
SanitizedConfig,
} from 'payload/types'
import { MissingEditorProp } from 'payload/errors'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
import React, { Fragment } from 'react'
@@ -566,6 +567,9 @@ export const mapFields = (args: {
style: field.admin?.style,
width: field.admin?.width,
}
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field?.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -1,6 +1,7 @@
import type { I18n } from '@payloadcms/translations'
import type { Field, SanitizedConfig } from 'payload/types'
import { MissingEditorProp } from 'payload/errors'
import { tabHasName } from 'payload/types'
import type { FieldSchemaMap } from './types.js'
@@ -68,6 +69,9 @@ export const traverseFields = ({
break
case 'richText':
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}
if (typeof field.editor === 'function') {
throw new Error('Attempted to access unsanitized rich text editor.')
}

View File

@@ -36,7 +36,7 @@
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"pnpm": {
"overrides": {

View File

@@ -36,7 +36,7 @@
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"pnpm": {
"overrides": {

View File

@@ -3,13 +3,13 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"ci": "payload migrate && pnpm build",
"dev": "next dev",
"generate:types": "payload generate:types",
"lint": "next lint",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"generate:types": "payload generate:types"
"start": "next start"
},
"dependencies": {
"@payloadcms/db-postgres": "beta",
@@ -34,5 +34,8 @@
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"
}
}

View File

@@ -77,6 +77,9 @@
"ts-node": "10.9.1",
"typescript": "^5.4.2"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"
},
"overrides": {
"@types/react": "18.2.74"
}

View File

@@ -36,7 +36,7 @@
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"pnpm": {
"overrides": {

View File

@@ -18,13 +18,13 @@
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"@payloadcms/storage-vercel-blob": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
"@payloadcms/storage-vercel-blob": "beta"
"react-dom": "^19.0.0-rc-f994737d14-20240522"
},
"devDependencies": {
"@types/node": "^20.12.12",
@@ -36,7 +36,7 @@
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"pnpm": {
"overrides": {

View File

@@ -14,17 +14,17 @@
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
},
"dependencies": {
"@payloadcms/db-postgres": "beta",
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"@payloadcms/storage-vercel-blob": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
"@payloadcms/db-postgres": "beta",
"@payloadcms/storage-vercel-blob": "beta"
"react-dom": "^19.0.0-rc-f994737d14-20240522"
},
"devDependencies": {
"@types/node": "^20.12.12",
@@ -36,7 +36,7 @@
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
"node": "^18.20.2 || >=20.9.0"
},
"pnpm": {
"overrides": {

View File

@@ -11,8 +11,10 @@ import { Uploads1 } from './collections/Upload1/index.js'
import { Uploads2 } from './collections/Upload2/index.js'
import {
audioSlug,
cropOnlySlug,
enlargeSlug,
focalNoSizesSlug,
globalWithMediaSlug,
mediaSlug,
reduceSlug,
relationSlug,
@@ -132,7 +134,7 @@ export default buildConfigWithDefaults({
},
},
{
slug: 'crop-only',
slug: cropOnlySlug,
fields: [],
upload: {
focalPoint: false,
@@ -462,6 +464,18 @@ export default buildConfigWithDefaults({
},
},
],
globals: [
{
slug: globalWithMediaSlug,
fields: [
{
type: 'upload',
name: 'media',
relationTo: cropOnlySlug,
},
],
},
],
onInit: async (payload) => {
const uploadsDir = path.resolve(dirname, './media')
removeFiles(path.normalize(uploadsDir))

View File

@@ -1,5 +1,4 @@
import type { Page } from '@playwright/test'
import type { Payload } from 'payload/types'
import { expect, test } from '@playwright/test'
import path from 'path'
@@ -23,6 +22,7 @@ import {
adminThumbnailFunctionSlug,
adminThumbnailSizeSlug,
audioSlug,
globalWithMediaSlug,
mediaSlug,
relationSlug,
} from './shared.js'
@@ -39,6 +39,7 @@ let audioURL: AdminUrlUtil
let relationURL: AdminUrlUtil
let adminThumbnailSizeURL: AdminUrlUtil
let adminThumbnailFunctionURL: AdminUrlUtil
let globalWithMediaURL: string
describe('uploads', () => {
let page: Page
@@ -56,6 +57,7 @@ describe('uploads', () => {
relationURL = new AdminUrlUtil(serverURL, relationSlug)
adminThumbnailSizeURL = new AdminUrlUtil(serverURL, adminThumbnailSizeSlug)
adminThumbnailFunctionURL = new AdminUrlUtil(serverURL, adminThumbnailFunctionSlug)
globalWithMediaURL = new AdminUrlUtil(serverURL, globalWithMediaURL).global(globalWithMediaSlug)
const context = await browser.newContext()
page = await context.newPage()
@@ -373,4 +375,17 @@ describe('uploads', () => {
expect(redDoc.filesize).toEqual(1207)
})
})
describe('globals', () => {
test('should be able to crop media from a global', async () => {
await page.goto(globalWithMediaURL)
await page.click('.upload__toggler.doc-drawer__toggler')
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './image.png'))
await page.click('.file-field__edit')
await page.click('.btn.edit-upload__save')
await saveDocAndAssert(page, '.drawer__content #action-save')
await saveDocAndAssert(page)
await expect(page.locator('.thumbnail img')).toBeVisible()
})
})
})

View File

@@ -5,8 +5,10 @@ export const audioSlug = 'audio'
export const enlargeSlug = 'enlarge'
export const focalNoSizesSlug = 'focal-no-sizes'
export const focalOnlySlug = 'focal-only'
export const cropOnlySlug = 'crop-only'
export const reduceSlug = 'reduce'
export const adminThumbnailFunctionSlug = 'admin-thumbnail-function'
export const adminThumbnailSizeSlug = 'admin-thumbnail-size'
export const unstoredMediaSlug = 'unstored-media'
export const versionSlug = 'versions'
export const globalWithMediaSlug = 'globals-with-media'

View File

@@ -0,0 +1,125 @@
import type { CollectionConfig } from 'payload/types'
import CollectionVersionButton from '../elements/CollectionVersionButton/index.js'
import CollectionVersionsButton from '../elements/CollectionVersionsButton/index.js'
import { CustomPublishButton } from '../elements/CustomSaveButton/index.js'
import { draftWithMaxCollectionSlug } from '../slugs.js'
const DraftWithMaxPosts: CollectionConfig = {
slug: draftWithMaxCollectionSlug,
access: {
read: ({ req: { user } }) => {
if (user) {
return true
}
return {
or: [
{
_status: {
equals: 'published',
},
},
{
_status: {
exists: false,
},
},
],
}
},
readVersions: ({ req: { user } }) => Boolean(user),
},
admin: {
components: {
edit: {
PublishButton: CustomPublishButton,
},
views: {
Edit: {
Version: {
actions: [CollectionVersionButton],
},
Versions: {
actions: [CollectionVersionsButton],
},
},
},
},
defaultColumns: ['title', 'description', 'createdAt', '_status'],
preview: () => 'https://payloadcms.com',
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
label: 'Title',
localized: true,
required: true,
unique: true,
},
{
name: 'description',
type: 'textarea',
label: 'Description',
required: true,
},
{
name: 'radio',
type: 'radio',
options: [
{
label: { en: 'Test en', es: 'Test es' },
value: 'test',
},
],
},
{
name: 'select',
type: 'select',
hasMany: true,
options: [
{
label: { en: 'Test1 en', es: 'Test1 es' },
value: 'test1',
},
{
label: { en: 'Test2 en', es: 'Test2 es' },
value: 'test2',
},
],
},
{
name: 'blocksField',
type: 'blocks',
blocks: [
{
slug: 'block',
fields: [
{
name: 'text',
type: 'text',
},
{
name: 'localized',
type: 'text',
localized: true,
},
],
},
],
},
{
name: 'relation',
type: 'relationship',
relationTo: draftWithMaxCollectionSlug,
},
],
versions: {
drafts: true,
maxPerDoc: 1,
},
}
export default DraftWithMaxPosts

View File

@@ -3,16 +3,26 @@ import AutosavePosts from './collections/Autosave.js'
import CustomIDs from './collections/CustomIDs.js'
import DisablePublish from './collections/DisablePublish.js'
import DraftPosts from './collections/Drafts.js'
import DraftWithMax from './collections/DraftsWithMax.js'
import Posts from './collections/Posts.js'
import VersionPosts from './collections/Versions.js'
import AutosaveGlobal from './globals/Autosave.js'
import DisablePublishGlobal from './globals/DisablePublish.js'
import DraftGlobal from './globals/Draft.js'
import DraftWithMaxGlobal from './globals/DraftWithMax.js'
import { seed } from './seed.js'
export default buildConfigWithDefaults({
collections: [DisablePublish, Posts, AutosavePosts, DraftPosts, VersionPosts, CustomIDs],
globals: [AutosaveGlobal, DraftGlobal, DisablePublishGlobal],
collections: [
DisablePublish,
Posts,
AutosavePosts,
DraftPosts,
DraftWithMax,
VersionPosts,
CustomIDs,
],
globals: [AutosaveGlobal, DraftGlobal, DraftWithMaxGlobal, DisablePublishGlobal],
indexSortableFields: true,
localization: {
defaultLocale: 'en',

View File

@@ -55,6 +55,8 @@ import {
disablePublishSlug,
draftCollectionSlug,
draftGlobalSlug,
draftWithMaxCollectionSlug,
draftWithMaxGlobalSlug,
postCollectionSlug,
} from './slugs.js'
@@ -352,6 +354,44 @@ describe('versions', () => {
expect(href).toBe(`${pathname}/versions`)
})
test('global — respects max number of versions', async () => {
await payload.updateGlobal({
slug: draftWithMaxGlobalSlug,
data: {
title: 'initial title',
},
})
const global = new AdminUrlUtil(serverURL, draftWithMaxGlobalSlug)
await page.goto(global.global(draftWithMaxGlobalSlug))
const titleFieldInitial = page.locator('#field-title')
await titleFieldInitial.fill('updated title')
await saveDocAndAssert(page, '#action-save-draft')
await expect(titleFieldInitial).toHaveValue('updated title')
const versionsTab = page.locator('.doc-tab', {
hasText: '1',
})
await versionsTab.waitFor({ state: 'visible' })
expect(versionsTab).toBeTruthy()
const titleFieldUpdated = page.locator('#field-title')
await titleFieldUpdated.fill('latest title')
await saveDocAndAssert(page, '#action-save-draft')
await expect(titleFieldUpdated).toHaveValue('latest title')
const versionsTabUpdated = page.locator('.doc-tab', {
hasText: '1',
})
await versionsTabUpdated.waitFor({ state: 'visible' })
expect(versionsTabUpdated).toBeTruthy()
})
test('global — has versions route', async () => {
const global = new AdminUrlUtil(serverURL, globalSlug)
const versionsURL = `${global.global(globalSlug)}/versions`
@@ -535,5 +575,45 @@ describe('versions', () => {
await expect(page.locator('.rs__option')).toHaveText('some title')
})
test('collection — respects max number of versions', async () => {
const maxOneCollection = await payload.create({
collection: draftWithMaxCollectionSlug,
data: {
title: 'initial title',
description: 'some description',
},
draft: true,
})
const collection = new AdminUrlUtil(serverURL, draftWithMaxCollectionSlug)
await page.goto(collection.edit(maxOneCollection.id))
const titleFieldInitial = page.locator('#field-title')
await titleFieldInitial.fill('updated title')
await saveDocAndAssert(page, '#action-save-draft')
await expect(titleFieldInitial).toHaveValue('updated title')
const versionsTab = page.locator('.doc-tab', {
hasText: '1',
})
await versionsTab.waitFor({ state: 'visible' })
expect(versionsTab).toBeTruthy()
const titleFieldUpdated = page.locator('#field-title')
await titleFieldUpdated.fill('latest title')
await saveDocAndAssert(page, '#action-save-draft')
await expect(titleFieldUpdated).toHaveValue('latest title')
const versionsTabUpdated = page.locator('.doc-tab', {
hasText: '1',
})
await versionsTabUpdated.waitFor({ state: 'visible' })
expect(versionsTabUpdated).toBeTruthy()
})
})
})

View File

@@ -0,0 +1,61 @@
import type { GlobalConfig } from 'payload/types'
import GlobalVersionButton from '../elements/GlobalVersionButton/index.js'
import GlobalVersionsButton from '../elements/GlobalVersionsButton/index.js'
import { draftWithMaxGlobalSlug } from '../slugs.js'
const DraftWithMaxGlobal: GlobalConfig = {
slug: draftWithMaxGlobalSlug,
label: 'Draft Global',
admin: {
preview: () => 'https://payloadcms.com',
components: {
views: {
Edit: {
Version: {
actions: [GlobalVersionButton],
},
Versions: {
actions: [GlobalVersionsButton],
},
},
},
},
},
versions: {
max: 1,
drafts: true,
},
access: {
read: ({ req: { user } }) => {
if (user) {
return true
}
return {
or: [
{
_status: {
equals: 'published',
},
},
{
_status: {
exists: false,
},
},
],
}
},
},
fields: [
{
name: 'title',
type: 'text',
required: true,
localized: true,
},
],
}
export default DraftWithMaxGlobal

View File

@@ -12,6 +12,7 @@ export interface Config {
posts: Post;
'autosave-posts': AutosavePost;
'draft-posts': DraftPost;
'draft-with-max-posts': DraftWithMaxPost;
'version-posts': VersionPost;
'custom-ids': CustomId;
users: User;
@@ -21,6 +22,7 @@ export interface Config {
globals: {
'autosave-global': AutosaveGlobal;
'draft-global': DraftGlobal;
'draft-with-max-global': DraftWithMaxGlobal;
'disable-publish-global': DisablePublishGlobal;
};
locale: 'en' | 'es';
@@ -98,6 +100,30 @@ export interface DraftPost {
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "draft-with-max-posts".
*/
export interface DraftWithMaxPost {
id: string;
title: string;
description: string;
radio?: 'test' | null;
select?: ('test1' | 'test2')[] | null;
blocksField?:
| {
text?: string | null;
localized?: string | null;
id?: string | null;
blockName?: string | null;
blockType: 'block';
}[]
| null;
relation?: (string | null) | DraftWithMaxPost;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "custom-ids".
@@ -181,6 +207,17 @@ export interface DraftGlobal {
updatedAt?: string | null;
createdAt?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "draft-with-max-global".
*/
export interface DraftWithMaxGlobal {
id: string;
title: string;
_status?: ('draft' | 'published') | null;
updatedAt?: string | null;
createdAt?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "disable-publish-global".

View File

@@ -1,16 +1,17 @@
export const autosaveCollectionSlug = 'autosave-posts' as const
export const autosaveCollectionSlug = 'autosave-posts'
export const customIDSlug = 'custom-ids' as const
export const customIDSlug = 'custom-ids'
export const draftCollectionSlug = 'draft-posts' as const
export const draftCollectionSlug = 'draft-posts'
export const draftWithMaxCollectionSlug = 'draft-with-max-posts'
export const postCollectionSlug = 'posts' as const
export const postCollectionSlug = 'posts'
export const versionCollectionSlug = 'version-posts' as const
export const versionCollectionSlug = 'version-posts'
export const disablePublishSlug = 'disable-publish' as const
export const disablePublishSlug = 'disable-publish'
export const disablePublishGlobalSlug = 'disable-publish-global' as const
export const disablePublishGlobalSlug = 'disable-publish-global'
export const collectionSlugs = [
autosaveCollectionSlug,
@@ -19,7 +20,8 @@ export const collectionSlugs = [
versionCollectionSlug,
]
export const autoSaveGlobalSlug = 'autosave-global' as const
export const draftGlobalSlug = 'draft-global' as const
export const autoSaveGlobalSlug = 'autosave-global'
export const draftGlobalSlug = 'draft-global'
export const draftWithMaxGlobalSlug = 'draft-with-max-global'
export const globalSlugs = [autoSaveGlobalSlug, draftGlobalSlug]