source: branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs @ 15760

Last change on this file since 15760 was 15760, checked in by jzenisek, 18 months ago

#2839

  • minor changes regarding project- & resource selection
  • adapted DeleteOnNull rules in dbml
File size: 22.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.ComponentModel;
25using System.Drawing;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Clients.Access;
29using HeuristicLab.Clients.Hive.Views;
30using HeuristicLab.Collections;
31using HeuristicLab.Common.Resources;
32using HeuristicLab.Core;
33using HeuristicLab.Core.Views;
34using HeuristicLab.MainForm;
35
36namespace HeuristicLab.Clients.Hive.Administrator.Views {
37  [View("Resources View")]
38  [Content(typeof(IItemList<Resource>), false)]
39  public partial class ResourcesView : ItemView, IDisposable {
40    private const int slaveImageIndex = 0;
41    private const int slaveGroupImageIndex = 1;
42    public const string ungroupedGroupName = "UNGROUPED";
43    public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group.";
44
45    private readonly Color changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
46    private readonly Color selectedColor = Color.FromArgb(255, 240, 194, 59); // #f0c23b
47    private Resource selectedResource = null;
48
49    private readonly object locker = new object();
50
51    public new IItemList<Resource> Content {
52      get { return (IItemList<Resource>)base.Content; }
53      set { base.Content = value; }
54    }
55
56    public ResourcesView() {
57      InitializeComponent();
58
59      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
60      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
61
62      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
63      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
64      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
65      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
66    }
67
68    #region Overrides
69    protected override void OnClosing(FormClosingEventArgs e) {
70      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
71      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
72      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
73      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
74      base.OnClosing(e);
75    }
76
77    protected override void RegisterContentEvents() {
78      base.RegisterContentEvents();
79      Content.ItemsAdded += Content_ItemsAdded;
80      Content.ItemsRemoved += Content_ItemsRemoved;
81    }
82
83    protected override void DeregisterContentEvents() {
84      Content.ItemsRemoved -= Content_ItemsRemoved;
85      Content.ItemsAdded -= Content_ItemsAdded;
86      base.DeregisterContentEvents();
87    }
88
89    protected override void OnContentChanged() {
90      base.OnContentChanged();
91      if (Content == null) {
92        treeView.Nodes.Clear();
93        viewHost.Content = null;
94        scheduleView.Content = null;
95      } else {
96        var top = BuildResourceTree(Content);
97        SetEnabledStateOfControlsForSelectedResource();
98
99        viewHost.Content = top;
100        bool locked = !IsAdmin();
101        viewHost.Locked = locked;
102        scheduleView.Locked = locked;
103
104        if (top != null && top.Id == Guid.Empty) {
105          scheduleView.SetEnabledStateOfSchedule(false);
106        }
107      }
108    }
109
110    protected override void SetEnabledStateOfControls() {
111      base.SetEnabledStateOfControls();
112      bool enabled = Content != null && !Locked && IsAdmin();
113      btnAddGroup.Enabled = enabled;
114      btnRemoveGroup.Enabled = enabled;
115      btnSave.Enabled = enabled;
116      scheduleView.SetEnabledStateOfSchedule(enabled && IsAdmin());
117    }
118    #endregion
119
120    #region Event Handlers
121    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
122      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsAdded, sender, e);
123      else {
124        OnContentChanged();
125      }
126    }
127
128    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
129      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsRemoved, sender, e);
130      else {
131        OnContentChanged();
132      }
133    }
134
135    private void SlaveViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
136      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)SlaveViewContent_PropertyChanged, sender, e);
137      else {
138        OnContentChanged();
139        if (e.PropertyName == "HbInterval") {
140          UpdateChildHbIntervall((Resource)viewHost.Content);
141        }
142      }
143    }
144
145    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
146      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
147      else {
148        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
149        mainForm.AddOperationProgressToView(this, "Refreshing ...");
150        SetEnabledStateOfControls();
151      }
152    }
153
154    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
155      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
156      else {
157        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
158        mainForm.RemoveOperationProgressFromView(this);
159        SetEnabledStateOfControls();
160      }
161    }
162
163    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
164      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
165      else {
166        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
167        mainForm.AddOperationProgressToView(this, "Refreshing ...");
168        SetEnabledStateOfControls();
169      }
170    }
171
172    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
173      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
174      else {
175        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
176        mainForm.RemoveOperationProgressFromView(this);
177        SetEnabledStateOfControls();
178      }
179    }
180
181    private async void ResourcesView_Load(object sender, EventArgs e) {
182      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
183        action: () => UpdateResources());
184    }
185
186    private async void btnRefresh_Click(object sender, EventArgs e) {
187      lock (locker) {
188        if (!btnRefresh.Enabled) return;
189        btnRefresh.Enabled = false;
190      }
191
192      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
193        action: () => UpdateResources(),
194        finallyCallback: () => btnRefresh.Enabled = true);
195    }
196
197    private void btnAddGroup_Click(object sender, EventArgs e) {
198      Guid? parentResourceId = null;
199      if(!IsAdmin()) {
200        MessageBox.Show(
201          "You have no permission to add a resource group.",
202          "HeuristicLab Hive Administrator",
203          MessageBoxButtons.OK,
204          MessageBoxIcon.Information);
205        return;
206      } else if(selectedResource != null && selectedResource.Id == Guid.Empty) {
207        MessageBox.Show(
208          "You cannot add a resource group to a not yet stored group.",
209          "HeuristicLab Hive Administrator",
210          MessageBoxButtons.OK,
211          MessageBoxIcon.Information);
212        return;
213      }
214     
215      if (selectedResource != null && selectedResource is SlaveGroup) parentResourceId = selectedResource.Id;
216      var group = new SlaveGroup {
217        Name = "New Group",
218        OwnerUserId = UserInformation.Instance.User.Id,
219        ParentResourceId = parentResourceId
220      };
221
222      selectedResource = group;
223      Content.Add(group);
224    }
225
226    private async void btnRemoveGroup_Click(object sender, EventArgs e) {
227      if (selectedResource == null) return;
228
229      lock (locker) {
230        if (!btnRemoveGroup.Enabled) return;
231        btnRemoveGroup.Enabled = false;
232      }
233
234      if (!IsAdmin()) {
235        MessageBox.Show(
236          "You have no permission to delete resources.",
237          "HeuristicLab Hive Administrator",
238          MessageBoxButtons.OK,
239          MessageBoxIcon.Information);
240        return;
241      }
242
243      if (Content.Any(x => x.ParentResourceId == selectedResource.Id)) {
244        MessageBox.Show(
245          "Only empty resources can be deleted.",
246          "HeuristicLab Hive Administrator",
247          MessageBoxButtons.OK,
248          MessageBoxIcon.Error);
249        return;
250      }
251
252      var result = MessageBox.Show(
253        "Do you really want to delete " + selectedResource.Name + "?",
254        "HeuristicLab Hive Administrator",
255        MessageBoxButtons.YesNo,
256        MessageBoxIcon.Question);
257      if (result == DialogResult.Yes) {
258        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
259          action: () => RemoveResource(selectedResource),
260          finallyCallback: () => {
261            btnRemoveGroup.Enabled = true;
262          });
263      } 
264    }
265
266    private async void btnSave_Click(object sender, EventArgs e) {
267      lock (locker) {
268        if (!btnSave.Enabled) return;
269        btnSave.Enabled = false;
270      }
271
272      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
273        action: () => {
274          var resourcesToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
275          foreach (var resource in resourcesToSave)
276            resource.Store();
277          UpdateResources();
278        },
279        finallyCallback: () => btnSave.Enabled = true);
280
281      OnContentChanged();
282    }
283
284    private void treeSlaveGroup_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
285      if (((Resource)e.Node.Tag).Name == ungroupedGroupName) e.Cancel = true;
286    }
287
288    private async void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) {
289      ChangeSelectedResourceNode(e.Node);
290      SetEnabledStateOfControlsForSelectedResource();
291
292      //if (viewHost.Content != null && viewHost.Content is SlaveGroup)
293      //  ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged;
294
295      viewHost.Content = selectedResource;
296      HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty;
297      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
298        action: () => UpdateSchedule(),
299        finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
300
301      //if (selectedResource != null && selectedResource is SlaveGroup)
302      //  selectedResource.PropertyChanged += SlaveViewContent_PropertyChanged;
303
304      bool locked = !IsAdmin();
305      viewHost.Locked = locked;
306      scheduleView.Locked = locked;
307    }
308
309    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
310      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
311      if (sourceNode == null) return;
312      var sourceResource = ((Resource)sourceNode.Tag);
313      if (sourceResource == null) return;
314
315      var treeView = (TreeView)sender;
316      if (sourceNode.TreeView != treeView) return;
317
318      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
319      var targetNode = treeView.GetNodeAt(targetPoint);
320
321      if (sourceNode == targetNode) return;
322      var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null;
323
324      if (!IsAdmin()) {
325        MessageBox.Show(
326          "You cannot drag resources. This is solely granted for HeuristicLab Hive Administrators.",
327          "HeuristicLab Hive Administrator",
328          MessageBoxButtons.OK,
329          MessageBoxIcon.Information);
330        return;
331      } else if (targetResource != null && targetResource is Slave) {
332        MessageBox.Show(
333          "You cannot drag resources on to a slave.",
334          "HeuristicLab Hive Administrator",
335          MessageBoxButtons.OK,
336          MessageBoxIcon.Information);
337        return;
338      } else if(targetResource != null && targetResource.Id == Guid.Empty) {
339        MessageBox.Show(
340          "You cannot drag resources to a not yet stored resource group.",
341          "HeuristicLab Hive Administrator",
342          MessageBoxButtons.OK,
343          MessageBoxIcon.Information);
344            return;
345      } else if (targetNode != null && (targetNode.Text == ungroupedGroupName || targetNode.Parent != null && targetNode.Parent.Text == ungroupedGroupName)) {
346        MessageBox.Show(
347          string.Format(@"You cannot drag resources to group ""{0}"". This group only contains slaves which have not been assigned to a real group yet.", ungroupedGroupName),
348          "HeuristicLab Hive Administrator",
349          MessageBoxButtons.OK,
350          MessageBoxIcon.Information);
351        return;
352      }
353
354      if (sourceNode.Parent == null)
355        treeView.Nodes.Remove(sourceNode);
356      else {
357        sourceNode.Parent.Nodes.Remove(sourceNode);
358        sourceResource.ParentResourceId = null;
359      }
360
361      if (targetNode == null) {
362        treeView.Nodes.Add(sourceNode);
363      } else if(targetResource.Id != Guid.Empty) {
364        targetNode.Nodes.Add(sourceNode);
365        sourceResource.ParentResourceId = targetResource.Id;
366      }
367
368      selectedResource = sourceResource;
369      OnContentChanged();
370    }
371
372    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
373      TreeNode sourceNode = (TreeNode)e.Item;
374      if (IsAdmin())
375        DoDragDrop(sourceNode, DragDropEffects.All);
376    }
377
378    private void treeSlaveGroup_DragEnter(object sender, DragEventArgs e) {
379      e.Effect = DragDropEffects.Move;
380    }
381
382    private void treeSlaveGroup_DragOver(object sender, DragEventArgs e) {
383      e.Effect = DragDropEffects.Move;
384    }
385
386    private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
387      e.Action = DragAction.Continue;
388    }
389    #endregion
390
391    #region Helpers
392    private void ResetTreeNodes(TreeNodeCollection nodes, Color c1, Color c2, bool resetText) {
393      foreach (TreeNode n in nodes) {
394        var res = ((Resource)n.Tag);
395        if (n.BackColor.Equals(c1)) {
396          n.BackColor = c2;
397        }
398        if (resetText) {
399          n.Text = res.Name;
400
401          if (res.Id == Guid.Empty && res.Name != ungroupedGroupName) {
402            n.Text += " [not stored]";
403          } else if (res.Modified && res.Name != ungroupedGroupName) {
404            n.Text += " [changes not stored]";
405          }
406        }
407        if (n.Nodes.Count > 0) {
408          ResetTreeNodes(n.Nodes, c1, c2, resetText);
409        }
410      }
411    }
412
413    private Resource BuildResourceTree(IEnumerable<Resource> resources) {
414      treeView.Nodes.Clear();
415      if (!resources.Any()) return null;
416
417      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
418        .Where(x => x.ParentResourceId == null));
419      var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
420        .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
421      mainResources.UnionWith(parentedMainResources);
422      var subResources = new HashSet<Resource>(resources.Except(mainResources).OrderByDescending(x => x.Name));
423
424      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
425      if (selectedResource != null) selectedResource = resources.Where(x => x.Id == selectedResource.Id).FirstOrDefault();
426      bool nodeSelected = false;
427
428      TreeNode currentNode = null;
429      Resource currentResource = null;
430
431      while(stack.Any()) {
432        var newResource = stack.Pop();
433        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
434
435        if(newResource.Id == Guid.Empty) {
436          newNode.Text += " [not stored]";
437        } else if(newResource.Modified) {
438          newNode.Text += " [changes not stored]";
439        }
440        if (selectedResource == null) {
441          selectedResource = newResource;
442        }
443        if (newResource.Id == selectedResource.Id && !nodeSelected) {
444          newNode.BackColor = selectedColor;
445          newNode.Text += " [selected]";
446          nodeSelected = true;
447        }
448
449        // search for parent node of newNode and save in currentNode
450        // necessary since newNodes (stack top items) might be siblings
451        // or grand..grandparents of previous node (currentNode)
452        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
453          currentNode = currentNode.Parent;
454          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
455        }
456
457        if(currentNode == null) {
458          treeView.Nodes.Add(newNode);
459        } else {
460          currentNode.Nodes.Add(newNode);
461        }
462
463        if(newResource is Slave) {
464          newNode.ImageIndex = slaveImageIndex;
465        } else {
466          newNode.ImageIndex = slaveGroupImageIndex;
467
468          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
469          if(childResources.Any()) {
470            foreach(var resource in childResources.OrderByDescending(x => x.Name)) {
471              subResources.Remove(resource);
472              stack.Push(resource);
473            }
474            currentNode = newNode;
475            currentResource = newResource;
476          }
477        }
478        newNode.SelectedImageIndex = newNode.ImageIndex;
479        //if (newResource.OwnerUserId == UserInformation.Instance.User.Id)
480        //  newNode.BackColor = ownedResourceColor;
481      }
482
483      var ungroupedNode = new TreeNode(ungroupedGroupName) {
484        ForeColor = SystemColors.GrayText,
485        Tag = new SlaveGroup() {
486          Name = ungroupedGroupName,
487          Description = ungroupedGroupDescription
488        }
489      };
490
491      foreach (var slave in subResources.OfType<Slave>().OrderBy(x => x.Name)) {
492        var slaveNode = new TreeNode(slave.Name) { Tag = slave };
493        ungroupedNode.Nodes.Add(slaveNode);
494        if(selectedResource == null) {
495          selectedResource = slave;
496        }
497        if (slave.Id == Guid.Empty) {
498          slaveNode.Text += " [not stored]";
499        } else if (slave.Modified) {
500          slaveNode.Text += " [changes not stored]";
501        }
502        if (slave.Id == selectedResource.Id && !nodeSelected) {
503          slaveNode.BackColor = selectedColor;
504          slaveNode.Text += " [selected]";
505          nodeSelected = true;
506        }
507      }
508
509      treeView.Nodes.Add(ungroupedNode);
510      treeView.ExpandAll();
511
512      return selectedResource;
513    }
514
515    private void UpdateChildHbIntervall(Resource resource) {
516      foreach (Resource r in Content.Where(x => x.ParentResourceId == resource.Id)) {
517        r.HbInterval = resource.HbInterval;
518        if (r is SlaveGroup) {
519          UpdateChildHbIntervall(r);
520        }
521      }
522    }
523
524    private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) {
525      TreeNode tmp = dest;
526
527      while (tmp != null) {
528        if (tmp == movedNode) {
529          return true;
530        }
531        tmp = tmp.Parent;
532      }
533      return false;
534    }
535
536    private void ResetView() {
537      if (InvokeRequired) Invoke((Action)ResetView);
538      else {
539        treeView.Nodes.Clear();
540
541        if (viewHost.Content != null && viewHost.Content is SlaveGroup) {
542          ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged;
543        }
544
545        viewHost.Content = null;
546        if (scheduleView.Content != null) {
547          scheduleView.Content.Clear();
548        }
549
550        HiveAdminClient.Instance.ResetDowntime();
551      }
552    }
553
554    private void UpdateResources() {
555      try {
556        HiveAdminClient.Instance.Refresh();
557        Content = HiveAdminClient.Instance.Resources;
558      } catch(AnonymousUserException) {
559        ShowHiveInformationDialog();
560      }
561    }
562
563    private void RemoveResource(Resource resource) {
564      if (resource == null || resource.Id == Guid.Empty) return;
565
566      try {
567        HiveAdminClient.Delete(resource);
568        Content.Remove(selectedResource);
569      } catch(AnonymousUserException) {
570        ShowHiveInformationDialog();
571      }
572    }
573
574    private void UpdateSchedule() {
575      try {
576        HiveAdminClient.Instance.RefreshCalendar();
577      } catch (AnonymousUserException) {
578        ShowHiveInformationDialog();
579      }
580    }
581
582    private bool IsAuthorized(Resource resource) {
583      return resource != null
584          && resource.Name != ungroupedGroupName
585          //&& resource.Id != Guid.Empty
586          && UserInformation.Instance.UserExists
587          && (HiveRoles.CheckAdminUserPermissions()
588            || HiveAdminClient.Instance.CheckOwnershipOfResource(resource, UserInformation.Instance.User.Id));
589    }
590
591    private bool IsAdmin() {
592      return HiveRoles.CheckAdminUserPermissions();
593    }
594
595    private void ChangeSelectedResourceNode(TreeNode resourceNode) {
596      selectedResource = (Resource)resourceNode.Tag;
597      ResetTreeNodes(treeView.Nodes, selectedColor, Color.Transparent, true);
598      resourceNode.BackColor = selectedColor;
599      resourceNode.Text += " [selected]";
600    }
601
602    private void SetEnabledStateOfControlsForSelectedResource() {
603      bool enabled = (IsAdmin()) ? true : false;
604      btnAddGroup.Enabled = enabled;
605      btnRemoveGroup.Enabled = enabled;
606      btnSave.Enabled = enabled;
607    }
608
609    private void ShowHiveInformationDialog() {
610      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
611      else {
612        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
613          dialog.ShowDialog(this);
614        }
615      }
616    }
617    #endregion
618  }
619}
Note: See TracBrowser for help on using the repository browser.