Просмотр исходного кода

wip: More on the permissions. Left are team picker and user picker, tests and error messages #10275

Johannes Schill 8 лет назад
Родитель
Сommit
44ea8e58e2

+ 34 - 130
public/app/core/components/Permissions/Permissions.tsx

@@ -5,6 +5,7 @@ import DevTools from 'mobx-react-devtools';
 import { inject, observer } from 'mobx-react';
 import { Provider } from 'mobx-react';
 import { store } from 'app/stores/store';
+import UserPicker from 'app/core/components/UserPicker/UserPicker';
 
 export interface DashboardAcl {
   id?: number;
@@ -75,140 +76,42 @@ class PermissionsInner extends Component<IProps, any> {
     this.permissionChanged = this.permissionChanged.bind(this);
     this.typeChanged = this.typeChanged.bind(this);
     this.removeItem = this.removeItem.bind(this);
+    this.update = this.update.bind(this);
     permissions.load(this.dashboardId);
 
     this.state = {
       newType: 'Group',
-      canUpdate: false,
-      error: '',
     };
   }
 
+  componentWillReceiveProps(nextProps) {
+    console.log('nextProps', nextProps);
+  }
+
   sortItems(items) {
     return _.orderBy(items, ['sortRank', 'sortName'], ['desc', 'asc']);
   }
 
-  permissionChanged(evt) {
-    // TODO
+  permissionChanged(index: number, permission: number, permissionName: string) {
+    const { permissions } = this.props;
+    // permissions.items[index].updatePermission(permission, permissionName);
+    permissions.updatePermissionOnIndex(index, permission, permissionName);
   }
 
-  removeItem(index) {
+  removeItem(index: number) {
     const { permissions } = this.props;
     permissions.removeStoreItem(index);
   }
 
   update() {
-    var updated = [];
-    for (let item of this.state.items) {
-      if (item.inherited) {
-        continue;
-      }
-      updated.push({
-        id: item.id,
-        userId: item.userId,
-        teamId: item.teamId,
-        role: item.role,
-        permission: item.permission,
-      });
-    }
-
-    return this.backendSrv
-      .post(`/api/dashboards/id/${this.dashboardId}/acl`, {
-        items: updated,
-      })
-      .then(() => {
-        this.setState(prevState => {
-          return {
-            ...prevState,
-            canUpdate: false,
-          };
-        });
-      });
-  }
-
-  prepareViewModel(item: DashboardAcl): DashboardAcl {
-    // TODO: this.meta
-    // item.inherited = !this.meta.isFolder && this.dashboardId !== item.dashboardId;
-    item.inherited = this.dashboardId !== item.dashboardId;
-    item.sortRank = 0;
-    if (item.userId > 0) {
-      item.icon = 'fa fa-fw fa-user';
-      //   item.nameHtml = this.$sce.trustAsHtml(item.userLogin);
-      item.nameHtml = item.userLogin;
-      item.sortName = item.userLogin;
-      item.sortRank = 10;
-    } else if (item.teamId > 0) {
-      item.icon = 'fa fa-fw fa-users';
-      //   item.nameHtml = this.$sce.trustAsHtml(item.team);
-      item.nameHtml = item.team;
-      item.sortName = item.team;
-      item.sortRank = 20;
-    } else if (item.role) {
-      item.icon = 'fa fa-fw fa-street-view';
-      //   item.nameHtml = this.$sce.trustAsHtml(`Everyone with <span class="query-keyword">${item.role}</span> Role`);
-      item.nameHtml = `Everyone with <span class="query-keyword">${item.role}</span> Role`;
-      item.sortName = item.role;
-      item.sortRank = 30;
-      if (item.role === 'Viewer') {
-        item.sortRank += 1;
-      }
-    }
-
-    if (item.inherited) {
-      item.sortRank += 100;
-    }
-
-    return item;
-  }
-
-  isDuplicate(origItem, newItem) {
-    if (origItem.inherited) {
-      return false;
-    }
-
-    return (
-      (origItem.role && newItem.role && origItem.role === newItem.role) ||
-      (origItem.userId && newItem.userId && origItem.userId === newItem.userId) ||
-      (origItem.teamId && newItem.teamId && origItem.teamId === newItem.teamId)
-    );
-  }
-
-  isValid(item) {
-    const dupe = _.find(this.items, it => {
-      return this.isDuplicate(it, item);
-    });
-
-    if (dupe) {
-      this.error = this.duplicateError;
-      return false;
-    }
-
-    return true;
-  }
-
-  addNewItem(item) {
-    if (!this.isValid(item)) {
-      return;
-    }
-    this.error = '';
-
-    item.dashboardId = this.dashboardId;
-
-    let newItems = this.state.items;
-    newItems.push(this.prepareViewModel(item));
-
-    this.setState(prevState => {
-      return {
-        ...prevState,
-        items: this.sortItems(newItems),
-        canUpdate: true,
-      };
-    });
+    const { permissions, dashboardId } = this.props;
+    permissions.update(dashboardId);
   }
 
   resetNewType() {
     this.setState(prevState => {
       return {
+        ...prevState,
         newType: 'Group',
       };
     });
@@ -216,6 +119,14 @@ class PermissionsInner extends Component<IProps, any> {
 
   typeChanged(evt) {
     const { value } = evt.target;
+    const { permissions } = this.props;
+
+    if (value === 'Viewer' || value === 'Editor') {
+      permissions.addStoreItem({ permission: 1, role: value, dashboardId: this.dashboardId }, this.dashboardId);
+      this.resetNewType();
+      return;
+    }
+
     this.setState(prevState => {
       return {
         ...prevState,
@@ -224,40 +135,27 @@ class PermissionsInner extends Component<IProps, any> {
     });
   }
 
-  typeChanged___() {
-    const { newType } = this.state;
-    if (newType === 'Viewer' || newType === 'Editor') {
-      this.addNewItem({ permission: 1, role: newType });
-      this.resetNewType();
-      this.setState(prevState => {
-        return {
-          ...prevState,
-          canUpdate: true,
-        };
-      });
-    }
-  }
-
   render() {
-    const { error, aclTypes, permissions } = this.props;
+    console.log('PermissionsInner render');
+    const { error, aclTypes, permissions, backendSrv } = this.props;
     const { newType } = this.state;
 
     return (
       <div className="gf-form-group">
         <PermissionsList
-          permissions={permissions.items.toJS()}
+          permissions={permissions.items}
           permissionsOptions={this.permissionOptions}
           removeItem={this.removeItem}
           permissionChanged={this.permissionChanged}
+          fetching={permissions.fetching}
         />
-        asd2
         <div className="gf-form-inline">
           <form name="addPermission" className="gf-form-group">
             <h6 className="muted">Add Permission For</h6>
             <div className="gf-form-inline">
               <div className="gf-form">
                 <div className="gf-form-select-wrapper">
-                  <select className="gf-form-input gf-size-auto" onChange={this.typeChanged}>
+                  <select className="gf-form-input gf-size-auto" value={newType} onChange={this.typeChanged}>
                     {aclTypes.map((option, idx) => {
                       return (
                         <option key={idx} value={option.value}>
@@ -280,6 +178,13 @@ class PermissionsInner extends Component<IProps, any> {
                   {' '}
                   User picker
                   <user-picker user-picked="ctrl.userPicked($user)" />
+                  <select-user-picker
+                    backendSrv="ctrl.backendSrv"
+                    teamId="ctrl.$routeParams.id"
+                    refreshList="ctrl.get"
+                    teamMembers="ctrl.teamMembers"
+                  />
+                  <UserPicker backendSrv={backendSrv} teamId={0} />
                 </div>
               ) : null}
 
@@ -306,7 +211,6 @@ class PermissionsInner extends Component<IProps, any> {
             Update Permissions
           </button>
         </div>
-        asd3
         <DevTools />
       </div>
     );

+ 15 - 3
public/app/core/components/Permissions/PermissionsList.tsx

@@ -1,23 +1,27 @@
 import React, { Component } from 'react';
 import PermissionsListItem from './PermissionsListItem';
+import { observer } from 'mobx-react';
 
 export interface IProps {
   permissions: any[];
   permissionsOptions: any[];
   removeItem: any;
   permissionChanged: any;
+  fetching: boolean;
 }
 
+@observer
 class PermissionsList extends Component<IProps, any> {
   render() {
-    const { permissions, permissionsOptions, removeItem, permissionChanged } = this.props;
+    const { permissions, permissionsOptions, removeItem, permissionChanged, fetching } = this.props;
+
     return (
       <table className="filter-table gf-form-group">
         <tbody>
           {permissions.map((item, idx) => {
             return (
               <PermissionsListItem
-                key={item.id}
+                key={idx}
                 item={item}
                 itemIndex={idx}
                 permissionsOptions={permissionsOptions}
@@ -55,7 +59,15 @@ class PermissionsList extends Component<IProps, any> {
                 <em>No permissions are set. Will only be accessible by admins.</em>
               </td>
             </tr> */}
-          {permissions.length < 1 ? (
+          {fetching === true && permissions.length < 1 ? (
+            <tr>
+              <td colSpan={4}>
+                <em>Loading permissions...</em>
+              </td>
+            </tr>
+          ) : null}
+
+          {fetching === false && permissions.length < 1 ? (
             <tr>
               <td colSpan={4}>
                 <em>No permissions are set. Will only be accessible by admins.</em>

+ 7 - 3
public/app/core/components/Permissions/PermissionsListItem.tsx

@@ -1,10 +1,11 @@
 import React from 'react';
+import { observer } from 'mobx-react';
 
 const setClassNameHelper = inherited => {
   return inherited ? 'gf-form-disabled' : '';
 };
 
-export default ({ item, permissionsOptions, removeItem, permissionChanged, itemIndex }) => {
+export default observer(({ item, permissionsOptions, removeItem, permissionChanged, itemIndex }) => {
   const handleRemoveItem = evt => {
     evt.preventDefault();
     removeItem(itemIndex);
@@ -12,7 +13,10 @@ export default ({ item, permissionsOptions, removeItem, permissionChanged, itemI
 
   const handleChangePermission = evt => {
     evt.preventDefault();
-    permissionChanged(itemIndex, evt.target.value);
+    const value = evt.target.value;
+    const valueAsInt = parseInt(value, 10);
+    const newPermission = permissionsOptions.find(opt => opt.value === valueAsInt);
+    permissionChanged(itemIndex, newPermission.value, newPermission.text);
   };
 
   return (
@@ -57,4 +61,4 @@ export default ({ item, permissionsOptions, removeItem, permissionChanged, itemI
       </td>
     </tr>
   );
-};
+});

+ 104 - 41
public/app/stores/PermissionsStore/PermissionsStore.ts

@@ -1,6 +1,8 @@
 import { types, getEnv, flow } from 'mobx-state-tree';
 import { PermissionsStoreItem } from './PermissionsStoreItem';
 
+const duplicateError = 'This permission exists already.';
+
 export const PermissionsStore = types
   .model('PermissionsStore', {
     fetching: types.boolean,
@@ -8,67 +10,128 @@ export const PermissionsStore = types
     items: types.optional(types.array(PermissionsStoreItem), []),
     originalItems: types.optional(types.array(PermissionsStoreItem), []),
   })
-  //   .views(self => ({
-  //     canUpdate: () => {
-  //         const itemsSnapshot = getSnapshot(self.items);
-  //         const originalItemsSnapshot = getSnapshot(self.originalItems);
-  //         console.log('itemsSnapshot', itemsSnapshot);
-  //         console.log('editItemsSnapshot', originalItemsSnapshot);
-  //         return true;
-  //     }
-  //   }))
+  .views(self => ({
+    isValid: item => {
+      const dupe = self.items.find(it => {
+        return isDuplicate(it, item);
+      });
+
+      if (dupe) {
+        this.error = duplicateError;
+        return false;
+      }
+
+      return true;
+    },
+  }))
   .actions(self => ({
     load: flow(function* load(dashboardId: number) {
-      self.fetching = true;
       const backendSrv = getEnv(self).backendSrv;
+      self.fetching = true;
       const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`);
       const items = prepareServerResponse(res, dashboardId);
       self.items = items;
       self.originalItems = items;
       self.fetching = false;
     }),
-    addStoreItem: () => {
+    addStoreItem: (item, dashboardId: number) => {
+      if (!self.isValid(item)) {
+        return;
+      }
+
+      self.items.push(prepareItem(item, dashboardId));
       self.canUpdate = true;
     },
     removeStoreItem: idx => {
       self.items.splice(idx, 1);
       self.canUpdate = true;
     },
+    updatePermissionOnIndex(idx: number, permission: number, permissionName: string) {
+      // self.items[idx].permission = permission;
+      // self.items[idx].permissionName = permissionName;
+      self.items[idx].updatePermission(permission, permissionName);
+      self.canUpdate = true;
+    },
+    // load: flow(function* load(dashboardId: number) {
+
+    update: flow(function* update(dashboardId: number) {
+      const backendSrv = getEnv(self).backendSrv;
+      const updated = [];
+      for (let item of self.items) {
+        if (item.inherited) {
+          continue;
+        }
+        updated.push({
+          id: item.id,
+          userId: item.userId,
+          teamId: item.teamId,
+          role: item.role,
+          permission: item.permission,
+        });
+      }
+
+      let res;
+      try {
+        res = backendSrv.post(`/api/dashboards/id/${dashboardId}/acl`, {
+          items: updated,
+        });
+      } catch (error) {
+        console.error(error);
+      }
+
+      self.canUpdate = false;
+      return res;
+    }),
   }));
 
 const prepareServerResponse = (response, dashboardId: number) => {
   return response.map(item => {
-    // TODO: this.meta
-    // item.inherited = !this.meta.isFolder && this.dashboardId !== item.dashboardId;
-    item.inherited = dashboardId !== item.dashboardId;
-    item.sortRank = 0;
-    if (item.userId > 0) {
-      item.icon = 'fa fa-fw fa-user';
-      //   item.nameHtml = this.$sce.trustAsHtml(item.userLogin);
-      item.nameHtml = item.userLogin;
-      item.sortName = item.userLogin;
-      item.sortRank = 10;
-    } else if (item.teamId > 0) {
-      item.icon = 'fa fa-fw fa-users';
-      //   item.nameHtml = this.$sce.trustAsHtml(item.team);
-      item.nameHtml = item.team;
-      item.sortName = item.team;
-      item.sortRank = 20;
-    } else if (item.role) {
-      item.icon = 'fa fa-fw fa-street-view';
-      //   item.nameHtml = this.$sce.trustAsHtml(`Everyone with <span class="query-keyword">${item.role}</span> Role`);
-      item.nameHtml = `Everyone with <span class="query-keyword">${item.role}</span> Role`;
-      item.sortName = item.role;
-      item.sortRank = 30;
-      if (item.role === 'Viewer') {
-        item.sortRank += 1;
-      }
-    }
+    return prepareItem(item, dashboardId);
+  });
+};
 
-    if (item.inherited) {
-      item.sortRank += 100;
+const prepareItem = (item, dashboardId: number) => {
+  // TODO: this.meta
+  // item.inherited = !this.meta.isFolder && this.dashboardId !== item.dashboardId;
+  item.inherited = dashboardId !== item.dashboardId;
+  item.sortRank = 0;
+  if (item.userId > 0) {
+    item.icon = 'fa fa-fw fa-user';
+    //   item.nameHtml = this.$sce.trustAsHtml(item.userLogin);
+    item.nameHtml = item.userLogin;
+    item.sortName = item.userLogin;
+    item.sortRank = 10;
+  } else if (item.teamId > 0) {
+    item.icon = 'fa fa-fw fa-users';
+    //   item.nameHtml = this.$sce.trustAsHtml(item.team);
+    item.nameHtml = item.team;
+    item.sortName = item.team;
+    item.sortRank = 20;
+  } else if (item.role) {
+    item.icon = 'fa fa-fw fa-street-view';
+    //   item.nameHtml = this.$sce.trustAsHtml(`Everyone with <span class="query-keyword">${item.role}</span> Role`);
+    item.nameHtml = `Everyone with <span class="query-keyword">${item.role}</span> Role`;
+    item.sortName = item.role;
+    item.sortRank = 30;
+    if (item.role === 'Viewer') {
+      item.sortRank += 1;
     }
+  }
 
-    return item;
-  });
+  if (item.inherited) {
+    item.sortRank += 100;
+  }
+  return item;
+};
+
+const isDuplicate = (origItem, newItem) => {
+  if (origItem.inherited) {
+    return false;
+  }
+
+  return (
+    (origItem.role && newItem.role && origItem.role === newItem.role) ||
+    (origItem.userId && newItem.userId && origItem.userId === newItem.userId) ||
+    (origItem.teamId && newItem.teamId && origItem.teamId === newItem.teamId)
+  );
 };

+ 12 - 8
public/app/stores/PermissionsStore/PermissionsStoreItem.ts

@@ -2,16 +2,16 @@
 
 export const PermissionsStoreItem = types
   .model('PermissionsStoreItem', {
-    dashboardId: types.number,
-    id: types.number,
+    dashboardId: types.optional(types.number, -1),
+    id: types.maybe(types.number),
     permission: types.number,
-    permissionName: types.string,
+    permissionName: types.maybe(types.string),
     role: types.maybe(types.string),
-    team: types.string,
-    teamId: types.number,
-    userEmail: types.string,
-    userId: types.number,
-    userLogin: types.string,
+    team: types.optional(types.string, ''),
+    teamId: types.optional(types.number, 0),
+    userEmail: types.optional(types.string, ''),
+    userId: types.optional(types.number, 0),
+    userLogin: types.optional(types.string, ''),
     inherited: types.maybe(types.boolean),
     sortRank: types.maybe(types.number),
     icon: types.maybe(types.string),
@@ -22,4 +22,8 @@ export const PermissionsStoreItem = types
     updateRole: role => {
       self.role = role;
     },
+    updatePermission(permission: number, permissionName: string) {
+      self.permission = permission;
+      self.permissionName = permissionName;
+    },
   }));