Feat: editorial workflow bitbucket gitlab (#3014)
* refactor: typescript the backends * feat: support multiple files upload for GitLab and BitBucket * fix: load entry media files from media folder or UI state * chore: cleanup log message * chore: code cleanup * refactor: typescript the test backend * refactor: cleanup getEntry unsued variables * refactor: moved shared backend code to lib util * chore: rename files to preserve history * fix: bind readFile method to API classes * test(e2e): switch to chrome in cypress tests * refactor: extract common api methods * refactor: remove most of immutable js usage from backends * feat(backend-gitlab): initial editorial workflow support * feat(backend-gitlab): implement missing workflow methods * chore: fix lint error * feat(backend-gitlab): support files deletion * test(e2e): add gitlab cypress tests * feat(backend-bitbucket): implement missing editorial workflow methods * test(e2e): add BitBucket backend e2e tests * build: update node version to 12 on netlify builds * fix(backend-bitbucket): extract BitBucket avatar url * test: fix git-gateway AuthenticationPage test * test(e2e): fix some backend tests * test(e2e): fix tests * test(e2e): add git-gateway editorial workflow test * chore: code cleanup * test(e2e): revert back to electron * test(e2e): add non editorial workflow tests * fix(git-gateway-gitlab): don't call unpublishedEntry in simple workflow gitlab git-gateway doesn't support editorial workflow APIs yet. This change makes sure not to call them in simple workflow * refactor(backend-bitbucket): switch to diffstat API instead of raw diff * chore: fix test * test(e2e): add more git-gateway tests * fix: post rebase typescript fixes * test(e2e): fix tests * fix: fix parsing of content key and add tests * refactor: rename test file * test(unit): add getStatues unit tests * chore: update cypress * docs: update beta docs
This commit is contained in:
committed by
Shawn Erquhart
parent
4ff5bc2ee0
commit
6f221ab3c1
@ -5,7 +5,6 @@ import reducer, {
|
||||
selectMediaFilePath,
|
||||
selectMediaFilePublicPath,
|
||||
} from '../entries';
|
||||
import { EDITORIAL_WORKFLOW } from '../../constants/publishModes';
|
||||
|
||||
const initialState = OrderedMap({
|
||||
posts: Map({ name: 'posts' }),
|
||||
@ -73,33 +72,26 @@ describe('entries', () => {
|
||||
});
|
||||
|
||||
describe('selectMediaFolder', () => {
|
||||
it('should return global media folder when not in editorial workflow', () => {
|
||||
expect(selectMediaFolder(Map({ media_folder: 'static/media' }))).toEqual('static/media');
|
||||
});
|
||||
|
||||
it("should return global media folder when in editorial workflow and collection doesn't specify media_folder", () => {
|
||||
it("should return global media folder when collection doesn't specify media_folder", () => {
|
||||
expect(
|
||||
selectMediaFolder(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ name: 'posts' }),
|
||||
),
|
||||
selectMediaFolder(Map({ media_folder: 'static/media' }), Map({ name: 'posts' })),
|
||||
).toEqual('static/media');
|
||||
});
|
||||
|
||||
it('should return draft media folder when in editorial workflow, collection specifies media_folder and entry path is null', () => {
|
||||
it('should return draft media folder when collection specifies media_folder and entry path is null', () => {
|
||||
expect(
|
||||
selectMediaFolder(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ media_folder: 'static/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', media_folder: '' }),
|
||||
null,
|
||||
),
|
||||
).toEqual('posts/DRAFT_MEDIA_FILES');
|
||||
});
|
||||
|
||||
it('should return relative media folder when in editorial workflow, collection specifies media_folder and entry path is not null', () => {
|
||||
it('should return relative media folder when collection specifies media_folder and entry path is not null', () => {
|
||||
expect(
|
||||
selectMediaFolder(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ media_folder: 'static/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', media_folder: '' }),
|
||||
'posts/title/index.md',
|
||||
),
|
||||
@ -109,7 +101,7 @@ describe('entries', () => {
|
||||
it('should resolve relative media folder', () => {
|
||||
expect(
|
||||
selectMediaFolder(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ media_folder: 'static/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', media_folder: '../' }),
|
||||
'posts/title/index.md',
|
||||
),
|
||||
@ -126,19 +118,14 @@ describe('entries', () => {
|
||||
|
||||
it('should resolve path from global media folder when absolute path', () => {
|
||||
expect(
|
||||
selectMediaFilePath(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
null,
|
||||
null,
|
||||
'/media/image.png',
|
||||
),
|
||||
selectMediaFilePath(Map({ media_folder: 'static/media' }), null, null, '/media/image.png'),
|
||||
).toBe('static/media/image.png');
|
||||
});
|
||||
|
||||
it('should resolve path from global media folder when relative path for collection with no media folder', () => {
|
||||
expect(
|
||||
selectMediaFilePath(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ media_folder: 'static/media' }),
|
||||
Map({ name: 'posts', folder: 'posts' }),
|
||||
null,
|
||||
'image.png',
|
||||
@ -149,7 +136,7 @@ describe('entries', () => {
|
||||
it('should resolve path from collection media folder when relative path for collection with media folder', () => {
|
||||
expect(
|
||||
selectMediaFilePath(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ media_folder: 'static/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', media_folder: '' }),
|
||||
null,
|
||||
'image.png',
|
||||
@ -160,7 +147,7 @@ describe('entries', () => {
|
||||
it('should handle relative media_folder', () => {
|
||||
expect(
|
||||
selectMediaFilePath(
|
||||
Map({ media_folder: 'static/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ media_folder: 'static/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', media_folder: '../../static/media/' }),
|
||||
'posts/title/index.md',
|
||||
'image.png',
|
||||
@ -176,26 +163,16 @@ describe('entries', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve path from public folder when not in editorial workflow', () => {
|
||||
it('should resolve path from public folder for collection with no media folder', () => {
|
||||
expect(
|
||||
selectMediaFilePublicPath(Map({ public_folder: '/media' }), null, '/media/image.png'),
|
||||
).toBe('/media/image.png');
|
||||
});
|
||||
|
||||
it('should resolve path from public folder when in editorial workflow for collection with no public folder', () => {
|
||||
it('should resolve path from collection media folder for collection with public folder', () => {
|
||||
expect(
|
||||
selectMediaFilePublicPath(
|
||||
Map({ public_folder: '/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ name: 'posts', folder: 'posts' }),
|
||||
'image.png',
|
||||
),
|
||||
).toBe('/media/image.png');
|
||||
});
|
||||
|
||||
it('should resolve path from collection media folder when in editorial workflow for collection with public folder', () => {
|
||||
expect(
|
||||
selectMediaFilePublicPath(
|
||||
Map({ public_folder: '/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ public_folder: '/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', public_folder: '' }),
|
||||
'image.png',
|
||||
),
|
||||
@ -205,7 +182,7 @@ describe('entries', () => {
|
||||
it('should handle relative public_folder', () => {
|
||||
expect(
|
||||
selectMediaFilePublicPath(
|
||||
Map({ public_folder: '/media', publish_mode: EDITORIAL_WORKFLOW }),
|
||||
Map({ public_folder: '/media' }),
|
||||
Map({ name: 'posts', folder: 'posts', public_folder: '../../static/media/' }),
|
||||
'image.png',
|
||||
),
|
||||
|
@ -7,7 +7,7 @@ import mediaLibrary, {
|
||||
} from '../mediaLibrary';
|
||||
|
||||
jest.mock('uuid/v4');
|
||||
jest.mock('Reducers/editorialWorkflow');
|
||||
jest.mock('Reducers/entries');
|
||||
jest.mock('Reducers');
|
||||
|
||||
describe('mediaLibrary', () => {
|
||||
@ -43,10 +43,10 @@ describe('mediaLibrary', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should select draft media files when editing a workflow draft', () => {
|
||||
const { selectEditingWorkflowDraft } = require('Reducers/editorialWorkflow');
|
||||
it('should select draft media files when editing a draft', () => {
|
||||
const { selectEditingDraft } = require('Reducers/entries');
|
||||
|
||||
selectEditingWorkflowDraft.mockReturnValue(true);
|
||||
selectEditingDraft.mockReturnValue(true);
|
||||
|
||||
const state = {
|
||||
entryDraft: fromJS({ entry: { mediaFiles: [{ id: 1 }] } }),
|
||||
@ -55,10 +55,10 @@ describe('mediaLibrary', () => {
|
||||
expect(selectMediaFiles(state)).toEqual([{ key: 1, id: 1 }]);
|
||||
});
|
||||
|
||||
it('should select global media files when not editing a workflow draft', () => {
|
||||
const { selectEditingWorkflowDraft } = require('Reducers/editorialWorkflow');
|
||||
it('should select global media files when not editing a draft', () => {
|
||||
const { selectEditingDraft } = require('Reducers/entries');
|
||||
|
||||
selectEditingWorkflowDraft.mockReturnValue(false);
|
||||
selectEditingDraft.mockReturnValue(false);
|
||||
|
||||
const state = {
|
||||
mediaLibrary: Map({ files: [{ id: 1 }] }),
|
||||
@ -80,9 +80,9 @@ describe('mediaLibrary', () => {
|
||||
});
|
||||
|
||||
it('should return media file by path', () => {
|
||||
const { selectEditingWorkflowDraft } = require('Reducers/editorialWorkflow');
|
||||
const { selectEditingDraft } = require('Reducers/entries');
|
||||
|
||||
selectEditingWorkflowDraft.mockReturnValue(false);
|
||||
selectEditingDraft.mockReturnValue(false);
|
||||
|
||||
const state = {
|
||||
mediaLibrary: Map({ files: [{ id: 1, path: 'path' }] }),
|
||||
|
@ -36,11 +36,6 @@ const collections = (state = null, action: CollectionsAction) => {
|
||||
}
|
||||
};
|
||||
|
||||
enum ListMethod {
|
||||
ENTRIES_BY_FOLDER = 'entriesByFolder',
|
||||
ENTRIES_BY_FILES = 'entriesByFiles',
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
[FOLDER]: {
|
||||
entryExtension(collection: Collection) {
|
||||
@ -65,9 +60,6 @@ const selectors = {
|
||||
|
||||
return slug;
|
||||
},
|
||||
listMethod() {
|
||||
return ListMethod.ENTRIES_BY_FOLDER;
|
||||
},
|
||||
allowNewEntries(collection: Collection) {
|
||||
return collection.get('create');
|
||||
},
|
||||
@ -102,16 +94,13 @@ const selectors = {
|
||||
const files = collection.get('files');
|
||||
return files && files.find(f => f?.get('file') === path).get('label');
|
||||
},
|
||||
listMethod() {
|
||||
return ListMethod.ENTRIES_BY_FILES;
|
||||
},
|
||||
allowNewEntries() {
|
||||
return false;
|
||||
},
|
||||
allowDeletion(collection: Collection) {
|
||||
return collection.get('delete', false);
|
||||
},
|
||||
templateName(collection: Collection, slug: string) {
|
||||
templateName(_collection: Collection, slug: string) {
|
||||
return slug;
|
||||
},
|
||||
},
|
||||
@ -127,8 +116,6 @@ export const selectEntryPath = (collection: Collection, slug: string) =>
|
||||
selectors[collection.get('type')].entryPath(collection, slug);
|
||||
export const selectEntrySlug = (collection: Collection, path: string) =>
|
||||
selectors[collection.get('type')].entrySlug(collection, path);
|
||||
export const selectListMethod = (collection: Collection) =>
|
||||
selectors[collection.get('type')].listMethod();
|
||||
export const selectAllowNewEntries = (collection: Collection) =>
|
||||
selectors[collection.get('type')].allowNewEntries(collection);
|
||||
export const selectAllowDeletion = (collection: Collection) =>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Map } from 'immutable';
|
||||
import { CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAILURE, CONFIG_MERGE } from '../actions/config';
|
||||
import { Config, ConfigAction } from '../types/redux';
|
||||
import { EDITORIAL_WORKFLOW } from '../constants/publishModes';
|
||||
|
||||
const config = (state = Map({ isFetching: true }), action: ConfigAction) => {
|
||||
switch (action.type) {
|
||||
@ -24,4 +25,7 @@ const config = (state = Map({ isFetching: true }), action: ConfigAction) => {
|
||||
|
||||
export const selectLocale = (state: Config) => state.get('locale', 'en') as string;
|
||||
|
||||
export const selectUseWorkflow = (state: Config) =>
|
||||
state.get('publish_mode') === EDITORIAL_WORKFLOW;
|
||||
|
||||
export default config;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Map, List, fromJS } from 'immutable';
|
||||
import { startsWith } from 'lodash';
|
||||
import { EDITORIAL_WORKFLOW } from 'Constants/publishModes';
|
||||
import { EDITORIAL_WORKFLOW } from '../constants/publishModes';
|
||||
import {
|
||||
UNPUBLISHED_ENTRY_REQUEST,
|
||||
UNPUBLISHED_ENTRY_REDIRECT,
|
||||
@ -16,32 +16,33 @@ import {
|
||||
UNPUBLISHED_ENTRY_PUBLISH_SUCCESS,
|
||||
UNPUBLISHED_ENTRY_PUBLISH_FAILURE,
|
||||
UNPUBLISHED_ENTRY_DELETE_SUCCESS,
|
||||
} from 'Actions/editorialWorkflow';
|
||||
import { CONFIG_SUCCESS } from 'Actions/config';
|
||||
} from '../actions/editorialWorkflow';
|
||||
import { CONFIG_SUCCESS } from '../actions/config';
|
||||
import { EditorialWorkflowAction, EditorialWorkflow, Entities } from '../types/redux';
|
||||
|
||||
const unpublishedEntries = (state = Map(), action) => {
|
||||
const unpublishedEntries = (state = Map(), action: EditorialWorkflowAction) => {
|
||||
switch (action.type) {
|
||||
case CONFIG_SUCCESS: {
|
||||
const publishMode = action.payload && action.payload.get('publish_mode');
|
||||
if (publishMode === EDITORIAL_WORKFLOW) {
|
||||
// Editorial workflow state is explicetelly initiated after the config.
|
||||
// Editorial workflow state is explicitly initiated after the config.
|
||||
return Map({ entities: Map(), pages: Map() });
|
||||
}
|
||||
return state;
|
||||
}
|
||||
case UNPUBLISHED_ENTRY_REQUEST:
|
||||
return state.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.slug}`, 'isFetching'],
|
||||
['entities', `${action.payload!.collection}.${action.payload!.slug}`, 'isFetching'],
|
||||
true,
|
||||
);
|
||||
|
||||
case UNPUBLISHED_ENTRY_REDIRECT:
|
||||
return state.deleteIn(['entities', `${action.payload.collection}.${action.payload.slug}`]);
|
||||
return state.deleteIn(['entities', `${action.payload!.collection}.${action.payload!.slug}`]);
|
||||
|
||||
case UNPUBLISHED_ENTRY_SUCCESS:
|
||||
return state.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.entry.slug}`],
|
||||
fromJS(action.payload.entry),
|
||||
['entities', `${action.payload!.collection}.${action.payload!.entry.slug}`],
|
||||
fromJS(action.payload!.entry),
|
||||
);
|
||||
|
||||
case UNPUBLISHED_ENTRIES_REQUEST:
|
||||
@ -49,7 +50,7 @@ const unpublishedEntries = (state = Map(), action) => {
|
||||
|
||||
case UNPUBLISHED_ENTRIES_SUCCESS:
|
||||
return state.withMutations(map => {
|
||||
action.payload.entries.forEach(entry =>
|
||||
action.payload!.entries.forEach(entry =>
|
||||
map.setIn(
|
||||
['entities', `${entry.collection}.${entry.slug}`],
|
||||
fromJS(entry).set('isFetching', false),
|
||||
@ -58,35 +59,38 @@ const unpublishedEntries = (state = Map(), action) => {
|
||||
map.set(
|
||||
'pages',
|
||||
Map({
|
||||
...action.payload.pages,
|
||||
ids: List(action.payload.entries.map(entry => entry.slug)),
|
||||
...action.payload!.pages,
|
||||
ids: List(action.payload!.entries.map(entry => entry.slug)),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
case UNPUBLISHED_ENTRY_PERSIST_REQUEST:
|
||||
case UNPUBLISHED_ENTRY_PERSIST_REQUEST: {
|
||||
// Update Optimistically
|
||||
return state.withMutations(map => {
|
||||
map.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.entry.get('slug')}`],
|
||||
fromJS(action.payload.entry),
|
||||
['entities', `${action.payload!.collection}.${action.payload!.entry.get('slug')}`],
|
||||
fromJS(action.payload!.entry),
|
||||
);
|
||||
map.setIn(
|
||||
[
|
||||
'entities',
|
||||
`${action.payload.collection}.${action.payload.entry.get('slug')}`,
|
||||
`${action.payload!.collection}.${action.payload!.entry.get('slug')}`,
|
||||
'isPersisting',
|
||||
],
|
||||
true,
|
||||
);
|
||||
map.updateIn(['pages', 'ids'], List(), list => list.push(action.payload.entry.get('slug')));
|
||||
map.updateIn(['pages', 'ids'], List(), list =>
|
||||
list.push(action.payload!.entry.get('slug')),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
case UNPUBLISHED_ENTRY_PERSIST_SUCCESS:
|
||||
// Update Optimistically
|
||||
return state.deleteIn([
|
||||
'entities',
|
||||
`${action.payload.collection}.${action.payload.slug}`,
|
||||
`${action.payload!.collection}.${action.payload!.slug}`,
|
||||
'isPersisting',
|
||||
]);
|
||||
|
||||
@ -94,11 +98,16 @@ const unpublishedEntries = (state = Map(), action) => {
|
||||
// Update Optimistically
|
||||
return state.withMutations(map => {
|
||||
map.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.slug}`, 'metaData', 'status'],
|
||||
action.payload.newStatus,
|
||||
[
|
||||
'entities',
|
||||
`${action.payload!.collection}.${action.payload!.slug}`,
|
||||
'metaData',
|
||||
'status',
|
||||
],
|
||||
action.payload!.newStatus,
|
||||
);
|
||||
map.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.slug}`, 'isUpdatingStatus'],
|
||||
['entities', `${action.payload!.collection}.${action.payload!.slug}`, 'isUpdatingStatus'],
|
||||
true,
|
||||
);
|
||||
});
|
||||
@ -106,55 +115,49 @@ const unpublishedEntries = (state = Map(), action) => {
|
||||
case UNPUBLISHED_ENTRY_STATUS_CHANGE_SUCCESS:
|
||||
case UNPUBLISHED_ENTRY_STATUS_CHANGE_FAILURE:
|
||||
return state.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.slug}`, 'isUpdatingStatus'],
|
||||
['entities', `${action.payload!.collection}.${action.payload!.slug}`, 'isUpdatingStatus'],
|
||||
false,
|
||||
);
|
||||
|
||||
case UNPUBLISHED_ENTRY_PUBLISH_REQUEST:
|
||||
return state.setIn(
|
||||
['entities', `${action.payload.collection}.${action.payload.slug}`, 'isPublishing'],
|
||||
['entities', `${action.payload!.collection}.${action.payload!.slug}`, 'isPublishing'],
|
||||
true,
|
||||
);
|
||||
|
||||
case UNPUBLISHED_ENTRY_PUBLISH_SUCCESS:
|
||||
case UNPUBLISHED_ENTRY_PUBLISH_FAILURE:
|
||||
return state.withMutations(map => {
|
||||
map.deleteIn(['entities', `${action.payload.collection}.${action.payload.slug}`]);
|
||||
map.deleteIn(['entities', `${action.payload!.collection}.${action.payload!.slug}`]);
|
||||
});
|
||||
|
||||
case UNPUBLISHED_ENTRY_DELETE_SUCCESS:
|
||||
return state.deleteIn(['entities', `${action.payload.collection}.${action.payload.slug}`]);
|
||||
return state.deleteIn(['entities', `${action.payload!.collection}.${action.payload!.slug}`]);
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const selectUnpublishedEntry = (state, collection, slug) =>
|
||||
state && state.getIn(['entities', `${collection}.${slug}`]);
|
||||
export const selectUnpublishedEntry = (
|
||||
state: EditorialWorkflow,
|
||||
collection: string,
|
||||
slug: string,
|
||||
) => state && state.getIn(['entities', `${collection}.${slug}`]);
|
||||
|
||||
export const selectUnpublishedEntriesByStatus = (state, status) => {
|
||||
export const selectUnpublishedEntriesByStatus = (state: EditorialWorkflow, status: string) => {
|
||||
if (!state) return null;
|
||||
return state
|
||||
.get('entities')
|
||||
.filter(entry => entry.getIn(['metaData', 'status']) === status)
|
||||
.valueSeq();
|
||||
const entities = state.get('entities') as Entities;
|
||||
return entities.filter(entry => entry.getIn(['metaData', 'status']) === status).valueSeq();
|
||||
};
|
||||
|
||||
export const selectUnpublishedSlugs = (state, collection) => {
|
||||
export const selectUnpublishedSlugs = (state: EditorialWorkflow, collection: string) => {
|
||||
if (!state.get('entities')) return null;
|
||||
return state
|
||||
.get('entities')
|
||||
.filter((v, k) => startsWith(k, `${collection}.`))
|
||||
const entities = state.get('entities') as Entities;
|
||||
return entities
|
||||
.filter((_v, k) => startsWith(k as string, `${collection}.`))
|
||||
.map(entry => entry.get('slug'))
|
||||
.valueSeq();
|
||||
};
|
||||
|
||||
export const selectEditingWorkflowDraft = state => {
|
||||
const entry = state.entryDraft.get('entry');
|
||||
const useWorkflow = state.config.get('publish_mode') === EDITORIAL_WORKFLOW;
|
||||
const workflowDraft = entry && !entry.isEmpty() && useWorkflow;
|
||||
return workflowDraft;
|
||||
};
|
||||
|
||||
export default unpublishedEntries;
|
@ -23,9 +23,9 @@ import {
|
||||
EntryFailurePayload,
|
||||
EntryDeletePayload,
|
||||
EntriesRequestPayload,
|
||||
EntryDraft,
|
||||
} from '../types/redux';
|
||||
import { isAbsolutePath, basename } from 'netlify-cms-lib-util/src';
|
||||
import { EDITORIAL_WORKFLOW } from '../constants/publishModes';
|
||||
|
||||
let collection: string;
|
||||
let loadedEntries: EntryObject[];
|
||||
@ -144,8 +144,7 @@ export const selectMediaFolder = (
|
||||
) => {
|
||||
let mediaFolder = config.get('media_folder');
|
||||
|
||||
const useWorkflow = config.get('publish_mode') === EDITORIAL_WORKFLOW;
|
||||
if (useWorkflow && collection && collection.has('media_folder')) {
|
||||
if (collection && collection.has('media_folder')) {
|
||||
if (entryPath) {
|
||||
const entryDir = dirname(entryPath);
|
||||
mediaFolder = join(entryDir, collection.get('media_folder') as string);
|
||||
@ -189,12 +188,17 @@ export const selectMediaFilePublicPath = (
|
||||
|
||||
let publicFolder = config.get('public_folder');
|
||||
|
||||
const useWorkflow = config.get('publish_mode') === EDITORIAL_WORKFLOW;
|
||||
if (useWorkflow && collection && collection.has('public_folder')) {
|
||||
if (collection && collection.has('public_folder')) {
|
||||
publicFolder = collection.get('public_folder') as string;
|
||||
}
|
||||
|
||||
return join(publicFolder, basename(mediaPath));
|
||||
};
|
||||
|
||||
export const selectEditingDraft = (state: EntryDraft) => {
|
||||
const entry = state.get('entry');
|
||||
const workflowDraft = entry && !entry.isEmpty();
|
||||
return workflowDraft;
|
||||
};
|
||||
|
||||
export default entries;
|
||||
|
@ -18,11 +18,29 @@ import {
|
||||
MEDIA_DISPLAY_URL_REQUEST,
|
||||
MEDIA_DISPLAY_URL_SUCCESS,
|
||||
MEDIA_DISPLAY_URL_FAILURE,
|
||||
} from 'Actions/mediaLibrary';
|
||||
import { selectEditingWorkflowDraft } from 'Reducers/editorialWorkflow';
|
||||
import { selectIntegration } from 'Reducers';
|
||||
} from '../actions/mediaLibrary';
|
||||
import { selectEditingDraft } from './entries';
|
||||
import { selectIntegration } from './';
|
||||
import {
|
||||
State,
|
||||
MediaLibraryAction,
|
||||
MediaLibraryInstance,
|
||||
MediaFile,
|
||||
MediaFileMap,
|
||||
DisplayURLState,
|
||||
} from '../types/redux';
|
||||
|
||||
const defaultState = {
|
||||
const defaultState: {
|
||||
isVisible: boolean;
|
||||
showMediaButton: boolean;
|
||||
controlMedia: Map<string, string>;
|
||||
displayURLs: Map<string, string>;
|
||||
externalLibrary?: MediaLibraryInstance;
|
||||
controlID?: string;
|
||||
page?: number;
|
||||
files?: MediaFile[];
|
||||
config: Map<string, string>;
|
||||
} = {
|
||||
isVisible: false,
|
||||
showMediaButton: true,
|
||||
controlMedia: Map(),
|
||||
@ -30,7 +48,7 @@ const defaultState = {
|
||||
config: Map(),
|
||||
};
|
||||
|
||||
const mediaLibrary = (state = Map(defaultState), action) => {
|
||||
const mediaLibrary = (state = Map(defaultState), action: MediaLibraryAction) => {
|
||||
switch (action.type) {
|
||||
case MEDIA_LIBRARY_CREATE:
|
||||
return state.withMutations(map => {
|
||||
@ -104,7 +122,7 @@ const mediaLibrary = (state = Map(defaultState), action) => {
|
||||
map.set('dynamicSearchQuery', dynamicSearchQuery);
|
||||
map.set('dynamicSearchActive', !!dynamicSearchQuery);
|
||||
if (page && page > 1) {
|
||||
const updatedFiles = map.get('files').concat(filesWithKeys);
|
||||
const updatedFiles = (map.get('files') as MediaFile[]).concat(filesWithKeys);
|
||||
map.set('files', updatedFiles);
|
||||
} else {
|
||||
map.set('files', filesWithKeys);
|
||||
@ -128,7 +146,8 @@ const mediaLibrary = (state = Map(defaultState), action) => {
|
||||
}
|
||||
return state.withMutations(map => {
|
||||
const fileWithKey = { ...file, key: uuid() };
|
||||
const updatedFiles = [fileWithKey, ...map.get('files')];
|
||||
const files = map.get('files') as MediaFile[];
|
||||
const updatedFiles = [fileWithKey, ...files];
|
||||
map.set('files', updatedFiles);
|
||||
map.set('isPersisting', false);
|
||||
});
|
||||
@ -149,9 +168,8 @@ const mediaLibrary = (state = Map(defaultState), action) => {
|
||||
return state;
|
||||
}
|
||||
return state.withMutations(map => {
|
||||
const updatedFiles = map
|
||||
.get('files')
|
||||
.filter(file => (key ? file.key !== key : file.id !== id));
|
||||
const files = map.get('files') as MediaFile[];
|
||||
const updatedFiles = files.filter(file => (key ? file.key !== key : file.id !== id));
|
||||
map.set('files', updatedFiles);
|
||||
map.deleteIn(['displayURLs', id]);
|
||||
map.set('isDeleting', false);
|
||||
@ -191,17 +209,17 @@ const mediaLibrary = (state = Map(defaultState), action) => {
|
||||
}
|
||||
};
|
||||
|
||||
export function selectMediaFiles(state) {
|
||||
export function selectMediaFiles(state: State) {
|
||||
const { mediaLibrary, entryDraft } = state;
|
||||
const workflowDraft = selectEditingWorkflowDraft(state);
|
||||
const editingDraft = selectEditingDraft(state.entryDraft);
|
||||
const integration = selectIntegration(state, null, 'assetStore');
|
||||
|
||||
let files;
|
||||
if (workflowDraft && !integration) {
|
||||
files = entryDraft
|
||||
.getIn(['entry', 'mediaFiles'], List())
|
||||
.toJS()
|
||||
.map(file => ({ key: file.id, ...file }));
|
||||
if (editingDraft && !integration) {
|
||||
const entryFiles = entryDraft
|
||||
.getIn(['entry', 'mediaFiles'], List<MediaFileMap>())
|
||||
.toJS() as MediaFile[];
|
||||
files = entryFiles.map(file => ({ key: file.id, ...file }));
|
||||
} else {
|
||||
files = mediaLibrary.get('files') || [];
|
||||
}
|
||||
@ -209,14 +227,17 @@ export function selectMediaFiles(state) {
|
||||
return files;
|
||||
}
|
||||
|
||||
export function selectMediaFileByPath(state, path) {
|
||||
export function selectMediaFileByPath(state: State, path: string) {
|
||||
const files = selectMediaFiles(state);
|
||||
const file = files.find(file => file.path === path);
|
||||
return file;
|
||||
}
|
||||
|
||||
export function selectMediaDisplayURL(state, id) {
|
||||
const displayUrlState = state.mediaLibrary.getIn(['displayURLs', id], Map());
|
||||
export function selectMediaDisplayURL(state: State, id: string) {
|
||||
const displayUrlState = state.mediaLibrary.getIn(
|
||||
['displayURLs', id],
|
||||
(Map() as unknown) as DisplayURLState,
|
||||
);
|
||||
return displayUrlState;
|
||||
}
|
||||
|
Reference in New Issue
Block a user