From 6eec0feb72ff4fdb820a0e8f28b27b3070723197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Zen?= Date: Mon, 29 Aug 2016 17:09:04 -0300 Subject: [PATCH] Small refactor preparing for branch creating/editing. Also closes #58 --- example/config.yml | 1 + src/actions/config.js | 10 +++++- src/actions/entries.js | 3 +- src/backends/backend.js | 21 ++++++++++--- src/backends/github/implementation.js | 7 ++--- src/backends/test-repo/implementation.js | 3 +- src/components/Widgets/ImageControl.js | 2 +- .../VisualEditor/index.js | 2 +- src/lib/randomGenerator.js | 31 +++++++++++++++++++ src/reducers/entryDraft.js | 2 +- src/reducers/medias.js | 2 +- src/valueObjects/MediaProxy.js | 5 +-- 12 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 src/lib/randomGenerator.js diff --git a/example/config.yml b/example/config.yml index a930e59b..06fe0687 100644 --- a/example/config.yml +++ b/example/config.yml @@ -3,6 +3,7 @@ backend: delay: 0.1 media_folder: "assets/uploads" +publish_mode: branch collections: # A list of collections the CMS should be able to edit - name: "posts" # Used in routes, ie.: /admin/collections/:slug/edit diff --git a/src/actions/config.js b/src/actions/config.js index 7a2d568f..4a18a046 100644 --- a/src/actions/config.js +++ b/src/actions/config.js @@ -62,7 +62,6 @@ export function loadConfig(config) { function parseConfig(data) { const config = yaml.safeLoad(data); - if (typeof CMS_ENV === 'string' && config[CMS_ENV]) { for (var key in config[CMS_ENV]) { if (config[CMS_ENV].hasOwnProperty(key)) { @@ -70,5 +69,14 @@ function parseConfig(data) { } } } + + if ('media_folder' in config && typeof config.media_folder === 'string') { + // Parse source & public paths for media folder. + config.media_folder = { + path: config.media_folder, + public_path: config.media_folder + }; + } + return config; } diff --git a/src/actions/entries.js b/src/actions/entries.js index 2f060a32..4f239d2f 100644 --- a/src/actions/entries.js +++ b/src/actions/entries.js @@ -175,9 +175,8 @@ export function persistEntry(collection, entry) { const state = getState(); const backend = currentBackend(state.config); const MediaProxies = entry.get('mediaFiles').map(path => getMedia(state, path)); - dispatch(entryPersisting(collection, entry)); - backend.persistEntry(collection, entry, MediaProxies.toJS()).then( + backend.persistEntry(state.config, collection, entry, MediaProxies.toJS()).then( () => { dispatch(entryPersisted(collection, entry)); }, diff --git a/src/backends/backend.js b/src/backends/backend.js index 312b93ec..05fbc153 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -1,6 +1,7 @@ import TestRepoBackend from './test-repo/implementation'; import GitHubBackend from './github/implementation'; import { resolveFormat } from '../formats/formats'; +import { randomStr } from '../lib/randomGenerator'; import { createEntry } from '../valueObjects/Entry'; class LocalStorageAuthStore { @@ -91,11 +92,21 @@ class Backend { }); } - persistEntry(collection, entryDraft, MediaFiles) { - const newEntry = entryDraft.getIn(['entry', 'newRecord']) || false; - const entryData = entryDraft.getIn(['entry', 'data']).toObject(); - let entryObj; + getPublishMode(config) { + const publish_modes = ['simple', 'branch']; + const mode = config.get('publish_mode'); + if (publish_modes.indexOf(mode) !== -1) { + return mode; + } else { + return 'simple'; + } + } + persistEntry(config, collection, entryDraft, MediaFiles) { + const mode = this.getPublishMode(config); + const newEntry = entryDraft.getIn(['entry', 'newRecord']) || false; + const entryData = entryDraft.getIn(['entry', 'data']).toJS(); + let entryObj; if (newEntry) { const slug = this.slugFormatter(collection.get('slug'), entryDraft.get('entry')); entryObj = { @@ -115,7 +126,7 @@ class Backend { collection.get('label') + ' “' + entryDraft.getIn(['entry', 'data', 'title']) + '”'; - return this.implementation.persistEntry(collection, entryObj, MediaFiles, { commitMessage }, newEntry); + return this.implementation.persistEntry(entryObj, MediaFiles, { newEntry, commitMessage, mode }); } entryToRaw(collection, entry) { diff --git a/src/backends/github/implementation.js b/src/backends/github/implementation.js index 194dd98b..fbd18407 100644 --- a/src/backends/github/implementation.js +++ b/src/backends/github/implementation.js @@ -43,11 +43,10 @@ class API { }); } - persistFiles(collection, entry, mediaFiles, options) { + persistFiles(entry, mediaFiles, options) { let filename, part, parts, subtree; const fileTree = {}; const files = []; - mediaFiles.concat(entry).forEach((file) => { if (file.uploaded) { return; } files.push(this.uploadBlob(file)); @@ -226,7 +225,7 @@ export default class GitHub { )); } - persistEntry(collection, entry, mediaFiles = [], options = {}) { - return this.api.persistFiles(collection, entry, mediaFiles, options); + persistEntry(entry, mediaFiles = [], options = {}) { + return this.api.persistFiles(entry, mediaFiles, options); } } diff --git a/src/backends/test-repo/implementation.js b/src/backends/test-repo/implementation.js index 3d534029..fce8d3d8 100644 --- a/src/backends/test-repo/implementation.js +++ b/src/backends/test-repo/implementation.js @@ -45,7 +45,8 @@ export default class TestRepo { )); } - persistEntry(collection, entry, mediaFiles = [], newEntry = false) { + persistEntry(entry, mediaFiles = [], options) { + const newEntry = options.newEntry || false; const folder = entry.path.substring(0, entry.path.lastIndexOf('/')); const fileName = entry.path.substring(entry.path.lastIndexOf('/') + 1); if (newEntry) { diff --git a/src/components/Widgets/ImageControl.js b/src/components/Widgets/ImageControl.js index d80cd80e..2006215b 100644 --- a/src/components/Widgets/ImageControl.js +++ b/src/components/Widgets/ImageControl.js @@ -53,7 +53,7 @@ export default class ImageControl extends React.Component { if (file) { const mediaProxy = new MediaProxy(file.name, file); this.props.onAddMedia(mediaProxy); - this.props.onChange(mediaProxy.path); + this.props.onChange(mediaProxy.public_path); } else { this.props.onChange(null); } diff --git a/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js b/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js index 6e8ecc18..7525bcb5 100644 --- a/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js +++ b/src/components/Widgets/MarkdownControlElements/VisualEditor/index.js @@ -253,7 +253,7 @@ class VisualEditor extends React.Component { .insertInline({ type: 'mediaproxy', isVoid: true, - data: { src: mediaProxy.path } + data: { src: mediaProxy.public_path } }) .collapseToEnd() .insertBlock(DEFAULT_NODE) diff --git a/src/lib/randomGenerator.js b/src/lib/randomGenerator.js new file mode 100644 index 00000000..7d73aadc --- /dev/null +++ b/src/lib/randomGenerator.js @@ -0,0 +1,31 @@ +/* + * Random number generator + */ + +let rng; + +if (window.crypto && crypto.getRandomValues) { + // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto + // Moderately fast, high quality + const _rnds32 = new Uint32Array(1); + rng = function whatwgRNG() { + crypto.getRandomValues(_rnds32); + return _rnds32[0]; + }; +} + +if (!rng) { + // Math.random()-based (RNG) + // If no Crypto available, use Math.random(). + rng = function() { + const r = Math.random() * 0x100000000; + const _rnds = r >>> 0; + return _rnds; + }; +} + +export function randomStr() { + return rng().toString(36); +} + +export default rng; diff --git a/src/reducers/entryDraft.js b/src/reducers/entryDraft.js index b00de43a..d9100ca0 100644 --- a/src/reducers/entryDraft.js +++ b/src/reducers/entryDraft.js @@ -26,7 +26,7 @@ const entryDraft = (state = Map(), action) => { return state.set('entry', action.payload); case ADD_MEDIA: - return state.update('mediaFiles', (list) => list.push(action.payload.path)); + return state.update('mediaFiles', (list) => list.push(action.payload.public_path)); case REMOVE_MEDIA: return state.update('mediaFiles', (list) => list.filterNot((path) => path === action.payload)); diff --git a/src/reducers/medias.js b/src/reducers/medias.js index c0cfaa34..87b7f8cc 100644 --- a/src/reducers/medias.js +++ b/src/reducers/medias.js @@ -5,7 +5,7 @@ import MediaProxy from '../valueObjects/MediaProxy'; const medias = (state = Map(), action) => { switch (action.type) { case ADD_MEDIA: - return state.set(action.payload.path, action.payload); + return state.set(action.payload.public_path, action.payload); case REMOVE_MEDIA: return state.delete(action.payload); diff --git a/src/valueObjects/MediaProxy.js b/src/valueObjects/MediaProxy.js index c02e39ca..141b7441 100644 --- a/src/valueObjects/MediaProxy.js +++ b/src/valueObjects/MediaProxy.js @@ -8,11 +8,12 @@ export default function MediaProxy(value, file, uploaded = false) { this.file = file; this.uploaded = uploaded; this.sha = null; - this.path = config.media_folder && !uploaded ? config.media_folder + '/' + value : value; + this.path = config.media_folder && !uploaded ? config.media_folder.path + '/' + value : value; + this.public_path = config.media_folder && !uploaded ? config.media_folder.public_path + '/' + value : value; } MediaProxy.prototype.toString = function() { - return this.uploaded ? this.path : window.URL.createObjectURL(this.file, { oneTimeOnly: true }); + return this.uploaded ? this.public_path : window.URL.createObjectURL(this.file, { oneTimeOnly: true }); }; MediaProxy.prototype.toBase64 = function() {