source: branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs @ 15658

Last change on this file since 15658 was 15658, checked in by jzenisek, 19 months ago

#2839

  • worked on (restricted) accessibility of hive's administration area for non-admin roles
  • adapted HiveClient & HiveAdminClient entity loading (client- & service-side)
File size: 15.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using HeuristicLab.Clients.Access;
28using HeuristicLab.Common.Resources;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31using System.Collections;
32
33namespace HeuristicLab.Clients.Hive.Administrator.Views {
34  [View("ProjectView")]
35  [Content(typeof(Project), IsDefaultView = false)]
36  public partial class ProjectResourcesView : ItemView {
37    private const int slaveImageIndex = 0;
38    private const int slaveGroupImageIndex = 1;
39    public const string ungroupedGroupName = "UNGROUPED";
40    public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group.";
41
42    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
43    private readonly HashSet<Resource> newAssignedResources = new HashSet<Resource>();
44    private readonly HashSet<Resource> inheritedResources = new HashSet<Resource>();
45    private readonly HashSet<Resource> newInheritedResources = new HashSet<Resource>();
46
47    private readonly Dictionary<Guid, HashSet<Project>> projectAncestors = new Dictionary<Guid, HashSet<Project>>();
48    private readonly Dictionary<Guid, HashSet<Project>> projectDescendants = new Dictionary<Guid, HashSet<Project>>();
49    private readonly Dictionary<Guid, HashSet<Resource>> resourceAncestors = new Dictionary<Guid, HashSet<Resource>>();
50    private readonly Dictionary<Guid, HashSet<Resource>> resourceDescendants = new Dictionary<Guid, HashSet<Resource>>();
51
52    //private readonly Color addedAssignmentColor = Color.FromArgb(255, 0, 174, 179); // #00aeb3
53    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
54    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
55    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
56    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
57
58    public new Project Content {
59      get { return (Project)base.Content; }
60      set { base.Content = value; }
61    }
62
63    public ProjectResourcesView() {
64      InitializeComponent();
65
66      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
67      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
68    }
69
70    #region Overrides
71    protected override void OnContentChanged() {
72      base.OnContentChanged();
73      if (Content == null) {
74        assignedResources.Clear();
75        newAssignedResources.Clear();
76        inheritedResources.Clear();
77        resourceAncestors.Clear();
78        treeView.Nodes.Clear();
79        detailsViewHost.Content = null;
80      } else {
81        UpdateProjectGenealogy();
82        UpdateAssignedResources();
83        UpdateResourceGenealogy();
84        var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
85        detailsViewHost.Content = top;
86        detailsViewHost.ActiveView.Locked = true;
87      }
88    }
89
90    #endregion
91
92    #region Event Handlers
93    private void ProjectResourcesView_Load(object sender, EventArgs e) {
94
95    }
96
97    private void refreshButton_Click(object sender, EventArgs e) {
98      UpdateProjectGenealogy();
99      UpdateAssignedResources();
100      UpdateResourceGenealogy();
101      var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
102      detailsViewHost.Content = top;
103    }
104
105    private async void inheritButton_Click(object sender, EventArgs e) {
106      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
107        action: () => {
108          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, true, false);
109        });
110      UpdateResourceTree();
111    }
112
113    private async void saveButton_Click(object sender, EventArgs e) {
114      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
115        action: () => {
116          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, false, false);
117        });
118      UpdateResourceTree();
119    }
120
121    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
122      var selectedResource = (Resource)e.Node.Tag;
123      detailsViewHost.Content = selectedResource;
124    }
125
126    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
127      var checkedResource = (Resource)e.Node.Tag;
128      if (newInheritedResources.Contains(checkedResource) || checkedResource.Id == Guid.Empty) {
129        e.Cancel = true;
130      } else if (!HiveRoles.CheckAdminUserPermissions()) {
131          if (!projectAncestors[Content.Id].Any()) {
132            e.Cancel = true;
133          }
134      }
135    }
136
137    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
138      var checkedResource = (Resource)e.Node.Tag;
139      if (e.Node.Checked) {
140        newAssignedResources.Add(checkedResource);
141      } else {
142        newAssignedResources.Remove(checkedResource);
143      }
144
145      UpdateNewResourceTree();
146    }
147    #endregion
148
149    #region Helpers
150
151    private void UpdateResourceTree() {
152      UpdateAssignedResources();
153      UpdateResourceGenealogy();
154      var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
155      detailsViewHost.Content = top;
156    }
157
158    private void UpdateNewResourceTree() {
159      UpdateNewAssignedResources();
160      UpdateNewInheritedResources();
161      var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
162      detailsViewHost.Content = top;
163    }
164
165    private static IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
166      var assignedProjectResources = HiveServiceLocator.Instance.CallHiveService(s => s.GetAssignedResourcesForProjectAdministration(projectId));
167      return HiveAdminClient.Instance.Resources.Where(x => assignedProjectResources.Select(y => y.ResourceId).Contains(x.Id));
168    }
169
170    private void SetAssignedProjectResources(Guid projectId, IEnumerable<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading) {
171      if (projectId == null || resourceIds == null) return;
172      HiveServiceLocator.Instance.CallHiveService(s => {
173       s.SaveProjectResourceAssignments(projectId, resourceIds.ToList(), reassign, cascading, reassignCascading);
174      });
175    }
176
177    private void UpdateNewAssignedResources() {
178      for(int i = newAssignedResources.Count -1; i >= 0; i--) {
179        if(newAssignedResources.Intersect(resourceAncestors[newAssignedResources.ElementAt(i).Id]).Any()) {
180          newAssignedResources.Remove(newAssignedResources.ElementAt(i));
181        }
182      }
183    }
184
185    private void UpdateAssignedResources() {
186      assignedResources.Clear();
187      newAssignedResources.Clear();
188      foreach (var r in GetAssignedResourcesForProject(Content.Id)) {
189        assignedResources.Add(r);
190        newAssignedResources.Add(r);
191      }
192    }
193
194    private void UpdateNewInheritedResources() {
195      newInheritedResources.Clear();
196      foreach (var a in newAssignedResources) {
197        if (resourceDescendants.ContainsKey(a.Id)) {
198          foreach (var r in resourceDescendants[a.Id]) {
199            newInheritedResources.Add(r);
200          }
201        }
202      }
203    }
204
205    private void UpdateResourceGenealogy() {
206      resourceAncestors.Clear();
207      resourceDescendants.Clear();
208      var resources = HiveAdminClient.Instance.Resources;
209
210      foreach(var r in resources) {
211        resourceAncestors.Add(r.Id, new HashSet<Resource>());
212        resourceDescendants.Add(r.Id, new HashSet<Resource>());
213      }
214
215      foreach(var r in resources) { 
216        var parentResourceId = r.ParentResourceId;
217        while(parentResourceId != null) {
218          var parent = resources.SingleOrDefault(x => x.Id == parentResourceId);
219          if(parent != null) {
220            resourceAncestors[r.Id].Add(parent);
221            resourceDescendants[parent.Id].Add(r);
222            parentResourceId = parent.ParentResourceId;
223          } else {
224            parentResourceId = null;
225          }
226        }
227      }
228
229      inheritedResources.Clear();
230      newInheritedResources.Clear();
231      foreach(var a in assignedResources) {
232        if (resourceDescendants.ContainsKey(a.Id)) {
233          foreach(var r in resourceDescendants[a.Id]) {
234            inheritedResources.Add(r);
235            newInheritedResources.Add(r);
236          }
237        }
238      }
239
240      //foreach(var r in resources) {
241      //  if (resourceAncestors.ContainsKey(r.Id)
242      //    && resourceAncestors[r.Id].Intersect(assignedResources.Select(x => x.Id)).Any()) {
243      //    inheritedResources.Add(r);
244      //  }
245      //}
246    }
247
248    private Resource BuildResourceTree(IEnumerable<Resource> resources) {
249      treeView.Nodes.Clear();
250      if (!resources.Any()) return null;
251
252      treeView.BeforeCheck -= treeView_BeforeCheck;
253      treeView.AfterCheck -= treeView_AfterCheck;
254
255      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>().Where(x => x.ParentResourceId == null));
256      var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
257        .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
258      mainResources.UnionWith(parentedMainResources);
259      var subResources = new HashSet<Resource>(resources.Except(mainResources));
260
261      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
262      Resource top = null;
263
264      TreeNode currentNode = null;
265      Resource currentResource = null;
266
267
268      var addedAssignments = newAssignedResources.Except(assignedResources);
269      var removedAssignments = assignedResources.Except(newAssignedResources);
270      var addedIncludes = newInheritedResources.Except(inheritedResources);
271      var removedIncludes = inheritedResources.Except(newInheritedResources);
272
273      //var assignmentDiff = new HashSet<Resource>(newAssignedResources);
274      //assignmentDiff.SymmetricExceptWith(assignedResources);
275      //var inheritanceDiff = new HashSet<Resource>(newInheritedResources);
276      //inheritanceDiff.SymmetricExceptWith(inheritedResources);
277
278      while (stack.Any()) {
279        if(top == null)  top = stack.Peek();
280        var newResource = stack.Pop();
281        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
282
283        // search for parent node of newNode and save in currentNode
284        // necessary since newNodes (stack top items) might be siblings
285        // or grand..grandparents of previous node (currentNode)
286        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
287          currentNode = currentNode.Parent;
288          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
289        }
290
291        if (currentNode == null) {
292          treeView.Nodes.Add(newNode);
293        } else {
294          currentNode.Nodes.Add(newNode);
295        }
296
297        if (newAssignedResources.Contains(newResource)) {
298          newNode.Checked = true;
299          if(!HiveRoles.CheckAdminUserPermissions()) {
300            if(!projectAncestors[Content.Id].Any()) {
301              newNode.ForeColor = SystemColors.GrayText;
302              newNode.Text += " [immutable]";
303            }
304          }
305
306        } else if (newInheritedResources.Contains(newResource)) {
307          newNode.Checked = true;
308          newNode.ForeColor = SystemColors.GrayText;
309        }
310
311          if (inheritedResources.Contains(newResource) && newInheritedResources.Contains(newResource)) {
312          newNode.Text += " [included]";
313        } else if (addedIncludes.Contains(newResource)) {
314          newNode.BackColor = addedIncludeColor;
315          newNode.ForeColor = SystemColors.GrayText;
316          newNode.Text += " [added include]";
317        } else if (removedIncludes.Contains(newResource)) {
318          newNode.BackColor = removedIncludeColor;
319          newNode.Text += " [removed include]";
320        }
321
322        if (addedAssignments.Contains(newResource)) {
323          newNode.BackColor = addedAssignmentColor;
324          newNode.ForeColor = SystemColors.ControlText;
325          newNode.Text += " [added assignment]";
326        } else if (removedAssignments.Contains(newResource)) {
327          newNode.BackColor = removedAssignmentColor;
328          newNode.ForeColor = SystemColors.ControlText;
329          newNode.Text += " [removed assignment]";
330        }
331
332        if (newResource is Slave) {
333          newNode.ImageIndex = slaveImageIndex;
334        } else {
335          newNode.ImageIndex = slaveGroupImageIndex;
336
337          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
338          if (childResources.Any()) {
339            foreach (var resource in childResources.OrderByDescending(x => x.Name)) {
340              subResources.Remove(resource);
341              stack.Push(resource);
342            }
343            currentNode = newNode;
344            currentResource = newResource;
345          }
346        }
347        newNode.SelectedImageIndex = newNode.ImageIndex;
348        //if (newResource.OwnerUserId == UserInformation.Instance.User.Id)
349        //  newNode.BackColor = ownedResourceColor;
350      }
351
352      var ungroupedSlaves = subResources.OfType<Slave>().OrderBy(x => x.Name);
353      if(ungroupedSlaves.Any()) {
354        var ungroupedNode = new TreeNode(ungroupedGroupName) {
355          ForeColor = SystemColors.GrayText,
356          Tag = new SlaveGroup() {
357            Name = ungroupedGroupName,
358            Description = ungroupedGroupDescription
359          }
360        };
361
362        foreach (var slave in ungroupedSlaves) {
363          var slaveNode = new TreeNode(slave.Name) { Tag = slave };
364          ungroupedNode.Nodes.Add(slaveNode);
365        }
366        treeView.Nodes.Add(ungroupedNode);
367      }
368
369      treeView.BeforeCheck += treeView_BeforeCheck;
370      treeView.AfterCheck += treeView_AfterCheck;
371      treeView.ExpandAll();
372
373      return top;
374    }
375
376    private void UpdateProjectGenealogy() {
377      projectAncestors.Clear();
378      projectDescendants.Clear();
379      var projects = HiveAdminClient.Instance.Projects;
380
381      foreach(var p in projects) {
382        projectAncestors.Add(p.Id, new HashSet<Project>());
383        projectDescendants.Add(p.Id, new HashSet<Project>());
384      }
385
386      foreach (var p in projects) {
387        var parentProjectId = p.ParentProjectId;
388        while (parentProjectId != null) {
389          var parent = projects.SingleOrDefault(x => x.Id == parentProjectId);
390          if (parent != null) {
391            projectAncestors[p.Id].Add(parent);
392            projectDescendants[parent.Id].Add(p);
393            parentProjectId = parent.ParentProjectId;
394          } else {
395            parentProjectId = null;
396          }
397        }
398      }
399    }
400
401    #endregion
402  }
403}
Note: See TracBrowser for help on using the repository browser.