import { fetchConfig } from '~/common/config';
import { addMenuActionSet, ContextMenuKind } from '~/views-components/context-menu/context-menu';
import { rootProjectActionSet } from "~/views-components/context-menu/action-sets/root-project-action-set";
-import { projectActionSet, readOnlyProjectActionSet } from "~/views-components/context-menu/action-sets/project-action-set";
+import { filterGroupActionSet, projectActionSet, readOnlyProjectActionSet } from "~/views-components/context-menu/action-sets/project-action-set";
import { resourceActionSet } from '~/views-components/context-menu/action-sets/resource-action-set';
import { favoriteActionSet } from "~/views-components/context-menu/action-sets/favorite-action-set";
import { collectionFilesActionSet, readOnlyCollectionFilesActionSet } from '~/views-components/context-menu/action-sets/collection-files-action-set';
import { Config } from '~/common/config';
import { addRouteChangeHandlers } from './routes/route-change-handlers';
import { setTokenDialogApiHost } from '~/store/token-dialog/token-dialog-actions';
-import { processResourceActionSet } from '~/views-components/context-menu/action-sets/process-resource-action-set';
+import { processResourceActionSet, readOnlyProcessResourceActionSet } from '~/views-components/context-menu/action-sets/process-resource-action-set';
import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
import { trashedCollectionActionSet } from '~/views-components/context-menu/action-sets/trashed-collection-action-set';
import { setBuildInfo } from '~/store/app-info/app-info-actions';
import { linkActionSet } from '~/views-components/context-menu/action-sets/link-action-set';
import { loadFileViewersConfig } from '~/store/file-viewers/file-viewers-actions';
import { processResourceAdminActionSet } from '~/views-components/context-menu/action-sets/process-resource-admin-action-set';
-import { projectAdminActionSet } from '~/views-components/context-menu/action-sets/project-admin-action-set';
+import { filterGroupAdminActionSet, projectAdminActionSet } from '~/views-components/context-menu/action-sets/project-admin-action-set';
import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions";
import { openNotFoundDialog } from './store/not-found-panel/not-found-panel-action';
import { storeRedirects } from './common/redirect-to';
addMenuActionSet(ContextMenuKind.ROOT_PROJECT, rootProjectActionSet);
addMenuActionSet(ContextMenuKind.PROJECT, projectActionSet);
addMenuActionSet(ContextMenuKind.READONLY_PROJECT, readOnlyProjectActionSet);
+addMenuActionSet(ContextMenuKind.FILTER_GROUP, filterGroupActionSet);
addMenuActionSet(ContextMenuKind.RESOURCE, resourceActionSet);
addMenuActionSet(ContextMenuKind.FAVORITE, favoriteActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_FILES, collectionFilesActionSet);
addMenuActionSet(ContextMenuKind.TRASHED_COLLECTION, trashedCollectionActionSet);
addMenuActionSet(ContextMenuKind.PROCESS, processActionSet);
addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet);
+addMenuActionSet(ContextMenuKind.READONLY_PROCESS_RESOURCE, readOnlyProcessResourceActionSet);
addMenuActionSet(ContextMenuKind.TRASH, trashActionSet);
addMenuActionSet(ContextMenuKind.REPOSITORY, repositoryActionSet);
addMenuActionSet(ContextMenuKind.SSH_KEY, sshKeyActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_ADMIN, collectionAdminActionSet);
addMenuActionSet(ContextMenuKind.PROCESS_ADMIN, processResourceAdminActionSet);
addMenuActionSet(ContextMenuKind.PROJECT_ADMIN, projectAdminActionSet);
+addMenuActionSet(ContextMenuKind.FILTER_GROUP_ADMIN, filterGroupAdminActionSet);
storeRedirects();
? error.errors[0]
: error.message}`,
kind: SnackbarKind.ERROR,
- hideDuration: 8000})
+ hideDuration: 8000
+ })
);
}
}
import { KeepServiceResource } from '~/models/keep-services';
import { ProcessResource } from '~/models/process';
import { CollectionResource } from '~/models/collection';
-import { GroupResource } from '~/models/group';
+import { GroupClass, GroupResource } from '~/models/group';
import { GroupContentsResource } from '~/services/groups-service/groups-service';
export const contextMenuActions = unionize({
ownerUuid: string;
description?: string;
kind: ResourceKind,
- menuKind: ContextMenuKind;
+ menuKind: ContextMenuKind | string;
isTrashed?: boolean;
isEditable?: boolean;
outputUuid?: string;
kind: res.kind,
menuKind,
ownerUuid: res.ownerUuid,
- isTrashed: ('isTrashed' in res) ? res.isTrashed: false,
+ isTrashed: ('isTrashed' in res) ? res.isTrashed : false,
}));
}
};
}
};
-export const resourceUuidToContextMenuKind = (uuid: string) =>
+export const resourceUuidToContextMenuKind = (uuid: string, readonly = false) =>
(dispatch: Dispatch, getState: () => RootState) => {
const { isAdmin: isAdminUser, uuid: userUuid } = getState().auth.user!;
const kind = extractUuidKind(uuid);
const resource = getResourceWithEditableStatus<GroupResource & EditableResource>(uuid, userUuid)(getState().resources);
- const isEditable = isAdminUser || (resource || {} as EditableResource).isEditable;
+
+ const isEditable = (isAdminUser || (resource || {} as EditableResource).isEditable) && !readonly;
switch (kind) {
case ResourceKind.PROJECT:
- return !isAdminUser
- ? isEditable
- ? ContextMenuKind.PROJECT
- : ContextMenuKind.READONLY_PROJECT
- : ContextMenuKind.PROJECT_ADMIN;
+ return (isAdminUser && !readonly)
+ ? (resource && resource.groupClass !== GroupClass.FILTER)
+ ? ContextMenuKind.PROJECT_ADMIN
+ : ContextMenuKind.FILTER_GROUP_ADMIN
+ : isEditable
+ ? (resource && resource.groupClass !== GroupClass.FILTER)
+ ? ContextMenuKind.PROJECT
+ : ContextMenuKind.FILTER_GROUP
+ : ContextMenuKind.READONLY_PROJECT;
case ResourceKind.COLLECTION:
const c = getResource<CollectionResource>(uuid)(getState().resources);
if (c === undefined) { return; }
? ContextMenuKind.OLD_VERSION_COLLECTION
: (isTrashed && isEditable)
? ContextMenuKind.TRASHED_COLLECTION
- : isAdminUser
+ : (isAdminUser && !readonly)
? ContextMenuKind.COLLECTION_ADMIN
: isEditable
? ContextMenuKind.COLLECTION
: ContextMenuKind.READONLY_COLLECTION;
case ResourceKind.PROCESS:
- return !isAdminUser
- ? ContextMenuKind.PROCESS_RESOURCE
- : ContextMenuKind.PROCESS_ADMIN;
+ return (isAdminUser && !readonly)
+ ? ContextMenuKind.PROCESS_ADMIN
+ : readonly
+ ? ContextMenuKind.READONLY_PROCESS_RESOURCE
+ : ContextMenuKind.PROCESS_RESOURCE;
case ResourceKind.USER:
return ContextMenuKind.ROOT_PROJECT;
case ResourceKind.LINK:
import { ResourceKind } from '~/models/resource';
import { GroupContentsResourcePrefix } from '~/services/groups-service/groups-service';
import { GroupClass } from '~/models/group';
+ import { CategoriesListReducer } from '~/common/plugintypes';
+ import { pluginConfig } from '~/plugins';
export enum SidePanelTreeCategory {
PROJECTS = 'Projects',
return [];
};
- const SIDE_PANEL_CATEGORIES = [
+ let SIDE_PANEL_CATEGORIES: string[] = [
+ SidePanelTreeCategory.PROJECTS,
+ SidePanelTreeCategory.SHARED_WITH_ME,
SidePanelTreeCategory.PUBLIC_FAVORITES,
SidePanelTreeCategory.FAVORITES,
SidePanelTreeCategory.WORKFLOWS,
SidePanelTreeCategory.ALL_PROCESSES,
- SidePanelTreeCategory.TRASH,
+ SidePanelTreeCategory.TRASH
];
+ const reduceCatsFn: (a: string[],
+ b: CategoriesListReducer) => string[] = (a, b) => b(a);
+
+ SIDE_PANEL_CATEGORIES = pluginConfig.sidePanelCategories.reduce(reduceCatsFn, SIDE_PANEL_CATEGORIES);
+
export const isSidePanelTreeCategory = (id: string) => SIDE_PANEL_CATEGORIES.some(category => category === id);
+
export const initSidePanelTree = () =>
(dispatch: Dispatch, getState: () => RootState, { authService }: ServiceRepository) => {
const rootProjectUuid = getUserUuid(getState());
if (!rootProjectUuid) { return; }
- const nodes = SIDE_PANEL_CATEGORIES.map(id => initTreeNode({ id, value: id }));
- const projectsNode = initTreeNode({ id: rootProjectUuid, value: SidePanelTreeCategory.PROJECTS });
- const sharedNode = initTreeNode({ id: SidePanelTreeCategory.SHARED_WITH_ME, value: SidePanelTreeCategory.SHARED_WITH_ME });
+ const nodes = SIDE_PANEL_CATEGORIES.map(id => {
+ if (id === SidePanelTreeCategory.PROJECTS) {
+ return initTreeNode({ id: rootProjectUuid, value: SidePanelTreeCategory.PROJECTS });
+ } else {
+ return initTreeNode({ id, value: id });
+ }
+ });
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
id: '',
pickerId: SIDE_PANEL_TREE,
- nodes: [projectsNode, sharedNode, ...nodes]
+ nodes
}));
SIDE_PANEL_CATEGORIES.forEach(category => {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- id: category,
- pickerId: SIDE_PANEL_TREE,
- nodes: []
- }));
+ if (category !== SidePanelTreeCategory.PROJECTS && category !== SidePanelTreeCategory.SHARED_WITH_ME) {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
+ id: category,
+ pickerId: SIDE_PANEL_TREE,
+ nodes: []
+ }));
+ }
});
};
const params = {
filters: `[${new FilterBuilder()
.addIsA('uuid', ResourceKind.PROJECT)
- .addEqual('group_class', GroupClass.PROJECT)
+ .addIn('group_class', [GroupClass.PROJECT, GroupClass.FILTER])
.addDistinct('uuid', getState().auth.config.uuidPrefix + '-j7d0g-publicfavorites')
.getFilters()}]`,
order: new OrderBuilder<ProjectResource>()
import { runProcessPanelActions } from '~/store/run-process-panel/run-process-panel-actions';
import { getUserUuid } from '~/common/getuser';
import { matchProjectRoute } from '~/routes/routes';
-import { GroupResource } from '~/models/group';
+import { GroupClass, GroupResource } from '~/models/group';
import { ResourcesState, getResource } from '~/store/resources/resources';
import { extractUuidKind, ResourceKind } from '~/models/resource';
+ import { pluginConfig } from '~/plugins';
+ import { ElementListReducer } from '~/common/plugintypes';
+ import { Location } from 'history';
type CssRules = 'button' | 'menuItem' | 'icon';
});
interface SidePanelDataProps {
- location: any;
+ location: Location;
currentItemId: string;
resources: ResourcesState;
currentUserUUID: string | undefined;
const currentProject = getResource<GroupResource>(currentItemId)(resources);
if (currentProject &&
currentProject.writableBy.indexOf(currentUserUUID || '') >= 0 &&
- !isProjectTrashed(currentProject, resources)) {
+ !isProjectTrashed(currentProject, resources) &&
+ currentProject.groupClass !== GroupClass.FILTER) {
enabled = true;
}
}
+
+ for (const enableFn of pluginConfig.enableNewButtonMatchers) {
+ if (enableFn(location, currentItemId, currentUserUUID, resources)) {
+ enabled = true;
+ }
+ }
+
+ let menuItems = <>
+ <MenuItem data-cy='side-panel-new-collection' className={classes.menuItem} onClick={this.handleNewCollectionClick}>
+ <CollectionIcon className={classes.icon} /> New collection
+ </MenuItem>
+ <MenuItem data-cy='side-panel-run-process' className={classes.menuItem} onClick={this.handleRunProcessClick}>
+ <ProcessIcon className={classes.icon} /> Run a process
+ </MenuItem>
+ <MenuItem data-cy='side-panel-new-project' className={classes.menuItem} onClick={this.handleNewProjectClick}>
+ <ProjectIcon className={classes.icon} /> New project
+ </MenuItem>
+ </>;
+
+ const reduceItemsFn: (a: React.ReactElement[], b: ElementListReducer) => React.ReactElement[] =
+ (a, b) => b(a, classes.menuItem);
+
+ menuItems = React.createElement(React.Fragment, null,
+ pluginConfig.newButtonMenuList.reduce(reduceItemsFn, React.Children.toArray(menuItems.props.children)));
+
return <Toolbar>
<Grid container>
<Grid container item xs alignItems="center" justify="flex-start">
onClose={this.handleClose}
onClick={this.handleClose}
transformOrigin={transformOrigin}>
- <MenuItem data-cy='side-panel-new-collection' className={classes.menuItem} onClick={this.handleNewCollectionClick}>
- <CollectionIcon className={classes.icon} /> New collection
- </MenuItem>
- <MenuItem data-cy='side-panel-run-process' className={classes.menuItem} onClick={this.handleRunProcessClick}>
- <ProcessIcon className={classes.icon} /> Run a process
- </MenuItem>
- <MenuItem data-cy='side-panel-new-project' className={classes.menuItem} onClick={this.handleNewProjectClick}>
- <ProjectIcon className={classes.icon} /> New project
- </MenuItem>
+ {menuItems}
</Menu>
</Grid>
</Grid>