Compare commits
7 Commits
postgres-d
...
feat/casca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0b96860f8 | ||
|
|
817d80752e | ||
|
|
f620b081b6 | ||
|
|
ee76ee1223 | ||
|
|
7640a2091d | ||
|
|
12392d4a94 | ||
|
|
f110a3fe8f |
@@ -5,9 +5,14 @@ import type { JSONSchema4 } from 'json-schema'
|
||||
import type { SanitizedCollectionConfig, TypeWithID } from '../collections/config/types.js'
|
||||
import type { Config, PayloadComponent, SanitizedConfig } from '../config/types.js'
|
||||
import type { ValidationFieldError } from '../errors/ValidationError.js'
|
||||
import type { FieldAffectingData, RichTextField, Validate } from '../fields/config/types.js'
|
||||
import type {
|
||||
FieldAffectingData,
|
||||
FlattenedField,
|
||||
RichTextField,
|
||||
Validate,
|
||||
} from '../fields/config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||
import type { RequestContext } from '../index.js'
|
||||
import type { CollectionSlug, RequestContext } from '../index.js'
|
||||
import type { JsonObject, PayloadRequest, PopulateType } from '../types/index.js'
|
||||
import type { RichTextFieldClientProps } from './fields/RichText.js'
|
||||
import type { FieldSchemaMap } from './types.js'
|
||||
@@ -234,6 +239,16 @@ type RichTextAdapterBase<
|
||||
interfaceNameDefinitions: Map<string, JSONSchema4>
|
||||
isRequired: boolean
|
||||
}) => JSONSchema4
|
||||
/**
|
||||
* Executes onRelationship for every relationship field in the richtext data.
|
||||
* Executes onFields for every node that has its fields in the richtext data.
|
||||
*/
|
||||
traverseData?: (args: {
|
||||
data: Value
|
||||
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
||||
onFields?: (args: { data: any; fields: FlattenedField[] }) => void
|
||||
onRelationship?: (args: { collectionSlug: CollectionSlug; id: number | string }) => void
|
||||
}) => void
|
||||
validate: Validate<
|
||||
Value,
|
||||
Value,
|
||||
|
||||
@@ -35,6 +35,7 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
||||
import { handleCascadePublish } from '../../versions/handleCascadePublish.js'
|
||||
import { saveVersion } from '../../versions/saveVersion.js'
|
||||
import { buildAfterOperation } from './utils.js'
|
||||
|
||||
@@ -239,6 +240,20 @@ export const createOperation = async <
|
||||
await uploadFiles(payload, filesToUpload, req)
|
||||
}
|
||||
|
||||
if (
|
||||
collectionConfig.versions.drafts &&
|
||||
collectionConfig.versions.drafts.cascadePublish &&
|
||||
!draft &&
|
||||
data._status !== 'draft'
|
||||
) {
|
||||
await handleCascadePublish({
|
||||
collectionSlug: collectionConfig.slug,
|
||||
doc: resultWithLocales,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
req,
|
||||
})
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Create
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -33,6 +33,7 @@ import { deleteAssociatedFiles } from '../../../uploads/deleteAssociatedFiles.js
|
||||
import { uploadFiles } from '../../../uploads/uploadFiles.js'
|
||||
import { checkDocumentLockStatus } from '../../../utilities/checkDocumentLockStatus.js'
|
||||
import { getLatestCollectionVersion } from '../../../versions/getLatestCollectionVersion.js'
|
||||
import { handleCascadePublish } from '../../../versions/handleCascadePublish.js'
|
||||
import { saveVersion } from '../../../versions/saveVersion.js'
|
||||
|
||||
export type SharedUpdateDocumentArgs<TSlug extends CollectionSlug> = {
|
||||
@@ -262,6 +263,21 @@ export const updateDocument = async <
|
||||
docWithLocales: publishedDocWithLocales,
|
||||
})
|
||||
|
||||
if (
|
||||
collectionConfig.versions.drafts &&
|
||||
collectionConfig.versions.drafts.cascadePublish &&
|
||||
!draftArg &&
|
||||
data._status !== 'draft'
|
||||
) {
|
||||
await handleCascadePublish({
|
||||
collectionSlug: collectionConfig.slug,
|
||||
doc: result,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
})
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Handle potential password update
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -99,6 +99,8 @@ export const sanitizeGlobal = async (
|
||||
if (global.versions.drafts === true) {
|
||||
global.versions.drafts = {
|
||||
autosave: false,
|
||||
cascadePublish: false,
|
||||
schedulePublish: false,
|
||||
validate: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import { getLatestGlobalVersion } from '../../versions/getLatestGlobalVersion.js'
|
||||
import { handleCascadePublish } from '../../versions/handleCascadePublish.js'
|
||||
import { saveVersion } from '../../versions/saveVersion.js'
|
||||
|
||||
type Args<TSlug extends GlobalSlug> = {
|
||||
@@ -240,6 +241,20 @@ export const updateOperation = async <
|
||||
docWithLocales: publishedDocWithLocales,
|
||||
})
|
||||
|
||||
if (
|
||||
globalConfig.versions?.drafts &&
|
||||
globalConfig.versions.drafts.cascadePublish &&
|
||||
!draftArg &&
|
||||
data._status !== 'draft'
|
||||
) {
|
||||
await handleCascadePublish({
|
||||
doc: result,
|
||||
fields: globalConfig.flattenedFields,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
})
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Update
|
||||
// /////////////////////////////////////
|
||||
|
||||
350
packages/payload/src/versions/handleCascadePublish.ts
Normal file
350
packages/payload/src/versions/handleCascadePublish.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
import type { FlattenedField, RelationshipField } from '../fields/config/types.js'
|
||||
import type { CollectionSlug } from '../index.js'
|
||||
import type { JsonObject, Payload, PayloadRequest } from '../types/index.js'
|
||||
|
||||
const extractID = (value: unknown): number | string => {
|
||||
if (value && typeof value === 'object' && 'id' in value) {
|
||||
return value.id as number | string
|
||||
}
|
||||
return value as number | string
|
||||
}
|
||||
|
||||
type Relation = {
|
||||
collectionSlug: CollectionSlug
|
||||
id: number | string
|
||||
}
|
||||
|
||||
const collectRelationField = ({
|
||||
addRelation,
|
||||
field,
|
||||
fieldData,
|
||||
payload,
|
||||
}: {
|
||||
addRelation: (relation: Relation) => void
|
||||
field: RelationshipField
|
||||
fieldData: any
|
||||
payload: Payload
|
||||
}) => {
|
||||
if (!fieldData) {
|
||||
return
|
||||
}
|
||||
|
||||
if (field.hasMany) {
|
||||
if (!Array.isArray(fieldData)) {
|
||||
return
|
||||
}
|
||||
|
||||
fieldData.forEach((relation) => {
|
||||
if (!relation) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
if (!relation?.relationTo || !relation?.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (payload.collections[relation.relationTo]?.config?.versions?.drafts) {
|
||||
addRelation({ id: extractID(relation.value), collectionSlug: relation.relationTo })
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (payload.collections[field.relationTo].config.versions.drafts) {
|
||||
addRelation({ id: extractID(relation), collectionSlug: field.relationTo })
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
if (!fieldData?.relationTo || !fieldData?.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (payload.collections[fieldData.relationTo]?.config?.versions?.drafts) {
|
||||
addRelation({ id: extractID(fieldData.value), collectionSlug: fieldData.relationTo })
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (payload.collections[field.relationTo].config.versions.drafts) {
|
||||
addRelation({ id: extractID(fieldData), collectionSlug: field.relationTo })
|
||||
}
|
||||
}
|
||||
|
||||
const collectRelationsToPublish = ({
|
||||
addRelation,
|
||||
data,
|
||||
fields,
|
||||
payload,
|
||||
}: {
|
||||
addRelation: (relation: { collectionSlug: CollectionSlug; id: number | string }) => void
|
||||
data: Record<string, unknown>
|
||||
fields: FlattenedField[]
|
||||
payload: Payload
|
||||
}) => {
|
||||
for (const field of fields) {
|
||||
const fieldData = data[field.name]
|
||||
if (!fieldData) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch (field.type) {
|
||||
case 'array':
|
||||
case 'blocks': {
|
||||
if (field.localized && payload.config.localization) {
|
||||
if (typeof fieldData !== 'object') {
|
||||
break
|
||||
}
|
||||
|
||||
for (const locale in fieldData) {
|
||||
const localeData = (fieldData as any)[locale]
|
||||
if (!Array.isArray(localeData)) {
|
||||
continue
|
||||
}
|
||||
|
||||
localeData.forEach((item) => {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
|
||||
let fields: FlattenedField[]
|
||||
|
||||
if (field.type === 'blocks') {
|
||||
const block = field.blocks.find((each) => each.slug === item.blockType)
|
||||
if (!block) {
|
||||
return
|
||||
}
|
||||
fields = block.flattenedFields
|
||||
} else {
|
||||
fields = field.flattenedFields
|
||||
}
|
||||
|
||||
collectRelationsToPublish({
|
||||
addRelation,
|
||||
data: item,
|
||||
fields,
|
||||
payload,
|
||||
})
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (!Array.isArray(fieldData)) {
|
||||
break
|
||||
}
|
||||
|
||||
fieldData.forEach((item) => {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
|
||||
let fields: FlattenedField[]
|
||||
|
||||
if (field.type === 'blocks') {
|
||||
const block = field.blocks.find((each) => each.slug === item.blockType)
|
||||
if (!block) {
|
||||
return
|
||||
}
|
||||
fields = block.flattenedFields
|
||||
} else {
|
||||
fields = field.flattenedFields
|
||||
}
|
||||
|
||||
collectRelationsToPublish({
|
||||
addRelation,
|
||||
data: item,
|
||||
fields,
|
||||
payload,
|
||||
})
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'group':
|
||||
case 'tab': {
|
||||
if (typeof fieldData !== 'object') {
|
||||
break
|
||||
}
|
||||
|
||||
if (field.localized && payload.config.localization) {
|
||||
for (const locale in fieldData) {
|
||||
const localeData = (fieldData as any)[locale]
|
||||
if (!localeData || typeof localeData !== 'object') {
|
||||
continue
|
||||
}
|
||||
|
||||
collectRelationsToPublish({
|
||||
addRelation,
|
||||
data: localeData,
|
||||
fields: field.flattenedFields,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
collectRelationsToPublish({
|
||||
addRelation,
|
||||
data: fieldData as any,
|
||||
fields: field.flattenedFields,
|
||||
payload,
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'relationship':
|
||||
collectRelationField({ addRelation, field, fieldData, payload })
|
||||
break
|
||||
|
||||
case 'richText':
|
||||
if (typeof field.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
if (!field.editor?.traverseData) {
|
||||
break
|
||||
}
|
||||
|
||||
if (!fieldData || typeof fieldData !== 'object') {
|
||||
break
|
||||
}
|
||||
|
||||
if (field.localized && payload.config.localization) {
|
||||
for (const locale in fieldData) {
|
||||
const localeData = (fieldData as any)[locale]
|
||||
if (!localeData || typeof localeData !== 'object') {
|
||||
continue
|
||||
}
|
||||
|
||||
field.editor.traverseData({
|
||||
data: localeData,
|
||||
field,
|
||||
onFields: ({ data, fields }) => {
|
||||
collectRelationsToPublish({ addRelation, data, fields, payload })
|
||||
},
|
||||
onRelationship: ({ id, collectionSlug }) => {
|
||||
collectRelationField({
|
||||
addRelation,
|
||||
field: {
|
||||
name: 'relation',
|
||||
type: 'relationship',
|
||||
relationTo: collectionSlug,
|
||||
},
|
||||
fieldData: id,
|
||||
payload,
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
field.editor.traverseData({
|
||||
data: fieldData,
|
||||
field,
|
||||
onFields: ({ data, fields }) => {
|
||||
collectRelationsToPublish({ addRelation, data, fields, payload })
|
||||
},
|
||||
onRelationship: ({ id, collectionSlug }) => {
|
||||
collectRelationField({
|
||||
addRelation,
|
||||
field: {
|
||||
name: 'relation',
|
||||
type: 'relationship',
|
||||
relationTo: collectionSlug,
|
||||
},
|
||||
fieldData: id,
|
||||
payload,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const handleCascadePublish = async ({
|
||||
collectionSlug,
|
||||
doc,
|
||||
fields,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
}: {
|
||||
collectionSlug?: string
|
||||
doc: JsonObject
|
||||
fields: FlattenedField[]
|
||||
publishSpecificLocale?: string
|
||||
req: PayloadRequest
|
||||
}) => {
|
||||
const relationsToPublish: Record<CollectionSlug, (number | string)[]> = {}
|
||||
|
||||
const addRelation = (relation: Relation) => {
|
||||
// Skip cascade itself
|
||||
if (collectionSlug && relation.collectionSlug === collectionSlug && relation.id === doc.id) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!relationsToPublish[relation.collectionSlug]) {
|
||||
relationsToPublish[relation.collectionSlug] = []
|
||||
}
|
||||
|
||||
if (relationsToPublish[relation.collectionSlug].some((doc) => doc === relation.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
relationsToPublish[relation.collectionSlug].push(relation.id)
|
||||
}
|
||||
|
||||
collectRelationsToPublish({
|
||||
addRelation,
|
||||
data: doc,
|
||||
fields,
|
||||
payload: req.payload,
|
||||
})
|
||||
|
||||
if (Object.keys(relationsToPublish).length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const collectionSlug in relationsToPublish) {
|
||||
try {
|
||||
await req.payload.update({
|
||||
collection: collectionSlug,
|
||||
data: {
|
||||
_status: 'published',
|
||||
},
|
||||
depth: 0,
|
||||
draft: true,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
where: {
|
||||
and: [
|
||||
{
|
||||
id: {
|
||||
in: relationsToPublish[collectionSlug],
|
||||
},
|
||||
},
|
||||
{
|
||||
_status: {
|
||||
equals: 'draft',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
// Eat the error
|
||||
req.payload.logger.error({
|
||||
err,
|
||||
msg: `Error publishing cascade of collection ${collectionSlug}, ids - ${JSON.stringify(relationsToPublish[collectionSlug])}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ export type IncomingDrafts = {
|
||||
* To enable, set to true or pass an object with options.
|
||||
*/
|
||||
autosave?: Autosave | boolean
|
||||
cascadePublish?: boolean
|
||||
/**
|
||||
* Allow for editors to schedule publish / unpublish events in the future.
|
||||
*/
|
||||
@@ -32,6 +33,7 @@ export type SanitizedDrafts = {
|
||||
* To enable, set to true or pass an object with options.
|
||||
*/
|
||||
autosave: Autosave | false
|
||||
cascadePublish: boolean
|
||||
/**
|
||||
* Allow for editors to schedule publish / unpublish events in the future.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@ import { i18n } from './i18n.js'
|
||||
import { getBlockMarkdownTransformers } from './markdownTransformer.js'
|
||||
import { ServerBlockNode } from './nodes/BlocksNode.js'
|
||||
import { ServerInlineBlockNode } from './nodes/InlineBlocksNode.js'
|
||||
import { traverseNodeDataHOC } from './traverseNodeData.js'
|
||||
import { blockValidationHOC } from './validate.js'
|
||||
|
||||
export type BlocksFeatureProps = {
|
||||
@@ -205,6 +206,7 @@ export const BlocksFeature = createServerFeature<BlocksFeatureProps, BlocksFeatu
|
||||
|
||||
nodes: [
|
||||
createNode({
|
||||
traverseNodeData: traverseNodeDataHOC(blockConfigs),
|
||||
// @ts-expect-error - TODO: fix this
|
||||
getSubFields: ({ node }) => {
|
||||
if (!node) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { type Block, flattenAllFields } from 'payload'
|
||||
|
||||
import type { TraverseNodeData } from '../../typesServer.js'
|
||||
import type { SerializedBlockNode } from './nodes/BlocksNode.js'
|
||||
import type { SerializedInlineBlockNode } from './nodes/InlineBlocksNode.js'
|
||||
|
||||
export const traverseNodeDataHOC = (
|
||||
blocks: Block[],
|
||||
): TraverseNodeData<SerializedBlockNode | SerializedInlineBlockNode> => {
|
||||
const traverseNodeData: TraverseNodeData<SerializedBlockNode | SerializedInlineBlockNode> = ({
|
||||
node,
|
||||
onFields,
|
||||
}) => {
|
||||
const blockFieldData = node.fields
|
||||
|
||||
// find block used in this node
|
||||
const block = blocks.find((block) => block.slug === blockFieldData.blockType)
|
||||
if (!block || !block?.fields?.length || !blockFieldData) {
|
||||
return
|
||||
}
|
||||
|
||||
if (onFields) {
|
||||
onFields({ data: blockFieldData, fields: flattenAllFields({ fields: block.fields }) })
|
||||
}
|
||||
}
|
||||
|
||||
return traverseNodeData
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import { LinkNode } from '../nodes/LinkNode.js'
|
||||
import { linkPopulationPromiseHOC } from './graphQLPopulationPromise.js'
|
||||
import { i18n } from './i18n.js'
|
||||
import { transformExtraFields } from './transformExtraFields.js'
|
||||
import { traverseNodeDataHOC } from './traverseNodeData.js'
|
||||
import { linkValidation } from './validate.js'
|
||||
|
||||
export type ExclusiveLinkCollectionsProps =
|
||||
@@ -261,6 +262,7 @@ export const LinkFeature = createServerFeature<
|
||||
},
|
||||
graphQLPopulationPromises: [linkPopulationPromiseHOC(props)],
|
||||
node: LinkNode,
|
||||
traverseNodeData: traverseNodeDataHOC(props),
|
||||
validations: [linkValidation(props, sanitizedFieldsWithoutText)],
|
||||
}),
|
||||
].filter(Boolean) as Array<NodeWithHooks>,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { flattenAllFields } from 'payload'
|
||||
|
||||
import type { TraverseNodeData } from '../../typesServer.js'
|
||||
import type { SerializedLinkNode } from '../nodes/types.js'
|
||||
import type { LinkFeatureServerProps } from './index.js'
|
||||
|
||||
export const traverseNodeDataHOC = (
|
||||
props: LinkFeatureServerProps,
|
||||
): TraverseNodeData<SerializedLinkNode> => {
|
||||
return ({ node, onFields }) => {
|
||||
if (!props.fields?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (onFields && Array.isArray(props.fields)) {
|
||||
onFields({
|
||||
data: node.fields,
|
||||
fields: flattenAllFields({ fields: props.fields }),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { createNode } from '../../typeUtilities.js'
|
||||
import { relationshipPopulationPromiseHOC } from './graphQLPopulationPromise.js'
|
||||
import { i18n } from './i18n.js'
|
||||
import { RelationshipServerNode } from './nodes/RelationshipNode.js'
|
||||
import { traverseNodeData } from './traverseNodeData.js'
|
||||
|
||||
export type ExclusiveRelationshipFeatureProps =
|
||||
| {
|
||||
@@ -102,6 +103,7 @@ export const RelationshipFeature = createServerFeature<
|
||||
],
|
||||
},
|
||||
node: RelationshipServerNode,
|
||||
traverseNodeData,
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { TraverseNodeData } from '../../typesServer.js'
|
||||
import type { SerializedRelationshipNode } from './nodes/RelationshipNode.js'
|
||||
|
||||
export const traverseNodeData: TraverseNodeData<SerializedRelationshipNode> = ({
|
||||
node,
|
||||
onRelationship,
|
||||
}) => {
|
||||
if (node?.value) {
|
||||
// @ts-expect-error
|
||||
const id = node?.value?.id || node?.value // for backwards-compatibility
|
||||
|
||||
if (node?.relationTo && onRelationship) {
|
||||
onRelationship({
|
||||
id,
|
||||
collectionSlug: node.relationTo,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,19 @@ import type {
|
||||
SerializedLexicalNode,
|
||||
} from 'lexical'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
Config,
|
||||
Field,
|
||||
FieldSchemaMap,
|
||||
FlattenedField,
|
||||
JsonObject,
|
||||
Payload,
|
||||
PayloadComponent,
|
||||
PayloadRequest,
|
||||
PopulateType,
|
||||
ReplaceAny,
|
||||
RequestContext,
|
||||
RichTextAdapter,
|
||||
RichTextField,
|
||||
RichTextHooks,
|
||||
SanitizedConfig,
|
||||
@@ -55,6 +59,12 @@ export type PopulationPromise<T extends SerializedLexicalNode = SerializedLexica
|
||||
siblingDoc: JsonObject
|
||||
}) => void
|
||||
|
||||
export type TraverseNodeData<T extends SerializedLexicalNode = SerializedLexicalNode> = (args: {
|
||||
node: T
|
||||
onFields?: (args: { data: any; fields: FlattenedField[] }) => void
|
||||
onRelationship?: (args: { collectionSlug: CollectionSlug; id: number | string }) => void
|
||||
}) => void
|
||||
|
||||
export type NodeValidation<T extends SerializedLexicalNode = SerializedLexicalNode> = ({
|
||||
node,
|
||||
nodeValidations,
|
||||
@@ -257,6 +267,7 @@ export type NodeWithHooks<T extends LexicalNode = any> = {
|
||||
graphQLPopulationPromises?: Array<
|
||||
PopulationPromise<ReturnType<ReplaceAny<T, LexicalNode>['exportJSON']>>
|
||||
>
|
||||
|
||||
/**
|
||||
* Just like payload fields, you can provide hooks which are run for this specific node. These are called Node Hooks.
|
||||
*/
|
||||
@@ -272,6 +283,7 @@ export type NodeWithHooks<T extends LexicalNode = any> = {
|
||||
* The actual lexical node needs to be provided here. This also supports [lexical node replacements](https://lexical.dev/docs/concepts/node-replacement).
|
||||
*/
|
||||
node: Klass<T> | LexicalNodeReplacement
|
||||
traverseNodeData?: TraverseNodeData<ReturnType<ReplaceAny<T, LexicalNode>['exportJSON']>>
|
||||
/**
|
||||
* This allows you to provide node validations, which are run when your document is being validated, alongside other payload fields.
|
||||
* You can use it to throw a validation error for a specific node in case its data is incorrect.
|
||||
@@ -411,6 +423,7 @@ export type SanitizedServerFeatures = {
|
||||
beforeChange?: Map<string, Array<BeforeChangeNodeHook<SerializedLexicalNode>>>
|
||||
beforeValidate?: Map<string, Array<BeforeValidateNodeHook<SerializedLexicalNode>>>
|
||||
} /** The node types mapped to their populationPromises */
|
||||
traverseNodeData?: Map<string, TraverseNodeData>
|
||||
/** The node types mapped to their validations */
|
||||
validations: Map<string, Array<NodeValidation>>
|
||||
} & Required<Pick<ResolvedServerFeature<any, any>, 'i18n' | 'nodes'>>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { i18n } from './i18n.js'
|
||||
import { defaultEditorFeatures } from './lexical/config/server/default.js'
|
||||
import { populateLexicalPopulationPromises } from './populateGraphQL/populateLexicalPopulationPromises.js'
|
||||
import { featuresInputToEditorConfig } from './utilities/editorConfigFactory.js'
|
||||
import { recurseNodes } from './utilities/forEachNodeRecursively.js'
|
||||
import { getGenerateImportMap } from './utilities/generateImportMap.js'
|
||||
import { getGenerateSchemaMap } from './utilities/generateSchemaMap.js'
|
||||
import { recurseNodeTree } from './utilities/recurseNodeTree.js'
|
||||
@@ -853,6 +854,26 @@ export function lexicalEditor(args?: LexicalEditorProps): LexicalRichTextAdapter
|
||||
|
||||
return outputSchema
|
||||
},
|
||||
traverseData: ({ data, onFields, onRelationship }) => {
|
||||
recurseNodes({
|
||||
callback: (node) => {
|
||||
const traverseNodeData = finalSanitizedEditorConfig.features.traverseNodeData?.get(
|
||||
node.type,
|
||||
)
|
||||
|
||||
if (!traverseNodeData) {
|
||||
return
|
||||
}
|
||||
|
||||
traverseNodeData({
|
||||
node,
|
||||
onFields,
|
||||
onRelationship,
|
||||
})
|
||||
},
|
||||
nodes: data.root.children,
|
||||
})
|
||||
},
|
||||
validate: richTextValidateHOC({
|
||||
editorConfig: finalSanitizedEditorConfig,
|
||||
}),
|
||||
|
||||
@@ -37,6 +37,7 @@ export const sanitizeServerFeatures = (
|
||||
beforeValidate: new Map(),
|
||||
},
|
||||
nodes: [],
|
||||
traverseNodeData: new Map(),
|
||||
|
||||
validations: new Map(),
|
||||
}
|
||||
@@ -89,6 +90,9 @@ export const sanitizeServerFeatures = (
|
||||
if (node?.graphQLPopulationPromises?.length) {
|
||||
sanitized.graphQLPopulationPromises.set(nodeType, node.graphQLPopulationPromises)
|
||||
}
|
||||
if (node?.traverseNodeData) {
|
||||
sanitized.traverseNodeData?.set(nodeType, node.traverseNodeData)
|
||||
}
|
||||
if (node?.validations?.length) {
|
||||
sanitized.validations.set(nodeType, node.validations)
|
||||
}
|
||||
|
||||
31
packages/richtext-slate/src/data/traverseData.ts
Normal file
31
packages/richtext-slate/src/data/traverseData.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { RichTextAdapter } from 'payload'
|
||||
|
||||
export const traverseData: RichTextAdapter['traverseData'] = ({
|
||||
data,
|
||||
field,
|
||||
onFields,
|
||||
onRelationship,
|
||||
}) => {
|
||||
if (
|
||||
field.admin?.elements?.includes('relationship') ||
|
||||
field.admin?.elements?.includes('upload') ||
|
||||
field.admin?.elements?.includes('link') ||
|
||||
!field?.admin?.elements
|
||||
) {
|
||||
if (Array.isArray(data)) {
|
||||
for (const element of data) {
|
||||
if (element.type === 'relationship' && element?.value?.id && element.relationTo) {
|
||||
onRelationship({ id: element.value.id, collectionSlug: element.relationTo })
|
||||
}
|
||||
|
||||
if (element.type === 'link' && element?.doc?.value && element?.doc?.relationTo) {
|
||||
onRelationship({ id: element.doc.value, collectionSlug: element.doc.relationTo })
|
||||
}
|
||||
|
||||
if (Array.isArray(element.children)) {
|
||||
traverseData({ data: element.children, field, onFields, onRelationship })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { sanitizeFields, withNullableJSONSchemaType } from 'payload'
|
||||
import type { AdapterArguments } from './types.js'
|
||||
|
||||
import { richTextRelationshipPromise } from './data/richTextRelationshipPromise.js'
|
||||
import { traverseData } from './data/traverseData.js'
|
||||
import { richTextValidate } from './data/validation.js'
|
||||
import { elements as elementTypes } from './field/elements/index.js'
|
||||
import { transformExtraFields } from './field/elements/link/utilities.js'
|
||||
@@ -205,6 +206,7 @@ export function slateEditor(
|
||||
},
|
||||
}
|
||||
},
|
||||
traverseData,
|
||||
validate: richTextValidate,
|
||||
}
|
||||
}
|
||||
|
||||
58
test/versions/collections/CascadePublish.ts
Normal file
58
test/versions/collections/CascadePublish.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { BlocksFeature, lexicalEditor, LinkFeature } from '@payloadcms/richtext-lexical'
|
||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
|
||||
import { cascadePublishSlug } from '../slugs.js'
|
||||
|
||||
export const CascadePublish: CollectionConfig = {
|
||||
slug: cascadePublishSlug,
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relation',
|
||||
type: 'relationship',
|
||||
relationTo: 'cascade-publish-relations',
|
||||
},
|
||||
{
|
||||
name: 'lexical',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features({ defaultFeatures }) {
|
||||
return [
|
||||
...defaultFeatures,
|
||||
LinkFeature({ enabledCollections: ['cascade-publish-relations'] }),
|
||||
BlocksFeature({
|
||||
blocks: [
|
||||
{
|
||||
slug: 'someBlock',
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'cascade-publish-relations',
|
||||
name: 'relation',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
]
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'slate',
|
||||
type: 'richText',
|
||||
editor: slateEditor({}),
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: { cascadePublish: true },
|
||||
},
|
||||
}
|
||||
19
test/versions/collections/CascadePublishRelations.ts
Normal file
19
test/versions/collections/CascadePublishRelations.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { cascadePublishRelationsSlug } from '../slugs.js'
|
||||
|
||||
export const CascadePublishRelations: CollectionConfig = {
|
||||
slug: cascadePublishRelationsSlug,
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: { cascadePublish: true },
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,8 @@ const dirname = path.dirname(filename)
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import AutosavePosts from './collections/Autosave.js'
|
||||
import AutosaveWithValidate from './collections/AutosaveWithValidate.js'
|
||||
import { CascadePublish } from './collections/CascadePublish.js'
|
||||
import { CascadePublishRelations } from './collections/CascadePublishRelations.js'
|
||||
import CustomIDs from './collections/CustomIDs.js'
|
||||
import { Diff } from './collections/Diff.js'
|
||||
import DisablePublish from './collections/DisablePublish.js'
|
||||
@@ -16,6 +18,7 @@ import { Media } from './collections/Media.js'
|
||||
import Posts from './collections/Posts.js'
|
||||
import VersionPosts from './collections/Versions.js'
|
||||
import AutosaveGlobal from './globals/Autosave.js'
|
||||
import { CascadePublishGlobal } from './globals/CascadePublishGlobal.js'
|
||||
import DisablePublishGlobal from './globals/DisablePublish.js'
|
||||
import DraftGlobal from './globals/Draft.js'
|
||||
import DraftWithMaxGlobal from './globals/DraftWithMax.js'
|
||||
@@ -43,8 +46,17 @@ export default buildConfigWithDefaults({
|
||||
CustomIDs,
|
||||
Diff,
|
||||
Media,
|
||||
CascadePublish,
|
||||
CascadePublishRelations,
|
||||
],
|
||||
globals: [
|
||||
AutosaveGlobal,
|
||||
DraftGlobal,
|
||||
DraftWithMaxGlobal,
|
||||
DisablePublishGlobal,
|
||||
LocalizedGlobal,
|
||||
CascadePublishGlobal,
|
||||
],
|
||||
globals: [AutosaveGlobal, DraftGlobal, DraftWithMaxGlobal, DisablePublishGlobal, LocalizedGlobal],
|
||||
indexSortableFields: true,
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
|
||||
42
test/versions/globals/CascadePublishGlobal.ts
Normal file
42
test/versions/globals/CascadePublishGlobal.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { CollectionConfig, GlobalConfig } from 'payload'
|
||||
|
||||
import { cascadePublishGlobalSlug, cascadePublishSlug } from '../slugs.js'
|
||||
|
||||
export const CascadePublish: CollectionConfig = {
|
||||
slug: cascadePublishSlug,
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relation',
|
||||
type: 'relationship',
|
||||
relationTo: 'cascade-publish-relations',
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: { cascadePublish: true },
|
||||
},
|
||||
}
|
||||
|
||||
export const CascadePublishGlobal: GlobalConfig = {
|
||||
slug: cascadePublishGlobalSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relation',
|
||||
type: 'relationship',
|
||||
relationTo: 'cascade-publish-relations',
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: { cascadePublish: true },
|
||||
},
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Payload } from 'payload';
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import { schedulePublishHandler } from '@payloadcms/ui/utilities/schedulePublishHandler'
|
||||
import path from 'path'
|
||||
import { createLocalReq , ValidationError } from 'payload'
|
||||
import { createLocalReq, ValidationError } from 'payload'
|
||||
import { wait } from 'payload/shared'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
@@ -16,6 +16,7 @@ import AutosaveGlobal from './globals/Autosave.js'
|
||||
import {
|
||||
autosaveCollectionSlug,
|
||||
autoSaveGlobalSlug,
|
||||
cascadePublishRelationsSlug,
|
||||
draftCollectionSlug,
|
||||
draftGlobalSlug,
|
||||
localizedCollectionSlug,
|
||||
@@ -2767,4 +2768,238 @@ describe('Versions', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Cascade publish', () => {
|
||||
it('should cascade publish collection', async () => {
|
||||
const relation = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation 1' },
|
||||
})
|
||||
expect(relation._status).toBe('draft')
|
||||
const doc = await payload.create({
|
||||
collection: 'cascade-publish',
|
||||
draft: true,
|
||||
data: { relation: relation.id, title: 'Cascade Publish 1' },
|
||||
})
|
||||
expect(doc._status).toBe('draft')
|
||||
|
||||
const publishedDoc = await payload.update({
|
||||
collection: 'cascade-publish',
|
||||
id: doc.id,
|
||||
depth: 0,
|
||||
data: { _status: 'published', title: 'Cascade Publish - published' },
|
||||
})
|
||||
|
||||
expect(publishedDoc._status).toBe('published')
|
||||
|
||||
const publishedRelation = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation.id,
|
||||
})
|
||||
// And Here it fails..
|
||||
expect(publishedRelation._status).toBe('published')
|
||||
})
|
||||
|
||||
it('should cascade publish collection with relationship data within Lexical', async () => {
|
||||
const relation_1 = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation 1', _status: 'draft' },
|
||||
})
|
||||
const relation_2 = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation 2', _status: 'draft' },
|
||||
})
|
||||
const relation_3 = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation 3', _status: 'draft' },
|
||||
})
|
||||
|
||||
const doc = await payload.create({
|
||||
collection: 'cascade-publish',
|
||||
draft: true,
|
||||
depth: 0,
|
||||
data: {
|
||||
_status: 'draft',
|
||||
title: 'Cascade Publish 1',
|
||||
lexical: {
|
||||
root: {
|
||||
children: [
|
||||
{
|
||||
format: '',
|
||||
type: 'relationship',
|
||||
version: 2,
|
||||
relationTo: cascadePublishRelationsSlug,
|
||||
value: relation_1.id,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'link to cascade relation',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
id: '665d10938106ab380c7f3730',
|
||||
type: 'link',
|
||||
version: 2,
|
||||
fields: {
|
||||
url: 'https://',
|
||||
doc: {
|
||||
value: relation_2.id,
|
||||
relationTo: cascadePublishRelationsSlug,
|
||||
},
|
||||
newTab: false,
|
||||
linkType: 'internal',
|
||||
},
|
||||
},
|
||||
{
|
||||
format: '',
|
||||
type: 'block',
|
||||
version: 2,
|
||||
fields: {
|
||||
id: '65298b13db4ef8c744a7faaa',
|
||||
relation: relation_3.id,
|
||||
blockName: 'Some Block Node, with Relationship Field',
|
||||
blockType: 'someBlock',
|
||||
},
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'root',
|
||||
version: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const publishedDoc = await payload.update({
|
||||
collection: 'cascade-publish',
|
||||
id: doc.id,
|
||||
depth: 0,
|
||||
draft: false,
|
||||
data: { _status: 'published', title: 'Cascade Publish - published' },
|
||||
})
|
||||
expect(publishedDoc._status).toBe('published')
|
||||
|
||||
const publishedRelation_1 = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation_1.id,
|
||||
})
|
||||
expect(publishedRelation_1._status).toBe('published')
|
||||
|
||||
const publishedRelation_2 = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation_2.id,
|
||||
})
|
||||
expect(publishedRelation_2._status).toBe('published')
|
||||
|
||||
const publishedRelation_3 = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation_3.id,
|
||||
})
|
||||
expect(publishedRelation_3._status).toBe('published')
|
||||
})
|
||||
|
||||
it('should cascade publish collection with relationship data within Slate', async () => {
|
||||
const relation_1 = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation 1', _status: 'draft' },
|
||||
})
|
||||
const relation_2 = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation 2', _status: 'draft' },
|
||||
})
|
||||
|
||||
const doc = await payload.create({
|
||||
collection: 'cascade-publish',
|
||||
draft: true,
|
||||
depth: 0,
|
||||
data: {
|
||||
_status: 'draft',
|
||||
title: 'Cascade Publish 1',
|
||||
slate: [
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: cascadePublishRelationsSlug,
|
||||
value: { id: relation_1.id },
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
linkType: 'internal',
|
||||
doc: {
|
||||
relationTo: cascadePublishRelationsSlug,
|
||||
value: relation_2.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const publishedDoc = await payload.update({
|
||||
collection: 'cascade-publish',
|
||||
id: doc.id,
|
||||
depth: 0,
|
||||
draft: false,
|
||||
data: { _status: 'published', title: 'Cascade Publish - published' },
|
||||
})
|
||||
expect(publishedDoc._status).toBe('published')
|
||||
|
||||
const publishedRelation_1 = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation_1.id,
|
||||
})
|
||||
expect(publishedRelation_1._status).toBe('published')
|
||||
|
||||
const publishedRelation_2 = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation_2.id,
|
||||
})
|
||||
expect(publishedRelation_2._status).toBe('published')
|
||||
})
|
||||
|
||||
it('should cascade publish global', async () => {
|
||||
const relation = await payload.create({
|
||||
collection: 'cascade-publish-relations',
|
||||
draft: true,
|
||||
data: { title: 'Relation to global', _status: 'draft' },
|
||||
})
|
||||
expect(relation._status).toBe('draft')
|
||||
const globalDoc = await payload.updateGlobal({
|
||||
slug: 'cascade-publish-global',
|
||||
draft: true,
|
||||
depth: 0,
|
||||
data: { _status: 'draft', title: 'Some title 1', relation: relation.id },
|
||||
})
|
||||
expect(globalDoc._status).toBe('draft')
|
||||
|
||||
const publishedGlobalDoc = await payload.updateGlobal({
|
||||
slug: 'cascade-publish-global',
|
||||
draft: false,
|
||||
depth: 0,
|
||||
data: { _status: 'published' },
|
||||
})
|
||||
expect(publishedGlobalDoc._status).toBe('published')
|
||||
|
||||
const relationDoc = await payload.findByID({
|
||||
collection: 'cascade-publish-relations',
|
||||
id: relation.id,
|
||||
})
|
||||
expect(relationDoc._status).toBe('published')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -78,6 +78,8 @@ export interface Config {
|
||||
'custom-ids': CustomId;
|
||||
diff: Diff;
|
||||
media: Media;
|
||||
'cascade-publish': CascadePublish;
|
||||
'cascade-publish-relations': CascadePublishRelation;
|
||||
users: User;
|
||||
'payload-jobs': PayloadJob;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
@@ -98,6 +100,8 @@ export interface Config {
|
||||
'custom-ids': CustomIdsSelect<false> | CustomIdsSelect<true>;
|
||||
diff: DiffSelect<false> | DiffSelect<true>;
|
||||
media: MediaSelect<false> | MediaSelect<true>;
|
||||
'cascade-publish': CascadePublishSelect<false> | CascadePublishSelect<true>;
|
||||
'cascade-publish-relations': CascadePublishRelationsSelect<false> | CascadePublishRelationsSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-jobs': PayloadJobsSelect<false> | PayloadJobsSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
@@ -113,6 +117,7 @@ export interface Config {
|
||||
'draft-with-max-global': DraftWithMaxGlobal;
|
||||
'disable-publish-global': DisablePublishGlobal;
|
||||
'localized-global': LocalizedGlobal;
|
||||
'cascade-publish-global': CascadePublishGlobal;
|
||||
};
|
||||
globalsSelect: {
|
||||
'autosave-global': AutosaveGlobalSelect<false> | AutosaveGlobalSelect<true>;
|
||||
@@ -120,6 +125,7 @@ export interface Config {
|
||||
'draft-with-max-global': DraftWithMaxGlobalSelect<false> | DraftWithMaxGlobalSelect<true>;
|
||||
'disable-publish-global': DisablePublishGlobalSelect<false> | DisablePublishGlobalSelect<true>;
|
||||
'localized-global': LocalizedGlobalSelect<false> | LocalizedGlobalSelect<true>;
|
||||
'cascade-publish-global': CascadePublishGlobalSelect<false> | CascadePublishGlobalSelect<true>;
|
||||
};
|
||||
locale: 'en' | 'es' | 'de';
|
||||
user: User & {
|
||||
@@ -395,6 +401,49 @@ export interface Media {
|
||||
focalX?: number | null;
|
||||
focalY?: number | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cascade-publish".
|
||||
*/
|
||||
export interface CascadePublish {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
relation?: (string | null) | CascadePublishRelation;
|
||||
lexical?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
slate?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cascade-publish-relations".
|
||||
*/
|
||||
export interface CascadePublishRelation {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
@@ -559,6 +608,14 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'media';
|
||||
value: string | Media;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'cascade-publish';
|
||||
value: string | CascadePublish;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'cascade-publish-relations';
|
||||
value: string | CascadePublishRelation;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
@@ -820,6 +877,29 @@ export interface MediaSelect<T extends boolean = true> {
|
||||
focalX?: T;
|
||||
focalY?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cascade-publish_select".
|
||||
*/
|
||||
export interface CascadePublishSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
relation?: T;
|
||||
lexical?: T;
|
||||
slate?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cascade-publish-relations_select".
|
||||
*/
|
||||
export interface CascadePublishRelationsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users_select".
|
||||
@@ -954,6 +1034,18 @@ export interface LocalizedGlobal {
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cascade-publish-global".
|
||||
*/
|
||||
export interface CascadePublishGlobal {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
relation?: (string | null) | CascadePublishRelation;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-global_select".
|
||||
@@ -1010,6 +1102,18 @@ export interface LocalizedGlobalSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cascade-publish-global_select".
|
||||
*/
|
||||
export interface CascadePublishGlobalSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
relation?: T;
|
||||
_status?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "TaskSchedulePublish".
|
||||
|
||||
@@ -38,3 +38,7 @@ export const globalSlugs = [autoSaveGlobalSlug, draftGlobalSlug]
|
||||
export const localizedCollectionSlug = 'localized-posts'
|
||||
|
||||
export const localizedGlobalSlug = 'localized-global'
|
||||
|
||||
export const cascadePublishSlug = 'cascade-publish'
|
||||
export const cascadePublishGlobalSlug = 'cascade-publish-global'
|
||||
export const cascadePublishRelationsSlug = 'cascade-publish-relations'
|
||||
|
||||
Reference in New Issue
Block a user