#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.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 const string SELECTED_TAG = ""; // " [selected]"; private const string NOT_STORED_TAG = "*"; // " [not stored]"; private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]"; private readonly Color selectedBackColor = Color.DodgerBlue; private readonly Color selectedForeColor = Color.White; private readonly Color grayTextColor = SystemColors.GrayText; private Project selectedProject = null; public Project SelectedProject { get { return selectedProject; } set { if (selectedProject != value) ChangeSelectedProject(value); } } 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; projectJobsView.Content = null; selectedProject = null; } else { BuildProjectTree(Content); } } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); bool selectedProjectExists = selectedProject != null && selectedProject.Id != Guid.Empty && Content.Contains(selectedProject); bool projectIsNew = selectedProject != null && selectedProject.Id == Guid.Empty; bool locked = Content == null || selectedProject == null || Locked || ReadOnly; bool parentOwner = HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id); bool saveLocked = locked || (!IsAdmin() && selectedProject.OwnerUserId != UserInformation.Instance.User.Id && !parentOwner); bool addLocked = saveLocked || projectIsNew || selectedProject.Id == Guid.Empty; bool deleteLocked = locked || (!IsAdmin() && !parentOwner) || Content.Where(x => x.ParentProjectId == selectedProject.Id).Any(); var now = DateTime.Now; if (!addLocked && (now < selectedProject.StartDate || now > selectedProject.EndDate)) addLocked = true; addButton.Enabled = !addLocked; removeButton.Enabled = !deleteLocked; saveProjectButton.Enabled = !saveLocked; projectView.Enabled = !locked; projectPermissionsView.Enabled = !locked; projectResourcesView.Enabled = !locked; projectJobsView.Enabled = !locked; projectView.Locked = locked; projectPermissionsView.Locked = locked; projectResourcesView.Locked = locked; projectJobsView.Locked = locked; } #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) { if (selectedProject == null && !IsAdmin()) { MessageBox.Show( "You are not allowed to add a root project - please select a parent project.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } 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; } var project = new Project { Name = "New Project", OwnerUserId = UserInformation.Instance.User.Id, }; if(selectedProject != null) { project.ParentProjectId = selectedProject.Id; project.EndDate = selectedProject.EndDate; } SelectedProject = project; Content.Add(project); } private async void removeButton_Click(object sender, EventArgs e) { if (selectedProject == null) return; lock (locker) { // for details go to ChangeSelectedProject(..) if (!removeButton.Enabled) return; removeButton.Enabled = false; } // double check of ChangeSelectedProject(..): // if the user is no admin nor owner of a parent project if (!IsAdmin() && !HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id)) { MessageBox.Show( "Only admins and owners of parent projects are allowed to delete this project.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (Content.Any(x => x.ParentProjectId == selectedProject.Id) || HiveAdminClient.Instance.ProjectDescendants[selectedProject.Id].Any()) { MessageBox.Show( "Only empty projects can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id) && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any()) { MessageBox.Show( "There are " + HiveAdminClient.Instance.Jobs[selectedProject.Id].Count + " job(s) using this project and/or child-projects. It is necessary to delete them before the project.", "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; }); } else { 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: () => { //if (selectedProject != null && selectedProject.Id == Guid.Empty) // SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(selectedProject.Id).LastOrDefault(); 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_MouseDown(object sender, MouseEventArgs e) { var node = projectsTreeView.GetNodeAt(e.Location); if (node == null) return; var p = (Project)node.Tag; if(!HiveAdminClient.Instance.DisabledParentProjects.Contains(p)) ChangeSelectedProjectNode(node); } private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) { e.Cancel = true; } 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); var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null; if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) { MessageBox.Show( "You cannot drag projects 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 || !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 BuildProjectTree(IEnumerable projects) { projectsTreeView.Nodes.Clear(); if (!projects.Any()) return; var disabledParentProjects = HiveAdminClient.Instance.DisabledParentProjects; 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 mainDisabledParentProjects = new HashSet(disabledParentProjects.Where(x => x.ParentProjectId == null || x.ParentProjectId == Guid.Empty)); mainProjects.UnionWith(mainDisabledParentProjects); var subProbjects = new HashSet(projects.Union(disabledParentProjects).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 }; StyleTreeNode(newNode, newProject); if (selectedProject == null && !disabledParentProjects.Contains(newProject)) { SelectedProject = newProject; } if (!nodeSelected && selectedProject != null && selectedProject.Id == newProject.Id) { newNode.BackColor = selectedBackColor; newNode.ForeColor = selectedForeColor; newNode.Text += SELECTED_TAG; 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; if (disabledParentProjects.Contains(newProject)) { newNode.Checked = false; newNode.ForeColor = grayTextColor; } 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(); } private void StyleTreeNode(TreeNode n, Project p) { n.Text = p.Name; n.BackColor = Color.Transparent; n.ForeColor = Color.Black; if (HiveAdminClient.Instance.DisabledParentProjects.Select(x => x.Id).Contains(p.Id)) { n.ForeColor = grayTextColor; } else if (p.Id == Guid.Empty) { n.Text += NOT_STORED_TAG; } else if (p.Modified) { n.Text += CHANGES_NOT_STORED_TAG; } } private void ResetTreeNodes(TreeNodeCollection nodes) { foreach (TreeNode n in nodes) { StyleTreeNode(n, (Project)n.Tag); if (n.Nodes.Count > 0) { ResetTreeNodes(n.Nodes); } } } private void ChangeSelectedProject(Project project) { projectView.Content = project; projectPermissionsView.Content = project; projectResourcesView.Content = project; projectJobsView.Content = project; selectedProject = project; SetEnabledStateOfControls(); } private void ChangeSelectedProjectNode(TreeNode projectNode) { if (projectNode == null) return; SelectedProject = (Project)projectNode.Tag; ResetTreeNodes(projectsTreeView.Nodes); projectNode.BackColor = selectedBackColor; projectNode.ForeColor = selectedForeColor; projectNode.Text += SELECTED_TAG; } private void UpdateProjects() { try { HiveAdminClient.Instance.Refresh(); Content = HiveAdminClient.Instance.Projects; } catch (AnonymousUserException) { ShowHiveInformationDialog(); } } private void RemoveProject(Project project) { if (project == null) return; try { if (project.Id != Guid.Empty) { var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified); foreach (var p in projectsToSave) p.Store(); SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(project.Id).LastOrDefault(); HiveAdminClient.Delete(project); UpdateProjects(); } else { Content.Remove(project); } } catch (AnonymousUserException) { ShowHiveInformationDialog(); } } private bool IsAuthorized(Project project) { return project != null && UserInformation.Instance.UserExists; } private bool IsAdmin() { return HiveRoles.CheckAdminUserPermissions(); } private void ShowHiveInformationDialog() { if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog); else { using (HiveInformationDialog dialog = new HiveInformationDialog()) { dialog.ShowDialog(this); } } } #endregion } }