import * as React from 'react';
import { Dispatch } from 'redux';
import { RootStore, RootState } from '~/store/store';
+import { ResourcesState } from '~/store/resources/resources';
+import { Location } from 'history';
-export type RouteListReducer = (startingList: React.ReactElement[]) => React.ReactElement[];
+export type ElementListReducer = (startingList: React.ReactElement[]) => React.ReactElement[];
export type CategoriesListReducer = (startingList: string[]) => string[];
export type NavigateMatcher = (dispatch: Dispatch, getState: () => RootState, uuid: string) => boolean;
export type LocationChangeMatcher = (store: RootStore, pathname: string) => boolean;
+export type EnableNew = (location: Location, currentItemId: string, currentUserUUID: string | undefined, resources: ResourcesState) => boolean;
export interface PluginConfig {
// Customize the list of possible center panels by adding or removing Route components.
- centerPanelList: RouteListReducer[];
+ centerPanelList: ElementListReducer[];
// Customize the list of side panel categories
sidePanelCategories: CategoriesListReducer[];
appBarMiddle?: React.ReactElement;
appBarRight?: React.ReactElement;
+
+ // Customize the list menu items in the account menu
+ accountMenuList: ElementListReducer[];
+
+ enableNewButtonMatchers: EnableNew[];
+
+ newButtonMenuList: ElementListReducer[];
}
appBarLeft: undefined,
appBarMiddle: undefined,
appBarRight: undefined,
+ accountMenuList: [],
+ enableNewButtonMatchers: [],
+ newButtonMenuList: []
};
// Starting here, import and register your Workbench 2 plugins. //
pluginConfig.sidePanelCategories.push((cats: string[]): string[] => []);
+ pluginConfig.accountMenuList.push((elms) => []);
+ pluginConfig.newButtonMenuList.push((elms) => []);
+
pluginConfig.appBarLeft = <span />;
pluginConfig.appBarMiddle = <span />;
pluginConfig.appBarRight = <span />;
import { RootStore } from '~/store/store';
import { activateSidePanelTreeItem } from '~/store/side-panel-tree/side-panel-tree-actions';
import { setSidePanelBreadcrumbs } from '~/store/breadcrumbs/breadcrumbs-actions';
+import { DispatchProp, connect } from 'react-redux';
+import { MenuItem } from "@material-ui/core";
+import { propertiesActions } from '~/store/properties/properties-actions';
+import { Location } from 'history';
const categoryName = "Plugin Example";
export const routePath = "/examplePlugin";
+const propertyKey = "Example_menu_item_pressed_count";
-const ExamplePluginMainPanel = (props: {}) => {
- return <Typography>
- This is a example main panel plugin.
- </Typography>;
+interface ExampleProps {
+ pressedCount: number;
+}
+
+const exampleMapStateToProps = (state: RootState) => ({ pressedCount: state.properties[propertyKey] || 0 });
+
+const incrementPressedCount = (dispatch: Dispatch, pressedCount: number) => {
+ dispatch(propertiesActions.SET_PROPERTY({ key: propertyKey, value: pressedCount + 1 }));
};
+const ExampleMenuComponent = connect(exampleMapStateToProps)(
+ ({ pressedCount, dispatch }: ExampleProps & DispatchProp<any>) =>
+ <MenuItem onClick={() => incrementPressedCount(dispatch, pressedCount)}>Example menu item</MenuItem >
+);
+
+const ExamplePluginMainPanel = connect(exampleMapStateToProps)(
+ ({ pressedCount }: ExampleProps) =>
+ <Typography>
+ This is a example main panel plugin. The example menu item has been pressed {pressedCount} times.
+ </Typography>);
+
export const register = (pluginConfig: PluginConfig) => {
pluginConfig.centerPanelList.push((elms) => {
return elms;
});
+ pluginConfig.accountMenuList.push((elms) => {
+ elms.push(<ExampleMenuComponent />);
+ return elms;
+ });
+
+ pluginConfig.newButtonMenuList.push((elms) => {
+ elms.push(<ExampleMenuComponent />);
+ return elms;
+ });
+
pluginConfig.navigateToHandlers.push((dispatch: Dispatch, getState: () => RootState, uuid: string) => {
if (uuid === categoryName) {
dispatch(push(routePath));
}
return false;
});
+
+ pluginConfig.enableNewButtonMatchers.push((location: Location) => (!!matchPath(location.pathname, { path: routePath, exact: true })));
};
navigateToLinkAccount
} from '~/store/navigation/navigation-action';
import { openUserVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions";
+import { pluginConfig } from '~/plugins';
+import { ElementListReducer } from '~/common/plugintypes';
interface AccountMenuProps {
user?: User;
});
export const AccountMenuComponent =
- ({ user, dispatch, currentRoute, workbenchURL, apiToken, localCluster, classes }: AccountMenuProps & DispatchProp<any> & WithStyles<CssRules>) =>
- user
- ? <DropdownMenu
- icon={<UserPanelIcon />}
- id="account-menu"
- title="Account Management"
- key={currentRoute}>
- <MenuItem disabled>
- {getUserDisplayName(user)} {user.uuid.substr(0, 5) !== localCluster && `(${user.uuid.substr(0, 5)})`}
- </MenuItem>
- {user.isActive ? <>
- <MenuItem onClick={() => dispatch(openUserVirtualMachines())}>Virtual Machines</MenuItem>
- {!user.isAdmin && <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</MenuItem>}
- <MenuItem onClick={() => {
- dispatch<any>(getNewExtraToken(true));
- dispatch(openTokenDialog);
- }}>Get API token</MenuItem>
- <MenuItem onClick={() => dispatch(navigateToSshKeysUser)}>Ssh Keys</MenuItem>
- <MenuItem onClick={() => dispatch(navigateToSiteManager)}>Site Manager</MenuItem>
- <MenuItem onClick={() => dispatch(navigateToMyAccount)}>My account</MenuItem>
- <MenuItem onClick={() => dispatch(navigateToLinkAccount)}>Link account</MenuItem>
- </> : null}
+ ({ user, dispatch, currentRoute, workbenchURL, apiToken, localCluster, classes }: AccountMenuProps & DispatchProp<any> & WithStyles<CssRules>) => {
+ let accountMenuItems = <>
+ <MenuItem onClick={() => dispatch(openUserVirtualMachines())}>Virtual Machines</MenuItem>
+ <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</MenuItem>
+ <MenuItem onClick={() => {
+ dispatch<any>(getNewExtraToken(true));
+ dispatch(openTokenDialog);
+ }}>Get API token</MenuItem>
+ <MenuItem onClick={() => dispatch(navigateToSshKeysUser)}>Ssh Keys</MenuItem>
+ <MenuItem onClick={() => dispatch(navigateToSiteManager)}>Site Manager</MenuItem>
+ <MenuItem onClick={() => dispatch(navigateToMyAccount)}>My account</MenuItem>
+ <MenuItem onClick={() => dispatch(navigateToLinkAccount)}>Link account</MenuItem>
<MenuItem>
<a href={`${workbenchURL.replace(/\/$/, "")}/${wb1URL(currentRoute)}?api_token=${apiToken}`}
className={classes.link}>
Switch to Workbench v1</a></MenuItem>
- <Divider />
- <MenuItem data-cy="logout-menuitem"
- onClick={() => dispatch(authActions.LOGOUT({deleteLinkData: true}))}>
- Logout
- </MenuItem>
- </DropdownMenu>
- : null;
+ </>;
-export const AccountMenu = withStyles(styles)( connect(mapStateToProps)(AccountMenuComponent) );
+ const reduceItemsFn: (a: React.ReactElement[],
+ b: ElementListReducer) => React.ReactElement[] = (a, b) => b(a);
+
+ accountMenuItems = React.createElement(React.Fragment, null,
+ pluginConfig.accountMenuList.reduce(reduceItemsFn, React.Children.toArray(accountMenuItems.props.children)));
+
+ return user
+ ? <DropdownMenu
+ icon={<UserPanelIcon />}
+ id="account-menu"
+ title="Account Management"
+ key={currentRoute}>
+ <MenuItem disabled>
+ {getUserDisplayName(user)} {user.uuid.substr(0, 5) !== localCluster && `(${user.uuid.substr(0, 5)})`}
+ </MenuItem>
+ {user.isActive && accountMenuItems}
+ <Divider />
+ <MenuItem data-cy="logout-menuitem"
+ onClick={() => dispatch(authActions.LOGOUT({ deleteLinkData: true }))}>
+ Logout
+ </MenuItem>
+ </DropdownMenu>
+ : null;
+ };
+
+export const AccountMenu = withStyles(styles)(connect(mapStateToProps)(AccountMenuComponent));
<Typography variant='h6' color="inherit" noWrap>
<Link to={Routes.ROOT} className={props.classes.link}>
<span dangerouslySetInnerHTML={{ __html: props.siteBanner }} /> ({props.uuidPrefix})
- </Link>
+ </Link>
</Typography>
<Typography variant="caption" color="inherit">{props.buildInfo}</Typography>
</Grid>}
alignItems="center"
justify="flex-end"
wrap="nowrap">
- {pluginConfig.appBarRight ||
- (props.user ? <>
- <NotificationsMenu />
- <AccountMenu />
- {props.user.isAdmin && <AdminMenu />}
- <HelpMenu />
- </> :
- <HelpMenu />)}
+ {props.user ? <>
+ <NotificationsMenu />
+ <AccountMenu />
+ {pluginConfig.appBarRight ||
+ <>
+ {props.user.isAdmin && <AdminMenu />}
+ <HelpMenu />
+ </>}
+ </> :
+ pluginConfig.appBarRight || <HelpMenu />
+ }
</Grid>
</Grid>
</Toolbar>
import { 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;
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);
+
+ 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>
}
}
)
-);
\ No newline at end of file
+);
import { RestoreCollectionVersionDialog } from '~/views-components/collections-dialog/restore-version-dialog';
import { WebDavS3InfoDialog } from '~/views-components/webdav-s3-dialog/webdav-s3-dialog';
import { pluginConfig } from '~/plugins';
-import { RouteListReducer } from '~/common/plugintypes';
+import { ElementListReducer } from '~/common/plugintypes';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
</>;
const reduceRoutesFn: (a: React.ReactElement[],
- b: RouteListReducer) => React.ReactElement[] = (a, b) => b(a);
+ b: ElementListReducer) => React.ReactElement[] = (a, b) => b(a);
routes = React.createElement(React.Fragment, null, pluginConfig.centerPanelList.reduce(reduceRoutesFn, React.Children.toArray(routes.props.children)));