#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 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;
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 UNGROUPED_GROUP_NAME = "UNGROUPED";
public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
public const string IMMUTABLE_TAG = " [assigned, immutable]";
public const string INCLUDED_TAG = " [included]";
public const string ADDED_ASSIGNMENT_TAG = " [added assignment]";
public const string REMOVED_ASSIGNMENT_TAG = " [removed assignment]";
public const string ADDED_INCLUDE_TAG = " [added include]";
public const string REMOVED_INCLUDE_TAG = " [removed include]";
private readonly HashSet assignedResources = new HashSet();
private readonly HashSet newAssignedResources = new HashSet();
private readonly HashSet includedResources = new HashSet();
private readonly HashSet newIncludedResources = new HashSet();
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
private readonly Color selectedBackColor = Color.DodgerBlue;
private readonly Color selectedForeColor = Color.White;
private readonly Color grayTextColor = SystemColors.GrayText;
private HashSet projectExclusiveResources = new HashSet();
private TreeNode ungroupedGroupNode;
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);
HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
}
#region Overrides
protected override void OnContentChanged() {
base.OnContentChanged();
if (Content == null) {
assignedResources.Clear();
newAssignedResources.Clear();
includedResources.Clear();
treeView.Nodes.Clear();
detailsViewHost.Content = null;
} else {
UpdateAssignedResources();
UpdateIncludedResources();
detailsViewHost.Content = BuildResourceTree();
}
SetEnabledStateOfControls();
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
bool enabled = Content != null && !Locked && !ReadOnly;
inheritButton.Enabled = enabled;
saveButton.Enabled = enabled;
treeView.Enabled = enabled;
if (detailsViewHost != null) {
detailsViewHost.Locked = true;
}
}
#endregion
#region Event Handlers
private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
if (InvokeRequired) Invoke((Action)HiveAdminClient_Instance_Refreshing, sender, e);
else {
SetEnabledStateOfControls();
}
}
private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
if (InvokeRequired) Invoke((Action)HiveAdminClient_Instance_Refreshed, sender, e);
else {
OnContentChanged();
}
}
private void ProjectResourcesView_Disposed(object sender, EventArgs e) {
HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
}
private void refreshButton_Click(object sender, EventArgs e) {
HiveAdminClient.Instance.Refresh();
UpdateAssignedResources();
UpdateIncludedResources();
var top = BuildResourceTree();
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);
});
HiveAdminClient.Instance.Refresh();
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);
});
HiveAdminClient.Instance.Refresh();
UpdateResourceTree();
}
private void treeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
if (e.Node == null || e.Node == ungroupedGroupNode)
e.Cancel = true;
var selectedResource = (Resource)e.Node.Tag;
if (HiveAdminClient.Instance.DisabledParentResources.Contains(selectedResource))
e.Cancel = true;
}
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) {
if (e.Node == null || e.Node == ungroupedGroupNode) {
e.Cancel = true;
return;
}
var checkedResource = (Resource)e.Node.Tag;
if (checkedResource == null
|| checkedResource.Id == Guid.Empty
|| HiveAdminClient.Instance.DisabledParentResources.Contains(checkedResource)
|| newIncludedResources.Contains(checkedResource)) {
e.Cancel = true;
} else if (!IsAdmin()) {
if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)
|| !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any()
|| projectExclusiveResources.Contains(checkedResource)) {
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();
UpdateIncludedResources();
var top = BuildResourceTree();
detailsViewHost.Content = top;
}
private void UpdateNewResourceTree() {
UpdateNewAssignedResources();
UpdateNewIncludedResources();
var top = BuildResourceTree();
detailsViewHost.Content = top;
}
private IEnumerable GetAvailableResourcesForProjectAdministration(Guid projectId) {
projectExclusiveResources.Clear();
if (projectId == Guid.Empty) return Enumerable.Empty();
var resources = HiveAdminClient.Instance.Resources.Where(x => x.Id != Guid.Empty);
if (!resources.Any()) return resources;
if (IsAdmin()) return resources;
// get project specific assigned resources
var projectResources = resources.Where(x =>
HiveAdminClient.Instance.ProjectResourceAssignments
.Where(a => a.ProjectId == projectId)
.Select(a => a.ResourceId)
.Contains(x.Id));
// look up for assignments of ancestor projects
var projectIds = new HashSet();
HiveAdminClient.Instance.GetAvailableProjectAncestors(projectId).ToList().ForEach(x => projectIds.Add(x.Id));
var ancestorProjectResources = resources.Where(x =>
HiveAdminClient.Instance.ProjectResourceAssignments
.Where(a => projectIds.Contains(a.ProjectId))
.Select(a => a.ResourceId)
.Contains(x.Id));
// look down for included resources of ancestor projects
HashSet availableResources = new HashSet(ancestorProjectResources);
foreach (var r in ancestorProjectResources) {
foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
availableResources.Add(d);
}
}
// determine resources, which are exclusively assigned to the currently selected project
projectResources
.Except(availableResources)
.ToList()
.ForEach(x => projectExclusiveResources.Add(x));
// look down for included resources of currently selected project
if (projectExclusiveResources.Any()) {
foreach (var r in projectExclusiveResources.ToArray()) {
foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
projectExclusiveResources.Add(d);
}
}
}
return availableResources.Union(projectExclusiveResources);
}
private IEnumerable GetAssignedResourcesForProject(Guid projectId) {
if (projectId == Guid.Empty) return Enumerable.Empty();
return HiveAdminClient.Instance.Resources.Where(x =>
HiveAdminClient.Instance.ProjectResourceAssignments
.Where(a => a.ProjectId == projectId)
.Select(a => a.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(HiveAdminClient.Instance.GetAvailableResourceAncestors(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 UpdateIncludedResources() {
includedResources.Clear();
newIncludedResources.Clear();
foreach (var a in assignedResources) {
foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
includedResources.Add(r);
newIncludedResources.Add(r);
}
}
}
private void UpdateNewIncludedResources() {
newIncludedResources.Clear();
foreach (var a in newAssignedResources) {
foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
newIncludedResources.Add(r);
}
}
}
private Resource BuildResourceTree() {
treeView.Nodes.Clear();
treeView.BeforeCheck -= treeView_BeforeCheck;
treeView.AfterCheck -= treeView_AfterCheck;
var resources = GetAvailableResourcesForProjectAdministration(Content.Id);
var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
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 mainDisabledParentResources = new HashSet(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
mainResources.UnionWith(mainDisabledParentResources);
var subResources = new HashSet(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
var stack = new Stack(mainResources.OrderByDescending(x => x.Name));
Resource top = null;
//bool nodeSelected = false;
if (detailsViewHost != null && detailsViewHost.Content != null && detailsViewHost.Content is Resource) {
var resourceId = ((Resource)detailsViewHost.Content).Id;
top = resources.Where(x => x.Id == resourceId).FirstOrDefault();
}
TreeNode currentNode = null;
Resource currentResource = null;
var addedAssignments = newAssignedResources.Except(assignedResources);
var removedAssignments = assignedResources.Except(newAssignedResources);
var addedIncludes = newIncludedResources.Except(includedResources);
var removedIncludes = includedResources.Except(newIncludedResources);
while (stack.Any()) {
var newResource = stack.Pop();
var newNode = new TreeNode(newResource.Name) { Tag = newResource };
if (top == null && !disabledParentResources.Contains(newResource)) {
top = newResource;
}
//if(!nodeSelected && top != null && newResource.Id == top.Id) {
// newNode.BackColor = selectedBackColor;
// newNode.ForeColor = selectedForeColor;
// nodeSelected = true;
//}
// 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 (disabledParentResources.Contains(newResource)) {
newNode.Checked = false;
newNode.ForeColor = grayTextColor;
} else if (newAssignedResources.Contains(newResource)) {
newNode.Checked = true;
if (!HiveRoles.CheckAdminUserPermissions()) {
if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)
|| !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any()
|| projectExclusiveResources.Contains(newResource)) {
newNode.ForeColor = SystemColors.GrayText;
newNode.Text += IMMUTABLE_TAG;
}
}
} else if (newIncludedResources.Contains(newResource)) {
newNode.Checked = true;
newNode.ForeColor = SystemColors.GrayText;
}
if (includedResources.Contains(newResource) && newIncludedResources.Contains(newResource)) {
newNode.Text += INCLUDED_TAG;
} else if (addedIncludes.Contains(newResource)) {
newNode.BackColor = addedIncludeColor;
newNode.ForeColor = SystemColors.GrayText;
newNode.Text += ADDED_INCLUDE_TAG;
} else if (removedIncludes.Contains(newResource)) {
newNode.BackColor = removedIncludeColor;
newNode.Text += REMOVED_INCLUDE_TAG;
}
if (addedAssignments.Contains(newResource)) {
newNode.BackColor = addedAssignmentColor;
newNode.ForeColor = SystemColors.ControlText;
newNode.Text += ADDED_ASSIGNMENT_TAG;
} else if (removedAssignments.Contains(newResource)) {
newNode.BackColor = removedAssignmentColor;
newNode.ForeColor = SystemColors.ControlText;
newNode.Text += REMOVED_ASSIGNMENT_TAG;
}
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;
}
ExpandResourceNodesOfInterest(treeView.Nodes);
bool expandUngroupedGroupNode = false;
var ungroupedSlaves = subResources.OfType().OrderBy(x => x.Name);
if (ungroupedSlaves.Any()) {
ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
ForeColor = SystemColors.GrayText,
Tag = new SlaveGroup() {
Name = UNGROUPED_GROUP_NAME,
Description = UNGROUPED_GROUP_DESCRIPTION
}
};
foreach (var slave in ungroupedSlaves) {
var slaveNode = new TreeNode(slave.Name) { Tag = slave };
if (newAssignedResources.Contains(slave)) {
slaveNode.Checked = true;
expandUngroupedGroupNode = true;
}
if (!HiveRoles.CheckAdminUserPermissions()) {
slaveNode.ForeColor = SystemColors.GrayText;
slaveNode.Text += IMMUTABLE_TAG;
} else {
if (addedAssignments.Contains(slave)) {
slaveNode.BackColor = addedAssignmentColor;
slaveNode.ForeColor = SystemColors.ControlText;
slaveNode.Text += ADDED_ASSIGNMENT_TAG;
} else if (removedAssignments.Contains(slave)) {
slaveNode.BackColor = removedAssignmentColor;
slaveNode.ForeColor = SystemColors.ControlText;
slaveNode.Text += REMOVED_ASSIGNMENT_TAG;
expandUngroupedGroupNode = true;
}
}
ungroupedGroupNode.Nodes.Add(slaveNode);
}
if (expandUngroupedGroupNode) ungroupedGroupNode.Expand();
treeView.Nodes.Add(ungroupedGroupNode);
} else if (ungroupedGroupNode != null) {
ungroupedGroupNode.Nodes.Clear();
}
treeView.BeforeCheck += treeView_BeforeCheck;
treeView.AfterCheck += treeView_AfterCheck;
return top;
}
private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
foreach (TreeNode n in nodes) {
Resource r = (Resource)n.Tag;
if (n.Nodes.Count > 0) {
if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType().Any()) {
n.Expand();
ExpandResourceNodesOfInterest(n.Nodes);
} else {
n.Collapse();
}
} else {
n.Collapse();
}
}
}
private bool IsAdmin() {
return HiveRoles.CheckAdminUserPermissions();
}
#endregion
}
}