cy.get('[data-cy=collection-files-panel]')
.should('contain', 'foo').and('contain', 'bar');
+ // Check that only old collection action are available on context menu
+ cy.get('[data-cy=collection-panel-options-btn]').click();
+ cy.get('[data-cy=context-menu]')
+ .should('contain', 'Restore version')
+ .and('not.contain', 'Add to favorites');
+ cy.get('body').click(); // Collapse the menu avoiding details panel expansion
+
// Click on "head version" link, confirm that it's the latest version.
cy.get('[data-cy=collection-info-panel]').contains('head version').click();
cy.get('[data-cy=collection-info-panel]')
cy.get('[data-cy=collection-files-panel]').
should('not.contain', 'foo').and('contain', 'bar');
+ // Check that old collection action isn't available on context menu
+ cy.get('[data-cy=collection-panel-options-btn]').click()
+ cy.get('[data-cy=context-menu]').should('not.contain', 'Restore version')
+ cy.get('body').click(); // Collapse the menu avoiding details panel expansion
+
// Make another change, confirm new version.
cy.get('[data-cy=collection-panel-options-btn]').click();
cy.get('[data-cy=context-menu]').contains('Edit collection').click();
// (and now an old version...)
cy.get('[data-cy=collection-version-browser-select-1]').rightclick()
cy.get('[data-cy=context-menu]')
- .should('contain', 'Add to favorites')
+ .should('not.contain', 'Add to favorites')
.and('contain', 'Make a copy')
.and('not.contain', 'Edit collection');
cy.get('body').click();
+
+ // Restore first version
+ cy.get('[data-cy=collection-version-browser]').within(() => {
+ cy.get('[data-cy=collection-version-browser-select-1]').click();
+ });
+ cy.get('[data-cy=collection-panel-options-btn]').click()
+ cy.get('[data-cy=context-menu]').contains('Restore version').click();
+ cy.get('[data-cy=confirmation-dialog]').should('contain', 'Restore version');
+ cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
+ cy.get('[data-cy=collection-info-panel]')
+ .should('not.contain', 'This is an old version');
+ cy.get('[data-cy=collection-version-number]').should('contain', '4');
+ cy.get('[data-cy=collection-info-panel]').should('contain', colName);
+ cy.get('[data-cy=collection-files-panel]')
+ .should('contain', 'foo').and('contain', 'bar');
});
});
})
"@types/react-router-dom": "4.3.1",
"@types/react-router-redux": "5.0.16",
"@types/redux-devtools": "3.0.44",
+ "@types/redux-mock-store": "1.0.2",
"@types/sinon": "7.5",
"@types/uuid": "3.4.4",
"axios-mock-adapter": "1.17.0",
"node-sass": "4.9.4",
"node-sass-chokidar": "1.3.4",
"redux-devtools": "3.4.1",
+ "redux-mock-store": "1.5.4",
"typescript": "3.1.1",
"wait-on": "4.0.2",
"yamljs": "0.3.0"
import Edit from '@material-ui/icons/Edit';
import ErrorRoundedIcon from '@material-ui/icons/ErrorRounded';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+import FlipToFront from '@material-ui/icons/FlipToFront';
import Folder from '@material-ui/icons/Folder';
import GetApp from '@material-ui/icons/GetApp';
import Help from '@material-ui/icons/Help';
export const RemoveFavoriteIcon: IconType = (props) => <Star {...props} />;
export const PublicFavoriteIcon: IconType = (props) => <Public {...props} />;
export const RenameIcon: IconType = (props) => <Edit {...props} />;
+export const RestoreVersionIcon: IconType = (props) => <FlipToFront {...props} />;
export const RestoreFromTrashIcon: IconType = (props) => <RestoreFromTrash {...props} />;
export const ReRunProcessIcon: IconType = (props) => <Cached {...props} />;
export const SearchIcon: IconType = (props) => <Search {...props} />;
import { collectionFilesActionSet, readOnlyCollectionFilesActionSet } from '~/views-components/context-menu/action-sets/collection-files-action-set';
import { collectionFilesItemActionSet, readOnlyCollectionFilesItemActionSet } from '~/views-components/context-menu/action-sets/collection-files-item-action-set';
import { collectionFilesNotSelectedActionSet } from '~/views-components/context-menu/action-sets/collection-files-not-selected-action-set';
-import { collectionActionSet, readOnlyCollectionActionSet } from '~/views-components/context-menu/action-sets/collection-action-set';
+import { collectionActionSet, collectionAdminActionSet, oldCollectionVersionActionSet, readOnlyCollectionActionSet } from '~/views-components/context-menu/action-sets/collection-action-set';
import { processActionSet } from '~/views-components/context-menu/action-sets/process-action-set';
import { loadWorkbench } from '~/store/workbench/workbench-actions';
import { Routes } from '~/routes/routes';
import { groupMemberActionSet } from '~/views-components/context-menu/action-sets/group-member-action-set';
import { linkActionSet } from '~/views-components/context-menu/action-sets/link-action-set';
import { loadFileViewersConfig } from '~/store/file-viewers/file-viewers-actions';
-import { collectionAdminActionSet } from '~/views-components/context-menu/action-sets/collection-admin-action-set';
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 { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions";
addMenuActionSet(ContextMenuKind.READONLY_COLLECTION_FILES_ITEM, readOnlyCollectionFilesItemActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION, collectionActionSet);
addMenuActionSet(ContextMenuKind.READONLY_COLLECTION, readOnlyCollectionActionSet);
+addMenuActionSet(ContextMenuKind.OLD_VERSION_COLLECTION, oldCollectionVersionActionSet);
addMenuActionSet(ContextMenuKind.TRASHED_COLLECTION, trashedCollectionActionSet);
addMenuActionSet(ContextMenuKind.PROCESS, processActionSet);
addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet);
const mapped = mapTreeValues(services.collectionService.extendFileURL)(sorted);
dispatch(collectionPanelFilesAction.SET_COLLECTION_FILES(mapped));
dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PANEL_LOAD_FILES));
- }).catch(e => {
+ }).catch(() => {
dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PANEL_LOAD_FILES));
dispatch(snackbarActions.OPEN_SNACKBAR({
- message: `Error getting file list: ${e.errors[0]}`,
+ message: `Error getting file list`,
hideDuration: 2000,
kind: SnackbarKind.ERROR }));
});
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { snackbarActions, SnackbarKind } from "../snackbar/snackbar-actions";
+import { resourcesActions } from "../resources/resources-actions";
+import { navigateTo } from "../navigation/navigation-action";
+import { dialogActions } from "../dialog/dialog-actions";
+
+export const COLLECTION_RESTORE_VERSION_DIALOG = 'collectionRestoreVersionDialog';
+
+export const openRestoreCollectionVersionDialog = (uuid: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(dialogActions.OPEN_DIALOG({
+ id: COLLECTION_RESTORE_VERSION_DIALOG,
+ data: {
+ title: 'Restore version',
+ text: "This will copy the content of the selected version to the head. To make a new collection with the content of the selected version, use 'Make a copy' instead.",
+ confirmButtonLabel: 'Restore',
+ uuid
+ }
+ }));
+ };
+
+export const restoreVersion = (resourceUuid: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ try {
+ // Request que entire record because stored old versions usually
+ // don't include the manifest_text field.
+ const oldVersion = await services.collectionService.get(resourceUuid);
+ const { uuid, version, ...rest} = oldVersion;
+ const headVersion = await services.collectionService.update(
+ oldVersion.currentVersionUuid,
+ { ...rest }
+ );
+ dispatch(resourcesActions.SET_RESOURCES([headVersion]));
+ dispatch<any>(navigateTo(headVersion.uuid));
+ } catch (e) {
+ dispatch(snackbarActions.OPEN_SNACKBAR({
+ message: `Couldn't restore version: ${e.errors[0]}`,
+ hideDuration: 2000,
+ kind: SnackbarKind.ERROR
+ }));
+ }
+ };
//
// SPDX-License-Identifier: AGPL-3.0
-import * as resource from '~/models/resource';
import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
-import { resourceKindToContextMenuKind } from './context-menu-actions';
+import { resourceUuidToContextMenuKind } from './context-menu-actions';
+import configureStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
describe('context-menu-actions', () => {
- describe('resourceKindToContextMenuKind', () => {
- const uuid = '123';
-
- describe('ResourceKind.PROJECT', () => {
- beforeEach(() => {
- // setup
- jest.spyOn(resource, 'extractUuidKind')
- .mockImplementation(() => resource.ResourceKind.PROJECT);
- });
-
- it('should return ContextMenuKind.PROJECT_ADMIN', () => {
- // given
- const isAdmin = true;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin);
-
- // then
- expect(result).toEqual(ContextMenuKind.PROJECT_ADMIN);
- });
-
- it('should return ContextMenuKind.PROJECT', () => {
- // given
- const isAdmin = false;
- const isEditable = true;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin, isEditable);
-
- // then
- expect(result).toEqual(ContextMenuKind.PROJECT);
- });
-
- it('should return ContextMenuKind.READONLY_PROJECT', () => {
- // given
- const isAdmin = false;
- const isEditable = false;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin, isEditable);
-
- // then
- expect(result).toEqual(ContextMenuKind.READONLY_PROJECT);
- });
- });
-
- describe('ResourceKind.COLLECTION', () => {
- beforeEach(() => {
- // setup
- jest.spyOn(resource, 'extractUuidKind')
- .mockImplementation(() => resource.ResourceKind.COLLECTION);
- });
-
- it('should return ContextMenuKind.COLLECTION_ADMIN', () => {
- // given
- const isAdmin = true;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin);
-
- // then
- expect(result).toEqual(ContextMenuKind.COLLECTION_ADMIN);
- });
-
- it('should return ContextMenuKind.COLLECTION', () => {
- // given
- const isAdmin = false;
- const isEditable = true;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin, isEditable);
-
- // then
- expect(result).toEqual(ContextMenuKind.COLLECTION);
- });
-
- it('should return ContextMenuKind.READONLY_COLLECTION', () => {
- // given
- const isAdmin = false;
- const isEditable = false;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin, isEditable);
-
- // then
- expect(result).toEqual(ContextMenuKind.READONLY_COLLECTION);
- });
- });
-
- describe('ResourceKind.PROCESS', () => {
- beforeEach(() => {
- // setup
- jest.spyOn(resource, 'extractUuidKind')
- .mockImplementation(() => resource.ResourceKind.PROCESS);
- });
-
- it('should return ContextMenuKind.PROCESS_ADMIN', () => {
- // given
- const isAdmin = true;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin);
-
- // then
- expect(result).toEqual(ContextMenuKind.PROCESS_ADMIN);
- });
-
- it('should return ContextMenuKind.PROCESS_RESOURCE', () => {
- // given
- const isAdmin = false;
-
- // when
- const result = resourceKindToContextMenuKind(uuid, isAdmin);
-
- // then
- expect(result).toEqual(ContextMenuKind.PROCESS_RESOURCE);
- });
- });
-
- describe('ResourceKind.USER', () => {
- beforeEach(() => {
- // setup
- jest.spyOn(resource, 'extractUuidKind')
- .mockImplementation(() => resource.ResourceKind.USER);
- });
-
- it('should return ContextMenuKind.ROOT_PROJECT', () => {
- // when
- const result = resourceKindToContextMenuKind(uuid);
-
- // then
- expect(result).toEqual(ContextMenuKind.ROOT_PROJECT);
- });
- });
-
- describe('ResourceKind.LINK', () => {
- beforeEach(() => {
- // setup
- jest.spyOn(resource, 'extractUuidKind')
- .mockImplementation(() => resource.ResourceKind.LINK);
- });
-
- it('should return ContextMenuKind.LINK', () => {
- // when
- const result = resourceKindToContextMenuKind(uuid);
-
- // then
- expect(result).toEqual(ContextMenuKind.LINK);
+ describe('resourceUuidToContextMenuKind', () => {
+ const middlewares = [thunk];
+ const mockStore = configureStore(middlewares);
+ const userUuid = 'zzzzz-tpzed-bbbbbbbbbbbbbbb';
+ const otherUserUuid = 'zzzzz-tpzed-bbbbbbbbbbbbbbc';
+ const headCollectionUuid = 'zzzzz-4zz18-aaaaaaaaaaaaaaa';
+ const oldCollectionUuid = 'zzzzz-4zz18-aaaaaaaaaaaaaab';
+ const projectUuid = 'zzzzz-j7d0g-ccccccccccccccc';
+ const linkUuid = 'zzzzz-o0j2j-0123456789abcde';
+ const containerRequestUuid = 'zzzzz-xvhdp-0123456789abcde';
+
+ it('should return the correct menu kind', () => {
+ const cases = [
+ // resourceUuid, isAdminUser, isEditable, isTrashed, expected
+ [headCollectionUuid, false, true, true, ContextMenuKind.TRASHED_COLLECTION],
+ [headCollectionUuid, false, true, false, ContextMenuKind.COLLECTION],
+ [headCollectionUuid, false, false, true, ContextMenuKind.READONLY_COLLECTION],
+ [headCollectionUuid, false, false, false, ContextMenuKind.READONLY_COLLECTION],
+ [headCollectionUuid, true, true, true, ContextMenuKind.TRASHED_COLLECTION],
+ [headCollectionUuid, true, true, false, ContextMenuKind.COLLECTION_ADMIN],
+ [headCollectionUuid, true, false, true, ContextMenuKind.TRASHED_COLLECTION],
+ [headCollectionUuid, true, false, false, ContextMenuKind.COLLECTION_ADMIN],
+
+ [oldCollectionUuid, false, true, true, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, false, true, false, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, false, false, true, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, false, false, false, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, true, true, true, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, true, true, false, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, true, false, true, ContextMenuKind.OLD_VERSION_COLLECTION],
+ [oldCollectionUuid, true, false, false, ContextMenuKind.OLD_VERSION_COLLECTION],
+
+ // FIXME: WB2 doesn't currently have context menu for trashed projects
+ // [projectUuid, false, true, true, ContextMenuKind.TRASHED_PROJECT],
+ [projectUuid, false, true, false, ContextMenuKind.PROJECT],
+ [projectUuid, false, false, true, ContextMenuKind.READONLY_PROJECT],
+ [projectUuid, false, false, false, ContextMenuKind.READONLY_PROJECT],
+ // [projectUuid, true, true, true, ContextMenuKind.TRASHED_PROJECT],
+ [projectUuid, true, true, false, ContextMenuKind.PROJECT_ADMIN],
+ // [projectUuid, true, false, true, ContextMenuKind.TRASHED_PROJECT],
+ [projectUuid, true, false, false, ContextMenuKind.PROJECT_ADMIN],
+
+ [linkUuid, false, true, true, ContextMenuKind.LINK],
+ [linkUuid, false, true, false, ContextMenuKind.LINK],
+ [linkUuid, false, false, true, ContextMenuKind.LINK],
+ [linkUuid, false, false, false, ContextMenuKind.LINK],
+ [linkUuid, true, true, true, ContextMenuKind.LINK],
+ [linkUuid, true, true, false, ContextMenuKind.LINK],
+ [linkUuid, true, false, true, ContextMenuKind.LINK],
+ [linkUuid, true, false, false, ContextMenuKind.LINK],
+
+ [userUuid, false, true, true, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, false, true, false, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, false, false, true, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, false, false, false, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, true, true, true, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, true, true, false, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, true, false, true, ContextMenuKind.ROOT_PROJECT],
+ [userUuid, true, false, false, ContextMenuKind.ROOT_PROJECT],
+
+ [containerRequestUuid, false, true, true, ContextMenuKind.PROCESS_RESOURCE],
+ [containerRequestUuid, false, true, false, ContextMenuKind.PROCESS_RESOURCE],
+ [containerRequestUuid, false, false, true, ContextMenuKind.PROCESS_RESOURCE],
+ [containerRequestUuid, false, false, false, ContextMenuKind.PROCESS_RESOURCE],
+ [containerRequestUuid, true, true, true, ContextMenuKind.PROCESS_ADMIN],
+ [containerRequestUuid, true, true, false, ContextMenuKind.PROCESS_ADMIN],
+ [containerRequestUuid, true, false, true, ContextMenuKind.PROCESS_ADMIN],
+ [containerRequestUuid, true, false, false, ContextMenuKind.PROCESS_ADMIN],
+ ]
+
+ cases.forEach(([resourceUuid, isAdminUser, isEditable, isTrashed, expected]) => {
+ const initialState = {
+ resources: {
+ [headCollectionUuid]: {
+ uuid: headCollectionUuid,
+ ownerUuid: projectUuid,
+ currentVersionUuid: headCollectionUuid,
+ isTrashed: isTrashed,
+ },
+ [oldCollectionUuid]: {
+ uuid: oldCollectionUuid,
+ currentVersionUuid: headCollectionUuid,
+ isTrashed: isTrashed,
+
+ },
+ [projectUuid]: {
+ uuid: projectUuid,
+ ownerUuid: isEditable ? userUuid : otherUserUuid,
+ writableBy: isEditable ? [userUuid] : [otherUserUuid],
+ },
+ [linkUuid]: {
+ uuid: linkUuid,
+ },
+ [userUuid]: {
+ uuid: userUuid,
+ },
+ [containerRequestUuid]: {
+ uuid: containerRequestUuid,
+ ownerUuid: projectUuid,
+ },
+ },
+ auth: {
+ user: {
+ uuid: userUuid,
+ isAdmin: isAdminUser,
+ },
+ },
+ };
+ const store = mockStore(initialState);
+
+ const menuKind = store.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid as string))
+ try {
+ expect(menuKind).toBe(expected);
+ } catch (err) {
+ throw new Error(`menuKind for resource ${JSON.stringify(initialState.resources[resourceUuid as string])} expected to be ${expected} but got ${menuKind}.`);
+ }
});
});
});
import { Dispatch } from 'redux';
import { RootState } from '~/store/store';
import { getResource, getResourceWithEditableStatus } from '../resources/resources';
-import { ProjectResource } from '~/models/project';
import { UserResource } from '~/models/user';
import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
import { extractUuidKind, ResourceKind, EditableResource } from '~/models/resource';
import { VirtualMachinesResource } from '~/models/virtual-machines';
import { KeepServiceResource } from '~/models/keep-services';
import { ProcessResource } from '~/models/process';
+import { CollectionResource } from '~/models/collection';
+import { GroupResource } from '~/models/group';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
export const contextMenuActions = unionize({
OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
export const openProjectContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) =>
(dispatch: Dispatch, getState: () => RootState) => {
- const { isAdmin, uuid: userUuid } = getState().auth.user!;
- const res = getResourceWithEditableStatus<ProjectResource & EditableResource>(resourceUuid, userUuid)(getState().resources);
- const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (res || {} as EditableResource).isEditable);
+ const res = getResource<GroupContentsResource>(resourceUuid)(getState().resources);
+ const menuKind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (res && menuKind) {
dispatch<any>(openContextMenu(event, {
name: res.name,
kind: res.kind,
menuKind,
ownerUuid: res.ownerUuid,
- isTrashed: res.isTrashed
+ isTrashed: ('isTrashed' in res) ? res.isTrashed: false,
}));
}
};
}
};
-export const resourceKindToContextMenuKind = (uuid: string, isAdmin?: boolean, isEditable?: boolean) => {
- const kind = extractUuidKind(uuid);
- switch (kind) {
- case ResourceKind.PROJECT:
- return !isAdmin
- ? isEditable
- ? ContextMenuKind.PROJECT
- : ContextMenuKind.READONLY_PROJECT
- : ContextMenuKind.PROJECT_ADMIN;
- case ResourceKind.COLLECTION:
- return !isAdmin
- ? isEditable
- ? ContextMenuKind.COLLECTION
- : ContextMenuKind.READONLY_COLLECTION
- : ContextMenuKind.COLLECTION_ADMIN;
- case ResourceKind.PROCESS:
- return !isAdmin
- ? ContextMenuKind.PROCESS_RESOURCE
- : ContextMenuKind.PROCESS_ADMIN;
- case ResourceKind.USER:
- return ContextMenuKind.ROOT_PROJECT;
- case ResourceKind.LINK:
- return ContextMenuKind.LINK;
- default:
- return;
- }
-};
+export const resourceUuidToContextMenuKind = (uuid: string) =>
+ (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;
+ switch (kind) {
+ case ResourceKind.PROJECT:
+ return !isAdminUser
+ ? isEditable
+ ? ContextMenuKind.PROJECT
+ : ContextMenuKind.READONLY_PROJECT
+ : ContextMenuKind.PROJECT_ADMIN;
+ case ResourceKind.COLLECTION:
+ const c = getResource<CollectionResource>(uuid)(getState().resources);
+ if (c === undefined) { return; }
+ const isOldVersion = c.uuid !== c.currentVersionUuid;
+ const isTrashed = c.isTrashed;
+ return isOldVersion
+ ? ContextMenuKind.OLD_VERSION_COLLECTION
+ : (isTrashed && isEditable)
+ ? ContextMenuKind.TRASHED_COLLECTION
+ : isAdminUser
+ ? ContextMenuKind.COLLECTION_ADMIN
+ : isEditable
+ ? ContextMenuKind.COLLECTION
+ : ContextMenuKind.READONLY_COLLECTION;
+ case ResourceKind.PROCESS:
+ return !isAdminUser
+ ? ContextMenuKind.PROCESS_RESOURCE
+ : ContextMenuKind.PROCESS_ADMIN;
+ case ResourceKind.USER:
+ return ContextMenuKind.ROOT_PROJECT;
+ case ResourceKind.LINK:
+ return ContextMenuKind.LINK;
+ default:
+ return;
+ }
+ };
export const getResourceWithEditableStatus = <T extends EditableResource & GroupResource>(id: string, userUuid?: string) =>
(state: ResourcesState): T | undefined => {
+ if (state[id] === undefined) { return; }
+
const resource = JSON.parse(JSON.stringify(state[id] as T));
if (resource) {
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch, compose } from 'redux';
+import { connect } from "react-redux";
+import { ConfirmationDialog } from "~/components/confirmation-dialog/confirmation-dialog";
+import { withDialog, WithDialogProps } from "~/store/dialog/with-dialog";
+import { COLLECTION_RESTORE_VERSION_DIALOG, restoreVersion } from '~/store/collections/collection-version-actions';
+
+const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
+ onConfirm: () => {
+ props.closeDialog();
+ dispatch<any>(restoreVersion(props.data.uuid));
+ }
+});
+
+export const RestoreCollectionVersionDialog = compose(
+ withDialog(COLLECTION_RESTORE_VERSION_DIALOG),
+ connect(null, mapDispatchToProps)
+)(ConfirmationDialog);
\ No newline at end of file
//
// SPDX-License-Identifier: AGPL-3.0
-import { ContextMenuActionSet } from "../context-menu-action-set";
+import {
+ ContextMenuAction,
+ ContextMenuActionSet
+} from "../context-menu-action-set";
import { ToggleFavoriteAction } from "../actions/favorite-action";
import { toggleFavorite } from "~/store/favorites/favorites-actions";
-import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, AdvancedIcon, OpenIcon, Link } from "~/components/icon/icon";
+import {
+ RenameIcon,
+ ShareIcon,
+ MoveToIcon,
+ CopyIcon,
+ DetailsIcon,
+ AdvancedIcon,
+ OpenIcon,
+ Link,
+ RestoreVersionIcon
+} from "~/components/icon/icon";
import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
import { openAdvancedTabDialog } from "~/store/advanced-tab/advanced-tab";
import { toggleDetailsPanel } from '~/store/details-panel/details-panel-action';
import { copyToClipboardAction, openInNewTabAction } from "~/store/open-in-new-tab/open-in-new-tab.actions";
+import { openRestoreCollectionVersionDialog } from "~/store/collections/collection-version-actions";
+import { TogglePublicFavoriteAction } from "../actions/public-favorite-action";
+import { togglePublicFavorite } from "~/store/public-favorites/public-favorites-actions";
+import { publicFavoritePanelActions } from "~/store/public-favorites-panel/public-favorites-action";
-export const readOnlyCollectionActionSet: ContextMenuActionSet = [[
- {
- component: ToggleFavoriteAction,
- name: 'ToggleFavoriteAction',
- execute: (dispatch, resource) => {
- dispatch<any>(toggleFavorite(resource)).then(() => {
- dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
- });
- }
- },
+const toggleFavoriteAction: ContextMenuAction = {
+ component: ToggleFavoriteAction,
+ name: 'ToggleFavoriteAction',
+ execute: (dispatch, resource) => {
+ dispatch<any>(toggleFavorite(resource)).then(() => {
+ dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
+ });
+ }
+};
+
+const commonActionSet: ContextMenuActionSet = [[
{
icon: OpenIcon,
name: "Open in new tab",
},
]];
+export const readOnlyCollectionActionSet: ContextMenuActionSet = [[
+ ...commonActionSet.reduce((prev, next) => prev.concat(next), []),
+ toggleFavoriteAction,
+]];
+
export const collectionActionSet: ContextMenuActionSet = [
[
...readOnlyCollectionActionSet.reduce((prev, next) => prev.concat(next), []),
},
]
];
+
+export const collectionAdminActionSet: ContextMenuActionSet = [
+ [
+ ...collectionActionSet.reduce((prev, next) => prev.concat(next), []),
+ {
+ component: TogglePublicFavoriteAction,
+ name: 'TogglePublicFavoriteAction',
+ execute: (dispatch, resource) => {
+ dispatch<any>(togglePublicFavorite(resource)).then(() => {
+ dispatch<any>(publicFavoritePanelActions.REQUEST_ITEMS());
+ });
+ }
+ },
+ ]
+];
+
+export const oldCollectionVersionActionSet: ContextMenuActionSet = [
+ [
+ ...commonActionSet.reduce((prev, next) => prev.concat(next), []),
+ {
+ icon: RestoreVersionIcon,
+ name: 'Restore version',
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(openRestoreCollectionVersionDialog(uuid));
+ }
+ },
+ ]
+];
\ No newline at end of file
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { ContextMenuActionSet } from "../context-menu-action-set";
-import { ToggleFavoriteAction } from "../actions/favorite-action";
-import { toggleFavorite } from "~/store/favorites/favorites-actions";
-import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, AdvancedIcon, OpenIcon, Link } from "~/components/icon/icon";
-import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
-import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
-import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
-import { openCollectionCopyDialog } from "~/store/collections/collection-copy-actions";
-import { ToggleTrashAction } from "~/views-components/context-menu/actions/trash-action";
-import { toggleCollectionTrashed } from "~/store/trash/trash-actions";
-import { openSharingDialog } from '~/store/sharing-dialog/sharing-dialog-actions';
-import { openAdvancedTabDialog } from "~/store/advanced-tab/advanced-tab";
-import { toggleDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { TogglePublicFavoriteAction } from "~/views-components/context-menu/actions/public-favorite-action";
-import { publicFavoritePanelActions } from "~/store/public-favorites-panel/public-favorites-action";
-import { togglePublicFavorite } from "~/store/public-favorites/public-favorites-actions";
-import { copyToClipboardAction, openInNewTabAction } from "~/store/open-in-new-tab/open-in-new-tab.actions";
-
-export const collectionAdminActionSet: ContextMenuActionSet = [[
- {
- icon: RenameIcon,
- name: "Edit collection",
- execute: (dispatch, resource) => {
- dispatch<any>(openCollectionUpdateDialog(resource));
- }
- },
- {
- icon: OpenIcon,
- name: "Open in new tab",
- execute: (dispatch, resource) => {
- dispatch<any>(openInNewTabAction(resource));
- }
- },
- {
- icon: Link,
- name: "Copy to clipboard",
- execute: (dispatch, resource) => {
- dispatch<any>(copyToClipboardAction(resource));
- }
- },
- {
- icon: ShareIcon,
- name: "Share",
- execute: (dispatch, { uuid }) => {
- dispatch<any>(openSharingDialog(uuid));
- }
- },
- {
- component: ToggleFavoriteAction,
- name: 'ToggleFavoriteAction',
- execute: (dispatch, resource) => {
- dispatch<any>(toggleFavorite(resource)).then(() => {
- dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
- });
- }
- },
- {
- component: TogglePublicFavoriteAction,
- name: 'TogglePublicFavoriteAction',
- execute: (dispatch, resource) => {
- dispatch<any>(togglePublicFavorite(resource)).then(() => {
- dispatch<any>(publicFavoritePanelActions.REQUEST_ITEMS());
- });
- }
- },
- {
- icon: MoveToIcon,
- name: "Move to",
- execute: (dispatch, resource) => dispatch<any>(openMoveCollectionDialog(resource))
- },
- {
- icon: CopyIcon,
- name: "Make a copy",
- execute: (dispatch, resource) => {
- dispatch<any>(openCollectionCopyDialog(resource));
- }
-
- },
- {
- icon: DetailsIcon,
- name: "View details",
- execute: dispatch => {
- dispatch<any>(toggleDetailsPanel());
- }
- },
- {
- icon: AdvancedIcon,
- name: "Advanced",
- execute: (dispatch, resource) => {
- dispatch<any>(openAdvancedTabDialog(resource.uuid));
- }
- },
- {
- component: ToggleTrashAction,
- name: 'ToggleTrashAction',
- execute: (dispatch, resource) => {
- dispatch<any>(toggleCollectionTrashed(resource.uuid, resource.isTrashed!!));
- }
- },
-]];
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { ContextMenuActionSet } from "../context-menu-action-set";
-import { ToggleFavoriteAction } from "../actions/favorite-action";
-import { ToggleTrashAction } from "~/views-components/context-menu/actions/trash-action";
-import { toggleFavorite } from "~/store/favorites/favorites-actions";
-import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, AdvancedIcon, OpenIcon } from '~/components/icon/icon';
-import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
-import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
-import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
-import { openCollectionCopyDialog } from '~/store/collections/collection-copy-actions';
-import { toggleCollectionTrashed } from "~/store/trash/trash-actions";
-import { openSharingDialog } from "~/store/sharing-dialog/sharing-dialog-actions";
-import { openAdvancedTabDialog } from '~/store/advanced-tab/advanced-tab';
-import { toggleDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { openInNewTabAction } from "~/store/open-in-new-tab/open-in-new-tab.actions";
-
-export const collectionResourceActionSet: ContextMenuActionSet = [[
- {
- icon: RenameIcon,
- name: "Edit collection",
- execute: (dispatch, resource) => {
- dispatch<any>(openCollectionUpdateDialog(resource));
- }
- },
- {
- icon: ShareIcon,
- name: "Share",
- execute: (dispatch, { uuid }) => {
- dispatch<any>(openSharingDialog(uuid));
- }
- },
- {
- component: ToggleFavoriteAction,
- execute: (dispatch, resource) => {
- dispatch<any>(toggleFavorite(resource)).then(() => {
- dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
- });
- }
- },
- {
- icon: OpenIcon,
- name: "Open in new tab",
- execute: (dispatch, resource) => {
- dispatch<any>(openInNewTabAction(resource));
- }
- },
- {
- icon: MoveToIcon,
- name: "Move to",
- execute: (dispatch, resource) => {
- dispatch<any>(openMoveCollectionDialog(resource));
- }
- },
- {
- icon: CopyIcon,
- name: "Copy to project",
- execute: (dispatch, resource) => {
- dispatch<any>(openCollectionCopyDialog(resource));
- }
- },
- {
- icon: DetailsIcon,
- name: "View details",
- execute: dispatch => {
- dispatch<any>(toggleDetailsPanel());
- }
- },
- {
- icon: AdvancedIcon,
- name: "Advanced",
- execute: (dispatch, resource) => {
- dispatch<any>(openAdvancedTabDialog(resource.uuid));
- }
- },
- {
- component: ToggleTrashAction,
- execute: (dispatch, resource) => {
- dispatch<any>(toggleCollectionTrashed(resource.uuid, resource.isTrashed!!));
- }
- },
- // {
- // icon: RemoveIcon,
- // name: "Remove",
- // execute: (dispatch, resource) => {
- // // add code
- // }
- // }
-]];
COLLECTION = 'Collection',
COLLECTION_ADMIN = 'CollectionAdmin',
READONLY_COLLECTION = 'ReadOnlyCollection',
+ OLD_VERSION_COLLECTION = 'OldVersionCollection',
TRASHED_COLLECTION = 'TrashedCollection',
PROCESS = "Process",
PROCESS_ADMIN = 'ProcessAdmin',
import { formatDate, formatFileSize } from '~/common/formatters';
import { Dispatch } from 'redux';
import { navigateTo } from '~/store/navigation/navigation-action';
-import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
-import { ContextMenuKind } from '../context-menu/context-menu';
+import { openContextMenu, resourceUuidToContextMenuKind } from '~/store/context-menu/context-menu-actions';
export type CssRules = 'versionBrowserHeader' | 'versionBrowserItem';
interface CollectionVersionBrowserProps {
currentCollection: CollectionResource | undefined;
versions: CollectionResource[];
- isAdmin: boolean;
}
interface CollectionVersionBrowserDispatchProps {
showVersion: (c: CollectionResource) => void;
- handleContextMenu: (event: React.MouseEvent<HTMLElement>, collection: CollectionResource, menuKind: ContextMenuKind | undefined) => void;
+ handleContextMenu: (event: React.MouseEvent<HTMLElement>, collection: CollectionResource) => void;
}
const mapStateToProps = (state: RootState): CollectionVersionBrowserProps => {
const currentCollection = getResource<CollectionResource>(state.detailsPanel.resourceUuid)(state.resources);
- const isAdmin = state.auth.user!.isAdmin;
const versions = currentCollection
&& filterResources(rsc =>
(rsc as CollectionResource).currentVersionUuid === currentCollection.currentVersionUuid)(state.resources)
.sort((a: CollectionResource, b: CollectionResource) => b.version - a.version) as CollectionResource[]
|| [];
- return { currentCollection, versions, isAdmin };
+ return { currentCollection, versions };
};
const mapDispatchToProps = () =>
(dispatch: Dispatch): CollectionVersionBrowserDispatchProps => ({
showVersion: (collection) => dispatch<any>(navigateTo(collection.uuid)),
- handleContextMenu: (event: React.MouseEvent<HTMLElement>, collection: CollectionResource, menuKind: ContextMenuKind) => {
+ handleContextMenu: (event: React.MouseEvent<HTMLElement>, collection: CollectionResource) => {
+ const menuKind = dispatch<any>(resourceUuidToContextMenuKind(collection.uuid));
if (collection && menuKind) {
dispatch<any>(openContextMenu(event, {
name: collection.name,
const CollectionVersionBrowser = withStyles(styles)(
connect(mapStateToProps, mapDispatchToProps)(
- ({ currentCollection, versions, isAdmin, showVersion, handleContextMenu, classes }: CollectionVersionBrowserProps & CollectionVersionBrowserDispatchProps & WithStyles<CssRules>) => {
+ ({ currentCollection, versions, showVersion, handleContextMenu, classes }: CollectionVersionBrowserProps & CollectionVersionBrowserDispatchProps & WithStyles<CssRules>) => {
return <div data-cy="collection-version-browser">
<Grid container>
<Grid item xs={2}>
data-cy={`collection-version-browser-select-${item.version}`}
key={item.version}
onClick={e => showVersion(item)}
- onContextMenu={event => handleContextMenu(
- event,
- item,
- resourceKindToContextMenuKind(
- item.uuid,
- isAdmin,
- (item.uuid === item.currentVersionUuid))
- )}
+ onContextMenu={event => handleContextMenu(event, item)}
selected={isSelectedVersion}>
<Grid item xs={2}>
<Typography variant="caption" className={classes.versionBrowserItem}>
// SPDX-License-Identifier: AGPL-3.0
import * as React from 'react';
-import { StyleRulesCallback, WithStyles, withStyles, Grid, Button } from '@material-ui/core';
+import {
+ StyleRulesCallback,
+ WithStyles,
+ withStyles,
+ Grid,
+ Button
+} from '@material-ui/core';
import { CollectionIcon } from '~/components/icon/icon';
import { ArvadosTheme } from '~/common/custom-theme';
import { BackIcon } from '~/components/icon/icon';
import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
import { Dispatch } from 'redux';
-import { getIsAdmin } from '~/store/public-favorites/public-favorites-actions';
-import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
+import {
+ resourceUuidToContextMenuKind,
+ openContextMenu
+} from '~/store/context-menu/context-menu-actions';
import { ResourceKind } from '~/models/resource';
import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
import { connect } from 'react-redux';
import { DataColumns } from '~/components/data-table/data-table';
import { SortDirection } from '~/components/data-table/data-column';
import { createTree } from '~/models/tree';
-import { ResourceName, ResourceOwnerName, ResourceLastModifiedDate, ResourceStatus } from '~/views-components/data-explorer/renderers';
+import {
+ ResourceName,
+ ResourceOwnerName,
+ ResourceLastModifiedDate,
+ ResourceStatus
+} from '~/views-components/data-explorer/renderers';
type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressPanelActionProps => ({
onContextMenu: (event, resourceUuid) => {
- const isAdmin = dispatch<any>(getIsAdmin());
- const kind = resourceKindToContextMenuKind(resourceUuid, isAdmin);
+ const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (kind) {
dispatch<any>(openContextMenu(event, {
name: '',
import { CollectionTagForm } from './collection-tag-form';
import { deleteCollectionTag, navigateToProcess, collectionPanelActions } from '~/store/collection-panel/collection-panel-action';
import { getResource } from '~/store/resources/resources';
-import { openContextMenu } from '~/store/context-menu/context-menu-actions';
-import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+import { openContextMenu, resourceUuidToContextMenuKind } from '~/store/context-menu/context-menu-actions';
import { formatDate, formatFileSize } from "~/common/formatters";
import { openDetailsPanel } from '~/store/details-panel/details-panel-action';
import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
}
handleContextMenu = (event: React.MouseEvent<any>) => {
- const { uuid, ownerUuid, name, description, kind, isTrashed } = this.props.item;
- const { isWritable } = this.props;
+ const { uuid, ownerUuid, name, description, kind } = this.props.item;
+ const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(uuid));
const resource = {
uuid,
ownerUuid,
name,
description,
kind,
- menuKind: isWritable
- ? isTrashed
- ? ContextMenuKind.TRASHED_COLLECTION
- : ContextMenuKind.COLLECTION
- : ContextMenuKind.READONLY_COLLECTION
+ menuKind,
};
// Avoid expanding/collapsing the panel
event.stopPropagation();
import { RouteComponentProps } from 'react-router';
import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
import { SortDirection } from '~/components/data-table/data-column';
-import { ResourceKind, EditableResource } from '~/models/resource';
+import { ResourceKind } from '~/models/resource';
import { ArvadosTheme } from '~/common/custom-theme';
import { FAVORITE_PANEL_ID } from "~/store/favorite-panel/favorite-panel-action";
import {
ResourceType
} from '~/views-components/data-explorer/renderers';
import { FavoriteIcon } from '~/components/icon/icon';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
+import {
+ openContextMenu,
+ resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
import { navigateTo } from '~/store/navigation/navigation-action';
import { ContainerRequestState } from "~/models/container-request";
import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
import { createTree } from '~/models/tree';
import { getSimpleObjectTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
-import { getResourceWithEditableStatus, ResourcesState } from '~/store/resources/resources';
-import { ProjectResource } from '~/models/project';
+import { ResourcesState } from '~/store/resources/resources';
type CssRules = "toolbar" | "button";
interface FavoritePanelDataProps {
favorites: FavoritesState;
resources: ResourcesState;
- isAdmin: boolean;
userUuid: string;
}
const mapStateToProps = (state : RootState): FavoritePanelDataProps => ({
favorites: state.favorites,
resources: state.resources,
- isAdmin: state.auth.user!.isAdmin,
userUuid: state.auth.user!.uuid,
});
class extends React.Component<FavoritePanelProps> {
handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
- const { isAdmin, userUuid, resources } = this.props;
- const resource = getResourceWithEditableStatus<ProjectResource & EditableResource>(resourceUuid, userUuid)(resources);
- const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (resource || {} as EditableResource).isEditable);
+ const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (menuKind) {
this.props.dispatch<any>(openContextMenu(event, {
name: '',
import { Dispatch } from "redux";
import { connect } from "react-redux";
import { RootState } from '~/store/store';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
-import { LinkPanelRoot, LinkPanelRootActionProps, LinkPanelRootDataProps } from '~/views/link-panel/link-panel-root';
+import {
+ openContextMenu,
+ resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
+import {
+ LinkPanelRoot,
+ LinkPanelRootActionProps,
+ LinkPanelRootDataProps
+} from '~/views/link-panel/link-panel-root';
import { ResourceKind } from '~/models/resource';
const mapStateToProps = (state: RootState): LinkPanelRootDataProps => {
const mapDispatchToProps = (dispatch: Dispatch): LinkPanelRootActionProps => ({
onContextMenu: (event, resourceUuid) => {
- const kind = resourceKindToContextMenuKind(resourceUuid);
+ const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (kind) {
dispatch<any>(openContextMenu(event, {
name: '',
import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
import { ContainerRequestState } from '~/models/container-request';
import { SortDirection } from '~/components/data-table/data-column';
-import { ResourceKind, Resource, EditableResource } from '~/models/resource';
+import { ResourceKind, Resource } from '~/models/resource';
import {
ResourceFileSize,
ResourceLastModifiedDate,
} from '~/views-components/data-explorer/renderers';
import { ProjectIcon } from '~/components/icon/icon';
import { ResourceName } from '~/views-components/data-explorer/renderers';
-import { ResourcesState, getResourceWithEditableStatus } from '~/store/resources/resources';
+import {
+ ResourcesState,
+ getResource
+} from '~/store/resources/resources';
import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
-import { ProjectResource } from '~/models/project';
+import {
+ openContextMenu,
+ resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
import { navigateTo } from '~/store/navigation/navigation-action';
import { getProperty } from '~/store/properties/properties';
import { PROJECT_PANEL_CURRENT_UUID } from '~/store/project-panel/project-panel-action';
import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
import { ArvadosTheme } from "~/common/custom-theme";
import { createTree } from '~/models/tree';
-import { getInitialResourceTypeFilters, getInitialProcessStatusFilters } from '~/store/resource-type-filters/resource-type-filters';
+import {
+ getInitialResourceTypeFilters,
+ getInitialProcessStatusFilters
+} from '~/store/resource-type-filters/resource-type-filters';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
type CssRules = 'root' | "button";
connect((state: RootState) => ({
currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
resources: state.resources,
- isAdmin: state.auth.user!.isAdmin,
userUuid: state.auth.user!.uuid,
}))(
class extends React.Component<ProjectPanelProps> {
}
handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
- const { isAdmin, userUuid, resources } = this.props;
- const resource = getResourceWithEditableStatus<ProjectResource & EditableResource>(resourceUuid, userUuid)(resources);
- const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (resource || {} as EditableResource).isEditable);
+ const { resources } = this.props;
+ const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
+ const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (menuKind && resource) {
this.props.dispatch<any>(openContextMenu(event, {
name: resource.name,
uuid: resource.uuid,
ownerUuid: resource.ownerUuid,
- isTrashed: resource.isTrashed,
+ isTrashed: ('isTrashed' in resource) ? resource.isTrashed: false,
kind: resource.kind,
menuKind
}));
} from '~/views-components/data-explorer/renderers';
import { PublicFavoriteIcon } from '~/components/icon/icon';
import { Dispatch } from 'redux';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
+import {
+ openContextMenu,
+ resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
import { navigateTo } from '~/store/navigation/navigation-action';
import { ContainerRequestState } from "~/models/container-request";
import { getSimpleObjectTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
import { PUBLIC_FAVORITE_PANEL_ID } from '~/store/public-favorites-panel/public-favorites-action';
import { PublicFavoritesState } from '~/store/public-favorites/public-favorites-reducer';
-import { getIsAdmin } from '~/store/public-favorites/public-favorites-actions';
type CssRules = "toolbar" | "button";
const mapDispatchToProps = (dispatch: Dispatch): PublicFavoritePanelActionProps => ({
onContextMenu: (event, resourceUuid) => {
- const isAdmin = dispatch<any>(getIsAdmin());
- const kind = resourceKindToContextMenuKind(resourceUuid, isAdmin);
+ const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (kind) {
dispatch<any>(openContextMenu(event, {
name: '',
import { RootState } from '~/store/store';
import { ArvadosTheme } from '~/common/custom-theme';
import { ShareMeIcon } from '~/components/icon/icon';
-import { ResourcesState, getResourceWithEditableStatus } from '~/store/resources/resources';
+import { ResourcesState, getResource } from '~/store/resources/resources';
import { navigateTo } from "~/store/navigation/navigation-action";
import { loadDetailsPanel } from "~/store/details-panel/details-panel-action";
import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
-import { GroupResource } from '~/models/group';
-import { EditableResource } from '~/models/resource';
+import {
+ openContextMenu,
+ resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
type CssRules = "toolbar" | "button";
interface SharedWithMePanelDataProps {
resources: ResourcesState;
- isAdmin: boolean;
userUuid: string;
}
export const SharedWithMePanel = withStyles(styles)(
connect((state: RootState) => ({
resources: state.resources,
- isAdmin: state.auth.user!.isAdmin,
userUuid: state.auth.user!.uuid,
}))(
class extends React.Component<SharedWithMePanelProps> {
}
handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
- const { isAdmin, userUuid, resources } = this.props;
- const resource = getResourceWithEditableStatus<GroupResource & EditableResource>(resourceUuid, userUuid)(resources);
- const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (resource || {} as EditableResource).isEditable);
+ const { resources } = this.props;
+ const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
+ const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
if (menuKind && resource) {
this.props.dispatch<any>(openContextMenu(event, {
name: '',
uuid: resource.uuid,
ownerUuid: resource.ownerUuid,
- isTrashed: resource.isTrashed,
+ isTrashed: ('isTrashed' in resource) ? resource.isTrashed: false,
kind: resource.kind,
menuKind
}));
import { AllProcessesPanel } from '../all-processes-panel/all-processes-panel';
import { NotFoundPanel } from '../not-found-panel/not-found-panel';
import { AutoLogout } from '~/views-components/auto-logout/auto-logout';
+import { RestoreCollectionVersionDialog } from '~/views-components/collections-dialog/restore-version-dialog';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
<ProcessCommandDialog />
<ProcessInputDialog />
<ProjectPropertiesDialog />
+ <RestoreCollectionVersionDialog />
<RemoveApiClientAuthorizationDialog />
<RemoveComputeNodeDialog />
<RemoveGroupDialog />
"@types/react" "*"
redux "^3.6.0 || ^4.0.0"
+"@types/redux-mock-store@1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-1.0.2.tgz#c27d5deadfb29d8514bdb0fc2cadae6feea1922d"
+ integrity sha512-6LBtAQBN34i7SI5X+Qs4zpTEZO1tTDZ6sZ9fzFjYwTl3nLQXaBtwYdoV44CzNnyKu438xJ1lSIYyw0YMvunESw==
+ dependencies:
+ redux "^4.0.5"
+
"@types/shell-quote@1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@types/shell-quote/-/shell-quote-1.6.0.tgz#537b2949a2ebdcb0d353e448fee45b081021963f"
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+ integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
+
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
prop-types "^15.6.1"
react-lifecycles-compat "^3.0.4"
+redux-mock-store@1.5.4:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.4.tgz#90d02495fd918ddbaa96b83aef626287c9ab5872"
+ integrity sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==
+ dependencies:
+ lodash.isplainobject "^4.0.6"
+
redux-thunk@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
+redux@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+ integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
reflect.ownkeys@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"