@pranaysahith/decap-cms-core 3.9.1

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.
Files changed (299) hide show
  1. package/README.md +9 -0
  2. package/dist/@pranaysahith/decap-cms-core.js +52 -0
  3. package/dist/@pranaysahith/decap-cms-core.js.LICENSE.txt +141 -0
  4. package/dist/@pranaysahith/decap-cms-core.js.map +1 -0
  5. package/dist/decap-cms-core.js +47 -0
  6. package/dist/decap-cms-core.js.LICENSE.txt +116 -0
  7. package/dist/decap-cms-core.js.map +1 -0
  8. package/dist/esm/actions/auth.js +97 -0
  9. package/dist/esm/actions/collections.js +15 -0
  10. package/dist/esm/actions/config.js +493 -0
  11. package/dist/esm/actions/deploys.js +79 -0
  12. package/dist/esm/actions/editorialWorkflow.js +480 -0
  13. package/dist/esm/actions/entries.js +865 -0
  14. package/dist/esm/actions/media.js +147 -0
  15. package/dist/esm/actions/mediaLibrary.js +552 -0
  16. package/dist/esm/actions/notifications.js +21 -0
  17. package/dist/esm/actions/search.js +149 -0
  18. package/dist/esm/actions/status.js +74 -0
  19. package/dist/esm/actions/waitUntil.js +32 -0
  20. package/dist/esm/backend.js +1082 -0
  21. package/dist/esm/bootstrap.js +101 -0
  22. package/dist/esm/components/App/App.js +289 -0
  23. package/dist/esm/components/App/Header.js +172 -0
  24. package/dist/esm/components/App/NotFoundPage.js +19 -0
  25. package/dist/esm/components/Collection/Collection.js +198 -0
  26. package/dist/esm/components/Collection/CollectionControls.js +46 -0
  27. package/dist/esm/components/Collection/CollectionSearch.js +222 -0
  28. package/dist/esm/components/Collection/CollectionTop.js +68 -0
  29. package/dist/esm/components/Collection/ControlButton.js +17 -0
  30. package/dist/esm/components/Collection/Entries/Entries.js +73 -0
  31. package/dist/esm/components/Collection/Entries/EntriesCollection.js +241 -0
  32. package/dist/esm/components/Collection/Entries/EntriesSearch.js +113 -0
  33. package/dist/esm/components/Collection/Entries/EntryCard.js +177 -0
  34. package/dist/esm/components/Collection/Entries/EntryListing.js +143 -0
  35. package/dist/esm/components/Collection/FilterControl.js +33 -0
  36. package/dist/esm/components/Collection/FolderRenameControl.js +403 -0
  37. package/dist/esm/components/Collection/GroupControl.js +33 -0
  38. package/dist/esm/components/Collection/NestedCollection.js +308 -0
  39. package/dist/esm/components/Collection/Sidebar.js +91 -0
  40. package/dist/esm/components/Collection/SortControl.js +59 -0
  41. package/dist/esm/components/Collection/ViewStyleControl.js +38 -0
  42. package/dist/esm/components/Editor/Editor.js +466 -0
  43. package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +395 -0
  44. package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +254 -0
  45. package/dist/esm/components/Editor/EditorControlPane/Widget.js +374 -0
  46. package/dist/esm/components/Editor/EditorInterface.js +386 -0
  47. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +47 -0
  48. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +66 -0
  49. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +288 -0
  50. package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +27 -0
  51. package/dist/esm/components/Editor/EditorToolbar.js +536 -0
  52. package/dist/esm/components/Editor/EntryPathEditor.js +272 -0
  53. package/dist/esm/components/Editor/withWorkflow.js +56 -0
  54. package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +18 -0
  55. package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +20 -0
  56. package/dist/esm/components/EditorWidgets/index.js +4 -0
  57. package/dist/esm/components/MediaLibrary/EmptyMessage.js +22 -0
  58. package/dist/esm/components/MediaLibrary/MediaLibrary.js +446 -0
  59. package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +93 -0
  60. package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +99 -0
  61. package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +198 -0
  62. package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +32 -0
  63. package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +156 -0
  64. package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +51 -0
  65. package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +123 -0
  66. package/dist/esm/components/UI/DragDrop.js +67 -0
  67. package/dist/esm/components/UI/ErrorBoundary.js +173 -0
  68. package/dist/esm/components/UI/FileUploadButton.js +27 -0
  69. package/dist/esm/components/UI/Modal.js +104 -0
  70. package/dist/esm/components/UI/Notifications.js +62 -0
  71. package/dist/esm/components/UI/SettingsDropdown.js +107 -0
  72. package/dist/esm/components/UI/index.js +6 -0
  73. package/dist/esm/components/Workflow/Workflow.js +133 -0
  74. package/dist/esm/components/Workflow/WorkflowCard.js +128 -0
  75. package/dist/esm/components/Workflow/WorkflowList.js +204 -0
  76. package/dist/esm/constants/collectionTypes.js +2 -0
  77. package/dist/esm/constants/collectionViews.js +2 -0
  78. package/dist/esm/constants/commitProps.js +2 -0
  79. package/dist/esm/constants/configSchema.js +644 -0
  80. package/dist/esm/constants/fieldInference.js +57 -0
  81. package/dist/esm/constants/publishModes.js +18 -0
  82. package/dist/esm/constants/validationErrorTypes.js +6 -0
  83. package/dist/esm/formats/formats.js +83 -0
  84. package/dist/esm/formats/frontmatter.js +146 -0
  85. package/dist/esm/formats/helpers.js +12 -0
  86. package/dist/esm/formats/json.js +8 -0
  87. package/dist/esm/formats/toml.js +32 -0
  88. package/dist/esm/formats/yaml.js +51 -0
  89. package/dist/esm/index.js +7 -0
  90. package/dist/esm/integrations/index.js +28 -0
  91. package/dist/esm/integrations/providers/algolia/implementation.js +174 -0
  92. package/dist/esm/integrations/providers/assetStore/implementation.js +165 -0
  93. package/dist/esm/lib/consoleError.js +3 -0
  94. package/dist/esm/lib/formatters.js +191 -0
  95. package/dist/esm/lib/i18n.js +367 -0
  96. package/dist/esm/lib/phrases.js +6 -0
  97. package/dist/esm/lib/polyfill.js +8 -0
  98. package/dist/esm/lib/registry.js +329 -0
  99. package/dist/esm/lib/serializeEntryValues.js +67 -0
  100. package/dist/esm/lib/stega.js +142 -0
  101. package/dist/esm/lib/textHelper.js +9 -0
  102. package/dist/esm/lib/urlHelper.js +111 -0
  103. package/dist/esm/mediaLibrary.js +37 -0
  104. package/dist/esm/reducers/auth.js +27 -0
  105. package/dist/esm/reducers/collections.js +428 -0
  106. package/dist/esm/reducers/combinedReducer.js +8 -0
  107. package/dist/esm/reducers/config.js +29 -0
  108. package/dist/esm/reducers/cursors.js +31 -0
  109. package/dist/esm/reducers/deploys.js +45 -0
  110. package/dist/esm/reducers/editorialWorkflow.js +83 -0
  111. package/dist/esm/reducers/entries.js +568 -0
  112. package/dist/esm/reducers/entryDraft.js +212 -0
  113. package/dist/esm/reducers/globalUI.js +25 -0
  114. package/dist/esm/reducers/index.js +66 -0
  115. package/dist/esm/reducers/integrations.js +53 -0
  116. package/dist/esm/reducers/mediaLibrary.js +252 -0
  117. package/dist/esm/reducers/medias.js +68 -0
  118. package/dist/esm/reducers/notifications.js +23 -0
  119. package/dist/esm/reducers/search.js +92 -0
  120. package/dist/esm/reducers/status.js +30 -0
  121. package/dist/esm/redux/index.js +7 -0
  122. package/dist/esm/redux/middleware/waitUntilAction.js +48 -0
  123. package/dist/esm/routing/history.js +12 -0
  124. package/dist/esm/types/diacritics.d.js +0 -0
  125. package/dist/esm/types/global.d.js +1 -0
  126. package/dist/esm/types/immutable.js +7 -0
  127. package/dist/esm/types/redux.js +14 -0
  128. package/dist/esm/types/tomlify-j0.4.d.js +0 -0
  129. package/dist/esm/valueObjects/AssetProxy.js +44 -0
  130. package/dist/esm/valueObjects/EditorComponent.js +34 -0
  131. package/dist/esm/valueObjects/Entry.js +20 -0
  132. package/index.d.ts +618 -0
  133. package/package.json +106 -0
  134. package/src/__tests__/backend.spec.js +1161 -0
  135. package/src/actions/__tests__/config.spec.js +1009 -0
  136. package/src/actions/__tests__/editorialWorkflow.spec.js +216 -0
  137. package/src/actions/__tests__/entries.spec.js +596 -0
  138. package/src/actions/__tests__/media.spec.ts +171 -0
  139. package/src/actions/__tests__/mediaLibrary.spec.js +327 -0
  140. package/src/actions/__tests__/search.spec.js +209 -0
  141. package/src/actions/auth.ts +127 -0
  142. package/src/actions/collections.ts +18 -0
  143. package/src/actions/config.ts +565 -0
  144. package/src/actions/deploys.ts +104 -0
  145. package/src/actions/editorialWorkflow.ts +567 -0
  146. package/src/actions/entries.ts +1055 -0
  147. package/src/actions/media.ts +139 -0
  148. package/src/actions/mediaLibrary.ts +574 -0
  149. package/src/actions/notifications.ts +36 -0
  150. package/src/actions/search.ts +221 -0
  151. package/src/actions/status.ts +99 -0
  152. package/src/actions/waitUntil.ts +49 -0
  153. package/src/backend.ts +1400 -0
  154. package/src/bootstrap.js +104 -0
  155. package/src/components/App/App.js +286 -0
  156. package/src/components/App/Header.js +266 -0
  157. package/src/components/App/NotFoundPage.js +23 -0
  158. package/src/components/Collection/Collection.js +210 -0
  159. package/src/components/Collection/CollectionControls.js +58 -0
  160. package/src/components/Collection/CollectionSearch.js +243 -0
  161. package/src/components/Collection/CollectionTop.js +81 -0
  162. package/src/components/Collection/ControlButton.js +27 -0
  163. package/src/components/Collection/Entries/Entries.js +82 -0
  164. package/src/components/Collection/Entries/EntriesCollection.js +277 -0
  165. package/src/components/Collection/Entries/EntriesSearch.js +102 -0
  166. package/src/components/Collection/Entries/EntryCard.js +246 -0
  167. package/src/components/Collection/Entries/EntryListing.js +151 -0
  168. package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +163 -0
  169. package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +46 -0
  170. package/src/components/Collection/FilterControl.js +39 -0
  171. package/src/components/Collection/GroupControl.js +39 -0
  172. package/src/components/Collection/NestedCollection.js +330 -0
  173. package/src/components/Collection/Sidebar.js +136 -0
  174. package/src/components/Collection/SortControl.js +68 -0
  175. package/src/components/Collection/ViewStyleControl.js +50 -0
  176. package/src/components/Collection/__tests__/Collection.spec.js +75 -0
  177. package/src/components/Collection/__tests__/NestedCollection.spec.js +445 -0
  178. package/src/components/Collection/__tests__/Sidebar.spec.js +87 -0
  179. package/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap +144 -0
  180. package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +550 -0
  181. package/src/components/Collection/__tests__/__snapshots__/Sidebar.spec.js.snap +312 -0
  182. package/src/components/Editor/Editor.js +497 -0
  183. package/src/components/Editor/EditorControlPane/EditorControl.js +452 -0
  184. package/src/components/Editor/EditorControlPane/EditorControlPane.js +269 -0
  185. package/src/components/Editor/EditorControlPane/Widget.js +384 -0
  186. package/src/components/Editor/EditorInterface.js +444 -0
  187. package/src/components/Editor/EditorPreviewPane/EditorPreview.js +40 -0
  188. package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +74 -0
  189. package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +333 -0
  190. package/src/components/Editor/EditorPreviewPane/PreviewHOC.js +33 -0
  191. package/src/components/Editor/EditorToolbar.js +691 -0
  192. package/src/components/Editor/__tests__/Editor.spec.js +221 -0
  193. package/src/components/Editor/__tests__/EditorToolbar.spec.js +120 -0
  194. package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +45 -0
  195. package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +4233 -0
  196. package/src/components/Editor/withWorkflow.js +61 -0
  197. package/src/components/EditorWidgets/Unknown/UnknownControl.js +17 -0
  198. package/src/components/EditorWidgets/Unknown/UnknownPreview.js +19 -0
  199. package/src/components/EditorWidgets/index.js +5 -0
  200. package/src/components/MediaLibrary/EmptyMessage.js +28 -0
  201. package/src/components/MediaLibrary/MediaLibrary.js +411 -0
  202. package/src/components/MediaLibrary/MediaLibraryButtons.js +136 -0
  203. package/src/components/MediaLibrary/MediaLibraryCard.js +128 -0
  204. package/src/components/MediaLibrary/MediaLibraryCardGrid.js +199 -0
  205. package/src/components/MediaLibrary/MediaLibraryHeader.js +48 -0
  206. package/src/components/MediaLibrary/MediaLibraryModal.js +200 -0
  207. package/src/components/MediaLibrary/MediaLibrarySearch.js +61 -0
  208. package/src/components/MediaLibrary/MediaLibraryTop.js +143 -0
  209. package/src/components/MediaLibrary/__tests__/MediaLibraryButtons.spec.js +45 -0
  210. package/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +49 -0
  211. package/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +264 -0
  212. package/src/components/UI/DragDrop.js +66 -0
  213. package/src/components/UI/ErrorBoundary.js +214 -0
  214. package/src/components/UI/FileUploadButton.js +24 -0
  215. package/src/components/UI/Modal.js +112 -0
  216. package/src/components/UI/Notifications.tsx +83 -0
  217. package/src/components/UI/SettingsDropdown.js +103 -0
  218. package/src/components/UI/__tests__/ErrorBoundary.spec.js +57 -0
  219. package/src/components/UI/index.js +6 -0
  220. package/src/components/Workflow/Workflow.js +169 -0
  221. package/src/components/Workflow/WorkflowCard.js +177 -0
  222. package/src/components/Workflow/WorkflowList.js +272 -0
  223. package/src/constants/__tests__/configSchema.spec.js +611 -0
  224. package/src/constants/collectionTypes.ts +2 -0
  225. package/src/constants/collectionViews.js +2 -0
  226. package/src/constants/commitProps.ts +2 -0
  227. package/src/constants/configSchema.js +441 -0
  228. package/src/constants/fieldInference.tsx +78 -0
  229. package/src/constants/publishModes.ts +22 -0
  230. package/src/constants/validationErrorTypes.js +6 -0
  231. package/src/formats/__tests__/formats.spec.js +87 -0
  232. package/src/formats/__tests__/frontmatter.spec.js +429 -0
  233. package/src/formats/__tests__/toml.spec.js +9 -0
  234. package/src/formats/__tests__/yaml.spec.js +162 -0
  235. package/src/formats/formats.ts +97 -0
  236. package/src/formats/frontmatter.ts +150 -0
  237. package/src/formats/helpers.ts +14 -0
  238. package/src/formats/json.ts +9 -0
  239. package/src/formats/toml.ts +33 -0
  240. package/src/formats/yaml.ts +58 -0
  241. package/src/index.js +8 -0
  242. package/src/integrations/index.js +35 -0
  243. package/src/integrations/providers/algolia/implementation.js +176 -0
  244. package/src/integrations/providers/assetStore/implementation.js +148 -0
  245. package/src/lib/__tests__/formatters.spec.js +751 -0
  246. package/src/lib/__tests__/i18n.spec.js +792 -0
  247. package/src/lib/__tests__/phrases.spec.js +119 -0
  248. package/src/lib/__tests__/registry.spec.js +261 -0
  249. package/src/lib/__tests__/serializeEntryValues.spec.js +22 -0
  250. package/src/lib/__tests__/urlHelper.spec.js +138 -0
  251. package/src/lib/consoleError.js +7 -0
  252. package/src/lib/formatters.ts +286 -0
  253. package/src/lib/i18n.ts +454 -0
  254. package/src/lib/phrases.js +8 -0
  255. package/src/lib/polyfill.js +9 -0
  256. package/src/lib/registry.js +312 -0
  257. package/src/lib/serializeEntryValues.js +75 -0
  258. package/src/lib/stega.ts +145 -0
  259. package/src/lib/textHelper.js +11 -0
  260. package/src/lib/urlHelper.ts +128 -0
  261. package/src/mediaLibrary.ts +51 -0
  262. package/src/reducers/__tests__/auth.spec.ts +38 -0
  263. package/src/reducers/__tests__/collections.spec.js +610 -0
  264. package/src/reducers/__tests__/config.spec.js +38 -0
  265. package/src/reducers/__tests__/entries.spec.js +694 -0
  266. package/src/reducers/__tests__/entryDraft.spec.js +297 -0
  267. package/src/reducers/__tests__/globalUI.js +43 -0
  268. package/src/reducers/__tests__/integrations.spec.ts +76 -0
  269. package/src/reducers/__tests__/mediaLibrary.spec.js +154 -0
  270. package/src/reducers/__tests__/medias.spec.ts +49 -0
  271. package/src/reducers/auth.ts +46 -0
  272. package/src/reducers/collections.ts +535 -0
  273. package/src/reducers/combinedReducer.ts +11 -0
  274. package/src/reducers/config.ts +38 -0
  275. package/src/reducers/cursors.js +36 -0
  276. package/src/reducers/deploys.ts +52 -0
  277. package/src/reducers/editorialWorkflow.ts +163 -0
  278. package/src/reducers/entries.ts +819 -0
  279. package/src/reducers/entryDraft.js +260 -0
  280. package/src/reducers/globalUI.ts +45 -0
  281. package/src/reducers/index.ts +82 -0
  282. package/src/reducers/integrations.ts +59 -0
  283. package/src/reducers/mediaLibrary.ts +296 -0
  284. package/src/reducers/medias.ts +66 -0
  285. package/src/reducers/notifications.ts +52 -0
  286. package/src/reducers/search.ts +111 -0
  287. package/src/reducers/status.ts +40 -0
  288. package/src/redux/index.ts +18 -0
  289. package/src/redux/middleware/waitUntilAction.ts +64 -0
  290. package/src/routing/__tests__/history.spec.ts +49 -0
  291. package/src/routing/history.ts +17 -0
  292. package/src/types/diacritics.d.ts +1 -0
  293. package/src/types/global.d.ts +8 -0
  294. package/src/types/immutable.ts +49 -0
  295. package/src/types/redux.ts +827 -0
  296. package/src/types/tomlify-j0.4.d.ts +13 -0
  297. package/src/valueObjects/AssetProxy.ts +48 -0
  298. package/src/valueObjects/EditorComponent.js +38 -0
  299. package/src/valueObjects/Entry.ts +63 -0
@@ -0,0 +1,97 @@
1
+ import { currentBackend } from '../backend';
2
+ import { addNotification, clearNotifications } from './notifications';
3
+ export const AUTH_REQUEST = 'AUTH_REQUEST';
4
+ export const AUTH_SUCCESS = 'AUTH_SUCCESS';
5
+ export const AUTH_FAILURE = 'AUTH_FAILURE';
6
+ export const AUTH_REQUEST_DONE = 'AUTH_REQUEST_DONE';
7
+ export const USE_OPEN_AUTHORING = 'USE_OPEN_AUTHORING';
8
+ export const LOGOUT = 'LOGOUT';
9
+ export function authenticating() {
10
+ return {
11
+ type: AUTH_REQUEST
12
+ };
13
+ }
14
+ export function authenticate(userData) {
15
+ return {
16
+ type: AUTH_SUCCESS,
17
+ payload: userData
18
+ };
19
+ }
20
+ export function authError(error) {
21
+ return {
22
+ type: AUTH_FAILURE,
23
+ error: 'Failed to authenticate',
24
+ payload: error
25
+ };
26
+ }
27
+ export function doneAuthenticating() {
28
+ return {
29
+ type: AUTH_REQUEST_DONE
30
+ };
31
+ }
32
+ export function useOpenAuthoring() {
33
+ return {
34
+ type: USE_OPEN_AUTHORING
35
+ };
36
+ }
37
+ export function logout() {
38
+ return {
39
+ type: LOGOUT
40
+ };
41
+ }
42
+
43
+ // Check if user data token is cached and is valid
44
+ export function authenticateUser() {
45
+ return (dispatch, getState) => {
46
+ const state = getState();
47
+ const backend = currentBackend(state.config);
48
+ dispatch(authenticating());
49
+ return Promise.resolve(backend.currentUser()).then(user => {
50
+ if (user) {
51
+ if (user.useOpenAuthoring) {
52
+ dispatch(useOpenAuthoring());
53
+ }
54
+ dispatch(authenticate(user));
55
+ } else {
56
+ dispatch(doneAuthenticating());
57
+ }
58
+ }).catch(error => {
59
+ dispatch(authError(error));
60
+ dispatch(logoutUser());
61
+ });
62
+ };
63
+ }
64
+ export function loginUser(credentials) {
65
+ return (dispatch, getState) => {
66
+ const state = getState();
67
+ const backend = currentBackend(state.config);
68
+ dispatch(authenticating());
69
+ return backend.authenticate(credentials).then(user => {
70
+ if (user.useOpenAuthoring) {
71
+ dispatch(useOpenAuthoring());
72
+ }
73
+ dispatch(authenticate(user));
74
+ }).catch(error => {
75
+ console.error(error);
76
+ dispatch(addNotification({
77
+ message: {
78
+ details: error.message,
79
+ key: 'ui.toast.onFailToAuth'
80
+ },
81
+ type: 'error',
82
+ dismissAfter: 8000
83
+ }));
84
+ dispatch(authError(error));
85
+ });
86
+ };
87
+ }
88
+ export function logoutUser() {
89
+ return (dispatch, getState) => {
90
+ const state = getState();
91
+ const backend = currentBackend(state.config);
92
+ Promise.resolve(backend.logout()).then(() => {
93
+ dispatch(logout());
94
+ dispatch(clearNotifications());
95
+ });
96
+ };
97
+ }
@@ -0,0 +1,15 @@
1
+ import { history } from '../routing/history';
2
+ import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper';
3
+ export function searchCollections(query, collection) {
4
+ if (collection) {
5
+ history.push(`/collections/${collection}/search/${query}`);
6
+ } else {
7
+ history.push(`/search/${query}`);
8
+ }
9
+ }
10
+ export function showCollection(collectionName) {
11
+ history.push(getCollectionUrl(collectionName));
12
+ }
13
+ export function createNewEntry(collectionName) {
14
+ history.push(getNewEntryUrl(collectionName));
15
+ }
@@ -0,0 +1,493 @@
1
+ import yaml from 'yaml';
2
+ import { fromJS } from 'immutable';
3
+ import deepmerge from 'deepmerge';
4
+ import { produce } from 'immer';
5
+ import trimStart from 'lodash/trimStart';
6
+ import trim from 'lodash/trim';
7
+ import isEmpty from 'lodash/isEmpty';
8
+ import { SIMPLE as SIMPLE_PUBLISH_MODE } from '../constants/publishModes';
9
+ import { validateConfig } from '../constants/configSchema';
10
+ import { selectDefaultSortableFields } from '../reducers/collections';
11
+ import { getIntegrations, selectIntegration } from '../reducers/integrations';
12
+ import { resolveBackend } from '../backend';
13
+ import { I18N, I18N_FIELD, I18N_STRUCTURE } from '../lib/i18n';
14
+ import { FILES, FOLDER } from '../constants/collectionTypes';
15
+ export const CONFIG_REQUEST = 'CONFIG_REQUEST';
16
+ export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
17
+ export const CONFIG_FAILURE = 'CONFIG_FAILURE';
18
+ function isObjectField(field) {
19
+ return 'fields' in field;
20
+ }
21
+ function isFieldList(field) {
22
+ return 'types' in field || 'field' in field;
23
+ }
24
+ function traverseFieldsJS(fields, updater) {
25
+ return fields.map(field => {
26
+ const newField = updater(field);
27
+ if (isObjectField(newField)) {
28
+ return {
29
+ ...newField,
30
+ fields: traverseFieldsJS(newField.fields, updater)
31
+ };
32
+ } else if (isFieldList(newField) && newField.field) {
33
+ return {
34
+ ...newField,
35
+ field: traverseFieldsJS([newField.field], updater)[0]
36
+ };
37
+ } else if (isFieldList(newField) && newField.types) {
38
+ return {
39
+ ...newField,
40
+ types: traverseFieldsJS(newField.types, updater)
41
+ };
42
+ }
43
+ return newField;
44
+ });
45
+ }
46
+ function getConfigUrl() {
47
+ const validTypes = {
48
+ 'text/yaml': 'yaml',
49
+ 'application/x-yaml': 'yaml'
50
+ };
51
+ const configLinkEl = document.querySelector('link[rel="cms-config-url"]');
52
+ if (configLinkEl && validTypes[configLinkEl.type] && configLinkEl.href) {
53
+ console.log(`Using config file path: "${configLinkEl.href}"`);
54
+ return configLinkEl.href;
55
+ }
56
+ return 'config.yml';
57
+ }
58
+ function setDefaultPublicFolderForField(field) {
59
+ if ('media_folder' in field && !('public_folder' in field)) {
60
+ return {
61
+ ...field,
62
+ public_folder: field.media_folder
63
+ };
64
+ }
65
+ return field;
66
+ }
67
+
68
+ // Mapping between existing camelCase and its snake_case counterpart
69
+ const WIDGET_KEY_MAP = {
70
+ dateFormat: 'date_format',
71
+ timeFormat: 'time_format',
72
+ pickerUtc: 'picker_utc',
73
+ editorComponents: 'editor_components',
74
+ valueType: 'value_type',
75
+ valueField: 'value_field',
76
+ searchFields: 'search_fields',
77
+ displayFields: 'display_fields',
78
+ optionsLength: 'options_length'
79
+ };
80
+ function setSnakeCaseConfig(field) {
81
+ const deprecatedKeys = Object.keys(WIDGET_KEY_MAP).filter(camel => camel in field);
82
+ const snakeValues = deprecatedKeys.map(camel => {
83
+ const snake = WIDGET_KEY_MAP[camel];
84
+ console.warn(`Field ${field.name} is using a deprecated configuration '${camel}'. Please use '${snake}'`);
85
+ return {
86
+ [snake]: field[camel]
87
+ };
88
+ });
89
+ return Object.assign({}, field, ...snakeValues);
90
+ }
91
+ function setI18nField(field) {
92
+ if (field[I18N] === true) {
93
+ return {
94
+ ...field,
95
+ [I18N]: I18N_FIELD.TRANSLATE
96
+ };
97
+ } else if (field[I18N] === false || !field[I18N]) {
98
+ return {
99
+ ...field,
100
+ [I18N]: I18N_FIELD.NONE
101
+ };
102
+ }
103
+ return field;
104
+ }
105
+ function getI18nDefaults(collectionOrFileI18n, defaultI18n) {
106
+ if (typeof collectionOrFileI18n === 'boolean') {
107
+ return defaultI18n;
108
+ } else {
109
+ const locales = collectionOrFileI18n.locales || defaultI18n.locales;
110
+ const defaultLocale = collectionOrFileI18n.default_locale || locales[0];
111
+ const mergedI18n = deepmerge(defaultI18n, collectionOrFileI18n);
112
+ mergedI18n.locales = locales;
113
+ mergedI18n.default_locale = defaultLocale;
114
+ throwOnMissingDefaultLocale(mergedI18n);
115
+ return mergedI18n;
116
+ }
117
+ }
118
+ function setI18nDefaultsForFields(collectionOrFileFields, hasI18n) {
119
+ if (hasI18n) {
120
+ return traverseFieldsJS(collectionOrFileFields, setI18nField);
121
+ } else {
122
+ return traverseFieldsJS(collectionOrFileFields, field => {
123
+ const newField = {
124
+ ...field
125
+ };
126
+ delete newField[I18N];
127
+ return newField;
128
+ });
129
+ }
130
+ }
131
+ function throwOnInvalidFileCollectionStructure(i18n) {
132
+ if (i18n && i18n.structure !== I18N_STRUCTURE.SINGLE_FILE) {
133
+ throw new Error(`i18n configuration for files collections is limited to ${I18N_STRUCTURE.SINGLE_FILE} structure`);
134
+ }
135
+ }
136
+ function throwOnMissingDefaultLocale(i18n) {
137
+ if (i18n && i18n.default_locale && !i18n.locales.includes(i18n.default_locale)) {
138
+ throw new Error(`i18n locales '${i18n.locales.join(', ')}' are missing the default locale ${i18n.default_locale}`);
139
+ }
140
+ }
141
+ function hasIntegration(config, collection) {
142
+ // TODO remove fromJS when Immutable is removed from the integrations state slice
143
+ const integrations = getIntegrations(fromJS(config));
144
+ const integration = selectIntegration(integrations, collection.name, 'listEntries');
145
+ return !!integration;
146
+ }
147
+ function normalizeSortableFields(sortableFields) {
148
+ return sortableFields.map(field => {
149
+ if (typeof field === 'string') {
150
+ return {
151
+ field,
152
+ default_sort: undefined
153
+ };
154
+ }
155
+ return field;
156
+ });
157
+ }
158
+ export function normalizeConfig(config) {
159
+ const {
160
+ collections = []
161
+ } = config;
162
+ const normalizedCollections = collections.map(collection => {
163
+ const {
164
+ fields,
165
+ files
166
+ } = collection;
167
+ let normalizedCollection = collection;
168
+ if (fields) {
169
+ const normalizedFields = traverseFieldsJS(fields, setSnakeCaseConfig);
170
+ normalizedCollection = {
171
+ ...normalizedCollection,
172
+ fields: normalizedFields
173
+ };
174
+ }
175
+ if (files) {
176
+ const normalizedFiles = files.map(file => {
177
+ const normalizedFileFields = traverseFieldsJS(file.fields, setSnakeCaseConfig);
178
+ return {
179
+ ...file,
180
+ fields: normalizedFileFields
181
+ };
182
+ });
183
+ normalizedCollection = {
184
+ ...normalizedCollection,
185
+ files: normalizedFiles
186
+ };
187
+ }
188
+ if (normalizedCollection.sortableFields) {
189
+ const {
190
+ sortableFields,
191
+ ...rest
192
+ } = normalizedCollection;
193
+ normalizedCollection = {
194
+ ...rest,
195
+ sortable_fields: sortableFields
196
+ };
197
+ console.warn(`Collection ${collection.name} is using a deprecated configuration 'sortableFields'. Please use 'sortable_fields'`);
198
+ }
199
+
200
+ // Normalize sortable_fields to consistent object format
201
+ if (normalizedCollection.sortable_fields) {
202
+ normalizedCollection = {
203
+ ...normalizedCollection,
204
+ sortable_fields: normalizeSortableFields(normalizedCollection.sortable_fields)
205
+ };
206
+ }
207
+ return normalizedCollection;
208
+ });
209
+ return {
210
+ ...config,
211
+ collections: normalizedCollections
212
+ };
213
+ }
214
+ export function applyDefaults(originalConfig) {
215
+ return produce(originalConfig, config => {
216
+ config.publish_mode = config.publish_mode || SIMPLE_PUBLISH_MODE;
217
+ config.slug = config.slug || {};
218
+ config.collections = config.collections || [];
219
+
220
+ // Use `site_url` as default `display_url`.
221
+ if (!config.display_url && config.site_url) {
222
+ config.display_url = config.site_url;
223
+ }
224
+
225
+ // Use media_folder as default public_folder.
226
+ const defaultPublicFolder = `/${trimStart(config.media_folder, '/')}`;
227
+ if (!('public_folder' in config)) {
228
+ config.public_folder = defaultPublicFolder;
229
+ }
230
+
231
+ // default values for the slug config
232
+ if (!('encoding' in config.slug)) {
233
+ config.slug.encoding = 'unicode';
234
+ }
235
+ if (!('clean_accents' in config.slug)) {
236
+ config.slug.clean_accents = false;
237
+ }
238
+ if (!('sanitize_replacement' in config.slug)) {
239
+ config.slug.sanitize_replacement = '-';
240
+ }
241
+ const i18n = config[I18N];
242
+ if (i18n) {
243
+ i18n.default_locale = i18n.default_locale || i18n.locales[0];
244
+ }
245
+ throwOnMissingDefaultLocale(i18n);
246
+ const backend = resolveBackend(config);
247
+ for (const collection of config.collections) {
248
+ if (!('publish' in collection)) {
249
+ collection.publish = true;
250
+ }
251
+ let collectionI18n = collection[I18N];
252
+ if (i18n && collectionI18n) {
253
+ collectionI18n = getI18nDefaults(collectionI18n, i18n);
254
+ collection[I18N] = collectionI18n;
255
+ } else {
256
+ collectionI18n = undefined;
257
+ delete collection[I18N];
258
+ }
259
+ if (collection.fields) {
260
+ collection.fields = setI18nDefaultsForFields(collection.fields, Boolean(collectionI18n));
261
+ }
262
+ const {
263
+ folder,
264
+ files,
265
+ view_filters,
266
+ view_groups,
267
+ meta
268
+ } = collection;
269
+ if (folder) {
270
+ collection.type = FOLDER;
271
+ if (collection.path && !collection.media_folder) {
272
+ // default value for media folder when using the path config
273
+ collection.media_folder = '';
274
+ }
275
+ if ('media_folder' in collection && !('public_folder' in collection)) {
276
+ collection.public_folder = collection.media_folder;
277
+ }
278
+ if (collection.fields) {
279
+ collection.fields = traverseFieldsJS(collection.fields, setDefaultPublicFolderForField);
280
+ }
281
+ collection.folder = trim(folder, '/');
282
+ if (meta && meta.path) {
283
+ const metaPath = meta.path;
284
+ const metaField = {
285
+ name: 'path',
286
+ meta: true,
287
+ required: true,
288
+ ...metaPath,
289
+ // Ensure index_file is a string to satisfy CmsFieldMeta
290
+ index_file: metaPath.index_file ?? ''
291
+ };
292
+ collection.fields = [metaField, ...(collection.fields || [])];
293
+ }
294
+ }
295
+ if (files) {
296
+ collection.type = FILES;
297
+ throwOnInvalidFileCollectionStructure(collectionI18n);
298
+ delete collection.nested;
299
+ delete collection.meta;
300
+ for (const file of files) {
301
+ file.file = trimStart(file.file, '/');
302
+ if ('media_folder' in file && !('public_folder' in file)) {
303
+ file.public_folder = file.media_folder;
304
+ }
305
+ if (file.fields) {
306
+ file.fields = traverseFieldsJS(file.fields, setDefaultPublicFolderForField);
307
+ }
308
+ let fileI18n = file[I18N];
309
+ if (fileI18n && collectionI18n) {
310
+ fileI18n = getI18nDefaults(fileI18n, collectionI18n);
311
+ file[I18N] = fileI18n;
312
+ } else {
313
+ fileI18n = undefined;
314
+ delete file[I18N];
315
+ }
316
+ throwOnInvalidFileCollectionStructure(fileI18n);
317
+ if (file.fields) {
318
+ file.fields = setI18nDefaultsForFields(file.fields, Boolean(fileI18n));
319
+ }
320
+ }
321
+ }
322
+ if (!collection.sortable_fields) {
323
+ collection.sortable_fields = selectDefaultSortableFields(
324
+ // TODO remove fromJS when Immutable is removed from the collections state slice
325
+ fromJS(collection), backend, hasIntegration(config, collection));
326
+ }
327
+ collection.view_filters = (view_filters || []).map(filter => {
328
+ return {
329
+ ...filter,
330
+ id: `${filter.field}__${filter.pattern}`
331
+ };
332
+ });
333
+ collection.view_groups = (view_groups || []).map(group => {
334
+ return {
335
+ ...group,
336
+ id: `${group.field}__${group.pattern}`
337
+ };
338
+ });
339
+ if (config.editor && !collection.editor) {
340
+ collection.editor = {
341
+ preview: config.editor.preview
342
+ };
343
+ }
344
+ }
345
+ });
346
+ }
347
+ export function parseConfig(data) {
348
+ const config = yaml.parse(data, {
349
+ maxAliasCount: -1,
350
+ prettyErrors: true,
351
+ merge: true
352
+ });
353
+ if (typeof window !== 'undefined' && typeof window.CMS_ENV === 'string' && config[window.CMS_ENV]) {
354
+ const configKeys = Object.keys(config[window.CMS_ENV]);
355
+ for (const key of configKeys) {
356
+ config[key] = config[window.CMS_ENV][key];
357
+ }
358
+ }
359
+ return config;
360
+ }
361
+ async function getConfigYaml(file, hasManualConfig) {
362
+ const response = await fetch(file, {
363
+ credentials: 'same-origin'
364
+ }).catch(error => error);
365
+ if (response instanceof Error || response.status !== 200) {
366
+ if (hasManualConfig) {
367
+ return {};
368
+ }
369
+ const message = response instanceof Error ? response.message : response.status;
370
+ throw new Error(`Failed to load config.yml (${message})`);
371
+ }
372
+ const contentType = response.headers.get('Content-Type') || 'Not-Found';
373
+ const isYaml = contentType.indexOf('yaml') !== -1;
374
+ if (!isYaml) {
375
+ console.log(`Response for ${file} was not yaml. (Content-Type: ${contentType})`);
376
+ if (hasManualConfig) {
377
+ return {};
378
+ }
379
+ }
380
+ return parseConfig(await response.text());
381
+ }
382
+ export function configLoaded(config) {
383
+ return {
384
+ type: CONFIG_SUCCESS,
385
+ payload: config
386
+ };
387
+ }
388
+ export function configLoading() {
389
+ return {
390
+ type: CONFIG_REQUEST
391
+ };
392
+ }
393
+ export function configFailed(err) {
394
+ return {
395
+ type: CONFIG_FAILURE,
396
+ error: 'Error loading config',
397
+ payload: err
398
+ };
399
+ }
400
+ export async function detectProxyServer(localBackend) {
401
+ const allowedHosts = ['localhost', '127.0.0.1', ...(typeof localBackend === 'boolean' ? [] : localBackend?.allowed_hosts || [])];
402
+ if (!allowedHosts.includes(location.hostname) || !localBackend) {
403
+ return {};
404
+ }
405
+ const defaultUrl = 'http://localhost:8081/api/v1';
406
+ const proxyUrl = localBackend === true ? defaultUrl : localBackend.url || defaultUrl.replace('localhost', location.hostname);
407
+ try {
408
+ console.log(`Looking for Decap CMS Proxy Server at '${proxyUrl}'`);
409
+ const res = await fetch(`${proxyUrl}`, {
410
+ method: 'POST',
411
+ headers: {
412
+ 'Content-Type': 'application/json'
413
+ },
414
+ body: JSON.stringify({
415
+ action: 'info'
416
+ })
417
+ });
418
+ const {
419
+ repo,
420
+ publish_modes,
421
+ type
422
+ } = await res.json();
423
+ if (typeof repo === 'string' && Array.isArray(publish_modes) && typeof type === 'string') {
424
+ console.log(`Detected Decap CMS Proxy Server at '${proxyUrl}' with repo: '${repo}'`);
425
+ return {
426
+ proxyUrl,
427
+ publish_modes,
428
+ type
429
+ };
430
+ } else {
431
+ console.log(`Decap CMS Proxy Server not detected at '${proxyUrl}'`);
432
+ return {};
433
+ }
434
+ } catch {
435
+ console.log(`Decap CMS Proxy Server not detected at '${proxyUrl}'`);
436
+ return {};
437
+ }
438
+ }
439
+ function getPublishMode(config, publishModes, backendType) {
440
+ if (config.publish_mode && publishModes && !publishModes.includes(config.publish_mode)) {
441
+ const newPublishMode = publishModes[0];
442
+ console.log(`'${config.publish_mode}' is not supported by '${backendType}' backend, switching to '${newPublishMode}'`);
443
+ return newPublishMode;
444
+ }
445
+ return config.publish_mode;
446
+ }
447
+ export async function handleLocalBackend(originalConfig) {
448
+ if (!originalConfig.local_backend) {
449
+ return originalConfig;
450
+ }
451
+ const {
452
+ proxyUrl,
453
+ publish_modes: publishModes,
454
+ type: backendType
455
+ } = await detectProxyServer(originalConfig.local_backend);
456
+ if (!proxyUrl) {
457
+ return originalConfig;
458
+ }
459
+ return produce(originalConfig, config => {
460
+ config.backend.name = 'proxy';
461
+ config.backend.proxy_url = proxyUrl;
462
+ if (config.publish_mode) {
463
+ config.publish_mode = getPublishMode(config, publishModes, backendType);
464
+ }
465
+ });
466
+ }
467
+ export function loadConfig(manualConfig = {}, onLoad) {
468
+ if (window.CMS_CONFIG) {
469
+ return configLoaded(window.CMS_CONFIG);
470
+ }
471
+ return async dispatch => {
472
+ dispatch(configLoading());
473
+ try {
474
+ const configUrl = getConfigUrl();
475
+ const hasManualConfig = !isEmpty(manualConfig);
476
+ const configYaml = manualConfig.load_config_file === false ? {} : await getConfigYaml(configUrl, hasManualConfig);
477
+
478
+ // Merge manual config into the config.yml one
479
+ const mergedConfig = deepmerge(configYaml, manualConfig);
480
+ validateConfig(mergedConfig);
481
+ const withLocalBackend = await handleLocalBackend(mergedConfig);
482
+ const normalizedConfig = normalizeConfig(withLocalBackend);
483
+ const config = applyDefaults(normalizedConfig);
484
+ dispatch(configLoaded(config));
485
+ if (typeof onLoad === 'function') {
486
+ onLoad();
487
+ }
488
+ } catch (err) {
489
+ dispatch(configFailed(err));
490
+ throw err;
491
+ }
492
+ };
493
+ }
@@ -0,0 +1,79 @@
1
+ import { currentBackend } from '../backend';
2
+ import { selectDeployPreview } from '../reducers';
3
+ import { addNotification } from './notifications';
4
+ export const DEPLOY_PREVIEW_REQUEST = 'DEPLOY_PREVIEW_REQUEST';
5
+ export const DEPLOY_PREVIEW_SUCCESS = 'DEPLOY_PREVIEW_SUCCESS';
6
+ export const DEPLOY_PREVIEW_FAILURE = 'DEPLOY_PREVIEW_FAILURE';
7
+ function deployPreviewLoading(collection, slug) {
8
+ return {
9
+ type: DEPLOY_PREVIEW_REQUEST,
10
+ payload: {
11
+ collection,
12
+ slug
13
+ }
14
+ };
15
+ }
16
+ function deployPreviewLoaded(collection, slug, deploy) {
17
+ const {
18
+ url,
19
+ status
20
+ } = deploy;
21
+ return {
22
+ type: DEPLOY_PREVIEW_SUCCESS,
23
+ payload: {
24
+ collection,
25
+ slug,
26
+ url,
27
+ status
28
+ }
29
+ };
30
+ }
31
+ function deployPreviewError(collection, slug) {
32
+ return {
33
+ type: DEPLOY_PREVIEW_FAILURE,
34
+ payload: {
35
+ collection,
36
+ slug
37
+ }
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Requests a deploy preview object from the registered backend.
43
+ */
44
+ export function loadDeployPreview(collection, slug, entry, published, opts) {
45
+ return async (dispatch, getState) => {
46
+ const state = getState();
47
+ const backend = currentBackend(state.config);
48
+ const collectionName = collection.get('name');
49
+
50
+ // Exit if currently fetching
51
+ const deployState = selectDeployPreview(state, collectionName, slug);
52
+ if (deployState && deployState.isFetching) {
53
+ return;
54
+ }
55
+ dispatch(deployPreviewLoading(collectionName, slug));
56
+ try {
57
+ /**
58
+ * `getDeploy` is for published entries, while `getDeployPreview` is for
59
+ * unpublished entries.
60
+ */
61
+ const deploy = published ? backend.getDeploy(collection, slug, entry) : await backend.getDeployPreview(collection, slug, entry, opts);
62
+ if (deploy) {
63
+ return dispatch(deployPreviewLoaded(collectionName, slug, deploy));
64
+ }
65
+ return dispatch(deployPreviewError(collectionName, slug));
66
+ } catch (error) {
67
+ console.error(error);
68
+ dispatch(addNotification({
69
+ message: {
70
+ details: error.message,
71
+ key: 'ui.toast.onFailToLoadDeployPreview'
72
+ },
73
+ type: 'error',
74
+ dismissAfter: 8000
75
+ }));
76
+ dispatch(deployPreviewError(collectionName, slug));
77
+ }
78
+ };
79
+ }