@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.
- package/README.md +9 -0
- package/dist/@pranaysahith/decap-cms-core.js +52 -0
- package/dist/@pranaysahith/decap-cms-core.js.LICENSE.txt +141 -0
- package/dist/@pranaysahith/decap-cms-core.js.map +1 -0
- package/dist/decap-cms-core.js +47 -0
- package/dist/decap-cms-core.js.LICENSE.txt +116 -0
- package/dist/decap-cms-core.js.map +1 -0
- package/dist/esm/actions/auth.js +97 -0
- package/dist/esm/actions/collections.js +15 -0
- package/dist/esm/actions/config.js +493 -0
- package/dist/esm/actions/deploys.js +79 -0
- package/dist/esm/actions/editorialWorkflow.js +480 -0
- package/dist/esm/actions/entries.js +865 -0
- package/dist/esm/actions/media.js +147 -0
- package/dist/esm/actions/mediaLibrary.js +552 -0
- package/dist/esm/actions/notifications.js +21 -0
- package/dist/esm/actions/search.js +149 -0
- package/dist/esm/actions/status.js +74 -0
- package/dist/esm/actions/waitUntil.js +32 -0
- package/dist/esm/backend.js +1082 -0
- package/dist/esm/bootstrap.js +101 -0
- package/dist/esm/components/App/App.js +289 -0
- package/dist/esm/components/App/Header.js +172 -0
- package/dist/esm/components/App/NotFoundPage.js +19 -0
- package/dist/esm/components/Collection/Collection.js +198 -0
- package/dist/esm/components/Collection/CollectionControls.js +46 -0
- package/dist/esm/components/Collection/CollectionSearch.js +222 -0
- package/dist/esm/components/Collection/CollectionTop.js +68 -0
- package/dist/esm/components/Collection/ControlButton.js +17 -0
- package/dist/esm/components/Collection/Entries/Entries.js +73 -0
- package/dist/esm/components/Collection/Entries/EntriesCollection.js +241 -0
- package/dist/esm/components/Collection/Entries/EntriesSearch.js +113 -0
- package/dist/esm/components/Collection/Entries/EntryCard.js +177 -0
- package/dist/esm/components/Collection/Entries/EntryListing.js +143 -0
- package/dist/esm/components/Collection/FilterControl.js +33 -0
- package/dist/esm/components/Collection/FolderRenameControl.js +403 -0
- package/dist/esm/components/Collection/GroupControl.js +33 -0
- package/dist/esm/components/Collection/NestedCollection.js +308 -0
- package/dist/esm/components/Collection/Sidebar.js +91 -0
- package/dist/esm/components/Collection/SortControl.js +59 -0
- package/dist/esm/components/Collection/ViewStyleControl.js +38 -0
- package/dist/esm/components/Editor/Editor.js +466 -0
- package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +395 -0
- package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +254 -0
- package/dist/esm/components/Editor/EditorControlPane/Widget.js +374 -0
- package/dist/esm/components/Editor/EditorInterface.js +386 -0
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +47 -0
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +66 -0
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +288 -0
- package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +27 -0
- package/dist/esm/components/Editor/EditorToolbar.js +536 -0
- package/dist/esm/components/Editor/EntryPathEditor.js +272 -0
- package/dist/esm/components/Editor/withWorkflow.js +56 -0
- package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +18 -0
- package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +20 -0
- package/dist/esm/components/EditorWidgets/index.js +4 -0
- package/dist/esm/components/MediaLibrary/EmptyMessage.js +22 -0
- package/dist/esm/components/MediaLibrary/MediaLibrary.js +446 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +93 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +99 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +198 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +32 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +156 -0
- package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +51 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +123 -0
- package/dist/esm/components/UI/DragDrop.js +67 -0
- package/dist/esm/components/UI/ErrorBoundary.js +173 -0
- package/dist/esm/components/UI/FileUploadButton.js +27 -0
- package/dist/esm/components/UI/Modal.js +104 -0
- package/dist/esm/components/UI/Notifications.js +62 -0
- package/dist/esm/components/UI/SettingsDropdown.js +107 -0
- package/dist/esm/components/UI/index.js +6 -0
- package/dist/esm/components/Workflow/Workflow.js +133 -0
- package/dist/esm/components/Workflow/WorkflowCard.js +128 -0
- package/dist/esm/components/Workflow/WorkflowList.js +204 -0
- package/dist/esm/constants/collectionTypes.js +2 -0
- package/dist/esm/constants/collectionViews.js +2 -0
- package/dist/esm/constants/commitProps.js +2 -0
- package/dist/esm/constants/configSchema.js +644 -0
- package/dist/esm/constants/fieldInference.js +57 -0
- package/dist/esm/constants/publishModes.js +18 -0
- package/dist/esm/constants/validationErrorTypes.js +6 -0
- package/dist/esm/formats/formats.js +83 -0
- package/dist/esm/formats/frontmatter.js +146 -0
- package/dist/esm/formats/helpers.js +12 -0
- package/dist/esm/formats/json.js +8 -0
- package/dist/esm/formats/toml.js +32 -0
- package/dist/esm/formats/yaml.js +51 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/integrations/index.js +28 -0
- package/dist/esm/integrations/providers/algolia/implementation.js +174 -0
- package/dist/esm/integrations/providers/assetStore/implementation.js +165 -0
- package/dist/esm/lib/consoleError.js +3 -0
- package/dist/esm/lib/formatters.js +191 -0
- package/dist/esm/lib/i18n.js +367 -0
- package/dist/esm/lib/phrases.js +6 -0
- package/dist/esm/lib/polyfill.js +8 -0
- package/dist/esm/lib/registry.js +329 -0
- package/dist/esm/lib/serializeEntryValues.js +67 -0
- package/dist/esm/lib/stega.js +142 -0
- package/dist/esm/lib/textHelper.js +9 -0
- package/dist/esm/lib/urlHelper.js +111 -0
- package/dist/esm/mediaLibrary.js +37 -0
- package/dist/esm/reducers/auth.js +27 -0
- package/dist/esm/reducers/collections.js +428 -0
- package/dist/esm/reducers/combinedReducer.js +8 -0
- package/dist/esm/reducers/config.js +29 -0
- package/dist/esm/reducers/cursors.js +31 -0
- package/dist/esm/reducers/deploys.js +45 -0
- package/dist/esm/reducers/editorialWorkflow.js +83 -0
- package/dist/esm/reducers/entries.js +568 -0
- package/dist/esm/reducers/entryDraft.js +212 -0
- package/dist/esm/reducers/globalUI.js +25 -0
- package/dist/esm/reducers/index.js +66 -0
- package/dist/esm/reducers/integrations.js +53 -0
- package/dist/esm/reducers/mediaLibrary.js +252 -0
- package/dist/esm/reducers/medias.js +68 -0
- package/dist/esm/reducers/notifications.js +23 -0
- package/dist/esm/reducers/search.js +92 -0
- package/dist/esm/reducers/status.js +30 -0
- package/dist/esm/redux/index.js +7 -0
- package/dist/esm/redux/middleware/waitUntilAction.js +48 -0
- package/dist/esm/routing/history.js +12 -0
- package/dist/esm/types/diacritics.d.js +0 -0
- package/dist/esm/types/global.d.js +1 -0
- package/dist/esm/types/immutable.js +7 -0
- package/dist/esm/types/redux.js +14 -0
- package/dist/esm/types/tomlify-j0.4.d.js +0 -0
- package/dist/esm/valueObjects/AssetProxy.js +44 -0
- package/dist/esm/valueObjects/EditorComponent.js +34 -0
- package/dist/esm/valueObjects/Entry.js +20 -0
- package/index.d.ts +618 -0
- package/package.json +106 -0
- package/src/__tests__/backend.spec.js +1161 -0
- package/src/actions/__tests__/config.spec.js +1009 -0
- package/src/actions/__tests__/editorialWorkflow.spec.js +216 -0
- package/src/actions/__tests__/entries.spec.js +596 -0
- package/src/actions/__tests__/media.spec.ts +171 -0
- package/src/actions/__tests__/mediaLibrary.spec.js +327 -0
- package/src/actions/__tests__/search.spec.js +209 -0
- package/src/actions/auth.ts +127 -0
- package/src/actions/collections.ts +18 -0
- package/src/actions/config.ts +565 -0
- package/src/actions/deploys.ts +104 -0
- package/src/actions/editorialWorkflow.ts +567 -0
- package/src/actions/entries.ts +1055 -0
- package/src/actions/media.ts +139 -0
- package/src/actions/mediaLibrary.ts +574 -0
- package/src/actions/notifications.ts +36 -0
- package/src/actions/search.ts +221 -0
- package/src/actions/status.ts +99 -0
- package/src/actions/waitUntil.ts +49 -0
- package/src/backend.ts +1400 -0
- package/src/bootstrap.js +104 -0
- package/src/components/App/App.js +286 -0
- package/src/components/App/Header.js +266 -0
- package/src/components/App/NotFoundPage.js +23 -0
- package/src/components/Collection/Collection.js +210 -0
- package/src/components/Collection/CollectionControls.js +58 -0
- package/src/components/Collection/CollectionSearch.js +243 -0
- package/src/components/Collection/CollectionTop.js +81 -0
- package/src/components/Collection/ControlButton.js +27 -0
- package/src/components/Collection/Entries/Entries.js +82 -0
- package/src/components/Collection/Entries/EntriesCollection.js +277 -0
- package/src/components/Collection/Entries/EntriesSearch.js +102 -0
- package/src/components/Collection/Entries/EntryCard.js +246 -0
- package/src/components/Collection/Entries/EntryListing.js +151 -0
- package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +163 -0
- package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +46 -0
- package/src/components/Collection/FilterControl.js +39 -0
- package/src/components/Collection/GroupControl.js +39 -0
- package/src/components/Collection/NestedCollection.js +330 -0
- package/src/components/Collection/Sidebar.js +136 -0
- package/src/components/Collection/SortControl.js +68 -0
- package/src/components/Collection/ViewStyleControl.js +50 -0
- package/src/components/Collection/__tests__/Collection.spec.js +75 -0
- package/src/components/Collection/__tests__/NestedCollection.spec.js +445 -0
- package/src/components/Collection/__tests__/Sidebar.spec.js +87 -0
- package/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap +144 -0
- package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +550 -0
- package/src/components/Collection/__tests__/__snapshots__/Sidebar.spec.js.snap +312 -0
- package/src/components/Editor/Editor.js +497 -0
- package/src/components/Editor/EditorControlPane/EditorControl.js +452 -0
- package/src/components/Editor/EditorControlPane/EditorControlPane.js +269 -0
- package/src/components/Editor/EditorControlPane/Widget.js +384 -0
- package/src/components/Editor/EditorInterface.js +444 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreview.js +40 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +74 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +333 -0
- package/src/components/Editor/EditorPreviewPane/PreviewHOC.js +33 -0
- package/src/components/Editor/EditorToolbar.js +691 -0
- package/src/components/Editor/__tests__/Editor.spec.js +221 -0
- package/src/components/Editor/__tests__/EditorToolbar.spec.js +120 -0
- package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +45 -0
- package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +4233 -0
- package/src/components/Editor/withWorkflow.js +61 -0
- package/src/components/EditorWidgets/Unknown/UnknownControl.js +17 -0
- package/src/components/EditorWidgets/Unknown/UnknownPreview.js +19 -0
- package/src/components/EditorWidgets/index.js +5 -0
- package/src/components/MediaLibrary/EmptyMessage.js +28 -0
- package/src/components/MediaLibrary/MediaLibrary.js +411 -0
- package/src/components/MediaLibrary/MediaLibraryButtons.js +136 -0
- package/src/components/MediaLibrary/MediaLibraryCard.js +128 -0
- package/src/components/MediaLibrary/MediaLibraryCardGrid.js +199 -0
- package/src/components/MediaLibrary/MediaLibraryHeader.js +48 -0
- package/src/components/MediaLibrary/MediaLibraryModal.js +200 -0
- package/src/components/MediaLibrary/MediaLibrarySearch.js +61 -0
- package/src/components/MediaLibrary/MediaLibraryTop.js +143 -0
- package/src/components/MediaLibrary/__tests__/MediaLibraryButtons.spec.js +45 -0
- package/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +49 -0
- package/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +264 -0
- package/src/components/UI/DragDrop.js +66 -0
- package/src/components/UI/ErrorBoundary.js +214 -0
- package/src/components/UI/FileUploadButton.js +24 -0
- package/src/components/UI/Modal.js +112 -0
- package/src/components/UI/Notifications.tsx +83 -0
- package/src/components/UI/SettingsDropdown.js +103 -0
- package/src/components/UI/__tests__/ErrorBoundary.spec.js +57 -0
- package/src/components/UI/index.js +6 -0
- package/src/components/Workflow/Workflow.js +169 -0
- package/src/components/Workflow/WorkflowCard.js +177 -0
- package/src/components/Workflow/WorkflowList.js +272 -0
- package/src/constants/__tests__/configSchema.spec.js +611 -0
- package/src/constants/collectionTypes.ts +2 -0
- package/src/constants/collectionViews.js +2 -0
- package/src/constants/commitProps.ts +2 -0
- package/src/constants/configSchema.js +441 -0
- package/src/constants/fieldInference.tsx +78 -0
- package/src/constants/publishModes.ts +22 -0
- package/src/constants/validationErrorTypes.js +6 -0
- package/src/formats/__tests__/formats.spec.js +87 -0
- package/src/formats/__tests__/frontmatter.spec.js +429 -0
- package/src/formats/__tests__/toml.spec.js +9 -0
- package/src/formats/__tests__/yaml.spec.js +162 -0
- package/src/formats/formats.ts +97 -0
- package/src/formats/frontmatter.ts +150 -0
- package/src/formats/helpers.ts +14 -0
- package/src/formats/json.ts +9 -0
- package/src/formats/toml.ts +33 -0
- package/src/formats/yaml.ts +58 -0
- package/src/index.js +8 -0
- package/src/integrations/index.js +35 -0
- package/src/integrations/providers/algolia/implementation.js +176 -0
- package/src/integrations/providers/assetStore/implementation.js +148 -0
- package/src/lib/__tests__/formatters.spec.js +751 -0
- package/src/lib/__tests__/i18n.spec.js +792 -0
- package/src/lib/__tests__/phrases.spec.js +119 -0
- package/src/lib/__tests__/registry.spec.js +261 -0
- package/src/lib/__tests__/serializeEntryValues.spec.js +22 -0
- package/src/lib/__tests__/urlHelper.spec.js +138 -0
- package/src/lib/consoleError.js +7 -0
- package/src/lib/formatters.ts +286 -0
- package/src/lib/i18n.ts +454 -0
- package/src/lib/phrases.js +8 -0
- package/src/lib/polyfill.js +9 -0
- package/src/lib/registry.js +312 -0
- package/src/lib/serializeEntryValues.js +75 -0
- package/src/lib/stega.ts +145 -0
- package/src/lib/textHelper.js +11 -0
- package/src/lib/urlHelper.ts +128 -0
- package/src/mediaLibrary.ts +51 -0
- package/src/reducers/__tests__/auth.spec.ts +38 -0
- package/src/reducers/__tests__/collections.spec.js +610 -0
- package/src/reducers/__tests__/config.spec.js +38 -0
- package/src/reducers/__tests__/entries.spec.js +694 -0
- package/src/reducers/__tests__/entryDraft.spec.js +297 -0
- package/src/reducers/__tests__/globalUI.js +43 -0
- package/src/reducers/__tests__/integrations.spec.ts +76 -0
- package/src/reducers/__tests__/mediaLibrary.spec.js +154 -0
- package/src/reducers/__tests__/medias.spec.ts +49 -0
- package/src/reducers/auth.ts +46 -0
- package/src/reducers/collections.ts +535 -0
- package/src/reducers/combinedReducer.ts +11 -0
- package/src/reducers/config.ts +38 -0
- package/src/reducers/cursors.js +36 -0
- package/src/reducers/deploys.ts +52 -0
- package/src/reducers/editorialWorkflow.ts +163 -0
- package/src/reducers/entries.ts +819 -0
- package/src/reducers/entryDraft.js +260 -0
- package/src/reducers/globalUI.ts +45 -0
- package/src/reducers/index.ts +82 -0
- package/src/reducers/integrations.ts +59 -0
- package/src/reducers/mediaLibrary.ts +296 -0
- package/src/reducers/medias.ts +66 -0
- package/src/reducers/notifications.ts +52 -0
- package/src/reducers/search.ts +111 -0
- package/src/reducers/status.ts +40 -0
- package/src/redux/index.ts +18 -0
- package/src/redux/middleware/waitUntilAction.ts +64 -0
- package/src/routing/__tests__/history.spec.ts +49 -0
- package/src/routing/history.ts +17 -0
- package/src/types/diacritics.d.ts +1 -0
- package/src/types/global.d.ts +8 -0
- package/src/types/immutable.ts +49 -0
- package/src/types/redux.ts +827 -0
- package/src/types/tomlify-j0.4.d.ts +13 -0
- package/src/valueObjects/AssetProxy.ts +48 -0
- package/src/valueObjects/EditorComponent.js +38 -0
- 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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
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
|
+
}
|