diff --git a/packages/netlify-cms-core/src/actions/__tests__/config.spec.js b/packages/netlify-cms-core/src/actions/__tests__/config.spec.js index b3e2075d..baff41c0 100644 --- a/packages/netlify-cms-core/src/actions/__tests__/config.spec.js +++ b/packages/netlify-cms-core/src/actions/__tests__/config.spec.js @@ -110,7 +110,7 @@ describe('config', () => { expect( applyDefaults( fromJS({ - collections: [{ folder: '/foo' }], + collections: [{ folder: '/foo', fields: [{ name: 'title', widget: 'string' }] }], }), ).getIn(['collections', 0, 'folder']), ).toEqual('foo'); @@ -120,18 +120,26 @@ describe('config', () => { expect( applyDefaults( fromJS({ - collections: [{ files: [{ file: '/foo' }] }], + collections: [ + { files: [{ file: '/foo', fields: [{ name: 'title', widget: 'string' }] }] }, + ], }), ).getIn(['collections', 0, 'files', 0, 'file']), ).toEqual('foo'); }); describe('public_folder and media_folder', () => { - it('should set collection public_folder collection based on media_folder if not set', () => { + it('should set collection public_folder based on media_folder if not set', () => { expect( applyDefaults( fromJS({ - collections: [{ folder: 'foo', media_folder: 'static/images/docs' }], + collections: [ + { + folder: 'foo', + media_folder: 'static/images/docs', + fields: [{ name: 'title', widget: 'string' }], + }, + ], }), ).getIn(['collections', 0, 'public_folder']), ).toEqual('static/images/docs'); @@ -146,6 +154,7 @@ describe('config', () => { folder: 'foo', media_folder: 'static/images/docs', public_folder: 'images/docs', + fields: [{ name: 'title', widget: 'string' }], }, ], }), @@ -156,12 +165,140 @@ describe('config', () => { it("should set collection media_folder and public_folder to an empty string when collection path exists, but collection media_folder doesn't", () => { const result = applyDefaults( fromJS({ - collections: [{ folder: 'foo', path: '{{slug}}/index' }], + collections: [ + { + folder: 'foo', + path: '{{slug}}/index', + fields: [{ name: 'title', widget: 'string' }], + }, + ], }), ); expect(result.getIn(['collections', 0, 'media_folder'])).toEqual(''); expect(result.getIn(['collections', 0, 'public_folder'])).toEqual(''); }); + + it('should set file public_folder based on media_folder if not set', () => { + expect( + applyDefaults( + fromJS({ + collections: [ + { + files: [ + { + file: 'foo', + media_folder: 'static/images/docs', + fields: [{ name: 'title', widget: 'string' }], + }, + ], + }, + ], + }), + ).getIn(['collections', 0, 'files', 0, 'public_folder']), + ).toEqual('static/images/docs'); + }); + + it('should not overwrite file public_folder if set', () => { + expect( + applyDefaults( + fromJS({ + collections: [ + { + files: [ + { + file: 'foo', + media_folder: 'static/images/docs', + public_folder: 'images/docs', + fields: [{ name: 'title', widget: 'string' }], + }, + ], + }, + ], + }), + ).getIn(['collections', 0, 'files', 0, 'public_folder']), + ).toEqual('images/docs'); + }); + + it('should set nested field public_folder based on media_folder if not set', () => { + const config = applyDefaults( + fromJS({ + collections: [ + { + folder: 'foo', + path: '{{slug}}/index', + fields: [ + { + name: 'title', + widget: 'string', + media_folder: 'collection/static/images/docs', + }, + ], + }, + { + files: [ + { + file: 'foo', + fields: [ + { + name: 'title', + widget: 'string', + media_folder: 'file/static/images/docs', + }, + ], + }, + ], + }, + ], + }), + ); + expect(config.getIn(['collections', 0, 'fields', 0, 'public_folder'])).toEqual( + 'collection/static/images/docs', + ); + expect( + config.getIn(['collections', 1, 'files', 0, 'fields', 0, 'public_folder']), + ).toEqual('file/static/images/docs'); + }); + + it('should not overwrite nested field public_folder if set', () => { + const config = applyDefaults( + fromJS({ + collections: [ + { + folder: 'foo', + path: '{{slug}}/index', + fields: [ + { + name: 'title', + widget: 'string', + media_folder: 'collection/static/images/docs', + public_folder: 'collection/public_folder', + }, + ], + }, + { + files: [ + { + file: 'foo', + fields: [ + { + name: 'title', + widget: 'string', + public_folder: 'file/public_folder', + }, + ], + }, + ], + }, + ], + }), + ); + expect(config.getIn(['collections', 0, 'fields', 0, 'public_folder'])).toEqual( + 'collection/public_folder', + ); + expect( + config.getIn(['collections', 1, 'files', 0, 'fields', 0, 'public_folder']), + ).toEqual('file/public_folder'); + }); }); describe('publish', () => { @@ -169,7 +306,13 @@ describe('config', () => { expect( applyDefaults( fromJS({ - collections: [{ folder: 'foo', media_folder: 'static/images/docs' }], + collections: [ + { + folder: 'foo', + media_folder: 'static/images/docs', + fields: [{ name: 'title', widget: 'string' }], + }, + ], }), ).getIn(['collections', 0, 'publish']), ).toEqual(true); @@ -180,7 +323,12 @@ describe('config', () => { applyDefaults( fromJS({ collections: [ - { folder: 'foo', media_folder: 'static/images/docs', publish: false }, + { + folder: 'foo', + media_folder: 'static/images/docs', + publish: false, + fields: [{ name: 'title', widget: 'string' }], + }, ], }), ).getIn(['collections', 0, 'publish']), diff --git a/packages/netlify-cms-core/src/actions/config.js b/packages/netlify-cms-core/src/actions/config.js index ee441ff4..85085c2c 100644 --- a/packages/netlify-cms-core/src/actions/config.js +++ b/packages/netlify-cms-core/src/actions/config.js @@ -4,7 +4,7 @@ import { trimStart, get, isPlainObject } from 'lodash'; import { authenticateUser } from 'Actions/auth'; import * as publishModes from 'Constants/publishModes'; import { validateConfig } from 'Constants/configSchema'; -import { selectDefaultSortableFields } from '../reducers/collections'; +import { selectDefaultSortableFields, traverseFields } from '../reducers/collections'; import { resolveBackend } from 'coreSrc/backend'; export const CONFIG_REQUEST = 'CONFIG_REQUEST'; @@ -24,6 +24,13 @@ const getConfigUrl = () => { return 'config.yml'; }; +const setDefaultPublicFolder = map => { + if (map.has('media_folder') && !map.has('public_folder')) { + map = map.set('public_folder', map.get('media_folder')); + } + return map; +}; + const defaults = { publish_mode: publishModes.SIMPLE, }; @@ -70,9 +77,11 @@ export function applyDefaults(config) { // default value for media folder when using the path config collection = collection.set('media_folder', ''); } - if (collection.has('media_folder') && !collection.has('public_folder')) { - collection = collection.set('public_folder', collection.get('media_folder')); - } + collection = setDefaultPublicFolder(collection); + collection = collection.set( + 'fields', + traverseFields(collection.get('fields'), setDefaultPublicFolder), + ); collection = collection.set('folder', trimStart(folder, '/')); } @@ -81,7 +90,13 @@ export function applyDefaults(config) { collection = collection.set( 'files', files.map(file => { - return file.set('file', trimStart(file.get('file'), '/')); + file = file.set('file', trimStart(file.get('file'), '/')); + file = setDefaultPublicFolder(file); + file = file.set( + 'fields', + traverseFields(file.get('fields'), setDefaultPublicFolder), + ); + return file; }), ); } diff --git a/packages/netlify-cms-core/src/reducers/collections.ts b/packages/netlify-cms-core/src/reducers/collections.ts index 6c81e501..8eae1675 100644 --- a/packages/netlify-cms-core/src/reducers/collections.ts +++ b/packages/netlify-cms-core/src/reducers/collections.ts @@ -234,6 +234,37 @@ export const selectField = (collection: Collection, key: string) => { return field; }; +export const traverseFields = ( + fields: List, + updater: (field: EntryField) => EntryField, + done = () => false, +) => { + if (done()) { + return fields; + } + fields = fields + .map(f => { + const field = updater(f as EntryField); + if (done()) { + return field; + } else if (field.has('fields')) { + return field.set('fields', traverseFields(field.get('fields')!, updater, done)); + } else if (field.has('field')) { + return field.set( + 'field', + traverseFields(List([field.get('field')!]), updater, done).get(0), + ); + } else if (field.has('types')) { + return field.set('types', traverseFields(field.get('types')!, updater, done)); + } else { + return field; + } + }) + .toList() as List; + + return fields; +}; + export const updateFieldByKey = ( collection: Collection, key: string, @@ -245,37 +276,19 @@ export const updateFieldByKey = ( } let updated = false; - - const traverseFields = (fields: List) => { - if (updated) { - // we can stop once the field is found - return fields; + const updateAndBreak = (f: EntryField) => { + const field = f as EntryField; + if (field === selected) { + updated = true; + return updater(field); + } else { + return field; } - - fields = fields - .map(f => { - const field = f as EntryField; - if (field === selected) { - updated = true; - return updater(field); - } else if (field.has('fields')) { - return field.set('fields', traverseFields(field.get('fields')!)); - } else if (field.has('field')) { - return field.set('field', traverseFields(List([field.get('field')!])).get(0)); - } else if (field.has('types')) { - return field.set('types', traverseFields(field.get('types')!)); - } else { - return field; - } - }) - .toList() as List; - - return fields; }; collection = collection.set( 'fields', - traverseFields(collection.get('fields', List())), + traverseFields(collection.get('fields', List()), updateAndBreak, () => updated), ); return collection;