Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs @ 16788

Last change on this file since 16788 was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

File size: 20.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2019 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;
31
32namespace HeuristicLab.Clients.Hive.Administrator.Views {
33  [View("ProjectView")]
34  [Content(typeof(Project), IsDefaultView = false)]
35  public partial class ProjectResourcesView : ItemView {
36    private const int slaveImageIndex = 0;
37    private const int slaveGroupImageIndex = 1;
38    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
39    public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
40    public const string IMMUTABLE_TAG = " [assigned, immutable]";
41    public const string INCLUDED_TAG = " [included]";
42    public const string ADDED_ASSIGNMENT_TAG = " [added assignment]";
43    public const string REMOVED_ASSIGNMENT_TAG = " [removed assignment]";
44    public const string ADDED_INCLUDE_TAG = " [added include]";
45    public const string REMOVED_INCLUDE_TAG = " [removed include]";
46
47    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
48    private readonly HashSet<Resource> newAssignedResources = new HashSet<Resource>();
49    private readonly HashSet<Resource> includedResources = new HashSet<Resource>();
50    private readonly HashSet<Resource> newIncludedResources = new HashSet<Resource>();
51
52    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
53    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
54    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
55    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
56    private readonly Color selectedBackColor = Color.DodgerBlue;
57    private readonly Color selectedForeColor = Color.White;
58    private readonly Color grayTextColor = SystemColors.GrayText;
59
60    private HashSet<Resource> projectExclusiveResources = new HashSet<Resource>();
61    private TreeNode ungroupedGroupNode;
62
63    public new Project Content {
64      get { return (Project)base.Content; }
65      set { base.Content = value; }
66    }
67
68    public ProjectResourcesView() {
69      InitializeComponent();
70
71      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
72      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
73
74      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
75      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
76    }
77
78    #region Overrides
79    protected override void OnContentChanged() {
80      base.OnContentChanged();
81      if (Content == null) {
82        assignedResources.Clear();
83        newAssignedResources.Clear();
84        includedResources.Clear();
85        treeView.Nodes.Clear();
86        detailsViewHost.Content = null;
87      } else {
88        UpdateAssignedResources();
89        UpdateIncludedResources();
90        detailsViewHost.Content = BuildResourceTree();
91      }
92      SetEnabledStateOfControls();
93    }
94
95    protected override void SetEnabledStateOfControls() {
96      base.SetEnabledStateOfControls();
97      bool enabled = Content != null && !Locked && !ReadOnly;
98
99      inheritButton.Enabled = enabled;
100      saveButton.Enabled = enabled;
101      treeView.Enabled = enabled;
102
103      if (detailsViewHost != null) {
104        detailsViewHost.Locked = true;
105      }
106    }
107    #endregion
108
109    #region Event Handlers
110    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
111      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
112      else {
113        SetEnabledStateOfControls();
114      }
115    }
116
117    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
118      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
119      else {
120        OnContentChanged();
121      }
122    }
123
124    private void ProjectResourcesView_Disposed(object sender, EventArgs e) {
125      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
126      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
127    }
128
129    private void refreshButton_Click(object sender, EventArgs e) {
130      HiveAdminClient.Instance.Refresh();
131      UpdateAssignedResources();
132      UpdateIncludedResources();
133      var top = BuildResourceTree();
134      detailsViewHost.Content = top;
135    }
136
137    private async void inheritButton_Click(object sender, EventArgs e) {
138      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
139        action: () => {
140          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, true, false);
141        });
142      HiveAdminClient.Instance.Refresh();
143      UpdateResourceTree();
144    }
145
146    private async void saveButton_Click(object sender, EventArgs e) {
147      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
148        action: () => {
149          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, false, false);
150        });
151      HiveAdminClient.Instance.Refresh();
152      UpdateResourceTree();
153    }
154
155    private void treeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
156      if (e.Node == null || e.Node == ungroupedGroupNode)
157        e.Cancel = true;
158
159      var selectedResource = (Resource)e.Node.Tag;
160      if (HiveAdminClient.Instance.DisabledParentResources.Contains(selectedResource))
161        e.Cancel = true;
162    }
163
164    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
165      var selectedResource = (Resource)e.Node.Tag;
166      detailsViewHost.Content = selectedResource;
167    }
168
169    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
170      if (e.Node == null || e.Node == ungroupedGroupNode) {
171        e.Cancel = true;
172        return;
173      }
174
175      var checkedResource = (Resource)e.Node.Tag;
176      if (checkedResource == null
177        || checkedResource.Id == Guid.Empty
178        || HiveAdminClient.Instance.DisabledParentResources.Contains(checkedResource)
179        || newIncludedResources.Contains(checkedResource)) {
180        e.Cancel = true;
181      } else if (!IsAdmin()) {
182        if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)
183          || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any()
184          || projectExclusiveResources.Contains(checkedResource)) {
185          e.Cancel = true;
186        }
187      }
188    }
189
190    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
191      var checkedResource = (Resource)e.Node.Tag;
192      if (e.Node.Checked) {
193        newAssignedResources.Add(checkedResource);
194      } else {
195        newAssignedResources.Remove(checkedResource);
196      }
197
198      UpdateNewResourceTree();
199    }
200    #endregion
201
202    #region Helpers
203
204    private void UpdateResourceTree() {
205      UpdateAssignedResources();
206      UpdateIncludedResources();
207
208      var top = BuildResourceTree();
209      detailsViewHost.Content = top;
210    }
211
212    private void UpdateNewResourceTree() {
213      UpdateNewAssignedResources();
214      UpdateNewIncludedResources();
215      var top = BuildResourceTree();
216      detailsViewHost.Content = top;
217    }
218
219    private IEnumerable<Resource> GetAvailableResourcesForProjectAdministration(Guid projectId) {
220      projectExclusiveResources.Clear();
221      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
222      var resources = HiveAdminClient.Instance.Resources.Where(x => x.Id != Guid.Empty);
223      if (!resources.Any()) return resources;
224      if (IsAdmin()) return resources;
225
226      // get project specific assigned resources
227      var projectResources = resources.Where(x =>
228        HiveAdminClient.Instance.ProjectResourceAssignments
229          .Where(a => a.ProjectId == projectId)
230          .Select(a => a.ResourceId)
231        .Contains(x.Id));
232
233      // look up for assignments of ancestor projects
234      var projectIds = new HashSet<Guid>();
235      HiveAdminClient.Instance.GetAvailableProjectAncestors(projectId).ToList().ForEach(x => projectIds.Add(x.Id));
236
237      var ancestorProjectResources = resources.Where(x =>
238        HiveAdminClient.Instance.ProjectResourceAssignments
239          .Where(a => projectIds.Contains(a.ProjectId))
240          .Select(a => a.ResourceId)
241        .Contains(x.Id));
242
243      // look down for included resources of ancestor projects
244      HashSet<Resource> availableResources = new HashSet<Resource>(ancestorProjectResources);
245      foreach (var r in ancestorProjectResources) {
246        foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
247          availableResources.Add(d);
248        }
249      }
250
251      // determine resources, which are exclusively assigned to the currently selected project
252      projectResources
253        .Except(availableResources)
254        .ToList()
255        .ForEach(x => projectExclusiveResources.Add(x));
256
257      // look down for included resources of currently selected project
258      if (projectExclusiveResources.Any()) {
259        foreach (var r in projectExclusiveResources.ToArray()) {
260          foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
261            projectExclusiveResources.Add(d);
262          }
263        }
264      }
265
266      return availableResources.Union(projectExclusiveResources);
267    }
268
269    private IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
270      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
271      return HiveAdminClient.Instance.Resources.Where(x =>
272        HiveAdminClient.Instance.ProjectResourceAssignments
273          .Where(a => a.ProjectId == projectId)
274          .Select(a => a.ResourceId)
275        .Contains(x.Id));
276    }
277
278    private void SetAssignedProjectResources(Guid projectId, IEnumerable<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading) {
279      if (projectId == null || resourceIds == null) return;
280      HiveServiceLocator.Instance.CallHiveService(s => {
281        s.SaveProjectResourceAssignments(projectId, resourceIds.ToList(), reassign, cascading, reassignCascading);
282      });
283    }
284
285    private void UpdateNewAssignedResources() {
286      for (int i = newAssignedResources.Count - 1; i >= 0; i--) {
287        if (newAssignedResources.Intersect(HiveAdminClient.Instance.GetAvailableResourceAncestors(newAssignedResources.ElementAt(i).Id)).Any()) {
288          newAssignedResources.Remove(newAssignedResources.ElementAt(i));
289        }
290      }
291    }
292
293    private void UpdateAssignedResources() {
294      assignedResources.Clear();
295      newAssignedResources.Clear();
296      foreach (var r in GetAssignedResourcesForProject(Content.Id)) {
297        assignedResources.Add(r);
298        newAssignedResources.Add(r);
299      }
300    }
301
302    private void UpdateIncludedResources() {
303      includedResources.Clear();
304      newIncludedResources.Clear();
305      foreach (var a in assignedResources) {
306        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
307          includedResources.Add(r);
308          newIncludedResources.Add(r);
309        }
310      }
311    }
312
313    private void UpdateNewIncludedResources() {
314      newIncludedResources.Clear();
315      foreach (var a in newAssignedResources) {
316        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
317          newIncludedResources.Add(r);
318        }
319      }
320    }
321
322    private Resource BuildResourceTree() {
323      treeView.Nodes.Clear();
324
325      treeView.BeforeCheck -= treeView_BeforeCheck;
326      treeView.AfterCheck -= treeView_AfterCheck;
327
328      var resources = GetAvailableResourcesForProjectAdministration(Content.Id);
329
330      var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
331      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>().Where(x => x.ParentResourceId == null));
332      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
333      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
334      //mainResources.UnionWith(parentedMainResources);
335      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
336      mainResources.UnionWith(mainDisabledParentResources);
337      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
338
339      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
340
341      Resource top = null;
342      //bool nodeSelected = false;
343      if (detailsViewHost != null && detailsViewHost.Content != null && detailsViewHost.Content is Resource) {
344        var resourceId = ((Resource)detailsViewHost.Content).Id;
345        top = resources.Where(x => x.Id == resourceId).FirstOrDefault();
346      }
347
348
349      TreeNode currentNode = null;
350      Resource currentResource = null;
351
352      var addedAssignments = newAssignedResources.Except(assignedResources);
353      var removedAssignments = assignedResources.Except(newAssignedResources);
354      var addedIncludes = newIncludedResources.Except(includedResources);
355      var removedIncludes = includedResources.Except(newIncludedResources);
356
357      while (stack.Any()) {
358        var newResource = stack.Pop();
359        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
360
361        if (top == null && !disabledParentResources.Contains(newResource)) {
362          top = newResource;
363        }
364
365        //if(!nodeSelected && top != null && newResource.Id == top.Id) {
366        //  newNode.BackColor = selectedBackColor;
367        //  newNode.ForeColor = selectedForeColor;
368        //  nodeSelected = true;
369        //}
370
371        // search for parent node of newNode and save in currentNode
372        // necessary since newNodes (stack top items) might be siblings
373        // or grand..grandparents of previous node (currentNode)
374        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
375          currentNode = currentNode.Parent;
376          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
377        }
378
379        if (currentNode == null) {
380          treeView.Nodes.Add(newNode);
381        } else {
382          currentNode.Nodes.Add(newNode);
383        }
384
385        if (disabledParentResources.Contains(newResource)) {
386          newNode.Checked = false;
387          newNode.ForeColor = grayTextColor;
388        } else if (newAssignedResources.Contains(newResource)) {
389          newNode.Checked = true;
390          if (!HiveRoles.CheckAdminUserPermissions()) {
391            if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)
392              || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any()
393              || projectExclusiveResources.Contains(newResource)) {
394              newNode.ForeColor = SystemColors.GrayText;
395              newNode.Text += IMMUTABLE_TAG;
396            }
397          }
398
399        } else if (newIncludedResources.Contains(newResource)) {
400          newNode.Checked = true;
401          newNode.ForeColor = SystemColors.GrayText;
402        }
403
404        if (includedResources.Contains(newResource) && newIncludedResources.Contains(newResource)) {
405          newNode.Text += INCLUDED_TAG;
406        } else if (addedIncludes.Contains(newResource)) {
407          newNode.BackColor = addedIncludeColor;
408          newNode.ForeColor = SystemColors.GrayText;
409          newNode.Text += ADDED_INCLUDE_TAG;
410        } else if (removedIncludes.Contains(newResource)) {
411          newNode.BackColor = removedIncludeColor;
412          newNode.Text += REMOVED_INCLUDE_TAG;
413        }
414
415        if (addedAssignments.Contains(newResource)) {
416          newNode.BackColor = addedAssignmentColor;
417          newNode.ForeColor = SystemColors.ControlText;
418          newNode.Text += ADDED_ASSIGNMENT_TAG;
419        } else if (removedAssignments.Contains(newResource)) {
420          newNode.BackColor = removedAssignmentColor;
421          newNode.ForeColor = SystemColors.ControlText;
422          newNode.Text += REMOVED_ASSIGNMENT_TAG;
423        }
424
425        if (newResource is Slave) {
426          newNode.ImageIndex = slaveImageIndex;
427        } else {
428          newNode.ImageIndex = slaveGroupImageIndex;
429
430          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
431          if (childResources.Any()) {
432            foreach (var resource in childResources.OrderByDescending(x => x.Name)) {
433              subResources.Remove(resource);
434              stack.Push(resource);
435            }
436            currentNode = newNode;
437            currentResource = newResource;
438          }
439        }
440        newNode.SelectedImageIndex = newNode.ImageIndex;
441      }
442
443      ExpandResourceNodesOfInterest(treeView.Nodes);
444
445      bool expandUngroupedGroupNode = false;
446      var ungroupedSlaves = subResources.OfType<Slave>().OrderBy(x => x.Name);
447      if (ungroupedSlaves.Any()) {
448        ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
449          ForeColor = SystemColors.GrayText,
450          Tag = new SlaveGroup() {
451            Name = UNGROUPED_GROUP_NAME,
452            Description = UNGROUPED_GROUP_DESCRIPTION
453          }
454        };
455
456        foreach (var slave in ungroupedSlaves) {
457          var slaveNode = new TreeNode(slave.Name) { Tag = slave };
458          if (newAssignedResources.Contains(slave)) {
459            slaveNode.Checked = true;
460            expandUngroupedGroupNode = true;
461          }
462
463          if (!HiveRoles.CheckAdminUserPermissions()) {
464            slaveNode.ForeColor = SystemColors.GrayText;
465            slaveNode.Text += IMMUTABLE_TAG;
466          } else {
467            if (addedAssignments.Contains(slave)) {
468              slaveNode.BackColor = addedAssignmentColor;
469              slaveNode.ForeColor = SystemColors.ControlText;
470              slaveNode.Text += ADDED_ASSIGNMENT_TAG;
471            } else if (removedAssignments.Contains(slave)) {
472              slaveNode.BackColor = removedAssignmentColor;
473              slaveNode.ForeColor = SystemColors.ControlText;
474              slaveNode.Text += REMOVED_ASSIGNMENT_TAG;
475              expandUngroupedGroupNode = true;
476            }
477          }
478          ungroupedGroupNode.Nodes.Add(slaveNode);
479        }
480
481        if (expandUngroupedGroupNode) ungroupedGroupNode.Expand();
482        treeView.Nodes.Add(ungroupedGroupNode);
483      } else if (ungroupedGroupNode != null) {
484        ungroupedGroupNode.Nodes.Clear();
485      }
486
487      treeView.BeforeCheck += treeView_BeforeCheck;
488      treeView.AfterCheck += treeView_AfterCheck;
489
490      return top;
491    }
492
493    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
494      foreach (TreeNode n in nodes) {
495        Resource r = (Resource)n.Tag;
496        if (n.Nodes.Count > 0) {
497          if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
498            n.Expand();
499            ExpandResourceNodesOfInterest(n.Nodes);
500          } else {
501            n.Collapse();
502          }
503        } else {
504          n.Collapse();
505        }
506      }
507    }
508
509    private bool IsAdmin() {
510      return HiveRoles.CheckAdminUserPermissions();
511    }
512
513    #endregion
514  }
515}
Note: See TracBrowser for help on using the repository browser.