2016-12-23 16:59:48 -02:00
import yaml from "js-yaml" ;
2018-02-28 15:45:16 -05:00
import { Map , List , fromJS } from "immutable" ;
2018-04-10 16:48:04 -04:00
import { trimStart , flow , isBoolean , get } from "lodash" ;
WIP - Global UI (#785)
* update top bar and collections sidebar UI
* update collection entries UI
* improve global layout
* merge search page into collection page
* enable new entry button
* search fixup
* wip -initial editor update
* update editor scrolling and markdown toolbar position
* wip
* editor toolbar progress
* editor toolbar wip
* finished basic editor toolbar
* add standalone toggle component
* improve markdown toolbar spacing
* add user avatar placeholder
* finish markdown toggle styling
* refactor icon setup, add new icons
* add new icons to markdown editor toolbar
* remove extra app container
* add markdown active mark style
* relation and text widget styling
* widget design updates, basic list/object design update
* widget style updates, image widget improvements
* refactor widget directory, fix file removal
* widget focus styles
* finish editor widget focus styles
* migrate media library modal to react-modal
* wip - migrate editor component form to modal
* wip - move editor component form to modal
* wip - embed plugin forms in the editor
* inline shortcode forms working
* disable react hot loading, its breaking things
* improve shortcode form styles
* make shortcode form collapsible, improve styling
* add close functionality to shortcode blocks
* improve base media library styling
* fix shortcode label
* migrate unstyled workflow to new UI
* wip - reorganizing everything
* more work moving everything
* finish more moving and eliminating stuff
* restructure, remove react-toolbox
* wip - removing old stuff, more restructure
* finish restructure
* wip - css arch
* switch back to test repo
* update react-datetime to ^2.11.0
* remove leftover react-toolbox button
* more restructuring clean-up
* fix UI component directory case
* wip -css editor control style
* wip - consolidate widget styles
* wip - use a single control renderer
* fixed object values breaking
* wip - editor control active styles
* pass control wrapper to widgets
* ensure branch name is trimmed
* wip - improve widget authoring support
* import Map to Widget component
* refactor toolbar buttons
* wip - more widget active styles
* break out editor toggle component
* add local scroll sync back
* update editor toggle icons
* limit editor control pane content width
* fix editor control spacing
* migrate markdown toolbar stickiness to css
* fix markdown toolbar border radius
* temporarily use test backend
* stop markdown toolbar from going to bottom
* restore disabled markdown toolbar buttons for raw
* test markdown widget without focus styles
* more widget updates
* remove card visuals from editor
* disable dragging editor split off screen
* use editorControl component for shortcode fields
* make header site link configurable
* add configurable collection descriptions
* temporarily add example assets
* add basic list view
* remove outdated css mixins
* add and implement search icon
* activate quick add menu
* visualize usable space in editor view
* fix entry close, other improvements
* wip - editorial workflow updates
* some dropshadow and other CSS tweaks
* workflow ui updates
* add worfklow card buttons
* fix workflow card button handlers
* some dropshadow and other CSS tweaks
* make workflow board wider
* center workflow and collection views
* add basic responsiveness
* a bunch of fun UI fixes! a BUNCH! (#875)
* give `.nc-entryEditor-toolbar-mainSection` left and right child divs
* a bunch of fun UI fixes! a BUNCH!
* remove obscure --buttonShadow
* revert to test repo
* fix not found page styling
* allow workflow publishing from any column
* disallow publishing from all columns, with feedback
* fix new entry button
* fix markdown state persisting across entries
* enable simple workflow save and new from editor
* update slug in address bar when saving new entry
* wip - workflow updates, deletion working
* add status change functionality to editor
* wip - improving status change from editor
* editor toolbar back button improvements, loading improvements, cleanup
* progress on the media library UI cleanup
* remove font smothing css
* a quick fix for these buttons
* tweaks
* progress on media library modal— broken FYI
* fix media library functionality, finish migrating footer
* remove media library footer files
* remove leftover css import
* fix media library
* editor publishing functionality complete (unstyled)
* remove leftover loader var from media library
* wip - editor publishing styles
* add status dropdown styling
* editor toolbar style updates
* editor toolbar state improvements
* progress on the media library UI cleanup, style improvements
* finish editorial workflow editor styling
* finish media library styling
* fix config
* add what-input to optimize focus styling
* fix button
* fix navigation blocking for simple workflow
* improve simple workflow publishing
* add avatar dropdown to editor top bar
* style github and test-repo auth pages
* add git gateway auth page styles
* improve editor error styling
2017-12-07 12:37:10 -05:00
import { authenticateUser } from "Actions/auth" ;
2018-05-25 10:15:01 -06:00
import { formatByExtension , supportedFormats , frontmatterFormats } from "Formats/formats" ;
import { selectIdentifier } from "Reducers/collections" ;
import { IDENTIFIER _FIELDS } from "Constants/fieldInference" ;
WIP - Global UI (#785)
* update top bar and collections sidebar UI
* update collection entries UI
* improve global layout
* merge search page into collection page
* enable new entry button
* search fixup
* wip -initial editor update
* update editor scrolling and markdown toolbar position
* wip
* editor toolbar progress
* editor toolbar wip
* finished basic editor toolbar
* add standalone toggle component
* improve markdown toolbar spacing
* add user avatar placeholder
* finish markdown toggle styling
* refactor icon setup, add new icons
* add new icons to markdown editor toolbar
* remove extra app container
* add markdown active mark style
* relation and text widget styling
* widget design updates, basic list/object design update
* widget style updates, image widget improvements
* refactor widget directory, fix file removal
* widget focus styles
* finish editor widget focus styles
* migrate media library modal to react-modal
* wip - migrate editor component form to modal
* wip - move editor component form to modal
* wip - embed plugin forms in the editor
* inline shortcode forms working
* disable react hot loading, its breaking things
* improve shortcode form styles
* make shortcode form collapsible, improve styling
* add close functionality to shortcode blocks
* improve base media library styling
* fix shortcode label
* migrate unstyled workflow to new UI
* wip - reorganizing everything
* more work moving everything
* finish more moving and eliminating stuff
* restructure, remove react-toolbox
* wip - removing old stuff, more restructure
* finish restructure
* wip - css arch
* switch back to test repo
* update react-datetime to ^2.11.0
* remove leftover react-toolbox button
* more restructuring clean-up
* fix UI component directory case
* wip -css editor control style
* wip - consolidate widget styles
* wip - use a single control renderer
* fixed object values breaking
* wip - editor control active styles
* pass control wrapper to widgets
* ensure branch name is trimmed
* wip - improve widget authoring support
* import Map to Widget component
* refactor toolbar buttons
* wip - more widget active styles
* break out editor toggle component
* add local scroll sync back
* update editor toggle icons
* limit editor control pane content width
* fix editor control spacing
* migrate markdown toolbar stickiness to css
* fix markdown toolbar border radius
* temporarily use test backend
* stop markdown toolbar from going to bottom
* restore disabled markdown toolbar buttons for raw
* test markdown widget without focus styles
* more widget updates
* remove card visuals from editor
* disable dragging editor split off screen
* use editorControl component for shortcode fields
* make header site link configurable
* add configurable collection descriptions
* temporarily add example assets
* add basic list view
* remove outdated css mixins
* add and implement search icon
* activate quick add menu
* visualize usable space in editor view
* fix entry close, other improvements
* wip - editorial workflow updates
* some dropshadow and other CSS tweaks
* workflow ui updates
* add worfklow card buttons
* fix workflow card button handlers
* some dropshadow and other CSS tweaks
* make workflow board wider
* center workflow and collection views
* add basic responsiveness
* a bunch of fun UI fixes! a BUNCH! (#875)
* give `.nc-entryEditor-toolbar-mainSection` left and right child divs
* a bunch of fun UI fixes! a BUNCH!
* remove obscure --buttonShadow
* revert to test repo
* fix not found page styling
* allow workflow publishing from any column
* disallow publishing from all columns, with feedback
* fix new entry button
* fix markdown state persisting across entries
* enable simple workflow save and new from editor
* update slug in address bar when saving new entry
* wip - workflow updates, deletion working
* add status change functionality to editor
* wip - improving status change from editor
* editor toolbar back button improvements, loading improvements, cleanup
* progress on the media library UI cleanup
* remove font smothing css
* a quick fix for these buttons
* tweaks
* progress on media library modal— broken FYI
* fix media library functionality, finish migrating footer
* remove media library footer files
* remove leftover css import
* fix media library
* editor publishing functionality complete (unstyled)
* remove leftover loader var from media library
* wip - editor publishing styles
* add status dropdown styling
* editor toolbar style updates
* editor toolbar state improvements
* progress on the media library UI cleanup, style improvements
* finish editorial workflow editor styling
* finish media library styling
* fix config
* add what-input to optimize focus styling
* fix button
* fix navigation blocking for simple workflow
* improve simple workflow publishing
* add avatar dropdown to editor top bar
* style github and test-repo auth pages
* add git gateway auth page styles
* improve editor error styling
2017-12-07 12:37:10 -05:00
import * as publishModes from "Constants/publishModes" ;
2016-02-25 00:45:56 -08:00
2016-12-23 16:59:48 -02:00
export const CONFIG _REQUEST = "CONFIG_REQUEST" ;
export const CONFIG _SUCCESS = "CONFIG_SUCCESS" ;
export const CONFIG _FAILURE = "CONFIG_FAILURE" ;
2018-02-28 15:45:16 -05:00
export const CONFIG _MERGE = "CONFIG_MERGE" ;
2016-02-25 00:45:56 -08:00
2018-04-10 16:48:04 -04:00
const getConfigUrl = ( ) => {
const validTypes = { 'text/yaml' : 'yaml' , 'application/x-yaml' : 'yaml' } ;
const configLinkEl = document . querySelector ( 'link[rel="cms-config-url"]' ) ;
const isValidLink = configLinkEl && validTypes [ configLinkEl . type ] && get ( configLinkEl , 'href' ) ;
if ( isValidLink ) {
const link = get ( configLinkEl , 'href' ) ;
console . log ( ` Using config file path: " ${ link } " ` ) ;
return link ;
}
return 'config.yml' ;
}
2016-11-11 12:17:01 +01:00
const defaults = {
publish _mode : publishModes . SIMPLE ,
} ;
export function applyDefaults ( config ) {
2018-02-28 15:45:16 -05:00
return Map ( defaults )
. mergeDeep ( config )
. withMutations ( map => {
/ * *
* Use media _folder as default public _folder .
* /
const defaultPublicFolder = ` / ${ trimStart ( map . get ( 'media_folder' ) , '/' ) } ` ;
if ( ! map . get ( 'public_folder' ) ) {
map . set ( 'public_folder' , defaultPublicFolder ) ;
}
} ) ;
2016-11-11 12:17:01 +01:00
}
2018-05-25 10:15:01 -06:00
function validateCollection ( collection ) {
const {
name ,
folder ,
files ,
format ,
extension ,
frontmatter _delimiter : delimiter ,
fields ,
} = collection . toJS ( ) ;
if ( ! folder && ! files ) {
throw new Error ( ` Unknown collection type for collection " ${ name } ". Collections can be either Folder based or File based. ` ) ;
}
if ( format && ! supportedFormats . includes ( format ) ) {
throw new Error ( ` Unknown collection format for collection " ${ name } ". Supported formats are ${ supportedFormats . join ( ',' ) } ` ) ;
}
if ( ! format && extension && ! formatByExtension ( extension ) ) {
// Cannot infer format from extension.
throw new Error ( ` Please set a format for collection " ${ name } ". Supported formats are ${ supportedFormats . join ( ',' ) } ` ) ;
}
if ( delimiter && ! frontmatterFormats . includes ( format ) ) {
// Cannot set custom delimiter without explicit and proper frontmatter format declaration
throw new Error ( ` Please set a proper frontmatter format for collection " ${ name } " to use a custom delimiter. Supported frontmatter formats are yaml-frontmatter, toml-frontmatter, and json-frontmatter. ` ) ;
}
if ( folder && ! selectIdentifier ( collection ) ) {
// Verify that folder-type collections have an identifier field for slug creation.
throw new Error ( ` Collection " ${ name } " must have a field that is a valid entry identifier. Supported fields are ${ IDENTIFIER _FIELDS . join ( ', ' ) } . ` ) ;
}
}
2017-04-14 22:36:41 +01:00
export function validateConfig ( config ) {
2018-02-28 15:45:16 -05:00
if ( ! config . get ( 'backend' ) ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: A `backend` wasn't found. Check your config.yml file." ) ;
}
2018-02-28 15:45:16 -05:00
if ( ! config . getIn ( [ 'backend' , 'name' ] ) ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: A `backend.name` wasn't found. Check your config.yml file." ) ;
}
2018-02-28 15:45:16 -05:00
if ( typeof config . getIn ( [ 'backend' , 'name' ] ) !== 'string' ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: Your `backend.name` must be a string. Check your config.yml file." ) ;
}
2018-02-28 15:45:16 -05:00
if ( ! config . get ( 'media_folder' ) ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: A `media_folder` wasn\'t found. Check your config.yml file." ) ;
}
2018-02-28 15:45:16 -05:00
if ( typeof config . get ( 'media_folder' ) !== 'string' ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: Your `media_folder` must be a string. Check your config.yml file." ) ;
}
2018-03-27 16:56:10 -06:00
const slug _encoding = config . getIn ( [ 'slug' , 'encoding' ] , "unicode" ) ;
if ( slug _encoding !== "unicode" && slug _encoding !== "ascii" ) {
throw new Error ( "Error in configuration file: Your `slug.encoding` must be either `unicode` or `ascii`. Check your config.yml file." )
}
if ( ! isBoolean ( config . getIn ( [ 'slug' , 'clean_accents' ] , false ) ) ) {
throw new Error ( "Error in configuration file: Your `slug.clean_accents` must be a boolean. Check your config.yml file." ) ;
}
2018-02-28 15:45:16 -05:00
if ( ! config . get ( 'collections' ) ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: A `collections` wasn\'t found. Check your config.yml file." ) ;
}
2018-02-28 15:45:16 -05:00
const collections = config . get ( 'collections' ) ;
if ( ! List . isList ( collections ) || collections . isEmpty ( ) || ! collections . first ( ) ) {
2017-04-14 22:36:41 +01:00
throw new Error ( "Error in configuration file: Your `collections` must be an array with at least one element. Check your config.yml file." ) ;
}
2018-05-25 10:15:01 -06:00
/ * *
* Validate Collections
* /
config . get ( 'collections' ) . forEach ( validateCollection ) ;
2017-04-14 22:36:41 +01:00
return config ;
}
2018-02-28 15:45:16 -05:00
function mergePreloadedConfig ( preloadedConfig , loadedConfig ) {
const map = fromJS ( loadedConfig ) || Map ( ) ;
return preloadedConfig ? preloadedConfig . mergeDeep ( map ) : map ;
}
2016-11-11 12:17:01 +01:00
function parseConfig ( data ) {
const config = yaml . safeLoad ( data ) ;
2016-12-23 16:59:48 -02:00
if ( typeof CMS _ENV === "string" && config [ CMS _ENV ] ) {
2017-04-14 22:36:41 +01:00
Object . keys ( config [ CMS _ENV ] ) . forEach ( ( key ) => {
config [ key ] = config [ CMS _ENV ] [ key ] ;
} ) ;
2016-11-11 12:17:01 +01:00
}
return config ;
}
2018-03-28 13:25:46 -07:00
async function getConfig ( file , isPreloaded ) {
const response = await fetch ( file , { credentials : 'same-origin' } ) ;
if ( response . status !== 200 ) {
if ( isPreloaded ) return parseConfig ( '' ) ;
throw new Error ( ` Failed to load config.yml ( ${ response . status } ) ` ) ;
}
const contentType = response . headers . get ( 'Content-Type' ) || 'Not-Found' ;
const isYaml = contentType . indexOf ( 'yaml' ) !== - 1 ;
if ( ! isYaml ) {
console . log ( ` Response for ${ file } was not yaml. (Content-Type: ${ contentType } ) ` ) ;
if ( isPreloaded ) return parseConfig ( '' ) ;
}
return parseConfig ( await response . text ( ) ) ;
}
2016-02-25 00:45:56 -08:00
export function configLoaded ( config ) {
return {
2016-02-25 12:31:21 -08:00
type : CONFIG _SUCCESS ,
2016-11-11 12:17:01 +01:00
payload : config ,
2016-02-25 00:45:56 -08:00
} ;
}
export function configLoading ( ) {
return {
2016-11-11 12:17:01 +01:00
type : CONFIG _REQUEST ,
2016-02-25 00:45:56 -08:00
} ;
}
export function configFailed ( err ) {
return {
2016-02-25 12:31:21 -08:00
type : CONFIG _FAILURE ,
2016-12-23 16:59:48 -02:00
error : "Error loading config" ,
2016-11-11 12:17:01 +01:00
payload : err ,
2016-02-25 00:45:56 -08:00
} ;
}
2016-06-06 21:53:22 -03:00
export function configDidLoad ( config ) {
return ( dispatch ) => {
dispatch ( configLoaded ( config ) ) ;
} ;
}
2018-02-28 15:45:16 -05:00
export function mergeConfig ( config ) {
return { type : CONFIG _MERGE , payload : config } ;
}
2016-11-11 12:17:01 +01:00
export function loadConfig ( ) {
2016-02-25 00:45:56 -08:00
if ( window . CMS _CONFIG ) {
2018-02-28 15:45:16 -05:00
return configDidLoad ( fromJS ( window . CMS _CONFIG ) ) ;
2016-02-25 00:45:56 -08:00
}
2018-02-28 15:45:16 -05:00
return async ( dispatch , getState ) => {
2016-02-25 00:45:56 -08:00
dispatch ( configLoading ( ) ) ;
2018-02-28 15:45:16 -05:00
try {
const preloadedConfig = getState ( ) . config ;
2018-04-10 16:48:04 -04:00
const configUrl = getConfigUrl ( ) ;
const loadedConfig = await getConfig ( configUrl , preloadedConfig && preloadedConfig . size > 1 ) ;
2018-02-28 15:45:16 -05:00
/ * *
* Merge any existing configuration so the result can be validated .
* /
2018-03-28 13:25:46 -07:00
const mergedConfig = mergePreloadedConfig ( preloadedConfig , loadedConfig ) ;
2018-02-28 15:45:16 -05:00
const config = flow ( validateConfig , applyDefaults ) ( mergedConfig ) ;
2017-04-14 17:12:13 +01:00
dispatch ( configDidLoad ( config ) ) ;
dispatch ( authenticateUser ( ) ) ;
2018-02-28 15:45:16 -05:00
}
catch ( err ) {
2016-02-25 00:45:56 -08:00
dispatch ( configFailed ( err ) ) ;
2018-02-28 15:45:16 -05:00
throw ( err )
}
2016-02-25 00:45:56 -08:00
} ;
}