#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("Projects View")] [Content(typeof(IItemList), false)] public partial class ProjectsView : ItemView, IDisposable { private const int greenFlagImageIndex = 0; private const int redFlagImageIndex = 1; private readonly Color changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1 private readonly Color selectedColor = Color.FromArgb(255, 240, 194, 59); // #f0c23b private Project selectedProject = null; private readonly object locker = new object(); public new IItemList Content { get { return (IItemList)base.Content; } set { base.Content = value; } } public ProjectsView() { InitializeComponent(); projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagGreen); projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagRed); 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) { projectsTreeView.Nodes.Clear(); projectView.Content = null; projectPermissionsView.Content = null; projectResourcesView.Content = null; } else { var top = BuildProjectTree(Content); projectView.Content = top; projectPermissionsView.Content = top; projectResourcesView.Content = top; if(top != null && top.Id == Guid.Empty) { projectPermissionsView.Locked = true; projectResourcesView.Locked = true; } } } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); bool enabled = Content != null && !ReadOnly; refreshButton.Enabled = enabled; addButton.Enabled = enabled; removeButton.Enabled = enabled; saveProjectButton.Enabled = enabled; projectView.Enabled = enabled; projectPermissionsView.Enabled = enabled; projectResourcesView.Enabled = enabled; } #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 ProjectViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (InvokeRequired) Invoke((Action)ProjectViewContent_PropertyChanged, sender, e); else { OnContentChanged(); } } 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 ProjectsView_Load(object sender, EventArgs e) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateProjects()); } private async void refreshButton_Click(object sender, EventArgs e) { lock (locker) { if (!refreshButton.Enabled) return; refreshButton.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateProjects(), finallyCallback: () => { refreshButton.Enabled = true; }); } private void addButton_Click(object sender, EventArgs e) { Guid? parentProjectId = null; if (selectedProject != null && selectedProject.Id == Guid.Empty) { MessageBox.Show( "You cannot add a project to a not yet stored project.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (selectedProject != null) parentProjectId = selectedProject.Id; var project = new Project { Name = "New Project", OwnerUserId = UserInformation.Instance.User.Id, ParentProjectId = parentProjectId }; selectedProject = project; Content.Add(project); } private async void removeButton_Click(object sender, EventArgs e) { if (selectedProject == null) return; lock (locker) { if (!removeButton.Enabled) return; removeButton.Enabled = false; } if(!IsAdmin() && HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id)) { MessageBox.Show( "Only subprojects of owned projects can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!IsAdmin() && Content.Any(x => x.ParentProjectId == selectedProject.Id)) { MessageBox.Show( "Only empty projects can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } var result = MessageBox.Show( "Do you really want to delete " + selectedProject.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => RemoveProject(selectedProject), finallyCallback: () => { removeButton.Enabled = true; }); } } private async void saveProjectButton_Click(object sender, EventArgs e) { lock (locker) { if (!saveProjectButton.Enabled) return; saveProjectButton.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => { var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified); foreach (var project in projectsToSave) project.Store(); UpdateProjects(); }, finallyCallback: () => saveProjectButton.Enabled = true); OnContentChanged(); } private void projectsTreeView_AfterSelect(object sender, TreeViewEventArgs e) { ChangeSelectedProjectNode(e.Node); //if (projectView.Content != null) // projectView.Content.PropertyChanged -= ProjectViewContent_PropertyChanged; projectView.Content = selectedProject; projectPermissionsView.Content = selectedProject; projectResourcesView.Content = selectedProject; //if (selectedProject != null) // selectedProject.PropertyChanged += ProjectViewContent_PropertyChanged; } private void projectsTreeView_DragDrop(object sender, DragEventArgs e) { if (e.Effect == DragDropEffects.None) return; var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode)); if (sourceNode == null) return; var sourceProject = ((Project)sourceNode.Tag); if (sourceProject == 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 targetProject = (targetNode != null) ? (Project)targetNode.Tag : null; if(targetProject != null && targetProject.Id == Guid.Empty) { MessageBox.Show( "You cannot drag projects to a not yet stored project.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if(!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) { MessageBox.Show( "You cannot drag to this project.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (sourceNode.Parent == null) { treeView.Nodes.Remove(sourceNode); } else { sourceNode.Parent.Nodes.Remove(sourceNode); sourceProject.ParentProjectId = null; } if (targetNode == null) { treeView.Nodes.Add(sourceNode); } else if(targetProject.Id != Guid.Empty) { targetNode.Nodes.Add(sourceNode); sourceProject.ParentProjectId = targetProject.Id; } selectedProject = sourceProject; OnContentChanged(); } private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) { var sourceNode = (TreeNode)e.Item; if (IsAuthorized((Project)sourceNode.Tag)) DoDragDrop(sourceNode, DragDropEffects.All); } private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode)); var sourceProject = ((Project)sourceNode.Tag); var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y)); var targetNode = projectsTreeView.GetNodeAt(targetPoint); var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null; if ((!IsAdmin() && (targetNode == null || targetProject == null)) || sourceNode == null || sourceProject == null || sourceNode == targetNode || (targetProject != null && targetProject.Id == Guid.Empty) || (targetProject != null && targetProject.Id == sourceProject.ParentProjectId) || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) { e.Effect = DragDropEffects.None; } } private void projectsTreeView_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 pro = ((Project)n.Tag); if (n.BackColor.Equals(c1)) { n.BackColor = c2; } if (resetText) { n.Text = pro.Name; if (pro.Id == Guid.Empty) { n.Text += " [not stored]"; } else if (pro.Modified) { n.Text += " [changes not stored]"; } } if (n.Nodes.Count > 0) { ResetTreeNodes(n.Nodes, c1, c2, resetText); } } } private void ChangeSelectedProjectNode(TreeNode projectNode) { selectedProject = (Project)projectNode.Tag; ResetTreeNodes(projectsTreeView.Nodes, selectedColor, Color.Transparent, true); projectNode.BackColor = selectedColor; projectNode.Text += " [selected]"; } private Project BuildProjectTree(IEnumerable projects) { projectsTreeView.Nodes.Clear(); if (!projects.Any()) return null; var mainProjects = new HashSet(projects.Where(x => x.ParentProjectId == null)); var parentedMainProjects = new HashSet(projects .Where(x => x.ParentProjectId.HasValue && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value))); mainProjects.UnionWith(parentedMainProjects); var subProbjects = new HashSet(projects.Except(mainProjects)); var stack = new Stack(mainProjects.OrderByDescending(x => x.Name)); if (selectedProject != null) selectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault(); bool nodeSelected = false; TreeNode currentNode = null; Project currentProject = null; while (stack.Any()) { var newProject = stack.Pop(); var newNode = new TreeNode(newProject.Name) { Tag = newProject }; if (newProject.Id == Guid.Empty) { newNode.Text += " [not stored]"; } else if(newProject.Modified) { newNode.Text += " [changes not stored]"; } if (selectedProject == null) { selectedProject = newProject; } if (newProject.Id == selectedProject.Id && !nodeSelected) { newNode.BackColor = selectedColor; newNode.Text += " [selected]"; 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 && newProject.ParentProjectId != currentProject.Id) { currentNode = currentNode.Parent; currentProject = currentNode == null ? null : (Project)currentNode.Tag; } if (currentNode == null) { projectsTreeView.Nodes.Add(newNode); newNode.ImageIndex = greenFlagImageIndex; } else { currentNode.Nodes.Add(newNode); newNode.ImageIndex = redFlagImageIndex; } newNode.SelectedImageIndex = newNode.ImageIndex; var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id); if (childProjects.Any()) { foreach (var project in childProjects.OrderByDescending(x => x.Name)) { subProbjects.Remove(project); stack.Push(project); } currentNode = newNode; currentProject = newProject; } } projectsTreeView.ExpandAll(); return selectedProject; } private void UpdateProjects() { try { HiveAdminClient.Instance.Refresh(); Content = HiveAdminClient.Instance.Projects; } catch (AnonymousUserException) { ShowHiveInformationDialog(); } } private void RemoveProject(Project project) { if (project == null || project.Id == Guid.Empty) return; try { HiveAdminClient.Delete(project); Content.Remove(selectedProject); } catch (AnonymousUserException) { ShowHiveInformationDialog(); } } private bool IsAuthorized(Project project) { return project != null && UserInformation.Instance.UserExists; //&& (project.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions()); } private bool IsAdmin() { return HiveRoles.CheckAdminUserPermissions(); } private void ShowHiveInformationDialog() { if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog); else { using (HiveInformationDialog dialog = new HiveInformationDialog()) { dialog.ShowDialog(this); } } } #endregion } }