#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.Drawing; using System.Linq; using System.Windows.Forms; using HeuristicLab.Clients.Access; using HeuristicLab.Common.Resources; using HeuristicLab.Core.Views; using HeuristicLab.MainForm; using System.Collections; namespace HeuristicLab.Clients.Hive.Administrator.Views { [View("ProjectView")] [Content(typeof(Project), IsDefaultView = false)] public partial class ProjectResourcesView : ItemView { 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 HashSet assignedResources = new HashSet(); private readonly HashSet newAssignedResources = new HashSet(); private readonly HashSet inheritedResources = new HashSet(); private readonly HashSet newInheritedResources = new HashSet(); private readonly Dictionary> projectAncestors = new Dictionary>(); private readonly Dictionary> projectDescendants = new Dictionary>(); private readonly Dictionary> resourceAncestors = new Dictionary>(); private readonly Dictionary> resourceDescendants = new Dictionary>(); private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1 private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48 private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291 public new Project Content { get { return (Project)base.Content; } set { base.Content = value; } } public ProjectResourcesView() { InitializeComponent(); treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge); treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge); } #region Overrides protected override void OnContentChanged() { base.OnContentChanged(); if (Content == null) { assignedResources.Clear(); newAssignedResources.Clear(); inheritedResources.Clear(); resourceAncestors.Clear(); treeView.Nodes.Clear(); detailsViewHost.Content = null; } else { UpdateProjectGenealogy(); UpdateAssignedResources(); UpdateResourceGenealogy(); var top = BuildResourceTree(HiveAdminClient.Instance.Resources); detailsViewHost.Content = top; } } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); bool enabled = Content != null && !Locked && !ReadOnly; inheritButton.Enabled = enabled; saveButton.Enabled = enabled; treeView.Enabled = enabled; } #endregion #region Event Handlers private void ProjectResourcesView_Load(object sender, EventArgs e) { } private void refreshButton_Click(object sender, EventArgs e) { UpdateProjectGenealogy(); UpdateAssignedResources(); UpdateResourceGenealogy(); var top = BuildResourceTree(HiveAdminClient.Instance.Resources); detailsViewHost.Content = top; } private async void inheritButton_Click(object sender, EventArgs e) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => { SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, true, false); }); UpdateResourceTree(); } private async void saveButton_Click(object sender, EventArgs e) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => { SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, false, false); }); UpdateResourceTree(); } private void treeView_AfterSelect(object sender, TreeViewEventArgs e) { var selectedResource = (Resource)e.Node.Tag; detailsViewHost.Content = selectedResource; } private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) { var checkedResource = (Resource)e.Node.Tag; if (newInheritedResources.Contains(checkedResource) || checkedResource.Id == Guid.Empty) { e.Cancel = true; } else if (!HiveRoles.CheckAdminUserPermissions()) { if (!projectAncestors[Content.Id].Any()) { e.Cancel = true; } } } private void treeView_AfterCheck(object sender, TreeViewEventArgs e) { var checkedResource = (Resource)e.Node.Tag; if (e.Node.Checked) { newAssignedResources.Add(checkedResource); } else { newAssignedResources.Remove(checkedResource); } UpdateNewResourceTree(); } #endregion #region Helpers private void UpdateResourceTree() { UpdateAssignedResources(); UpdateResourceGenealogy(); var top = BuildResourceTree(HiveAdminClient.Instance.Resources); detailsViewHost.Content = top; } private void UpdateNewResourceTree() { UpdateNewAssignedResources(); UpdateNewInheritedResources(); var top = BuildResourceTree(HiveAdminClient.Instance.Resources); detailsViewHost.Content = top; } private static IEnumerable GetAssignedResourcesForProject(Guid projectId) { var assignedProjectResources = HiveServiceLocator.Instance.CallHiveService(s => s.GetAssignedResourcesForProjectAdministration(projectId)); return HiveAdminClient.Instance.Resources.Where(x => assignedProjectResources.Select(y => y.ResourceId).Contains(x.Id)); } private void SetAssignedProjectResources(Guid projectId, IEnumerable resourceIds, bool reassign, bool cascading, bool reassignCascading) { if (projectId == null || resourceIds == null) return; HiveServiceLocator.Instance.CallHiveService(s => { s.SaveProjectResourceAssignments(projectId, resourceIds.ToList(), reassign, cascading, reassignCascading); }); } private void UpdateNewAssignedResources() { for(int i = newAssignedResources.Count -1; i >= 0; i--) { if(newAssignedResources.Intersect(resourceAncestors[newAssignedResources.ElementAt(i).Id]).Any()) { newAssignedResources.Remove(newAssignedResources.ElementAt(i)); } } } private void UpdateAssignedResources() { assignedResources.Clear(); newAssignedResources.Clear(); foreach (var r in GetAssignedResourcesForProject(Content.Id)) { assignedResources.Add(r); newAssignedResources.Add(r); } } private void UpdateNewInheritedResources() { newInheritedResources.Clear(); foreach (var a in newAssignedResources) { if (resourceDescendants.ContainsKey(a.Id)) { foreach (var r in resourceDescendants[a.Id]) { newInheritedResources.Add(r); } } } } private void UpdateResourceGenealogy() { resourceAncestors.Clear(); resourceDescendants.Clear(); var resources = HiveAdminClient.Instance.Resources; foreach(var r in resources.Where(x => x.Id != Guid.Empty)) { resourceAncestors.Add(r.Id, new HashSet()); resourceDescendants.Add(r.Id, new HashSet()); } foreach(var r in resources.Where(x => x.Id != Guid.Empty)) { var parentResourceId = r.ParentResourceId; while(parentResourceId != null) { var parent = resources.SingleOrDefault(x => x.Id == parentResourceId); if(parent != null) { resourceAncestors[r.Id].Add(parent); resourceDescendants[parent.Id].Add(r); parentResourceId = parent.ParentResourceId; } else { parentResourceId = null; } } } inheritedResources.Clear(); newInheritedResources.Clear(); foreach(var a in assignedResources) { if (resourceDescendants.ContainsKey(a.Id)) { foreach(var r in resourceDescendants[a.Id]) { inheritedResources.Add(r); newInheritedResources.Add(r); } } } //foreach(var r in resources) { // if (resourceAncestors.ContainsKey(r.Id) // && resourceAncestors[r.Id].Intersect(assignedResources.Select(x => x.Id)).Any()) { // inheritedResources.Add(r); // } //} } private Resource BuildResourceTree(IEnumerable resources) { treeView.Nodes.Clear(); if (!resources.Any()) return null; treeView.BeforeCheck -= treeView_BeforeCheck; treeView.AfterCheck -= treeView_AfterCheck; resources = resources.Where(x => x.Id != Guid.Empty); 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)); var stack = new Stack(mainResources.OrderByDescending(x => x.Name)); Resource top = null; TreeNode currentNode = null; Resource currentResource = null; var addedAssignments = newAssignedResources.Except(assignedResources); var removedAssignments = assignedResources.Except(newAssignedResources); var addedIncludes = newInheritedResources.Except(inheritedResources); var removedIncludes = inheritedResources.Except(newInheritedResources); //var assignmentDiff = new HashSet(newAssignedResources); //assignmentDiff.SymmetricExceptWith(assignedResources); //var inheritanceDiff = new HashSet(newInheritedResources); //inheritanceDiff.SymmetricExceptWith(inheritedResources); while (stack.Any()) { if(top == null) top = stack.Peek(); var newResource = stack.Pop(); var newNode = new TreeNode(newResource.Name) { Tag = newResource }; // 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 (newAssignedResources.Contains(newResource)) { newNode.Checked = true; if(!HiveRoles.CheckAdminUserPermissions()) { if(!projectAncestors[Content.Id].Any()) { newNode.ForeColor = SystemColors.GrayText; newNode.Text += " [immutable]"; } } } else if (newInheritedResources.Contains(newResource)) { newNode.Checked = true; newNode.ForeColor = SystemColors.GrayText; } if (inheritedResources.Contains(newResource) && newInheritedResources.Contains(newResource)) { newNode.Text += " [included]"; } else if (addedIncludes.Contains(newResource)) { newNode.BackColor = addedIncludeColor; newNode.ForeColor = SystemColors.GrayText; newNode.Text += " [added include]"; } else if (removedIncludes.Contains(newResource)) { newNode.BackColor = removedIncludeColor; newNode.Text += " [removed include]"; } if (addedAssignments.Contains(newResource)) { newNode.BackColor = addedAssignmentColor; newNode.ForeColor = SystemColors.ControlText; newNode.Text += " [added assignment]"; } else if (removedAssignments.Contains(newResource)) { newNode.BackColor = removedAssignmentColor; newNode.ForeColor = SystemColors.ControlText; newNode.Text += " [removed assignment]"; } 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 ungroupedSlaves = subResources.OfType().OrderBy(x => x.Name); if(ungroupedSlaves.Any()) { var ungroupedNode = new TreeNode(ungroupedGroupName) { ForeColor = SystemColors.GrayText, Tag = new SlaveGroup() { Name = ungroupedGroupName, Description = ungroupedGroupDescription } }; foreach (var slave in ungroupedSlaves) { var slaveNode = new TreeNode(slave.Name) { Tag = slave }; ungroupedNode.Nodes.Add(slaveNode); } treeView.Nodes.Add(ungroupedNode); } treeView.BeforeCheck += treeView_BeforeCheck; treeView.AfterCheck += treeView_AfterCheck; treeView.ExpandAll(); return top; } private void UpdateProjectGenealogy() { projectAncestors.Clear(); projectDescendants.Clear(); var projects = HiveAdminClient.Instance.Projects; foreach(var p in projects.Where(x => x.Id != Guid.Empty)) { projectAncestors.Add(p.Id, new HashSet()); projectDescendants.Add(p.Id, new HashSet()); } foreach (var p in projects.Where(x => x.Id != Guid.Empty)) { var parentProjectId = p.ParentProjectId; while (parentProjectId != null) { var parent = projects.SingleOrDefault(x => x.Id == parentProjectId); if (parent != null) { projectAncestors[p.Id].Add(parent); projectDescendants[parent.Id].Add(p); parentProjectId = parent.ParentProjectId; } else { parentProjectId = null; } } } } #endregion } }