Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs @ 15620

Last change on this file since 15620 was 15620, checked in by swagner, 6 years ago

#2877: Improved groups/slaves treeview of Hive Administrator

  • corrected showing number of contained slaves for each group
File size: 21.7 KB
RevLine 
[6976]1#region License Information
2/* HeuristicLab
[15583]3 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[6976]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
[8051]23using System.Collections.Generic;
[6976]24using System.Drawing;
25using System.Linq;
26using System.ServiceModel.Security;
27using System.Threading;
28using System.Threading.Tasks;
29using System.Windows.Forms;
[8051]30using HeuristicLab.Clients.Access;
[6976]31using HeuristicLab.Clients.Hive.Views;
32using HeuristicLab.Core;
33using HeuristicLab.Core.Views;
34using HeuristicLab.MainForm;
35using TS = System.Threading.Tasks;
36
37namespace HeuristicLab.Clients.Hive.Administrator.Views {
38  [View("Resources View")]
[7928]39  [Content(typeof(IItemList<Resource>), false)]
[6976]40  public partial class ResourcesView : ItemView, IDisposable {
41    public new IItemList<Resource> Content {
42      get { return (IItemList<Resource>)base.Content; }
43      set { base.Content = value; }
44    }
45
46    public const string UngroupedGroupName = "UNGROUPED";
47    private const int slaveImageIndex = 0;
48    private const int slaveGroupImageIndex = 1;
[8051]49    private readonly Color ownedResourceColor = Color.LightGreen;
[15613]50    private readonly Color calculatingColor = Color.DarkGreen;
51    private readonly Color offlineColor = Color.Red;
[6976]52    private TS.Task progressTask;
53    private bool stopProgressTask;
[8051]54    private bool currentlyAuthorized;
[6976]55
56
57    public ResourcesView() {
58      InitializeComponent();
[7256]59      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.MonitorLarge);
60      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NetworkCenterLarge);
[6976]61
62      HiveAdminClient.Instance.Refreshing += new EventHandler(Instance_Refreshing);
63      HiveAdminClient.Instance.Refreshed += new EventHandler(Instance_Refreshed);
[8051]64
[8075]65      Access.AccessClient.Instance.Refreshing += new EventHandler(AccessClient_Refreshing);
66      Access.AccessClient.Instance.Refreshed += new EventHandler(AccessClient_Refreshed);
[6976]67    }
68
69    private void UpdateProgress() {
70      while (!stopProgressTask) {
71        int diff = (progressBar.Maximum - progressBar.Minimum) / 10;
72
73        if (progressBar.InvokeRequired) {
74          progressBar.Invoke(new Action(delegate() { progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum; }));
75        } else {
76          progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum;
77        }
78
79        //ok, this is not very clever...
80        Thread.Sleep(500);
81      }
82      if (progressBar.InvokeRequired) {
83        progressBar.Invoke(new Action(delegate() { progressBar.Value = progressBar.Minimum; }));
84      } else {
85        progressBar.Value = progressBar.Minimum;
86      }
87    }
88
89    void Instance_Refreshing(object sender, EventArgs e) {
90      stopProgressTask = false;
91      progressTask = new TS.Task(UpdateProgress);
92      progressTask.Start();
[8075]93      SetEnabledStateOfControls();
[6976]94    }
95
96    void Instance_Refreshed(object sender, EventArgs e) {
97      stopProgressTask = true;
[8075]98      SetEnabledStateOfControls();
[6976]99    }
100
[8075]101    void AccessClient_Refreshing(object sender, EventArgs e) {
[8051]102      stopProgressTask = false;
103      progressTask = new TS.Task(UpdateProgress);
104      progressTask.Start();
[8075]105      SetEnabledStateOfControls();
[8089]106      btnPermissionsSave.Enabled = false;
[8051]107    }
108
[8075]109    void AccessClient_Refreshed(object sender, EventArgs e) {
[8051]110      stopProgressTask = true;
[8075]111      SetEnabledStateOfControls();
[8051]112    }
113
[6976]114    #region Register Content Events
115    protected override void DeregisterContentEvents() {
116      Content.ItemsAdded -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
117      Content.ItemsRemoved -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
118      base.DeregisterContentEvents();
119    }
120    protected override void RegisterContentEvents() {
121      base.RegisterContentEvents();
122      Content.ItemsAdded += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
123      Content.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
124    }
125    #endregion
126
127    protected override void OnContentChanged() {
128      base.OnContentChanged();
129      if (Content == null) {
130        slaveView.Content = null;
[8051]131        scheduleView.Content = null;
132        permissionView.Content = null;
133        permissionView.FetchSelectedUsers = null;
[6976]134        treeSlaveGroup.Nodes.Clear();
135      } else {
[8051]136        permissionView.Content = Access.AccessClient.Instance;
[6976]137        treeSlaveGroup.Nodes.Clear();
138
139        //rebuild
[15613]140        var ungroupedSlaves = new SlaveGroup {
141          Id = Guid.NewGuid(),
142          Name = UngroupedGroupName,
143          Description = "Slaves without a group"
144        };
145        var ungroupedSlavesCount = 0;
146        var ungroupedNode = CreateTreeNodeFromResource(ungroupedSlaves);
[6976]147
[15613]148        var parentlessResources = from r in Content
149                                  where r.ParentResourceId == null
150                                  orderby r.Name
151                                  select r;
152        foreach (Resource r in parentlessResources) {
153          if (r.GetType() == typeof(SlaveGroup)) {  // root node
154            var root = CreateTreeNodeFromResource(r);
155            treeSlaveGroup.Nodes.Add(root);
156            BuildSlaveGroupTree((SlaveGroup)r, root);
157          } else if (r.GetType() == typeof(Slave)) {  // ungrouped slaves
158            ungroupedNode.Nodes.Add(CreateTreeNodeFromResource(r));
159            ungroupedSlavesCount++;
[6976]160          }
161        }
[15613]162        if (ungroupedSlavesCount > 0)
163          ungroupedNode.Text += "   [" + ungroupedSlavesCount.ToString() + "]";
164        treeSlaveGroup.Nodes.Add(ungroupedNode);
[6976]165      }
166    }
167
[15613]168    private void BuildSlaveGroupTree(SlaveGroup group, TreeNode node) {
169      var childGroups = from r in Content.OfType<SlaveGroup>()
170                        where r.ParentResourceId == @group.Id
171                        orderby r.Name
172                        select r;
173      foreach (var g in childGroups) {
174        var childNode = CreateTreeNodeFromResource(g);
175        node.Nodes.Add(childNode);
176        BuildSlaveGroupTree(g, childNode);
177      }
178
179      var childSlaves = from r in Content.OfType<Slave>()
180                        where r.ParentResourceId == @group.Id
181                        orderby r.Name
182                        select r;
[15620]183      var childSlavesCount = 0;
184      foreach (var s in childSlaves) {
[15613]185        node.Nodes.Add(CreateTreeNodeFromResource(s));
[15620]186        childSlavesCount++;
187      }
[15613]188      if (childSlavesCount > 0)
[15620]189        node.Text = group.Name + "   [" + childSlavesCount.ToString() + "]";
[15613]190    }
191
192    private TreeNode CreateTreeNodeFromResource(Resource r) {
193      var node = new TreeNode();
194      node.Text = r.Name;
195
196      if (r is SlaveGroup) {
197        node.ImageIndex = slaveGroupImageIndex;
198      } else if (r is Slave) {
199        var s = r as Slave;
200        node.ImageIndex = slaveImageIndex;
201        if (s.SlaveState == SlaveState.Calculating) {
202          node.ForeColor = calculatingColor;
203          node.Text += "   [" + s.CpuUtilization.ToString("N2") + " %]";
204        } else if (s.SlaveState == SlaveState.Offline) {
205          node.ForeColor = offlineColor;
206          if (s.LastHeartbeat.HasValue)
207            node.Text += "   [" + s.LastHeartbeat?.ToString("g") + "]";
[6976]208        }
[15613]209      }
210      node.SelectedImageIndex = node.ImageIndex;
[6976]211
[15613]212      if (r.OwnerUserId == Access.UserInformation.Instance.User.Id) node.BackColor = ownedResourceColor;
213
214      node.Tag = r;
215      return node;
[6976]216    }
217
218    protected override void SetEnabledStateOfControls() {
219      base.SetEnabledStateOfControls();
[7250]220      if (Content == null) {
221        btnAddGroup.Enabled = false;
222        btnRemoveGroup.Enabled = false;
223        btnSave.Enabled = false;
[8051]224        btnPermissionsSave.Enabled = false;
225        permissionView.Enabled = false;
[8075]226        scheduleView.SetEnabledStateOfSchedule(false);
227        btnPermissionsSave.Enabled = false;
228        permissionView.Enabled = false;
[7250]229      } else {
230        btnAddGroup.Enabled = true;
231        btnRemoveGroup.Enabled = true;
232        btnSave.Enabled = true;
[8075]233        scheduleView.SetEnabledStateOfSchedule(IsAuthorized(slaveView.Content));
234        btnPermissionsSave.Enabled = permissionView.FetchSelectedUsers != null;
235        permissionView.Enabled = permissionView.FetchSelectedUsers != null;
[7250]236      }
[6976]237    }
238
[8051]239    private bool IsAuthorized(Resource resource) {
240      return resource != null
241          && resource.Name != UngroupedGroupName
242          && resource.Id != Guid.Empty
[8071]243          && UserInformation.Instance.UserExists
244          && (resource.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions());
[8051]245    }
[6976]246
[8051]247    private void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) {
248      if (e.Action != TreeViewAction.Unknown) {
249        Resource selectedResource = ((Resource)e.Node.Tag);
250        currentlyAuthorized = IsAuthorized(selectedResource);
251        if (currentlyAuthorized) {
252          permissionView.FetchSelectedUsers = new Func<List<Guid>>(() => {
253            return HiveServiceLocator.Instance.CallHiveService<List<ResourcePermission>>(service => {
254              return service.GetResourcePermissions(selectedResource.Id);
255            }).Select(x => x.GrantedUserId).ToList();
256          });
257          if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
258        } else {
259          permissionView.FetchSelectedUsers = null;
260          btnPermissionsSave.Enabled = false;
261          if (selectedResource.Id == Guid.Empty) {
262            if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
263          } else tabSlaveGroup.TabPages.Remove(tabPermissions);
264        }
[6976]265
[8051]266        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
267          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
268        }
269
270        slaveView.Content = selectedResource;
271        HiveAdminClient.Instance.DowntimeForResourceId = selectedResource.Id;
272
273        if (selectedResource is SlaveGroup) {
274          slaveView.Content.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
275        }
276
277        if (tabSlaveGroup.SelectedIndex == 1) {
278          UpdateScheduleAsync();
279        } else if (tabSlaveGroup.SelectedIndex == 2) {
280          UpdatePermissionsAsync();
281        }
[6976]282      }
283    }
284
285    void SlaveViewContent_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
286      OnContentChanged();
287      if (e.PropertyName == "HbInterval") {
288        UpdateChildHbIntervall(slaveView.Content);
289      }
290    }
291
292    private void UpdateChildHbIntervall(Resource resource) {
293      foreach (Resource r in Content.Where(x => x.ParentResourceId == resource.Id)) {
294        r.HbInterval = resource.HbInterval;
295        if (r is SlaveGroup) {
296          UpdateChildHbIntervall(r);
297        }
298      }
299    }
300
301    private void btnAddGroup_Click(object sender, EventArgs e) {
302      SlaveGroup newGroup = new SlaveGroup();
303      newGroup.Name = "New Group";
[8051]304      newGroup.OwnerUserId = UserInformation.Instance.User.Id;
[6976]305      Content.Add(newGroup);
306    }
307
308    void Content_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
309      OnContentChanged();
310    }
311
312    void Content_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
313      OnContentChanged();
314    }
315
316    private void btnRemoveGroup_Click(object sender, EventArgs e) {
317      if (treeSlaveGroup.SelectedNode != null && treeSlaveGroup.SelectedNode.Tag != null) {
318        Resource res = (Resource)treeSlaveGroup.SelectedNode.Tag;
319
320        DialogResult diagRes = MessageBox.Show("Do you really want to delete " + res.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
321        if (diagRes == DialogResult.Yes) {
322          if (res is Slave) {
323            Content.Remove(res);
324            HiveAdminClient.Delete(res);
325          } else if (res is SlaveGroup) {
326            //only delete empty groups
327            if (Content.Where(s => s.ParentResourceId == res.Id).Count() < 1) {
328              Content.Remove(res);
329              HiveAdminClient.Delete(res);
330            } else {
331              MessageBox.Show("Only empty groups can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
332            }
333          }
334        }
335      }
336    }
337
338    private void btnSave_Click(object sender, EventArgs e) {
339      foreach (Resource res in Content) {
340        if (res is SlaveGroup && res.Id == Guid.Empty) {
341          SlaveGroup slaveGroup = (SlaveGroup)res;
342          slaveGroup.Store();
343        } else if (res.Id != Guid.Empty && res.Modified) {
344          res.Store();
345        }
346      }
347    }
348
349    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
350      if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false)) {
351        Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
352        TreeNode destNode = ((TreeView)sender).GetNodeAt(pt);
353        TreeNode newNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
354
355        if (destNode.TreeView == newNode.TreeView) {
356          if (destNode.Text == UngroupedGroupName || (destNode.Parent != null && destNode.Parent.Text == UngroupedGroupName)) {
[8051]357            MessageBox.Show(string.Format("You can't drag items to the group \"{0}\".{1}This group only contains slaves which haven't yet been assigned to a real group.",
[6976]358              UngroupedGroupName, Environment.NewLine), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information);
359            return;
360          }
361
362          SlaveGroup sgrp = null;
363          TreeNode parentNode = null;
364          if (destNode.Tag != null && destNode.Tag is SlaveGroup) {
365            sgrp = (SlaveGroup)destNode.Tag;
366            parentNode = destNode;
367          } else if (destNode.Parent != null && destNode.Parent.Tag is SlaveGroup) {
368            sgrp = (SlaveGroup)destNode.Parent.Tag;
369            parentNode = destNode.Parent;
370          }
371
372          if (newNode.Tag is SlaveGroup && CheckParentsEqualsMovedNode(parentNode, newNode)) {
373            return;
374          }
375
[8051]376          SlaveGroup parent = (SlaveGroup)parentNode.Tag;
377
378          if (parent.OwnerUserId != null && !IsAuthorized(parent)) {
379            MessageBox.Show(string.Format("You don't have the permissions to drag items to the group \"{0}\".", ((Resource)parentNode.Tag).Name),
380              "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
381            return;
382          }
383
[6976]384          if (sgrp != null && newNode.Tag != null) {
385            //save parent group to get an id
386            if (sgrp.Id == Guid.Empty) {
387              sgrp.Store();
388            }
389
390            if (newNode.Tag is Slave) {
391              Slave slave = (Slave)newNode.Tag;
392              if (slave.ParentResourceId == null || (slave.ParentResourceId != null && slave.ParentResourceId != sgrp.Id)) {
393                slave.ParentResourceId = sgrp.Id;
394                newNode.Remove();
395                parentNode.Nodes.Clear();
396                BuildSlaveGroupTree(sgrp, parentNode);
397              }
398            } else if (newNode.Tag is SlaveGroup) {
399              SlaveGroup slaveGroup = (SlaveGroup)newNode.Tag;
400              if (slaveGroup.ParentResourceId == null || (slaveGroup.ParentResourceId != null && slaveGroup.ParentResourceId != sgrp.Id)) {
401                slaveGroup.ParentResourceId = sgrp.Id;
402                newNode.Remove();
403                parentNode.Nodes.Clear();
404                BuildSlaveGroupTree(sgrp, parentNode);
405              }
406            }
407          }
408        }
409      }
410    }
411
412    private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) {
413      TreeNode tmp = dest;
414
415      while (tmp != null) {
416        if (tmp == movedNode) {
417          return true;
418        }
419        tmp = tmp.Parent;
420      }
421      return false;
422    }
423
424    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
425      TreeNode sourceNode = (TreeNode)e.Item;
[8051]426      if (IsAuthorized((Resource)sourceNode.Tag))
427        DoDragDrop(sourceNode, DragDropEffects.All);
[6976]428    }
429
430    private void treeSlaveGroup_DragEnter(object sender, DragEventArgs e) {
431      e.Effect = DragDropEffects.Move;
432    }
433
434    private void treeSlaveGroup_DragOver(object sender, DragEventArgs e) {
435      e.Effect = DragDropEffects.Move;
436    }
437
438    private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
439      e.Action = DragAction.Continue;
440    }
441
442    void ResetView() {
[7256]443      if (this.InvokeRequired) {
444        Invoke(new Action(ResetView));
445      } else {
446        treeSlaveGroup.Nodes.Clear();
[6976]447
[7256]448        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
449          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
450        }
451        slaveView.Content = null;
452        if (scheduleView.Content != null) {
453          scheduleView.Content.Clear();
454        }
455        HiveAdminClient.Instance.ResetDowntime();
[6976]456      }
457    }
458
459    private void UpdateResources() {
[7256]460      ResetView();
[6976]461
462      try {
[9063]463        if (!Access.UserInformation.Instance.UserExists) {
464          //do a refresh just in case that the user has changed his usr and pwd in between
465          Access.UserInformation.Instance.Refresh();
466        }
[6976]467        HiveAdminClient.Instance.Refresh();
468        Content = HiveAdminClient.Instance.Resources;
469      }
470      catch (MessageSecurityException) {
[7256]471        ShowMessageSecurityException();
[6976]472      }
[7249]473      catch (AnonymousUserException) {
[7256]474        ShowHiveInformationDialog();
[7249]475      }
[6976]476    }
477
[7256]478    private void ShowMessageSecurityException() {
479      if (this.InvokeRequired) {
480        Invoke(new Action(ShowMessageSecurityException));
481      } else {
482        MessageBox.Show("A Message Security error has occured. This normally means that your user name or password is wrong.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
483      }
484    }
485
486    private void ShowHiveInformationDialog() {
487      if (this.InvokeRequired) {
488        Invoke(new Action(ShowHiveInformationDialog));
489      } else {
490        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
491          dialog.ShowDialog(this);
492        }
493      }
494    }
495
[6976]496    private void UpdateResourcesAsync() {
497      TS.Task.Factory.StartNew(UpdateResources).ContinueWith((t) => {
498        DisplayError(t.Exception);
499      }, TaskContinuationOptions.OnlyOnFaulted);
500    }
501
502    private void UpdateSchedule() {
503      HiveAdminClient.Instance.RefreshCalendar();
[8051]504      scheduleView.Invoke(new Action(() => {
505        scheduleView.Content = HiveAdminClient.Instance.Downtimes;
[8075]506        SetEnabledStateOfControls();
[8051]507      }));
[6976]508    }
509
510    private void UpdateScheduleAsync() {
511      TS.Task.Factory.StartNew(UpdateSchedule).ContinueWith((t) => {
512        DisplayError(t.Exception);
513      }, TaskContinuationOptions.OnlyOnFaulted);
514    }
515
[8051]516    private void UpdatePermissions() {
517      if (permissionView.Content != null && permissionView.FetchSelectedUsers != null)
518        permissionView.Invoke(new Action(() => permissionView.ManualRefresh()));
519    }
520
521    private void UpdatePermissionsAsync() {
522      TS.Task.Factory.StartNew(UpdatePermissions).ContinueWith((t) => {
523        DisplayError(t.Exception);
524      }, TaskContinuationOptions.OnlyOnFaulted);
525    }
526
527
[6976]528    private void DisplayError(Exception ex) {
529      MessageBox.Show(string.Format("An error occured while updating: {0} {1}", Environment.NewLine, ex.Message), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
530    }
531
532    private void tabSlaveGroup_SelectedIndexChanged(object sender, EventArgs e) {
533      if (tabSlaveGroup.SelectedIndex == 1) {
534        UpdateScheduleAsync();
[8051]535      } else if (tabSlaveGroup.SelectedIndex == 2) {
536        UpdatePermissionsAsync();
[6976]537      }
538    }
539
540    private void btnRefresh_Click(object sender, EventArgs e) {
541      UpdateResourcesAsync();
542    }
[6994]543
544    private void ResourcesView_Load(object sender, EventArgs e) {
545      UpdateResourcesAsync();
546    }
[8051]547
548    private void btnPermissionsSave_Click(object sender, EventArgs e) {
[8075]549      SetEnabledStateOfControls();
[8051]550      HiveServiceLocator.Instance.CallHiveService(service => {
551        service.GrantResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetAddedUsers().Select(x => x.Id).ToList());
552        service.RevokeResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetDeletedUsers().Select(x => x.Id).ToList());
553      });
[8075]554      SetEnabledStateOfControls();
[8051]555    }
[6976]556  }
557}
Note: See TracBrowser for help on using the repository browser.