@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,865 @@
1
+ import { fromJS, List, Map } from 'immutable';
2
+ import isEqual from 'lodash/isEqual';
3
+ import { Cursor } from 'decap-cms-lib-util';
4
+ import { selectCollectionEntriesCursor } from '../reducers/cursors';
5
+ import { selectFields, updateFieldByKey, selectDefaultSortField } from '../reducers/collections';
6
+ import { selectIntegration, selectPublishedSlugs } from '../reducers';
7
+ import { getIntegrationProvider } from '../integrations';
8
+ import { currentBackend } from '../backend';
9
+ import { serializeValues } from '../lib/serializeEntryValues';
10
+ import { createEntry } from '../valueObjects/Entry';
11
+ import { createAssetProxy } from '../valueObjects/AssetProxy';
12
+ import ValidationErrorTypes from '../constants/validationErrorTypes';
13
+ import { addAssets, getAsset } from './media';
14
+ import { SortDirection } from '../types/redux';
15
+ import { waitForMediaLibraryToLoad, loadMedia } from './mediaLibrary';
16
+ import { waitUntil } from './waitUntil';
17
+ import { selectIsFetching, selectEntriesSortFields, selectEntryByPath } from '../reducers/entries';
18
+ import { selectCustomPath } from '../reducers/entryDraft';
19
+ import { navigateToEntry } from '../routing/history';
20
+ import { getProcessSegment } from '../lib/formatters';
21
+ import { hasI18n, duplicateDefaultI18nFields, serializeI18n, I18N, I18N_FIELD } from '../lib/i18n';
22
+ import { addNotification } from './notifications';
23
+ /*
24
+ * Constant Declarations
25
+ */
26
+ export const ENTRY_REQUEST = 'ENTRY_REQUEST';
27
+ export const ENTRY_SUCCESS = 'ENTRY_SUCCESS';
28
+ export const ENTRY_FAILURE = 'ENTRY_FAILURE';
29
+ export const ENTRIES_REQUEST = 'ENTRIES_REQUEST';
30
+ export const ENTRIES_SUCCESS = 'ENTRIES_SUCCESS';
31
+ export const ENTRIES_FAILURE = 'ENTRIES_FAILURE';
32
+ export const SORT_ENTRIES_REQUEST = 'SORT_ENTRIES_REQUEST';
33
+ export const SORT_ENTRIES_SUCCESS = 'SORT_ENTRIES_SUCCESS';
34
+ export const SORT_ENTRIES_FAILURE = 'SORT_ENTRIES_FAILURE';
35
+ export const FILTER_ENTRIES_REQUEST = 'FILTER_ENTRIES_REQUEST';
36
+ export const FILTER_ENTRIES_SUCCESS = 'FILTER_ENTRIES_SUCCESS';
37
+ export const FILTER_ENTRIES_FAILURE = 'FILTER_ENTRIES_FAILURE';
38
+ export const GROUP_ENTRIES_REQUEST = 'GROUP_ENTRIES_REQUEST';
39
+ export const GROUP_ENTRIES_SUCCESS = 'GROUP_ENTRIES_SUCCESS';
40
+ export const GROUP_ENTRIES_FAILURE = 'GROUP_ENTRIES_FAILURE';
41
+ export const DRAFT_CREATE_FROM_ENTRY = 'DRAFT_CREATE_FROM_ENTRY';
42
+ export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY';
43
+ export const DRAFT_DISCARD = 'DRAFT_DISCARD';
44
+ export const DRAFT_CHANGE_FIELD = 'DRAFT_CHANGE_FIELD';
45
+ export const DRAFT_VALIDATION_ERRORS = 'DRAFT_VALIDATION_ERRORS';
46
+ export const DRAFT_CLEAR_ERRORS = 'DRAFT_CLEAR_ERRORS';
47
+ export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED';
48
+ export const DRAFT_CREATE_FROM_LOCAL_BACKUP = 'DRAFT_CREATE_FROM_LOCAL_BACKUP';
49
+ export const DRAFT_CREATE_DUPLICATE_FROM_ENTRY = 'DRAFT_CREATE_DUPLICATE_FROM_ENTRY';
50
+ export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST';
51
+ export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS';
52
+ export const ENTRY_PERSIST_FAILURE = 'ENTRY_PERSIST_FAILURE';
53
+ export const ENTRY_DELETE_REQUEST = 'ENTRY_DELETE_REQUEST';
54
+ export const ENTRY_DELETE_SUCCESS = 'ENTRY_DELETE_SUCCESS';
55
+ export const ENTRY_DELETE_FAILURE = 'ENTRY_DELETE_FAILURE';
56
+ export const ADD_DRAFT_ENTRY_MEDIA_FILE = 'ADD_DRAFT_ENTRY_MEDIA_FILE';
57
+ export const REMOVE_DRAFT_ENTRY_MEDIA_FILE = 'REMOVE_DRAFT_ENTRY_MEDIA_FILE';
58
+ export const CHANGE_VIEW_STYLE = 'CHANGE_VIEW_STYLE';
59
+
60
+ /*
61
+ * Simple Action Creators (Internal)
62
+ * We still need to export them for tests
63
+ */
64
+ export function entryLoading(collection, slug) {
65
+ return {
66
+ type: ENTRY_REQUEST,
67
+ payload: {
68
+ collection: collection.get('name'),
69
+ slug
70
+ }
71
+ };
72
+ }
73
+ export function entryLoaded(collection, entry) {
74
+ return {
75
+ type: ENTRY_SUCCESS,
76
+ payload: {
77
+ collection: collection.get('name'),
78
+ entry
79
+ }
80
+ };
81
+ }
82
+ export function entryLoadError(error, collection, slug) {
83
+ return {
84
+ type: ENTRY_FAILURE,
85
+ payload: {
86
+ error,
87
+ collection: collection.get('name'),
88
+ slug
89
+ }
90
+ };
91
+ }
92
+ export function entriesLoading(collection) {
93
+ return {
94
+ type: ENTRIES_REQUEST,
95
+ payload: {
96
+ collection: collection.get('name')
97
+ }
98
+ };
99
+ }
100
+ export function entriesLoaded(collection, entries, pagination, cursor, append = true) {
101
+ return {
102
+ type: ENTRIES_SUCCESS,
103
+ payload: {
104
+ collection: collection.get('name'),
105
+ entries,
106
+ page: pagination,
107
+ cursor: Cursor.create(cursor),
108
+ append
109
+ }
110
+ };
111
+ }
112
+ export function entriesFailed(collection, error) {
113
+ return {
114
+ type: ENTRIES_FAILURE,
115
+ error: 'Failed to load entries',
116
+ payload: error.toString(),
117
+ meta: {
118
+ collection: collection.get('name')
119
+ }
120
+ };
121
+ }
122
+ export async function getAllEntries(state, collection) {
123
+ const backend = currentBackend(state.config);
124
+ const integration = selectIntegration(state, collection.get('name'), 'listEntries');
125
+ const provider = integration ? getIntegrationProvider(state.integrations, backend.getToken, integration) : backend;
126
+ const entries = await provider.listAllEntries(collection);
127
+ return entries;
128
+ }
129
+ export function sortByField(collection, key, direction = SortDirection.Ascending) {
130
+ return async (dispatch, getState) => {
131
+ const state = getState();
132
+ // if we're already fetching we update the sort key, but skip loading entries
133
+ const isFetching = selectIsFetching(state.entries, collection.get('name'));
134
+ dispatch({
135
+ type: SORT_ENTRIES_REQUEST,
136
+ payload: {
137
+ collection: collection.get('name'),
138
+ key,
139
+ direction
140
+ }
141
+ });
142
+ if (isFetching) {
143
+ return;
144
+ }
145
+ try {
146
+ const entries = await getAllEntries(state, collection);
147
+ dispatch({
148
+ type: SORT_ENTRIES_SUCCESS,
149
+ payload: {
150
+ collection: collection.get('name'),
151
+ key,
152
+ direction,
153
+ entries
154
+ }
155
+ });
156
+ } catch (error) {
157
+ dispatch({
158
+ type: SORT_ENTRIES_FAILURE,
159
+ payload: {
160
+ collection: collection.get('name'),
161
+ key,
162
+ direction,
163
+ error
164
+ }
165
+ });
166
+ }
167
+ };
168
+ }
169
+ export function filterByField(collection, filter) {
170
+ return async (dispatch, getState) => {
171
+ const state = getState();
172
+ // if we're already fetching we update the filter key, but skip loading entries
173
+ const isFetching = selectIsFetching(state.entries, collection.get('name'));
174
+ dispatch({
175
+ type: FILTER_ENTRIES_REQUEST,
176
+ payload: {
177
+ collection: collection.get('name'),
178
+ filter
179
+ }
180
+ });
181
+ if (isFetching) {
182
+ return;
183
+ }
184
+ try {
185
+ const entries = await getAllEntries(state, collection);
186
+ dispatch({
187
+ type: FILTER_ENTRIES_SUCCESS,
188
+ payload: {
189
+ collection: collection.get('name'),
190
+ filter,
191
+ entries
192
+ }
193
+ });
194
+ } catch (error) {
195
+ dispatch({
196
+ type: FILTER_ENTRIES_FAILURE,
197
+ payload: {
198
+ collection: collection.get('name'),
199
+ filter,
200
+ error
201
+ }
202
+ });
203
+ }
204
+ };
205
+ }
206
+ export function groupByField(collection, group) {
207
+ return async (dispatch, getState) => {
208
+ const state = getState();
209
+ const isFetching = selectIsFetching(state.entries, collection.get('name'));
210
+ dispatch({
211
+ type: GROUP_ENTRIES_REQUEST,
212
+ payload: {
213
+ collection: collection.get('name'),
214
+ group
215
+ }
216
+ });
217
+ if (isFetching) {
218
+ return;
219
+ }
220
+ try {
221
+ const entries = await getAllEntries(state, collection);
222
+ dispatch({
223
+ type: GROUP_ENTRIES_SUCCESS,
224
+ payload: {
225
+ collection: collection.get('name'),
226
+ group,
227
+ entries
228
+ }
229
+ });
230
+ } catch (error) {
231
+ dispatch({
232
+ type: GROUP_ENTRIES_FAILURE,
233
+ payload: {
234
+ collection: collection.get('name'),
235
+ group,
236
+ error
237
+ }
238
+ });
239
+ }
240
+ };
241
+ }
242
+ export function changeViewStyle(viewStyle) {
243
+ return {
244
+ type: CHANGE_VIEW_STYLE,
245
+ payload: {
246
+ style: viewStyle
247
+ }
248
+ };
249
+ }
250
+ export function entryPersisting(collection, entry) {
251
+ return {
252
+ type: ENTRY_PERSIST_REQUEST,
253
+ payload: {
254
+ collectionName: collection.get('name'),
255
+ entrySlug: entry.get('slug')
256
+ }
257
+ };
258
+ }
259
+ export function entryPersisted(collection, entry, slug) {
260
+ return {
261
+ type: ENTRY_PERSIST_SUCCESS,
262
+ payload: {
263
+ collectionName: collection.get('name'),
264
+ entrySlug: entry.get('slug'),
265
+ /**
266
+ * Pass slug from backend for newly created entries.
267
+ */
268
+ slug
269
+ }
270
+ };
271
+ }
272
+ export function entryPersistFail(collection, entry, error) {
273
+ return {
274
+ type: ENTRY_PERSIST_FAILURE,
275
+ error: 'Failed to persist entry',
276
+ payload: {
277
+ collectionName: collection.get('name'),
278
+ entrySlug: entry.get('slug'),
279
+ error: error.toString()
280
+ }
281
+ };
282
+ }
283
+ export function entryDeleting(collection, slug) {
284
+ return {
285
+ type: ENTRY_DELETE_REQUEST,
286
+ payload: {
287
+ collectionName: collection.get('name'),
288
+ entrySlug: slug
289
+ }
290
+ };
291
+ }
292
+ export function entryDeleted(collection, slug) {
293
+ return {
294
+ type: ENTRY_DELETE_SUCCESS,
295
+ payload: {
296
+ collectionName: collection.get('name'),
297
+ entrySlug: slug
298
+ }
299
+ };
300
+ }
301
+ export function entryDeleteFail(collection, slug, error) {
302
+ return {
303
+ type: ENTRY_DELETE_FAILURE,
304
+ payload: {
305
+ collectionName: collection.get('name'),
306
+ entrySlug: slug,
307
+ error: error.toString()
308
+ }
309
+ };
310
+ }
311
+ export function emptyDraftCreated(entry) {
312
+ return {
313
+ type: DRAFT_CREATE_EMPTY,
314
+ payload: entry
315
+ };
316
+ }
317
+ /*
318
+ * Exported simple Action Creators
319
+ */
320
+ export function createDraftFromEntry(entry) {
321
+ return {
322
+ type: DRAFT_CREATE_FROM_ENTRY,
323
+ payload: {
324
+ entry
325
+ }
326
+ };
327
+ }
328
+ export function draftDuplicateEntry(entry) {
329
+ return {
330
+ type: DRAFT_CREATE_DUPLICATE_FROM_ENTRY,
331
+ payload: createEntry(entry.get('collection'), '', '', {
332
+ data: entry.get('data'),
333
+ i18n: entry.get('i18n'),
334
+ mediaFiles: entry.get('mediaFiles').toJS()
335
+ })
336
+ };
337
+ }
338
+ export function discardDraft() {
339
+ return {
340
+ type: DRAFT_DISCARD
341
+ };
342
+ }
343
+ export function changeDraftField({
344
+ field,
345
+ value,
346
+ metadata,
347
+ entries,
348
+ i18n
349
+ }) {
350
+ return {
351
+ type: DRAFT_CHANGE_FIELD,
352
+ payload: {
353
+ field,
354
+ value,
355
+ metadata,
356
+ entries,
357
+ i18n
358
+ }
359
+ };
360
+ }
361
+ export function changeDraftFieldValidation(uniquefieldId, errors) {
362
+ return {
363
+ type: DRAFT_VALIDATION_ERRORS,
364
+ payload: {
365
+ uniquefieldId,
366
+ errors
367
+ }
368
+ };
369
+ }
370
+ export function clearFieldErrors(uniqueFieldId) {
371
+ return {
372
+ type: DRAFT_CLEAR_ERRORS,
373
+ payload: {
374
+ uniqueFieldId
375
+ }
376
+ };
377
+ }
378
+ export function localBackupRetrieved(entry) {
379
+ return {
380
+ type: DRAFT_LOCAL_BACKUP_RETRIEVED,
381
+ payload: {
382
+ entry
383
+ }
384
+ };
385
+ }
386
+ export function loadLocalBackup() {
387
+ return {
388
+ type: DRAFT_CREATE_FROM_LOCAL_BACKUP
389
+ };
390
+ }
391
+ export function addDraftEntryMediaFile(file) {
392
+ return {
393
+ type: ADD_DRAFT_ENTRY_MEDIA_FILE,
394
+ payload: file
395
+ };
396
+ }
397
+ export function removeDraftEntryMediaFile({
398
+ id
399
+ }) {
400
+ return {
401
+ type: REMOVE_DRAFT_ENTRY_MEDIA_FILE,
402
+ payload: {
403
+ id
404
+ }
405
+ };
406
+ }
407
+ export function persistLocalBackup(entry, collection) {
408
+ return (_dispatch, getState) => {
409
+ const state = getState();
410
+ const backend = currentBackend(state.config);
411
+ return backend.persistLocalDraftBackup(entry, collection);
412
+ };
413
+ }
414
+ export function createDraftDuplicateFromEntry(entry) {
415
+ return dispatch => {
416
+ dispatch(waitUntil({
417
+ predicate: ({
418
+ type
419
+ }) => type === DRAFT_CREATE_EMPTY,
420
+ run: () => dispatch(draftDuplicateEntry(entry))
421
+ }));
422
+ };
423
+ }
424
+ export function retrieveLocalBackup(collection, slug) {
425
+ return async (dispatch, getState) => {
426
+ const state = getState();
427
+ const backend = currentBackend(state.config);
428
+ const {
429
+ entry
430
+ } = await backend.getLocalDraftBackup(collection, slug);
431
+ if (entry) {
432
+ // load assets from backup
433
+ const mediaFiles = entry.mediaFiles || [];
434
+ const assetProxies = await Promise.all(mediaFiles.map(file => {
435
+ if (file.file || file.url) {
436
+ return createAssetProxy({
437
+ path: file.path,
438
+ file: file.file,
439
+ url: file.url,
440
+ field: file.field
441
+ });
442
+ } else {
443
+ return getAsset({
444
+ collection,
445
+ entry: fromJS(entry),
446
+ path: file.path,
447
+ field: file.field
448
+ })(dispatch, getState);
449
+ }
450
+ }));
451
+ dispatch(addAssets(assetProxies));
452
+ return dispatch(localBackupRetrieved(entry));
453
+ }
454
+ };
455
+ }
456
+ export function deleteLocalBackup(collection, slug) {
457
+ return (_dispatch, getState) => {
458
+ const state = getState();
459
+ const backend = currentBackend(state.config);
460
+ return backend.deleteLocalDraftBackup(collection, slug);
461
+ };
462
+ }
463
+
464
+ /*
465
+ * Exported Thunk Action Creators
466
+ */
467
+
468
+ export function loadEntry(collection, slug) {
469
+ return async (dispatch, getState) => {
470
+ await waitForMediaLibraryToLoad(dispatch, getState());
471
+ dispatch(entryLoading(collection, slug));
472
+ try {
473
+ const loadedEntry = await tryLoadEntry(getState(), collection, slug);
474
+ dispatch(entryLoaded(collection, loadedEntry));
475
+ dispatch(createDraftFromEntry(loadedEntry));
476
+ } catch (error) {
477
+ dispatch(addNotification({
478
+ message: {
479
+ details: error.message,
480
+ key: 'ui.toast.onFailToLoadEntries'
481
+ },
482
+ type: 'error',
483
+ dismissAfter: 8000
484
+ }));
485
+ dispatch(entryLoadError(error, collection, slug));
486
+ }
487
+ };
488
+ }
489
+ export async function tryLoadEntry(state, collection, slug) {
490
+ const backend = currentBackend(state.config);
491
+ const loadedEntry = await backend.getEntry(state, collection, slug);
492
+ return loadedEntry;
493
+ }
494
+ const appendActions = fromJS({
495
+ ['append_next']: {
496
+ action: 'next',
497
+ append: true
498
+ }
499
+ });
500
+ function addAppendActionsToCursor(cursor) {
501
+ return Cursor.create(cursor).updateStore('actions', actions => {
502
+ return actions.union(appendActions.filter(v => actions.has(v.get('action'))).keySeq());
503
+ });
504
+ }
505
+ export function loadEntries(collection, page = 0) {
506
+ return async (dispatch, getState) => {
507
+ if (collection.get('isFetching')) {
508
+ return;
509
+ }
510
+ const state = getState();
511
+ const sortFields = selectEntriesSortFields(state.entries, collection.get('name'));
512
+
513
+ // If user has already set a sort, use it
514
+ if (sortFields && sortFields.length > 0) {
515
+ const field = sortFields[0];
516
+ return dispatch(sortByField(collection, field.get('key'), field.get('direction')));
517
+ }
518
+
519
+ // Otherwise, check for a default sort field in the collection configuration
520
+ const defaultSort = selectDefaultSortField(collection);
521
+ if (defaultSort) {
522
+ const direction = defaultSort.direction === 'desc' ? SortDirection.Descending : SortDirection.Ascending;
523
+ return dispatch(sortByField(collection, defaultSort.field, direction));
524
+ }
525
+ const backend = currentBackend(state.config);
526
+ const integration = selectIntegration(state, collection.get('name'), 'listEntries');
527
+ const provider = integration ? getIntegrationProvider(state.integrations, backend.getToken, integration) : backend;
528
+ const append = !!(page && !isNaN(page) && page > 0);
529
+ dispatch(entriesLoading(collection));
530
+ try {
531
+ const loadAllEntries = collection.has('nested') || hasI18n(collection);
532
+ let response = await (loadAllEntries ?
533
+ // nested collections require all entries to construct the tree
534
+ provider.listAllEntries(collection).then(entries => ({
535
+ entries
536
+ })) : provider.listEntries(collection, page));
537
+ response = {
538
+ ...response,
539
+ // The only existing backend using the pagination system is the
540
+ // Algolia integration, which is also the only integration used
541
+ // to list entries. Thus, this checking for an integration can
542
+ // determine whether or not this is using the old integer-based
543
+ // pagination API. Other backends will simply store an empty
544
+ // cursor, which behaves identically to no cursor at all.
545
+ cursor: integration ? Cursor.create({
546
+ actions: ['next'],
547
+ meta: {
548
+ usingOldPaginationAPI: true
549
+ },
550
+ data: {
551
+ nextPage: page + 1
552
+ }
553
+ }) : Cursor.create(response.cursor)
554
+ };
555
+ dispatch(entriesLoaded(collection, response.cursor.meta.get('usingOldPaginationAPI') ? response.entries.reverse() : response.entries, response.pagination, addAppendActionsToCursor(response.cursor), append));
556
+ } catch (err) {
557
+ dispatch(addNotification({
558
+ message: {
559
+ details: err,
560
+ key: 'ui.toast.onFailToLoadEntries'
561
+ },
562
+ type: 'error',
563
+ dismissAfter: 8000
564
+ }));
565
+ return Promise.reject(dispatch(entriesFailed(collection, err)));
566
+ }
567
+ };
568
+ }
569
+ function traverseCursor(backend, cursor, action) {
570
+ if (!cursor.actions.has(action)) {
571
+ throw new Error(`The current cursor does not support the pagination action "${action}".`);
572
+ }
573
+ return backend.traverseCursor(cursor, action);
574
+ }
575
+ export function traverseCollectionCursor(collection, action) {
576
+ return async (dispatch, getState) => {
577
+ const state = getState();
578
+ const collectionName = collection.get('name');
579
+ if (state.entries.getIn(['pages', `${collectionName}`, 'isFetching'])) {
580
+ return;
581
+ }
582
+ const backend = currentBackend(state.config);
583
+ const {
584
+ action: realAction,
585
+ append
586
+ } = appendActions.has(action) ? appendActions.get(action).toJS() : {
587
+ action,
588
+ append: false
589
+ };
590
+ const cursor = selectCollectionEntriesCursor(state.cursors, collection.get('name'));
591
+
592
+ // Handle cursors representing pages in the old, integer-based
593
+ // pagination API
594
+ if (cursor.meta.get('usingOldPaginationAPI', false)) {
595
+ return dispatch(loadEntries(collection, cursor.data.get('nextPage')));
596
+ }
597
+ try {
598
+ dispatch(entriesLoading(collection));
599
+ const {
600
+ entries,
601
+ cursor: newCursor
602
+ } = await traverseCursor(backend, cursor, realAction);
603
+ const pagination = newCursor.meta?.get('page');
604
+ return dispatch(entriesLoaded(collection, entries, pagination, addAppendActionsToCursor(newCursor), append));
605
+ } catch (err) {
606
+ console.error(err);
607
+ dispatch(addNotification({
608
+ message: {
609
+ details: err,
610
+ key: 'ui.toast.onFailToLoadEntries'
611
+ },
612
+ type: 'error',
613
+ dismissAfter: 8000
614
+ }));
615
+ return Promise.reject(dispatch(entriesFailed(collection, err)));
616
+ }
617
+ };
618
+ }
619
+ function escapeHtml(unsafe) {
620
+ return unsafe.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
621
+ }
622
+ function processValue(unsafe) {
623
+ if (['true', 'True', 'TRUE'].includes(unsafe)) {
624
+ return true;
625
+ }
626
+ if (['false', 'False', 'FALSE'].includes(unsafe)) {
627
+ return false;
628
+ }
629
+ return escapeHtml(unsafe);
630
+ }
631
+ function getDataFields(fields) {
632
+ return fields.filter(f => !f.get('meta')).toList();
633
+ }
634
+ function getMetaFields(fields) {
635
+ return fields.filter(f => f.get('meta') === true).toList();
636
+ }
637
+ export function createEmptyDraft(collection, search) {
638
+ return async (dispatch, getState) => {
639
+ const params = new URLSearchParams(search);
640
+ params.forEach((value, key) => {
641
+ collection = updateFieldByKey(collection, key, field => field.set('default', processValue(value)));
642
+ });
643
+ const fields = collection.get('fields', List());
644
+ const dataFields = getDataFields(fields);
645
+ const data = createEmptyDraftData(dataFields);
646
+ const metaFields = getMetaFields(fields);
647
+ const meta = createEmptyDraftData(metaFields);
648
+ const state = getState();
649
+ const backend = currentBackend(state.config);
650
+ if (!collection.has('media_folder')) {
651
+ await waitForMediaLibraryToLoad(dispatch, getState());
652
+ }
653
+ const i18nFields = createEmptyDraftI18nData(collection, dataFields);
654
+ let newEntry = createEntry(collection.get('name'), '', '', {
655
+ data,
656
+ i18n: i18nFields,
657
+ mediaFiles: [],
658
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
659
+ meta: meta
660
+ });
661
+ newEntry = await backend.processEntry(state, collection, newEntry);
662
+ dispatch(emptyDraftCreated(newEntry));
663
+ };
664
+ }
665
+ export function createEmptyDraftData(fields, skipField = () => false) {
666
+ return fields.reduce((reduction, value) => {
667
+ const acc = reduction;
668
+ const item = value;
669
+ if (skipField(item)) {
670
+ return acc;
671
+ }
672
+ const subfields = item.get('field') || item.get('fields');
673
+ const list = item.get('widget') == 'list';
674
+ const name = item.get('name');
675
+ const defaultValue = item.get('default', null);
676
+ function isEmptyDefaultValue(val) {
677
+ return [[{}], {}].some(e => isEqual(val, e));
678
+ }
679
+ const hasSubfields = List.isList(subfields) || Map.isMap(subfields);
680
+ if (hasSubfields) {
681
+ if (list && List.isList(defaultValue)) {
682
+ acc[name] = defaultValue;
683
+ } else {
684
+ const asList = List.isList(subfields) ? subfields : List([subfields]);
685
+ const subDefaultValue = list ? [createEmptyDraftData(asList, skipField)] : createEmptyDraftData(asList, skipField);
686
+ if (!isEmptyDefaultValue(subDefaultValue)) {
687
+ acc[name] = subDefaultValue;
688
+ }
689
+ }
690
+ return acc;
691
+ }
692
+ if (defaultValue !== null) {
693
+ acc[name] = defaultValue;
694
+ }
695
+ return acc;
696
+ }, {});
697
+ }
698
+ function createEmptyDraftI18nData(collection, dataFields) {
699
+ if (!hasI18n(collection)) {
700
+ return {};
701
+ }
702
+ function skipField(field) {
703
+ return field.get(I18N) !== I18N_FIELD.DUPLICATE && field.get(I18N) !== I18N_FIELD.TRANSLATE;
704
+ }
705
+ const i18nData = createEmptyDraftData(dataFields, skipField);
706
+ return duplicateDefaultI18nFields(collection, i18nData);
707
+ }
708
+ export function getMediaAssets({
709
+ entry
710
+ }) {
711
+ const filesArray = entry.get('mediaFiles').toArray();
712
+ const assets = filesArray.filter(file => file.get('draft')).map(file => createAssetProxy({
713
+ path: file.get('path'),
714
+ file: file.get('file'),
715
+ url: file.get('url'),
716
+ field: file.get('field')
717
+ }));
718
+ return assets;
719
+ }
720
+ export function getSerializedEntry(collection, entry) {
721
+ /**
722
+ * Serialize the values of any fields with registered serializers, and
723
+ * update the entry and entryDraft with the serialized values.
724
+ */
725
+ const fields = selectFields(collection, entry.get('slug'));
726
+
727
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
728
+ function serializeData(data) {
729
+ return serializeValues(data, fields);
730
+ }
731
+ const serializedData = serializeData(entry.get('data'));
732
+ let serializedEntry = entry.set('data', serializedData);
733
+ if (hasI18n(collection)) {
734
+ serializedEntry = serializeI18n(collection, serializedEntry, serializeData);
735
+ }
736
+ return serializedEntry;
737
+ }
738
+ export function persistEntry(collection) {
739
+ return async (dispatch, getState) => {
740
+ const state = getState();
741
+ const entryDraft = state.entryDraft;
742
+ const fieldsErrors = entryDraft.get('fieldsErrors');
743
+ const usedSlugs = selectPublishedSlugs(state, collection.get('name'));
744
+
745
+ // Early return if draft contains validation errors
746
+ if (!fieldsErrors.isEmpty()) {
747
+ const hasPresenceErrors = fieldsErrors.some(errors => errors.some(error => error.type && error.type === ValidationErrorTypes.PRESENCE));
748
+ if (hasPresenceErrors) {
749
+ dispatch(addNotification({
750
+ message: {
751
+ key: 'ui.toast.missingRequiredField'
752
+ },
753
+ type: 'error',
754
+ dismissAfter: 8000
755
+ }));
756
+ }
757
+ return Promise.reject();
758
+ }
759
+ const backend = currentBackend(state.config);
760
+ const entry = entryDraft.get('entry');
761
+ const assetProxies = getMediaAssets({
762
+ entry
763
+ });
764
+ const serializedEntry = getSerializedEntry(collection, entry);
765
+ const serializedEntryDraft = entryDraft.set('entry', serializedEntry);
766
+ dispatch(entryPersisting(collection, serializedEntry));
767
+ return backend.persistEntry({
768
+ config: state.config,
769
+ collection,
770
+ entryDraft: serializedEntryDraft,
771
+ assetProxies,
772
+ usedSlugs
773
+ }).then(async newSlug => {
774
+ dispatch(addNotification({
775
+ message: {
776
+ key: 'ui.toast.entrySaved'
777
+ },
778
+ type: 'success',
779
+ dismissAfter: 4000
780
+ }));
781
+
782
+ // re-load media library if entry had media files
783
+ if (assetProxies.length > 0) {
784
+ await dispatch(loadMedia());
785
+ }
786
+ dispatch(entryPersisted(collection, serializedEntry, newSlug));
787
+ if (collection.has('nested')) {
788
+ await dispatch(loadEntries(collection));
789
+ }
790
+ if (entry.get('slug') !== newSlug) {
791
+ await dispatch(loadEntry(collection, newSlug));
792
+ navigateToEntry(collection.get('name'), newSlug);
793
+ }
794
+ }).catch(error => {
795
+ console.error(error);
796
+ dispatch(addNotification({
797
+ message: {
798
+ details: error,
799
+ key: 'ui.toast.onFailToPersist'
800
+ },
801
+ type: 'error',
802
+ dismissAfter: 8000
803
+ }));
804
+ return Promise.reject(dispatch(entryPersistFail(collection, serializedEntry, error)));
805
+ });
806
+ };
807
+ }
808
+ export function deleteEntry(collection, slug) {
809
+ return (dispatch, getState) => {
810
+ const state = getState();
811
+ const backend = currentBackend(state.config);
812
+ dispatch(entryDeleting(collection, slug));
813
+ return backend.deleteEntry(state, collection, slug).then(() => {
814
+ return dispatch(entryDeleted(collection, slug));
815
+ }).catch(error => {
816
+ dispatch(addNotification({
817
+ message: {
818
+ details: error,
819
+ key: 'ui.toast.onFailToDelete'
820
+ },
821
+ type: 'error',
822
+ dismissAfter: 8000
823
+ }));
824
+ console.error(error);
825
+ return Promise.reject(dispatch(entryDeleteFail(collection, slug, error)));
826
+ });
827
+ };
828
+ }
829
+ function getPathError(path, key, t) {
830
+ return {
831
+ error: {
832
+ type: ValidationErrorTypes.CUSTOM,
833
+ message: t(`editor.editorControlPane.widget.${key}`, {
834
+ path
835
+ })
836
+ }
837
+ };
838
+ }
839
+ export function validateMetaField(state, collection, field, value, t) {
840
+ if (field.get('meta') && field.get('name') === 'path') {
841
+ if (!value) {
842
+ return getPathError(value, 'invalidPath', t);
843
+ }
844
+ const sanitizedPath = value.split('/').map(getProcessSegment(state.config.slug, undefined, true)).join('/');
845
+ if (value !== sanitizedPath) {
846
+ return getPathError(value, 'invalidPath', t);
847
+ }
848
+ const customPath = selectCustomPath(collection, fromJS({
849
+ entry: {
850
+ meta: {
851
+ path: value
852
+ }
853
+ }
854
+ }));
855
+ const existingEntry = customPath ? selectEntryByPath(state.entries, collection.get('name'), customPath) : undefined;
856
+ const existingEntryPath = existingEntry?.get('path');
857
+ const draftPath = state.entryDraft?.getIn(['entry', 'path']);
858
+ if (existingEntryPath && existingEntryPath !== draftPath) {
859
+ return getPathError(value, 'pathExists', t);
860
+ }
861
+ }
862
+ return {
863
+ error: false
864
+ };
865
+ }