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

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

#2839 worked on HiveAdministrator:

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