#region License Information /* HeuristicLab * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; using HeuristicLab.Clients.Access; using HeuristicLab.Clients.Hive.Views; using HeuristicLab.Collections; using HeuristicLab.Common.Resources; using HeuristicLab.Core; using HeuristicLab.Core.Views; using HeuristicLab.MainForm; namespace HeuristicLab.Clients.Hive.Administrator.Views { [View("Resources View")] [Content(typeof(IItemList), false)] public partial class ResourcesView : ItemView, IDisposable { private const int slaveImageIndex = 0; private const int slaveGroupImageIndex = 1; public const string ungroupedGroupName = "UNGROUPED"; public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group."; private readonly Color changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1 private readonly Color selectedColor = Color.FromArgb(255, 240, 194, 59); // #f0c23b private Resource selectedResource = null; private readonly object locker = new object(); public new IItemList Content { get { return (IItemList)base.Content; } set { base.Content = value; } } public ResourcesView() { InitializeComponent(); treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge); treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge); HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing; HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed; AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing; AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed; } #region Overrides protected override void OnClosing(FormClosingEventArgs e) { AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed; AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing; HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed; HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing; base.OnClosing(e); } protected override void RegisterContentEvents() { base.RegisterContentEvents(); Content.ItemsAdded += Content_ItemsAdded; Content.ItemsRemoved += Content_ItemsRemoved; } protected override void DeregisterContentEvents() { Content.ItemsRemoved -= Content_ItemsRemoved; Content.ItemsAdded -= Content_ItemsAdded; base.DeregisterContentEvents(); } protected override void OnContentChanged() { base.OnContentChanged(); if (Content == null) { treeView.Nodes.Clear(); viewHost.Content = null; scheduleView.Content = null; } else { var top = BuildResourceTree(Content); viewHost.Content = top; } } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); bool enabled = Content != null && !Locked && IsAdmin(); btnAddGroup.Enabled = enabled; btnRemoveGroup.Enabled = enabled; btnSave.Enabled = enabled; scheduleView.SetEnabledStateOfSchedule(enabled && IsAdmin()); // IsAuthorized((Resource)viewHost.Content)); } #endregion #region Event Handlers private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs> e) { if (InvokeRequired) Invoke((Action>>)Content_ItemsAdded, sender, e); else { OnContentChanged(); } } private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs> e) { if (InvokeRequired) Invoke((Action>>)Content_ItemsRemoved, sender, e); else { OnContentChanged(); } } private void SlaveViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (InvokeRequired) Invoke((Action)SlaveViewContent_PropertyChanged, sender, e); else { OnContentChanged(); if (e.PropertyName == "HbInterval") { UpdateChildHbIntervall((Resource)viewHost.Content); } } } private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)HiveAdminClient_Instance_Refreshing, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.AddOperationProgressToView(this, "Refreshing ..."); SetEnabledStateOfControls(); } } private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)HiveAdminClient_Instance_Refreshed, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.RemoveOperationProgressFromView(this); SetEnabledStateOfControls(); } } private void AccessClient_Instance_Refreshing(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)AccessClient_Instance_Refreshing, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.AddOperationProgressToView(this, "Refreshing ..."); SetEnabledStateOfControls(); } } private void AccessClient_Instance_Refreshed(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)AccessClient_Instance_Refreshed, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.RemoveOperationProgressFromView(this); SetEnabledStateOfControls(); } } private async void ResourcesView_Load(object sender, EventArgs e) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateResources()); } private async void btnRefresh_Click(object sender, EventArgs e) { lock (locker) { if (!btnRefresh.Enabled) return; btnRefresh.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateResources(), finallyCallback: () => btnRefresh.Enabled = true); } private void btnAddGroup_Click(object sender, EventArgs e) { Guid? parentResourceId = null; if(!IsAdmin()) { MessageBox.Show( "You have no permission to add a resource group.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else if(selectedResource != null && selectedResource.Id == Guid.Empty) { MessageBox.Show( "You cannot add a resource group to a not yet stored group.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (selectedResource != null && selectedResource is SlaveGroup) parentResourceId = selectedResource.Id; var group = new SlaveGroup { Name = "New Group", OwnerUserId = UserInformation.Instance.User.Id, ParentResourceId = parentResourceId }; selectedResource = group; Content.Add(group); } private async void btnRemoveGroup_Click(object sender, EventArgs e) { if (selectedResource == null) return; lock (locker) { if (!btnRemoveGroup.Enabled) return; btnRemoveGroup.Enabled = false; } if (!IsAdmin()) { MessageBox.Show( "You have no permission to delete resources.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (Content.Any(x => x.ParentResourceId == selectedResource.Id)) { MessageBox.Show( "Only empty resources can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } var result = MessageBox.Show( "Do you really want to delete " + selectedResource.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => RemoveResource(selectedResource), finallyCallback: () => { btnRemoveGroup.Enabled = true; }); } } private async void btnSave_Click(object sender, EventArgs e) { lock (locker) { if (!btnSave.Enabled) return; btnSave.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => { var resourcesToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified); foreach (var resource in resourcesToSave) resource.Store(); UpdateResources(); }, finallyCallback: () => btnSave.Enabled = true); OnContentChanged(); } private void treeSlaveGroup_BeforeSelect(object sender, TreeViewCancelEventArgs e) { if (((Resource)e.Node.Tag).Name == ungroupedGroupName) e.Cancel = true; } private async void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) { ChangeSelectedResourceNode(e.Node); SetEnabledStateOfControlsForSelectedResource(); //if (viewHost.Content != null && viewHost.Content is SlaveGroup) // ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged; viewHost.Content = selectedResource; HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty; await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateSchedule(), finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes); //if (selectedResource != null && selectedResource is SlaveGroup) // selectedResource.PropertyChanged += SlaveViewContent_PropertyChanged; bool locked = !IsAdmin(); viewHost.Locked = locked; scheduleView.Locked = locked; } private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) { var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode)); if (sourceNode == null) return; var sourceResource = ((Resource)sourceNode.Tag); if (sourceResource == null) return; var treeView = (TreeView)sender; if (sourceNode.TreeView != treeView) return; var targetPoint = treeView.PointToClient(new Point(e.X, e.Y)); var targetNode = treeView.GetNodeAt(targetPoint); if (sourceNode == targetNode) return; var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null; if (!IsAdmin()) { MessageBox.Show( "You cannot drag resources. This is solely granted for HeuristicLab Hive Administrators.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else if (targetResource != null && targetResource is Slave) { MessageBox.Show( "You cannot drag resources on to a slave.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else if(targetResource != null && targetResource.Id == Guid.Empty) { MessageBox.Show( "You cannot drag resources to a not yet stored resource group.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else if (targetNode != null && (targetNode.Text == ungroupedGroupName || targetNode.Parent != null && targetNode.Parent.Text == ungroupedGroupName)) { MessageBox.Show( 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), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (sourceNode.Parent == null) treeView.Nodes.Remove(sourceNode); else { sourceNode.Parent.Nodes.Remove(sourceNode); sourceResource.ParentResourceId = null; } if (targetNode == null) { treeView.Nodes.Add(sourceNode); } else if(targetResource.Id != Guid.Empty) { targetNode.Nodes.Add(sourceNode); sourceResource.ParentResourceId = targetResource.Id; } selectedResource = sourceResource; OnContentChanged(); } private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) { TreeNode sourceNode = (TreeNode)e.Item; if (IsAdmin()) DoDragDrop(sourceNode, DragDropEffects.All); } private void treeSlaveGroup_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void treeSlaveGroup_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) { e.Action = DragAction.Continue; } #endregion #region Helpers private void ResetTreeNodes(TreeNodeCollection nodes, Color c1, Color c2, bool resetText) { foreach (TreeNode n in nodes) { var res = ((Resource)n.Tag); if (n.BackColor.Equals(c1)) { n.BackColor = c2; } if (resetText) { n.Text = res.Name; if (res.Id == Guid.Empty && res.Name != ungroupedGroupName) { n.Text += " [not stored]"; } else if (res.Modified && res.Name != ungroupedGroupName) { n.Text += " [changes not stored]"; } } if (n.Nodes.Count > 0) { ResetTreeNodes(n.Nodes, c1, c2, resetText); } } } private Resource BuildResourceTree(IEnumerable resources) { treeView.Nodes.Clear(); if (!resources.Any()) return null; var mainResources = new HashSet(resources.OfType() .Where(x => x.ParentResourceId == null)); var parentedMainResources = new HashSet(resources.OfType() .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value))); mainResources.UnionWith(parentedMainResources); var subResources = new HashSet(resources.Except(mainResources).OrderByDescending(x => x.Name)); var stack = new Stack(mainResources.OrderByDescending(x => x.Name)); if (selectedResource != null) selectedResource = resources.Where(x => x.Id == selectedResource.Id).FirstOrDefault(); //bool nodeSelected = false; TreeNode currentNode = null; Resource currentResource = null; while(stack.Any()) { var newResource = stack.Pop(); var newNode = new TreeNode(newResource.Name) { Tag = newResource }; if(newResource.Id == Guid.Empty) { newNode.Text += " [not stored]"; } else if(newResource.Modified) { newNode.Text += " [changes not stored]"; } if (selectedResource == null) { selectedResource = newResource; } if (newResource.Id == selectedResource.Id) { newNode.BackColor = selectedColor; newNode.Text += " [selected]"; } // search for parent node of newNode and save in currentNode // necessary since newNodes (stack top items) might be siblings // or grand..grandparents of previous node (currentNode) while (currentNode != null && newResource.ParentResourceId != currentResource.Id) { currentNode = currentNode.Parent; currentResource = currentNode == null ? null : (Resource)currentNode.Tag; } if(currentNode == null) { treeView.Nodes.Add(newNode); } else { currentNode.Nodes.Add(newNode); } if(newResource is Slave) { newNode.ImageIndex = slaveImageIndex; } else { newNode.ImageIndex = slaveGroupImageIndex; var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id); if(childResources.Any()) { foreach(var resource in childResources.OrderByDescending(x => x.Name)) { subResources.Remove(resource); stack.Push(resource); } currentNode = newNode; currentResource = newResource; } } newNode.SelectedImageIndex = newNode.ImageIndex; //if (newResource.OwnerUserId == UserInformation.Instance.User.Id) // newNode.BackColor = ownedResourceColor; } var ungroupedNode = new TreeNode(ungroupedGroupName) { ForeColor = SystemColors.GrayText, Tag = new SlaveGroup() { Name = ungroupedGroupName, Description = ungroupedGroupDescription } }; foreach (var slave in subResources.OfType().OrderBy(x => x.Name)) { var slaveNode = new TreeNode(slave.Name) { Tag = slave }; ungroupedNode.Nodes.Add(slaveNode); if(selectedResource == null) { selectedResource = slave; } if (slave.Id == Guid.Empty) { slaveNode.Text += " [not stored]"; } else if (slave.Modified) { slaveNode.Text += " [changes not stored]"; } if (slave.Id == selectedResource.Id) { slaveNode.BackColor = selectedColor; slaveNode.Text += " [selected]"; } } treeView.Nodes.Add(ungroupedNode); treeView.ExpandAll(); return selectedResource; } private void UpdateChildHbIntervall(Resource resource) { foreach (Resource r in Content.Where(x => x.ParentResourceId == resource.Id)) { r.HbInterval = resource.HbInterval; if (r is SlaveGroup) { UpdateChildHbIntervall(r); } } } private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) { TreeNode tmp = dest; while (tmp != null) { if (tmp == movedNode) { return true; } tmp = tmp.Parent; } return false; } private void ResetView() { if (InvokeRequired) Invoke((Action)ResetView); else { treeView.Nodes.Clear(); if (viewHost.Content != null && viewHost.Content is SlaveGroup) { ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged; } viewHost.Content = null; if (scheduleView.Content != null) { scheduleView.Content.Clear(); } HiveAdminClient.Instance.ResetDowntime(); } } private void UpdateResources() { try { HiveAdminClient.Instance.Refresh(); Content = HiveAdminClient.Instance.Resources; } catch(AnonymousUserException) { ShowHiveInformationDialog(); } } private void RemoveResource(Resource resource) { if (resource == null || resource.Id == Guid.Empty) return; try { HiveAdminClient.Delete(resource); Content.Remove(selectedResource); } catch(AnonymousUserException) { ShowHiveInformationDialog(); } } private void UpdateSchedule() { try { HiveAdminClient.Instance.RefreshCalendar(); } catch (AnonymousUserException) { ShowHiveInformationDialog(); } } private bool IsAuthorized(Resource resource) { return resource != null && resource.Name != ungroupedGroupName //&& resource.Id != Guid.Empty && UserInformation.Instance.UserExists && (HiveRoles.CheckAdminUserPermissions() || HiveAdminClient.Instance.CheckOwnershipOfResource(resource, UserInformation.Instance.User.Id)); } private bool IsAdmin() { return HiveRoles.CheckAdminUserPermissions(); } private void ChangeSelectedResourceNode(TreeNode resourceNode) { selectedResource = (Resource)resourceNode.Tag; ResetTreeNodes(treeView.Nodes, selectedColor, Color.Transparent, true); resourceNode.BackColor = selectedColor; resourceNode.Text += " [selected]"; } private void SetEnabledStateOfControlsForSelectedResource() { bool enabled = (IsAdmin()) ? true : false; btnAddGroup.Enabled = enabled; btnRemoveGroup.Enabled = enabled; btnSave.Enabled = enabled; } private void ShowHiveInformationDialog() { if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog); else { using (HiveInformationDialog dialog = new HiveInformationDialog()) { dialog.ShowDialog(this); } } } #endregion } }