diff --git a/dev-test/backends/default-workflow-status/config.yml b/dev-test/backends/default-workflow-status/config.yml new file mode 100644 index 000000000000..5b44c1177ead --- /dev/null +++ b/dev-test/backends/default-workflow-status/config.yml @@ -0,0 +1,62 @@ +backend: + name: test-repo + +publish_mode: editorial_workflow +default_workflow_status: pending_publish +media_folder: static/media +public_folder: /media +collections: + - name: posts + label: Posts + label_singular: 'Post' + folder: content/posts + create: true + slug: '{{year}}-{{month}}-{{day}}-{{slug}}' + fields: + - label: Template + name: template + widget: hidden + default: post + - label: Title + name: title + widget: string + - label: 'Cover Image' + name: 'image' + widget: 'image' + required: false + - label: Publish Date + name: date + widget: datetime + - label: Description + name: description + widget: text + - label: Category + name: category + widget: string + - label: Body + name: body + widget: markdown + - label: Tags + name: tags + widget: list + - name: pages + label: Pages + label_singular: 'Page' + folder: content/pages + create: true + slug: '{{slug}}' + fields: + - label: Template + name: template + widget: hidden + default: page + - label: Title + name: title + widget: string + - label: Draft + name: draft + widget: boolean + default: true + - label: Body + name: body + widget: markdown diff --git a/dev-test/backends/default-workflow-status/index.html b/dev-test/backends/default-workflow-status/index.html new file mode 100644 index 000000000000..103dbb272d68 --- /dev/null +++ b/dev-test/backends/default-workflow-status/index.html @@ -0,0 +1,41 @@ + + + + + + Netlify CMS Development Test + + + + + + diff --git a/packages/decap-cms-core/index.d.ts b/packages/decap-cms-core/index.d.ts index 9eb5e7f9855e..49fbcae7a769 100644 --- a/packages/decap-cms-core/index.d.ts +++ b/packages/decap-cms-core/index.d.ts @@ -43,6 +43,8 @@ declare module 'decap-cms-core' { export type CmsPublishMode = 'simple' | 'editorial_workflow' | ''; + export type CmsPublishWorkflowStatus = 'draft' | 'pending_review' | 'pending_publish'; + export type CmsSlugEncoding = 'unicode' | 'ascii'; export interface CmsI18nConfig { @@ -388,6 +390,7 @@ declare module 'decap-cms-core' { media_folder_relative?: boolean; media_library?: CmsMediaLibrary; publish_mode?: CmsPublishMode; + default_workflow_status?: CmsPublishWorkflowStatus; load_config_file?: boolean; integrations?: { hooks: string[]; diff --git a/packages/decap-cms-core/src/actions/__tests__/config.spec.js b/packages/decap-cms-core/src/actions/__tests__/config.spec.js index 119345517ea8..6f6eecae02dc 100644 --- a/packages/decap-cms-core/src/actions/__tests__/config.spec.js +++ b/packages/decap-cms-core/src/actions/__tests__/config.spec.js @@ -9,6 +9,7 @@ import { detectProxyServer, handleLocalBackend, } from '../config'; +import { Statues } from '../../constants/publishModes'; jest.spyOn(console, 'log').mockImplementation(() => {}); jest.spyOn(console, 'warn').mockImplementation(() => {}); @@ -44,6 +45,7 @@ describe('config', () => { local_backend: true site_url: https://www.decapcms.org publish_mode: editorial_workflow + default_workflow_status: pending_publish media_folder: website/static/img public_folder: img docs_collection: &docs_collection @@ -69,6 +71,7 @@ describe('config', () => { local_backend: true, site_url: 'https://www.decapcms.org', publish_mode: 'editorial_workflow', + default_workflow_status: 'pending_publish', media_folder: 'website/static/img', public_folder: 'img', docs_collection: { @@ -120,6 +123,31 @@ describe('config', () => { }); }); + describe('default_workflow_status', () => { + it('should set default_workflow_status to "draft" by default if publish_mode is "editorial_workflow"', () => { + const config = { + publish_mode: 'editorial_workflow', + }; + expect(applyDefaults(config).default_workflow_status).toEqual(Statues.DRAFT); + }); + + it('should set default_workflow_status to "draft" if the given one is unknown', () => { + const config = { + publish_mode: 'editorial_workflow', + default_workflow_status: 'unknown', + }; + expect(applyDefaults(config).default_workflow_status).toEqual(Statues.DRAFT); + }); + + it('should set default_workflow_status from config', () => { + const config = { + publish_mode: 'editorial_workflow', + default_workflow_status: Statues.PENDING_REVIEW, + }; + expect(applyDefaults(config).default_workflow_status).toEqual(Statues.PENDING_REVIEW); + }); + }); + describe('public_folder', () => { it('should set public_folder based on media_folder if not set', () => { expect( diff --git a/packages/decap-cms-core/src/actions/config.ts b/packages/decap-cms-core/src/actions/config.ts index 66062a50a1ee..e20c8eccd957 100644 --- a/packages/decap-cms-core/src/actions/config.ts +++ b/packages/decap-cms-core/src/actions/config.ts @@ -4,7 +4,11 @@ import deepmerge from 'deepmerge'; import { produce } from 'immer'; import { trimStart, trim, isEmpty } from 'lodash'; -import { SIMPLE as SIMPLE_PUBLISH_MODE } from '../constants/publishModes'; +import { + EDITORIAL_WORKFLOW, + SIMPLE as SIMPLE_PUBLISH_MODE, + Statues, +} from '../constants/publishModes'; import { validateConfig } from '../constants/configSchema'; import { selectDefaultSortableFields } from '../reducers/collections'; import { getIntegrations, selectIntegration } from '../reducers/integrations'; @@ -210,6 +214,14 @@ export function applyDefaults(originalConfig: CmsConfig) { config.slug = config.slug || {}; config.collections = config.collections || []; + if (config.publish_mode === EDITORIAL_WORKFLOW) { + config.default_workflow_status = + config.default_workflow_status && + Object.values(Statues).includes(config.default_workflow_status) + ? config.default_workflow_status + : Statues.DRAFT; + } + // Use `site_url` as default `display_url`. if (!config.display_url && config.site_url) { config.display_url = config.site_url; diff --git a/packages/decap-cms-core/src/backend.ts b/packages/decap-cms-core/src/backend.ts index bcb8881de045..9447d5a82f62 100644 --- a/packages/decap-cms-core/src/backend.ts +++ b/packages/decap-cms-core/src/backend.ts @@ -14,7 +14,7 @@ import { basename, join, extname, dirname } from 'path'; import { stringTemplate } from 'decap-cms-lib-widgets'; import { resolveFormat } from './formats/formats'; -import { selectUseWorkflow } from './reducers/config'; +import { selectUseWorkflow, selectDefaultWorkflowStatus } from './reducers/config'; import { selectMediaFilePath, selectEntry } from './reducers/entries'; import { selectIntegration } from './reducers/integrations'; import { @@ -33,7 +33,6 @@ import { createEntry } from './valueObjects/Entry'; import { sanitizeChar } from './lib/urlHelper'; import { getBackend, invokeEvent } from './lib/registry'; import { commitMessageFormatter, slugFormatter, previewUrlFormatter } from './lib/formatters'; -import { status } from './constants/publishModes'; import { FOLDER, FILES } from './constants/collectionTypes'; import { selectCustomPath } from './reducers/entryDraft'; import { @@ -354,7 +353,7 @@ export class Backend { this.implementation = implementation.init(this.config, { useWorkflow: selectUseWorkflow(this.config), updateUserCredentials: this.updateUserCredentials, - initialWorkflowStatus: status.first(), + initialWorkflowStatus: selectDefaultWorkflowStatus(this.config), }); this.backendName = backendName; this.authStore = authStore; diff --git a/packages/decap-cms-core/src/constants/configSchema.js b/packages/decap-cms-core/src/constants/configSchema.js index 5efd2cd4c172..7dd1e8bdd73d 100644 --- a/packages/decap-cms-core/src/constants/configSchema.js +++ b/packages/decap-cms-core/src/constants/configSchema.js @@ -8,6 +8,7 @@ import { import ajvErrors from 'ajv-errors'; import { v4 as uuid } from 'uuid'; +import { Statues } from './publishModes'; import { frontmatterFormats, extensionFormatters } from '../formats/formats'; import { getWidgets } from '../lib/registry'; import { I18N_STRUCTURE, I18N_FIELD } from '../lib/i18n'; @@ -178,6 +179,11 @@ function getConfigSchema() { enum: ['simple', 'editorial_workflow', ''], examples: ['editorial_workflow'], }, + default_workflow_status: { + type: 'string', + enum: Object.values(Statues), + examples: [Statues.PENDING_PUBLISH], + }, slug: { type: 'object', properties: { diff --git a/packages/decap-cms-core/src/constants/publishModes.ts b/packages/decap-cms-core/src/constants/publishModes.ts index 56fba78ffb3d..7ac9ad5fba84 100644 --- a/packages/decap-cms-core/src/constants/publishModes.ts +++ b/packages/decap-cms-core/src/constants/publishModes.ts @@ -8,7 +8,7 @@ export const Statues = { DRAFT: 'draft', PENDING_REVIEW: 'pending_review', PENDING_PUBLISH: 'pending_publish', -}; +} as const; // Available status export const status = OrderedMap(Statues); @@ -20,3 +20,4 @@ export const statusDescriptions = Map({ }); export type Status = keyof typeof Statues; +export type StatusValues = (typeof Statues)[Status]; diff --git a/packages/decap-cms-core/src/reducers/config.ts b/packages/decap-cms-core/src/reducers/config.ts index d98546a48c1a..3553dbba6cca 100644 --- a/packages/decap-cms-core/src/reducers/config.ts +++ b/packages/decap-cms-core/src/reducers/config.ts @@ -1,7 +1,7 @@ import { produce } from 'immer'; import { CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAILURE } from '../actions/config'; -import { EDITORIAL_WORKFLOW } from '../constants/publishModes'; +import { EDITORIAL_WORKFLOW, status } from '../constants/publishModes'; import type { ConfigAction } from '../actions/config'; import type { CmsConfig } from '../types/redux'; @@ -35,4 +35,8 @@ export function selectUseWorkflow(state: CmsConfig) { return state.publish_mode === EDITORIAL_WORKFLOW; } +export function selectDefaultWorkflowStatus(state: CmsConfig) { + return state.default_workflow_status || status.first(); +} + export default config; diff --git a/packages/decap-cms-core/src/types/redux.ts b/packages/decap-cms-core/src/types/redux.ts index b69a82311532..3b212aa89084 100644 --- a/packages/decap-cms-core/src/types/redux.ts +++ b/packages/decap-cms-core/src/types/redux.ts @@ -11,6 +11,7 @@ import type { Search } from '../reducers/search'; import type { GlobalUI } from '../reducers/globalUI'; import type { NotificationsState } from '../reducers/notifications'; import type { formatExtensions } from '../formats/formats'; +import type { StatusValues as PublishStatusValues } from '../constants/publishModes'; export type CmsBackendType = | 'azure' @@ -402,6 +403,7 @@ export interface CmsConfig { media_folder_relative?: boolean; media_library?: CmsMediaLibrary; publish_mode?: CmsPublishMode; + default_workflow_status?: PublishStatusValues; load_config_file?: boolean; integrations?: { hooks: string[]; @@ -457,6 +459,7 @@ export type Config = StaticallyTypedRecord<{ media_folder: string; public_folder: string; publish_mode?: string; + default_workflow_status?: string; media_library: StaticallyTypedRecord<{ name: string }> & { name: string }; locale?: string; slug: SlugConfig;