Feat: media folders templates (#3116)

* refactor: typescript backendHelper

* test: add string templating tests

* test: add createPreviewUrl invalid date test

* refactor: move all formatters to one file

* feat: support media folders templating

* feat: add filename and extension template variables

* feat: support paths in string templates

* docs: add media folder templating docs

* style(docs): remove line break
This commit is contained in:
Erez Rokah
2020-01-22 20:42:24 +02:00
committed by Shawn Erquhart
parent 4bc4490c6f
commit cf57da223d
20 changed files with 762 additions and 316 deletions

View File

@ -93,7 +93,7 @@ describe('entries', () => {
selectMediaFolder(
Map({ media_folder: 'static/media' }),
Map({ name: 'posts', folder: 'posts', media_folder: '' }),
'posts/title/index.md',
Map({ path: 'posts/title/index.md' }),
),
).toEqual('posts/title');
});
@ -103,10 +103,37 @@ describe('entries', () => {
selectMediaFolder(
Map({ media_folder: 'static/media' }),
Map({ name: 'posts', folder: 'posts', media_folder: '../' }),
'posts/title/index.md',
Map({ path: 'posts/title/index.md' }),
),
).toEqual('posts/');
});
it('should resolve media folder template', () => {
const slugConfig = {
encoding: 'unicode',
clean_accents: false,
sanitize_replacement: '-',
};
const entry = fromJS({
path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md',
data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' },
});
const collection = fromJS({
name: 'posts',
folder: 'content',
media_folder: '../../../{{media_folder}}/{{category}}/{{slug}}',
fields: [{ name: 'title', widget: 'string' }],
});
expect(
selectMediaFolder(
fromJS({ media_folder: 'static/media', slug: slugConfig }),
collection,
entry,
),
).toEqual('static/media/hosting-and-deployment/deployment-with-nanobox');
});
});
describe('selectMediaFilePath', () => {
@ -149,7 +176,7 @@ describe('entries', () => {
selectMediaFilePath(
Map({ media_folder: 'static/media' }),
Map({ name: 'posts', folder: 'posts', media_folder: '../../static/media/' }),
'posts/title/index.md',
Map({ path: 'posts/title/index.md' }),
'image.png',
),
).toBe('static/media/image.png');
@ -188,5 +215,33 @@ describe('entries', () => {
),
).toBe('../../static/media/image.png');
});
it('should resolve public folder template', () => {
const slugConfig = {
encoding: 'unicode',
clean_accents: false,
sanitize_replacement: '-',
};
const entry = fromJS({
path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md',
data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' },
});
const collection = fromJS({
name: 'posts',
folder: 'content',
public_folder: '/{{public_folder}}/{{category}}/{{slug}}',
fields: [{ name: 'title', widget: 'string' }],
});
expect(
selectMediaFilePublicPath(
fromJS({ public_folder: 'static/media', slug: slugConfig }),
collection,
'image.png',
entry,
),
).toEqual('/static/media/hosting-and-deployment/deployment-with-nanobox/image.png');
});
});
});

View File

@ -9,7 +9,6 @@ import {
ENTRIES_FAILURE,
ENTRY_DELETE_SUCCESS,
} from '../actions/entries';
import { SEARCH_ENTRIES_SUCCESS } from '../actions/search';
import {
EntriesAction,
@ -24,8 +23,10 @@ import {
EntryDeletePayload,
EntriesRequestPayload,
EntryDraft,
EntryMap,
} from '../types/redux';
import { isAbsolutePath, basename } from 'netlify-cms-lib-util/src';
import { folderFormatter } from '../lib/formatters';
import { isAbsolutePath, basename } from 'netlify-cms-lib-util';
let collection: string;
let loadedEntries: EntryObject[];
@ -140,14 +141,23 @@ const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES';
export const selectMediaFolder = (
config: Config,
collection: Collection | null,
entryPath: string | null,
entryMap: EntryMap | undefined,
) => {
let mediaFolder = config.get('media_folder');
if (collection && collection.has('media_folder')) {
const entryPath = entryMap?.get('path');
if (entryPath) {
const entryDir = dirname(entryPath);
mediaFolder = join(entryDir, collection.get('media_folder') as string);
const folder = folderFormatter(
collection.get('media_folder') as string,
entryMap as EntryMap,
collection,
mediaFolder,
'media_folder',
config.get('slug'),
);
mediaFolder = join(entryDir, folder as string);
} else {
mediaFolder = join(collection.get('folder') as string, DRAFT_MEDIA_FILES);
}
@ -159,7 +169,7 @@ export const selectMediaFolder = (
export const selectMediaFilePath = (
config: Config,
collection: Collection | null,
entryPath: string | null,
entryMap: EntryMap | undefined,
mediaPath: string,
) => {
if (isAbsolutePath(mediaPath)) {
@ -169,9 +179,9 @@ export const selectMediaFilePath = (
let mediaFolder;
if (mediaPath.startsWith('/')) {
// absolute media paths are not bound to a collection
mediaFolder = selectMediaFolder(config, null, null);
mediaFolder = selectMediaFolder(config, null, entryMap);
} else {
mediaFolder = selectMediaFolder(config, collection, entryPath);
mediaFolder = selectMediaFolder(config, collection, entryMap);
}
return join(mediaFolder, basename(mediaPath));
@ -181,6 +191,7 @@ export const selectMediaFilePublicPath = (
config: Config,
collection: Collection | null,
mediaPath: string,
entryMap: EntryMap | undefined,
) => {
if (isAbsolutePath(mediaPath)) {
return mediaPath;
@ -189,7 +200,14 @@ export const selectMediaFilePublicPath = (
let publicFolder = config.get('public_folder');
if (collection && collection.has('public_folder')) {
publicFolder = collection.get('public_folder') as string;
publicFolder = folderFormatter(
collection.get('public_folder') as string,
entryMap,
collection,
publicFolder,
'public_folder',
config.get('slug'),
);
}
return join(publicFolder, basename(mediaPath));