decap-cms-core 3.12.0 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/decap-cms-core.js +3 -3
- package/dist/decap-cms-core.js.map +1 -1
- package/dist/esm/bootstrap.js +2 -2
- package/dist/esm/components/UI/ErrorBoundary.js +2 -2
- package/dist/esm/constants/configSchema.js +6 -0
- package/dist/esm/lib/formatters.js +6 -4
- package/dist/esm/lib/urlHelper.js +26 -8
- package/index.d.ts +1 -0
- package/package.json +2 -2
- package/src/constants/configSchema.js +2 -0
- package/src/lib/__tests__/formatters.spec.js +60 -0
- package/src/lib/__tests__/urlHelper.spec.js +7 -0
- package/src/lib/formatters.ts +15 -5
- package/src/lib/urlHelper.ts +33 -9
- package/src/types/redux.ts +3 -0
package/dist/esm/bootstrap.js
CHANGED
|
@@ -49,8 +49,8 @@ function bootstrap(opts = {}) {
|
|
|
49
49
|
/**
|
|
50
50
|
* Log the version number.
|
|
51
51
|
*/
|
|
52
|
-
if (typeof "3.
|
|
53
|
-
console.log(`decap-cms-core ${"3.
|
|
52
|
+
if (typeof "3.13.0" === 'string') {
|
|
53
|
+
console.log(`decap-cms-core ${"3.13.0"}`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -42,8 +42,8 @@ function buildIssueTemplate({
|
|
|
42
42
|
let version = '';
|
|
43
43
|
if (typeof DECAP_CMS_VERSION === 'string') {
|
|
44
44
|
version = `decap-cms@${DECAP_CMS_VERSION}`;
|
|
45
|
-
} else if (typeof "3.12.
|
|
46
|
-
version = `decap-cms-app@${"3.12.
|
|
45
|
+
} else if (typeof "3.12.2" === 'string') {
|
|
46
|
+
version = `decap-cms-app@${"3.12.2"}`;
|
|
47
47
|
}
|
|
48
48
|
const template = getIssueTemplate({
|
|
49
49
|
version,
|
|
@@ -339,6 +339,9 @@ function getConfigSchema() {
|
|
|
339
339
|
preview_path_date_field: {
|
|
340
340
|
type: 'string'
|
|
341
341
|
},
|
|
342
|
+
preview_path_preserve_slashes: {
|
|
343
|
+
type: 'boolean'
|
|
344
|
+
},
|
|
342
345
|
fields: fieldsConfig()
|
|
343
346
|
},
|
|
344
347
|
required: ['name', 'label', 'file', 'fields']
|
|
@@ -363,6 +366,9 @@ function getConfigSchema() {
|
|
|
363
366
|
preview_path_date_field: {
|
|
364
367
|
type: 'string'
|
|
365
368
|
},
|
|
369
|
+
preview_path_preserve_slashes: {
|
|
370
|
+
type: 'boolean'
|
|
371
|
+
},
|
|
366
372
|
create: {
|
|
367
373
|
type: 'boolean'
|
|
368
374
|
},
|
|
@@ -93,8 +93,8 @@ export function prepareSlug(slug) {
|
|
|
93
93
|
// Replace periods with dashes.
|
|
94
94
|
.replace(/[.]/g, '-');
|
|
95
95
|
}
|
|
96
|
-
export function getProcessSegment(slugConfig, ignoreValues) {
|
|
97
|
-
return value => ignoreValues && ignoreValues.includes(value) ? value : flow([value => String(value), prepareSlug, partialRight(sanitizeSlug, slugConfig)])(value);
|
|
96
|
+
export function getProcessSegment(slugConfig, ignoreValues, preserveSlashes) {
|
|
97
|
+
return value => ignoreValues && ignoreValues.includes(value) ? value : flow([value => String(value), prepareSlug, partialRight(sanitizeSlug, slugConfig, preserveSlashes)])(value);
|
|
98
98
|
}
|
|
99
99
|
export function slugFormatter(collection, entryData, slugConfig) {
|
|
100
100
|
const slugTemplate = collection.get('slug') || '{{slug}}';
|
|
@@ -147,10 +147,12 @@ export function previewUrlFormatter(baseUrl, collection, slug, entry, slugConfig
|
|
|
147
147
|
fields = addFileTemplateFields(entry.get('path'), fields, collection.get('folder'));
|
|
148
148
|
const dateFieldName = getDateField() || selectInferredField(collection, 'date');
|
|
149
149
|
const date = parseDateFromEntry(entry, dateFieldName);
|
|
150
|
+
const previewPathPreserveSlashes = collection.get('preview_path_preserve_slashes');
|
|
151
|
+
const preserveSlashes = !!(previewPathPreserveSlashes ?? collection.has('nested'));
|
|
150
152
|
|
|
151
153
|
// Prepare and sanitize slug variables only, leave the rest of the
|
|
152
154
|
// `preview_path` template as is.
|
|
153
|
-
const processSegment = getProcessSegment(slugConfig, [fields.get('dirname')]);
|
|
155
|
+
const processSegment = getProcessSegment(slugConfig, [fields.get('dirname')], preserveSlashes);
|
|
154
156
|
let compiledPath;
|
|
155
157
|
try {
|
|
156
158
|
compiledPath = compileStringTemplate(pathTemplate, date, slug, fields, processSegment);
|
|
@@ -158,7 +160,7 @@ export function previewUrlFormatter(baseUrl, collection, slug, entry, slugConfig
|
|
|
158
160
|
// Print an error and ignore `preview_path` if both:
|
|
159
161
|
// 1. Date is invalid (according to DayJs), and
|
|
160
162
|
// 2. A date expression (eg. `{{year}}`) is used in `preview_path`
|
|
161
|
-
if (err.name === SLUG_MISSING_REQUIRED_DATE) {
|
|
163
|
+
if (err instanceof Error && err.name === SLUG_MISSING_REQUIRED_DATE) {
|
|
162
164
|
console.error(stripIndent`
|
|
163
165
|
Collection "${collection.get('name')}" configuration error:
|
|
164
166
|
\`preview_path_date_field\` must be a field with a valid date. Ignoring \`preview_path\`.
|
|
@@ -43,7 +43,11 @@ function validURIChar(char) {
|
|
|
43
43
|
function validIRIChar(char) {
|
|
44
44
|
return uriChars.test(char) || ucsChars.test(char);
|
|
45
45
|
}
|
|
46
|
-
export function getCharReplacer(encoding,
|
|
46
|
+
export function getCharReplacer(encoding, options) {
|
|
47
|
+
const {
|
|
48
|
+
replacement,
|
|
49
|
+
preserveSlashes
|
|
50
|
+
} = options;
|
|
47
51
|
let validChar;
|
|
48
52
|
if (encoding === 'unicode') {
|
|
49
53
|
validChar = validIRIChar;
|
|
@@ -57,13 +61,19 @@ export function getCharReplacer(encoding, replacement) {
|
|
|
57
61
|
if (!Array.from(replacement).every(validChar)) {
|
|
58
62
|
throw new Error('The replacement character(s) (options.replacement) is itself unsafe.');
|
|
59
63
|
}
|
|
60
|
-
return char
|
|
64
|
+
return (char, i = 0, arr = [char]) => {
|
|
65
|
+
if (preserveSlashes && char === '/' && i !== 0 && i !== arr.length - 1) {
|
|
66
|
+
return char;
|
|
67
|
+
}
|
|
68
|
+
return validChar(char) ? char : replacement;
|
|
69
|
+
};
|
|
61
70
|
}
|
|
62
71
|
// `sanitizeURI` does not actually URI-encode the chars (that is the browser's and server's job), just removes the ones that are not allowed.
|
|
63
72
|
export function sanitizeURI(str, options) {
|
|
64
73
|
const {
|
|
65
74
|
replacement = '',
|
|
66
|
-
encoding = 'unicode'
|
|
75
|
+
encoding = 'unicode',
|
|
76
|
+
preserveSlashes
|
|
67
77
|
} = options || {};
|
|
68
78
|
if (!isString(str)) {
|
|
69
79
|
throw new Error('The input slug must be a string.');
|
|
@@ -74,16 +84,21 @@ export function sanitizeURI(str, options) {
|
|
|
74
84
|
|
|
75
85
|
// `Array.from` must be used instead of `String.split` because
|
|
76
86
|
// `split` converts things like emojis into UTF-16 surrogate pairs.
|
|
77
|
-
return Array.from(str).map(getCharReplacer(encoding,
|
|
87
|
+
return Array.from(str).map(getCharReplacer(encoding, {
|
|
88
|
+
replacement,
|
|
89
|
+
preserveSlashes
|
|
90
|
+
})).join('');
|
|
78
91
|
}
|
|
79
92
|
export function sanitizeChar(char, options) {
|
|
80
93
|
const {
|
|
81
94
|
encoding = 'unicode',
|
|
82
95
|
sanitize_replacement: replacement = ''
|
|
83
96
|
} = options || {};
|
|
84
|
-
return getCharReplacer(encoding,
|
|
97
|
+
return getCharReplacer(encoding, {
|
|
98
|
+
replacement
|
|
99
|
+
})(char);
|
|
85
100
|
}
|
|
86
|
-
export function sanitizeSlug(str, options) {
|
|
101
|
+
export function sanitizeSlug(str, options, preserveSlashes) {
|
|
87
102
|
if (!isString(str)) {
|
|
88
103
|
throw new Error('The input slug must be a string.');
|
|
89
104
|
}
|
|
@@ -94,8 +109,11 @@ export function sanitizeSlug(str, options) {
|
|
|
94
109
|
} = options || {};
|
|
95
110
|
const sanitizedSlug = flow([...(stripDiacritics ? [diacritics.remove] : []), partialRight(sanitizeURI, {
|
|
96
111
|
replacement,
|
|
97
|
-
encoding
|
|
98
|
-
|
|
112
|
+
encoding,
|
|
113
|
+
preserveSlashes
|
|
114
|
+
}), preserveSlashes ? slug => slug.split('/').filter(Boolean).map(part => sanitizeFilename(part, {
|
|
115
|
+
replacement
|
|
116
|
+
})).join('/') : partialRight(sanitizeFilename, {
|
|
99
117
|
replacement
|
|
100
118
|
})])(str);
|
|
101
119
|
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decap-cms-core",
|
|
3
3
|
"description": "Decap CMS core application, see decap-cms package for the main distribution.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.13.0",
|
|
5
5
|
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-core",
|
|
6
6
|
"bugs": "https://github.com/decaporg/decap-cms/issues",
|
|
7
7
|
"module": "dist/esm/index.js",
|
|
@@ -98,5 +98,5 @@
|
|
|
98
98
|
"browser": {
|
|
99
99
|
"path": "path-browserify"
|
|
100
100
|
},
|
|
101
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "02e3fe3e42ffac8cf7ac50fde1984ef3bd4d788c"
|
|
102
102
|
}
|
|
@@ -224,6 +224,7 @@ function getConfigSchema() {
|
|
|
224
224
|
file: { type: 'string' },
|
|
225
225
|
preview_path: { type: 'string' },
|
|
226
226
|
preview_path_date_field: { type: 'string' },
|
|
227
|
+
preview_path_preserve_slashes: { type: 'boolean' },
|
|
227
228
|
fields: fieldsConfig(),
|
|
228
229
|
},
|
|
229
230
|
required: ['name', 'label', 'file', 'fields'],
|
|
@@ -236,6 +237,7 @@ function getConfigSchema() {
|
|
|
236
237
|
path: { type: 'string' },
|
|
237
238
|
preview_path: { type: 'string' },
|
|
238
239
|
preview_path_date_field: { type: 'string' },
|
|
240
|
+
preview_path_preserve_slashes: { type: 'boolean' },
|
|
239
241
|
create: { type: 'boolean' },
|
|
240
242
|
publish: { type: 'boolean' },
|
|
241
243
|
hide: { type: 'boolean' },
|
|
@@ -587,6 +587,66 @@ describe('formatters', () => {
|
|
|
587
587
|
'Collection "posts" configuration error:\n `preview_path_date_field` must be a field with a valid date. Ignoring `preview_path`.',
|
|
588
588
|
);
|
|
589
589
|
});
|
|
590
|
+
|
|
591
|
+
it('should preserve slashes in value when configured', () => {
|
|
592
|
+
expect(
|
|
593
|
+
previewUrlFormatter(
|
|
594
|
+
'https://www.example.com',
|
|
595
|
+
Map({
|
|
596
|
+
preview_path: 'prefix/{{value}}',
|
|
597
|
+
preview_path_preserve_slashes: true,
|
|
598
|
+
}),
|
|
599
|
+
'backendSlug',
|
|
600
|
+
Map({ data: Map({ value: 'nested/value' }) }),
|
|
601
|
+
slugConfig,
|
|
602
|
+
),
|
|
603
|
+
).toBe('https://www.example.com/prefix/nested/value');
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it('should sanitize slashes in value when not configured', () => {
|
|
607
|
+
expect(
|
|
608
|
+
previewUrlFormatter(
|
|
609
|
+
'https://www.example.com',
|
|
610
|
+
Map({
|
|
611
|
+
preview_path: 'prefix/{{value}}',
|
|
612
|
+
}),
|
|
613
|
+
'backendSlug',
|
|
614
|
+
Map({ data: Map({ value: 'nested/value' }) }),
|
|
615
|
+
slugConfig,
|
|
616
|
+
),
|
|
617
|
+
).toBe('https://www.example.com/prefix/nested-value');
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('should preserve slashes in value for nested collections by default', () => {
|
|
621
|
+
expect(
|
|
622
|
+
previewUrlFormatter(
|
|
623
|
+
'https://www.example.com',
|
|
624
|
+
Map({
|
|
625
|
+
preview_path: 'prefix/{{value}}',
|
|
626
|
+
nested: { depth: 10 },
|
|
627
|
+
}),
|
|
628
|
+
'backendSlug',
|
|
629
|
+
Map({ data: Map({ value: 'nested/value' }) }),
|
|
630
|
+
slugConfig,
|
|
631
|
+
),
|
|
632
|
+
).toBe('https://www.example.com/prefix/nested/value');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should sanitize slashes in value for nested collections when explicitly disabled', () => {
|
|
636
|
+
expect(
|
|
637
|
+
previewUrlFormatter(
|
|
638
|
+
'https://www.example.com',
|
|
639
|
+
Map({
|
|
640
|
+
preview_path: 'prefix/{{value}}',
|
|
641
|
+
nested: { depth: 10 },
|
|
642
|
+
preview_path_preserve_slashes: false,
|
|
643
|
+
}),
|
|
644
|
+
'backendSlug',
|
|
645
|
+
Map({ data: Map({ value: 'nested/value' }) }),
|
|
646
|
+
slugConfig,
|
|
647
|
+
),
|
|
648
|
+
).toBe('https://www.example.com/prefix/nested-value');
|
|
649
|
+
});
|
|
590
650
|
});
|
|
591
651
|
|
|
592
652
|
describe('summaryFormatter', () => {
|
|
@@ -125,6 +125,13 @@ describe('sanitizeSlug', () => {
|
|
|
125
125
|
'test_test',
|
|
126
126
|
);
|
|
127
127
|
});
|
|
128
|
+
|
|
129
|
+
it('preserves slashes when requested', () => {
|
|
130
|
+
const input = '/this-is-a/nested/page';
|
|
131
|
+
|
|
132
|
+
expect(sanitizeSlug(input, slugConfig, false)).toEqual('this-is-a-nested-page');
|
|
133
|
+
expect(sanitizeSlug(input, slugConfig, true)).toEqual('this-is-a/nested/page');
|
|
134
|
+
});
|
|
128
135
|
});
|
|
129
136
|
|
|
130
137
|
describe('sanitizeChar', () => {
|
package/src/lib/formatters.ts
CHANGED
|
@@ -120,11 +120,19 @@ export function prepareSlug(slug: string) {
|
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
export function getProcessSegment(
|
|
123
|
+
export function getProcessSegment(
|
|
124
|
+
slugConfig?: CmsSlug,
|
|
125
|
+
ignoreValues?: string[],
|
|
126
|
+
preserveSlashes?: boolean,
|
|
127
|
+
) {
|
|
124
128
|
return (value: string) =>
|
|
125
129
|
ignoreValues && ignoreValues.includes(value)
|
|
126
130
|
? value
|
|
127
|
-
: flow([
|
|
131
|
+
: flow([
|
|
132
|
+
value => String(value),
|
|
133
|
+
prepareSlug,
|
|
134
|
+
partialRight(sanitizeSlug, slugConfig, preserveSlashes),
|
|
135
|
+
])(value);
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
export function slugFormatter(
|
|
@@ -205,19 +213,21 @@ export function previewUrlFormatter(
|
|
|
205
213
|
fields = addFileTemplateFields(entry.get('path'), fields, collection.get('folder'));
|
|
206
214
|
const dateFieldName = getDateField() || selectInferredField(collection, 'date');
|
|
207
215
|
const date = parseDateFromEntry(entry as unknown as Map<string, unknown>, dateFieldName);
|
|
216
|
+
const previewPathPreserveSlashes = collection.get('preview_path_preserve_slashes');
|
|
217
|
+
const preserveSlashes = !!(previewPathPreserveSlashes ?? collection.has('nested'));
|
|
208
218
|
|
|
209
219
|
// Prepare and sanitize slug variables only, leave the rest of the
|
|
210
220
|
// `preview_path` template as is.
|
|
211
|
-
const processSegment = getProcessSegment(slugConfig, [fields.get('dirname')]);
|
|
221
|
+
const processSegment = getProcessSegment(slugConfig, [fields.get('dirname')], preserveSlashes);
|
|
212
222
|
let compiledPath;
|
|
213
223
|
|
|
214
224
|
try {
|
|
215
225
|
compiledPath = compileStringTemplate(pathTemplate, date, slug, fields, processSegment);
|
|
216
|
-
} catch (err) {
|
|
226
|
+
} catch (err: unknown) {
|
|
217
227
|
// Print an error and ignore `preview_path` if both:
|
|
218
228
|
// 1. Date is invalid (according to DayJs), and
|
|
219
229
|
// 2. A date expression (eg. `{{year}}`) is used in `preview_path`
|
|
220
|
-
if (err.name === SLUG_MISSING_REQUIRED_DATE) {
|
|
230
|
+
if (err instanceof Error && err.name === SLUG_MISSING_REQUIRED_DATE) {
|
|
221
231
|
console.error(stripIndent`
|
|
222
232
|
Collection "${collection.get('name')}" configuration error:
|
|
223
233
|
\`preview_path_date_field\` must be a field with a valid date. Ignoring \`preview_path\`.
|
package/src/lib/urlHelper.ts
CHANGED
|
@@ -51,7 +51,14 @@ function validIRIChar(char: string) {
|
|
|
51
51
|
return uriChars.test(char) || ucsChars.test(char);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export function getCharReplacer(
|
|
54
|
+
export function getCharReplacer(
|
|
55
|
+
encoding: string,
|
|
56
|
+
options: {
|
|
57
|
+
replacement: NonNullable<CmsSlug['sanitize_replacement']>;
|
|
58
|
+
preserveSlashes?: boolean;
|
|
59
|
+
},
|
|
60
|
+
) {
|
|
61
|
+
const { replacement, preserveSlashes } = options;
|
|
55
62
|
let validChar: (char: string) => boolean;
|
|
56
63
|
|
|
57
64
|
if (encoding === 'unicode') {
|
|
@@ -67,14 +74,24 @@ export function getCharReplacer(encoding: string, replacement: string) {
|
|
|
67
74
|
throw new Error('The replacement character(s) (options.replacement) is itself unsafe.');
|
|
68
75
|
}
|
|
69
76
|
|
|
70
|
-
return (char: string
|
|
77
|
+
return (char: string, i = 0, arr: string[] = [char]) => {
|
|
78
|
+
if (preserveSlashes && char === '/' && i !== 0 && i !== arr.length - 1) {
|
|
79
|
+
return char;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return validChar(char) ? char : replacement;
|
|
83
|
+
};
|
|
71
84
|
}
|
|
72
85
|
// `sanitizeURI` does not actually URI-encode the chars (that is the browser's and server's job), just removes the ones that are not allowed.
|
|
73
86
|
export function sanitizeURI(
|
|
74
87
|
str: string,
|
|
75
|
-
options?: {
|
|
88
|
+
options?: {
|
|
89
|
+
replacement: CmsSlug['sanitize_replacement'];
|
|
90
|
+
encoding: CmsSlug['encoding'];
|
|
91
|
+
preserveSlashes?: boolean;
|
|
92
|
+
},
|
|
76
93
|
) {
|
|
77
|
-
const { replacement = '', encoding = 'unicode' } = options || {};
|
|
94
|
+
const { replacement = '', encoding = 'unicode', preserveSlashes } = options || {};
|
|
78
95
|
|
|
79
96
|
if (!isString(str)) {
|
|
80
97
|
throw new Error('The input slug must be a string.');
|
|
@@ -85,15 +102,15 @@ export function sanitizeURI(
|
|
|
85
102
|
|
|
86
103
|
// `Array.from` must be used instead of `String.split` because
|
|
87
104
|
// `split` converts things like emojis into UTF-16 surrogate pairs.
|
|
88
|
-
return Array.from(str).map(getCharReplacer(encoding, replacement)).join('');
|
|
105
|
+
return Array.from(str).map(getCharReplacer(encoding, { replacement, preserveSlashes })).join('');
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
export function sanitizeChar(char: string, options?: CmsSlug) {
|
|
92
109
|
const { encoding = 'unicode', sanitize_replacement: replacement = '' } = options || {};
|
|
93
|
-
return getCharReplacer(encoding, replacement)(char);
|
|
110
|
+
return getCharReplacer(encoding, { replacement })(char);
|
|
94
111
|
}
|
|
95
112
|
|
|
96
|
-
export function sanitizeSlug(str: string, options?: CmsSlug) {
|
|
113
|
+
export function sanitizeSlug(str: string, options?: CmsSlug, preserveSlashes?: boolean) {
|
|
97
114
|
if (!isString(str)) {
|
|
98
115
|
throw new Error('The input slug must be a string.');
|
|
99
116
|
}
|
|
@@ -106,8 +123,15 @@ export function sanitizeSlug(str: string, options?: CmsSlug) {
|
|
|
106
123
|
|
|
107
124
|
const sanitizedSlug = flow([
|
|
108
125
|
...(stripDiacritics ? [diacritics.remove] : []),
|
|
109
|
-
partialRight(sanitizeURI, { replacement, encoding }),
|
|
110
|
-
|
|
126
|
+
partialRight(sanitizeURI, { replacement, encoding, preserveSlashes }),
|
|
127
|
+
preserveSlashes
|
|
128
|
+
? (slug: string) =>
|
|
129
|
+
slug
|
|
130
|
+
.split('/')
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.map(part => sanitizeFilename(part, { replacement }))
|
|
133
|
+
.join('/')
|
|
134
|
+
: partialRight(sanitizeFilename, { replacement }),
|
|
111
135
|
])(str);
|
|
112
136
|
|
|
113
137
|
// Remove any doubled or leading/trailing replacement characters (that were added in the sanitizers).
|
package/src/types/redux.ts
CHANGED
|
@@ -290,6 +290,7 @@ export interface CmsCollectionFile {
|
|
|
290
290
|
description?: string;
|
|
291
291
|
preview_path?: string;
|
|
292
292
|
preview_path_date_field?: string;
|
|
293
|
+
preview_path_preserve_slashes?: boolean;
|
|
293
294
|
i18n?: boolean | CmsI18nConfig;
|
|
294
295
|
media_folder?: string;
|
|
295
296
|
public_folder?: string;
|
|
@@ -327,6 +328,7 @@ export interface CmsCollection {
|
|
|
327
328
|
slug?: string;
|
|
328
329
|
preview_path?: string;
|
|
329
330
|
preview_path_date_field?: string;
|
|
331
|
+
preview_path_preserve_slashes?: boolean;
|
|
330
332
|
create?: boolean;
|
|
331
333
|
delete?: boolean;
|
|
332
334
|
editor?: {
|
|
@@ -634,6 +636,7 @@ type CollectionObject = {
|
|
|
634
636
|
public_folder?: string;
|
|
635
637
|
preview_path?: string;
|
|
636
638
|
preview_path_date_field?: string;
|
|
639
|
+
preview_path_preserve_slashes?: boolean;
|
|
637
640
|
summary?: string;
|
|
638
641
|
filter?: FilterRule;
|
|
639
642
|
type: 'file_based_collection' | 'folder_based_collection';
|