mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
folder permissions in redux
This commit is contained in:
142
public/app/core/components/PermissionList/AddPermission.tsx
Normal file
142
public/app/core/components/PermissionList/AddPermission.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
import React, { Component } from 'react';
|
||||
import { UserPicker, User } from 'app/core/components/Picker/UserPicker';
|
||||
import { TeamPicker, Team } from 'app/core/components/Picker/TeamPicker';
|
||||
import DescriptionPicker, { OptionWithDescription } from 'app/core/components/Picker/DescriptionPicker';
|
||||
import {
|
||||
dashboardPermissionLevels,
|
||||
dashboardAclTargets,
|
||||
AclTarget,
|
||||
PermissionLevel,
|
||||
NewDashboardAclItem,
|
||||
} from 'app/types/acl';
|
||||
|
||||
export interface Props {
|
||||
onAddPermission: (item: NewDashboardAclItem) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
class AddPermissions extends Component<Props, NewDashboardAclItem> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = this.getCleanState();
|
||||
}
|
||||
|
||||
getCleanState() {
|
||||
return {
|
||||
userId: 0,
|
||||
teamId: 0,
|
||||
role: '',
|
||||
type: AclTarget.Team,
|
||||
permission: PermissionLevel.View,
|
||||
};
|
||||
}
|
||||
|
||||
onTypeChanged = evt => {
|
||||
this.setState({ type: evt.target.value as AclTarget });
|
||||
};
|
||||
|
||||
onUserSelected = (user: User) => {
|
||||
this.setState({
|
||||
userId: user ? user.id : 0,
|
||||
teamId: 0,
|
||||
});
|
||||
};
|
||||
|
||||
onTeamSelected = (team: Team) => {
|
||||
this.setState({
|
||||
userId: 0,
|
||||
teamId: team ? team.id : 0,
|
||||
});
|
||||
};
|
||||
|
||||
onPermissionChanged = (permission: OptionWithDescription) => {
|
||||
this.setState({ permission: permission.value });
|
||||
};
|
||||
|
||||
onSubmit = async evt => {
|
||||
evt.preventDefault();
|
||||
await this.props.onAddPermission(this.state);
|
||||
this.setState(this.getCleanState());
|
||||
};
|
||||
|
||||
isValid() {
|
||||
switch (this.state.type) {
|
||||
case AclTarget.Team:
|
||||
return this.state.teamId > 0;
|
||||
case AclTarget.User:
|
||||
return this.state.userId > 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onCancel } = this.props;
|
||||
const newItem = this.state;
|
||||
const pickerClassName = 'width-20';
|
||||
const isValid = this.isValid();
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline cta-form">
|
||||
<button className="cta-form__close btn btn-transparent" onClick={onCancel}>
|
||||
<i className="fa fa-close" />
|
||||
</button>
|
||||
<form name="addPermission" onSubmit={this.onSubmit}>
|
||||
<h5>Add Permission For</h5>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-select-wrapper">
|
||||
<select className="gf-form-input gf-size-auto" value={newItem.type} onChange={this.onTypeChanged}>
|
||||
{dashboardAclTargets.map((option, idx) => {
|
||||
return (
|
||||
<option key={idx} value={option.value}>
|
||||
{option.text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{newItem.type === AclTarget.User ? (
|
||||
<div className="gf-form">
|
||||
<UserPicker
|
||||
onSelected={this.onUserSelected}
|
||||
value={newItem.userId.toString()}
|
||||
className={pickerClassName}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{newItem.type === AclTarget.Team ? (
|
||||
<div className="gf-form">
|
||||
<TeamPicker
|
||||
onSelected={this.onTeamSelected}
|
||||
value={newItem.teamId.toString()}
|
||||
className={pickerClassName}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={dashboardPermissionLevels}
|
||||
onSelected={this.onPermissionChanged}
|
||||
value={newItem.permission}
|
||||
disabled={false}
|
||||
className={'gf-form-input--form-dropdown-right'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="gf-form">
|
||||
<button data-save-permission className="btn btn-success" type="submit" disabled={!isValid}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddPermissions;
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import PermissionsListItem from './PermissionListItem';
|
||||
import DisabledPermissionsListItem from './DisabledPermissionListItem';
|
||||
import { DashboardAcl, FolderInfo } from 'app/types';
|
||||
import { FolderInfo } from 'app/types';
|
||||
import { DashboardAcl } from 'app/types/acl';
|
||||
|
||||
export interface Props {
|
||||
items: DashboardAcl[];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
|
||||
import { dashboardPermissionLevels } from 'app/types/acl';
|
||||
import { DashboardAcl, FolderInfo, PermissionLevel } from 'app/types';
|
||||
import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
|
||||
import { FolderInfo } from 'app/types';
|
||||
|
||||
const setClassNameHelper = inherited => {
|
||||
return inherited ? 'gf-form-disabled' : '';
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import PageHeader from 'app/core/components/PageHeader/PageHeader';
|
||||
import Permissions from 'app/core/components/Permissions/Permissions';
|
||||
import Tooltip from 'app/core/components/Tooltip/Tooltip';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
import AddPermissions from 'app/core/components/Permissions/AddPermissions';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { NavModel, StoreState, FolderState, DashboardAcl, PermissionLevel } from 'app/types';
|
||||
import { getFolderByUid, getFolderPermissions, updateFolderPermission, removeFolderPermission } from './state/actions';
|
||||
import { NavModel, StoreState, FolderState } from 'app/types';
|
||||
import { DashboardAcl, PermissionLevel, NewDashboardAclItem } from 'app/types/acl';
|
||||
import {
|
||||
getFolderByUid,
|
||||
getFolderPermissions,
|
||||
updateFolderPermission,
|
||||
removeFolderPermission,
|
||||
addFolderPermission,
|
||||
} from './state/actions';
|
||||
import { getLoadingNav } from './state/navModel';
|
||||
import PermissionList from 'app/core/components/PermissionList/PermissionList';
|
||||
import AddPermission from 'app/core/components/PermissionList/AddPermission';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
@@ -21,13 +27,14 @@ export interface Props {
|
||||
getFolderPermissions: typeof getFolderPermissions;
|
||||
updateFolderPermission: typeof updateFolderPermission;
|
||||
removeFolderPermission: typeof removeFolderPermission;
|
||||
addFolderPermission: typeof addFolderPermission;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
isAdding: boolean;
|
||||
}
|
||||
|
||||
export class FolderPermissions extends Component<Props, State> {
|
||||
export class FolderPermissions extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -53,6 +60,14 @@ export class FolderPermissions extends Component<Props, State> {
|
||||
this.props.updateFolderPermission(item, level);
|
||||
};
|
||||
|
||||
onAddPermission = (newItem: NewDashboardAclItem) => {
|
||||
return this.props.addFolderPermission(newItem);
|
||||
};
|
||||
|
||||
onCancelAddPermission = () => {
|
||||
this.setState({ isAdding: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navModel, folder } = this.props;
|
||||
const { isAdding } = this.state;
|
||||
@@ -61,8 +76,7 @@ export class FolderPermissions extends Component<Props, State> {
|
||||
return <PageHeader model={navModel} />;
|
||||
}
|
||||
|
||||
const dashboardId = folder.id;
|
||||
const folderInfo = { title: folder.tile, url: folder.url, id: folder.id };
|
||||
const folderInfo = { title: folder.title, url: folder.url, id: folder.id };
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -78,6 +92,9 @@ export class FolderPermissions extends Component<Props, State> {
|
||||
<i className="fa fa-plus" /> Add Permission
|
||||
</button>
|
||||
</div>
|
||||
<SlideDown in={isAdding}>
|
||||
<AddPermission onAddPermission={this.onAddPermission} onCancel={this.onCancelAddPermission} />
|
||||
</SlideDown>
|
||||
<PermissionList
|
||||
items={folder.permissions}
|
||||
onRemoveItem={this.onRemoveItem}
|
||||
@@ -105,6 +122,7 @@ const mapDispatchToProps = {
|
||||
getFolderPermissions,
|
||||
updateFolderPermission,
|
||||
removeFolderPermission,
|
||||
addFolderPermission,
|
||||
};
|
||||
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(FolderPermissions));
|
||||
|
||||
@@ -15,6 +15,7 @@ const setup = (propOverrides?: object) => {
|
||||
url: 'url',
|
||||
hasChanged: false,
|
||||
version: 1,
|
||||
permissions: [],
|
||||
},
|
||||
getFolderByUid: jest.fn(),
|
||||
setFolderTitle: jest.fn(),
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { StoreState } from 'app/types';
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
import { FolderDTO, FolderState } from 'app/types';
|
||||
import {
|
||||
FolderDTO,
|
||||
FolderState,
|
||||
DashboardAcl,
|
||||
DashboardAclDTO,
|
||||
PermissionLevel,
|
||||
DashboardAclUpdateDTO,
|
||||
} from 'app/types';
|
||||
NewDashboardAclItem,
|
||||
} from 'app/types/acl';
|
||||
|
||||
import { updateNavIndex, updateLocation } from 'app/core/actions';
|
||||
import { buildNavModel } from './navModel';
|
||||
import appEvents from 'app/core/app_events';
|
||||
@@ -140,3 +141,27 @@ export function removeFolderPermission(itemToDelete: DashboardAcl): ThunkResult<
|
||||
await dispatch(getFolderPermissions(folder.uid));
|
||||
};
|
||||
}
|
||||
|
||||
export function addFolderPermission(newItem: NewDashboardAclItem): ThunkResult<void> {
|
||||
return async (dispatch, getStore) => {
|
||||
const folder = getStore().folder;
|
||||
const itemsToUpdate = [];
|
||||
|
||||
for (const item of folder.permissions) {
|
||||
if (item.inherited) {
|
||||
continue;
|
||||
}
|
||||
itemsToUpdate.push(toUpdateItem(item));
|
||||
}
|
||||
|
||||
itemsToUpdate.push({
|
||||
userId: newItem.userId,
|
||||
teamId: newItem.teamId,
|
||||
role: item.role,
|
||||
permission: item.permission,
|
||||
});
|
||||
|
||||
await getBackendSrv().post(`/api/folders/${folder.uid}/permissions`, { items: itemsToUpdate });
|
||||
await dispatch(getFolderPermissions(folder.uid));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FolderState, DashboardAcl, DashboardAclDTO } from 'app/types';
|
||||
import { FolderState } from 'app/types';
|
||||
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
|
||||
import { Action, ActionTypes } from './actions';
|
||||
|
||||
export const inititalState: FolderState = {
|
||||
|
||||
@@ -43,12 +43,39 @@ export interface DashboardPermissionInfo {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface NewDashboardAclItem {
|
||||
teamId: number;
|
||||
userId: number;
|
||||
role: string;
|
||||
permission: PermissionLevel;
|
||||
type: AclTarget;
|
||||
}
|
||||
|
||||
export enum PermissionLevel {
|
||||
View = 1,
|
||||
Edit = 2,
|
||||
Admin = 4,
|
||||
}
|
||||
|
||||
export enum AclTarget {
|
||||
Team = 'team',
|
||||
User = 'user',
|
||||
Viewer = 'viewer',
|
||||
Editor = 'editor',
|
||||
}
|
||||
|
||||
export interface AclTargetInfo {
|
||||
value: AclTarget;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const dashboardAclTargets: AclTargetInfo[] = [
|
||||
{ value: AclTarget.Team, text: 'Team' },
|
||||
{ value: AclTarget.User, text: 'User' },
|
||||
{ value: AclTarget.Viewer, text: 'Everyone With Viewer Role' },
|
||||
{ value: AclTarget.Editor, text: 'Everyone With Editor Role' },
|
||||
];
|
||||
|
||||
export const dashboardPermissionLevels: DashboardPermissionInfo[] = [
|
||||
{ value: PermissionLevel.View, label: 'View', description: 'Can view dashboards.' },
|
||||
{ value: PermissionLevel.Edit, label: 'Edit', description: 'Can add, edit and delete dashboards.' },
|
||||
|
||||
@@ -3,7 +3,6 @@ import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting';
|
||||
import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location';
|
||||
import { NavModel, NavModelItem, NavIndex } from './navModel';
|
||||
import { FolderDTO, FolderState, FolderInfo } from './folder';
|
||||
import { DashboardAcl, DashboardAclDTO, PermissionLevel, DashboardAclUpdateDTO } from './acl';
|
||||
|
||||
export {
|
||||
Team,
|
||||
@@ -24,10 +23,6 @@ export {
|
||||
FolderDTO,
|
||||
FolderState,
|
||||
FolderInfo,
|
||||
DashboardAcl,
|
||||
DashboardAclDTO,
|
||||
DashboardAclUpdateDTO,
|
||||
PermissionLevel,
|
||||
};
|
||||
|
||||
export interface StoreState {
|
||||
|
||||
Reference in New Issue
Block a user