Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs @ 16643

Last change on this file since 16643 was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

File size: 20.7 KB
RevLine 
[15401]1#region License Information
2/* HeuristicLab
[16565]3 * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[15401]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Drawing;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Clients.Access;
29using HeuristicLab.Clients.Hive.Views;
30using HeuristicLab.Collections;
31using HeuristicLab.Common.Resources;
32using HeuristicLab.Core;
33using HeuristicLab.Core.Views;
34using HeuristicLab.MainForm;
35
36namespace HeuristicLab.Clients.Hive.Administrator.Views {
37  [View("Projects View")]
38  [Content(typeof(IItemList<Project>), false)]
39  public partial class ProjectsView : ItemView, IDisposable {
40    private const int greenFlagImageIndex = 0;
41    private const int redFlagImageIndex = 1;
[16117]42    private const string SELECTED_TAG = ""; // " [selected]";
43    private const string NOT_STORED_TAG = "*"; // " [not stored]";
44    private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]";
[16430]45    private const string INACTIVE_TAG = " [inactive]";
[15742]46
[16117]47    private readonly Color selectedBackColor = Color.DodgerBlue;
48    private readonly Color selectedForeColor = Color.White;
49    private readonly Color grayTextColor = SystemColors.GrayText;
50
[15742]51    private Project selectedProject = null;
[16117]52    public Project SelectedProject {
53      get { return selectedProject; }
54      set { if (selectedProject != value) ChangeSelectedProject(value); }
55    }
[15401]56
57    private readonly object locker = new object();
[16446]58    private bool refreshingInternal = false;
59    private bool refreshingExternal = false;
[15401]60
61    public new IItemList<Project> Content {
62      get { return (IItemList<Project>)base.Content; }
63      set { base.Content = value; }
64    }
65
66    public ProjectsView() {
67      InitializeComponent();
68
69      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagGreen);
70      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagRed);
71
72      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
73      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
74    }
75
76    #region Overrides
77    protected override void RegisterContentEvents() {
78      base.RegisterContentEvents();
79      Content.ItemsAdded += Content_ItemsAdded;
80      Content.ItemsRemoved += Content_ItemsRemoved;
81    }
82
83    protected override void DeregisterContentEvents() {
84      Content.ItemsRemoved -= Content_ItemsRemoved;
85      Content.ItemsAdded -= Content_ItemsAdded;
86      base.DeregisterContentEvents();
87    }
88
89    protected override void OnContentChanged() {
90      base.OnContentChanged();
91      if (Content == null) {
92        projectsTreeView.Nodes.Clear();
93        projectView.Content = null;
[15422]94        projectPermissionsView.Content = null;
95        projectResourcesView.Content = null;
[16117]96        projectJobsView.Content = null;
97        selectedProject = null;
[15401]98      } else {
[16117]99        BuildProjectTree(Content);
[15401]100      }
[16185]101      SetEnabledStateOfControls();
[15401]102    }
103
104    protected override void SetEnabledStateOfControls() {
[16430]105      base.SetEnabledStateOfControls();
[16117]106
[16430]107      bool locked = Content == null || Locked || ReadOnly;
[16185]108      bool parentOwner = selectedProject != null && HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id);
[16430]109      bool selectedProjectDisabled = selectedProject == null
[16185]110                                     || selectedProject != null && selectedProject.Id == Guid.Empty;
[16117]111
[16185]112      bool selectedProjectHasJobs =
113        !selectedProjectDisabled && HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id)
114                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null
115                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any();
116
117      bool addLocked = locked
118                       || (selectedProject == null && !IsAdmin())
119                       || (selectedProject != null && selectedProject.Id == Guid.Empty)
120                       || (selectedProject != null && !IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id)
121                       || (selectedProject != null && (DateTime.Now < selectedProject.StartDate || DateTime.Now > selectedProject.EndDate));
122
123      bool deleteLocked = locked
124                          || !Content.Any()
125                          || selectedProject == null
126                          || Content.Any(x => x.ParentProjectId == selectedProject.Id)
127                          || selectedProjectHasJobs
128                          || (!IsAdmin() && !parentOwner);
129
130      bool saveLocked = locked
131                        || !Content.Any()
132                        || selectedProject == null
133                        || (!IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id);
134
135
[16117]136      addButton.Enabled = !addLocked;
137      removeButton.Enabled = !deleteLocked;
138      saveProjectButton.Enabled = !saveLocked;
[16185]139
[16117]140      projectView.Enabled = !locked;
[16185]141      projectPermissionsView.Enabled = !locked && !selectedProjectDisabled;
142      projectResourcesView.Enabled = !locked && !selectedProjectDisabled;
143      projectJobsView.Enabled = !locked && !selectedProjectDisabled;
[16117]144
145      projectView.Locked = locked;
[16185]146      projectPermissionsView.Locked = locked || selectedProjectDisabled;
147      projectResourcesView.Locked = locked || selectedProjectDisabled;
148      projectJobsView.Locked = locked || selectedProjectDisabled;
[15401]149    }
150    #endregion
151
152    #region Event Handlers
153    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
[15412]154      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsAdded, sender, e);
155      else {
156        OnContentChanged();
157      }
[15401]158    }
159
160    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
[15412]161      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsRemoved, sender, e);
162      else {
163        OnContentChanged();
164      }
[15401]165    }
166
167    private void ProjectViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
[15412]168      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)ProjectViewContent_PropertyChanged, sender, e);
169      else {
170        OnContentChanged();
171      }
[15401]172    }
173
174    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
175      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
176      else {
[16446]177        lock (locker) {
178          if (refreshingExternal) return;
179          if (!refreshingInternal) refreshingExternal = true;
180        }
181
[16430]182        Progress.Show(this, "Refreshing ...", ProgressMode.Indeterminate);
[15401]183        SetEnabledStateOfControls();
184      }
185    }
186
187    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
188      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
189      else {
[16446]190        if (refreshingExternal) refreshingExternal = false;
191        Content = HiveAdminClient.Instance.Projects;
[15401]192
[16430]193        Progress.Hide(this);
[15401]194        SetEnabledStateOfControls();
195      }
196    }
197
198    private async void ProjectsView_Load(object sender, EventArgs e) {
199      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
[15742]200        action: () => UpdateProjects());
[15401]201    }
202
[16446]203    private void ProjectsView_Disposed(object sender, EventArgs e) {
204      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
205      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
206    }
207
[15401]208    private async void refreshButton_Click(object sender, EventArgs e) {
209      lock (locker) {
[15422]210        if (!refreshButton.Enabled) return;
211        refreshButton.Enabled = false;
[15401]212      }
213
214      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
215        action: () => UpdateProjects(),
[15412]216        finallyCallback: () => {
[15422]217          refreshButton.Enabled = true;
[15412]218        });
[15401]219    }
220
[16430]221    private void addButton_Click(object sender, EventArgs e) {
[15742]222
[16117]223      if (selectedProject == null && !IsAdmin()) {
224        MessageBox.Show(
225          "You are not allowed to add a root project - please select a parent project.",
226          "HeuristicLab Hive Administrator",
227          MessageBoxButtons.OK,
228          MessageBoxIcon.Information);
229        return;
230      }
[16430]231
[15742]232      if (selectedProject != null && selectedProject.Id == Guid.Empty) {
233        MessageBox.Show(
234          "You cannot add a project to a not yet stored project.",
235          "HeuristicLab Hive Administrator",
236          MessageBoxButtons.OK,
237          MessageBoxIcon.Information);
238        return;
239      }
240
[15401]241      var project = new Project {
242        Name = "New Project",
[16430]243        OwnerUserId = UserInformation.Instance.User.Id,
[15401]244      };
[16430]245      if (selectedProject != null) {
[16117]246        project.ParentProjectId = selectedProject.Id;
247        project.EndDate = selectedProject.EndDate;
248      }
[15742]249
[16117]250      SelectedProject = project;
[15401]251      Content.Add(project);
252    }
253
254    private async void removeButton_Click(object sender, EventArgs e) {
[15742]255      if (selectedProject == null) return;
256
[15401]257      lock (locker) {
[16117]258        // for details go to ChangeSelectedProject(..)
[15422]259        if (!removeButton.Enabled) return;
260        removeButton.Enabled = false;
[15401]261      }
262
[15412]263      var result = MessageBox.Show(
[15742]264        "Do you really want to delete " + selectedProject.Name + "?",
[15412]265        "HeuristicLab Hive Administrator",
266        MessageBoxButtons.YesNo,
267        MessageBoxIcon.Question);
268
269      if (result == DialogResult.Yes) {
[15742]270        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
[16117]271          action: () => {
[16430]272            RemoveProject(selectedProject);
[15742]273          });
[15412]274      }
[16185]275      SetEnabledStateOfControls();
[15401]276    }
277
278    private async void saveProjectButton_Click(object sender, EventArgs e) {
279      lock (locker) {
[15422]280        if (!saveProjectButton.Enabled) return;
281        saveProjectButton.Enabled = false;
[15401]282      }
283
284      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
285        action: () => {
[16117]286          //if (selectedProject != null && selectedProject.Id == Guid.Empty)
287          //  SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(selectedProject.Id).LastOrDefault();
[15401]288          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
289          foreach (var project in projectsToSave)
290            project.Store();
[16430]291
[15742]292          UpdateProjects();
[15401]293        },
[15422]294        finallyCallback: () => saveProjectButton.Enabled = true);
[15627]295
296      OnContentChanged();
[15401]297    }
298
[16117]299    private void projectsTreeView_MouseDown(object sender, MouseEventArgs e) {
300      var node = projectsTreeView.GetNodeAt(e.Location);
301      if (node == null) return;
302      var p = (Project)node.Tag;
[16430]303      if (!HiveAdminClient.Instance.DisabledParentProjects.Contains(p)) ChangeSelectedProjectNode(node);
[16117]304    }
[15401]305
[16117]306    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
307      e.Cancel = true;
[15401]308    }
309
310    private void projectsTreeView_DragDrop(object sender, DragEventArgs e) {
[16117]311      if (e.Effect == DragDropEffects.None) return;
312
[15401]313      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
314      if (sourceNode == null) return;
[16117]315      var sourceProject = ((Project)sourceNode.Tag);
316      if (sourceProject == null) return;
[15401]317
318      var treeView = (TreeView)sender;
319      if (sourceNode.TreeView != treeView) return;
320
321      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
322      var targetNode = treeView.GetNodeAt(targetPoint);
323
[15742]324      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
[15401]325
[16117]326      if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
[15742]327        MessageBox.Show(
[16117]328          "You cannot drag projects to this project.",
[15742]329          "HeuristicLab Hive Administrator",
330          MessageBoxButtons.OK,
331          MessageBoxIcon.Information);
[16117]332        return;
[15742]333      }
334
335      if (sourceNode.Parent == null) {
[15401]336        treeView.Nodes.Remove(sourceNode);
[15742]337      } else {
[15401]338        sourceNode.Parent.Nodes.Remove(sourceNode);
[16117]339        sourceProject.ParentProjectId = null;
[15401]340      }
341
342      if (targetNode == null) {
343        treeView.Nodes.Add(sourceNode);
[16430]344      } else if (targetProject.Id != Guid.Empty) {
[15401]345        targetNode.Nodes.Add(sourceNode);
[16117]346        sourceProject.ParentProjectId = targetProject.Id;
[15401]347      }
[15742]348
[16117]349      SelectedProject = sourceProject;
[15742]350      OnContentChanged();
[15401]351    }
352
353    private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
354      var sourceNode = (TreeNode)e.Item;
355      if (IsAuthorized((Project)sourceNode.Tag))
356        DoDragDrop(sourceNode, DragDropEffects.All);
357    }
358
[16117]359    private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) {
[15401]360      e.Effect = DragDropEffects.Move;
361
[16117]362      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
363      var sourceProject = ((Project)sourceNode.Tag);
364
365      var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y));
366      var targetNode = projectsTreeView.GetNodeAt(targetPoint);
367      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
368
369      if ((!IsAdmin() && (targetNode == null || targetProject == null))
370      || sourceNode == null
371      || sourceProject == null
372      || sourceNode == targetNode
373      || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
374        e.Effect = DragDropEffects.None;
375      }
[15401]376    }
377
378    private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
379      e.Action = DragAction.Continue;
380    }
381    #endregion
382
383    #region Helpers
[16117]384    private void BuildProjectTree(IEnumerable<Project> projects) {
[15401]385      projectsTreeView.Nodes.Clear();
[16117]386      if (!projects.Any()) return;
[15401]387
[16117]388      var disabledParentProjects = HiveAdminClient.Instance.DisabledParentProjects;
[15401]389      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
[16117]390      //var parentedMainProjects = new HashSet<Project>(projects
391      //  .Where(x => x.ParentProjectId.HasValue
392      //  && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)));
393      //mainProjects.UnionWith(parentedMainProjects);
394      var mainDisabledParentProjects = new HashSet<Project>(disabledParentProjects.Where(x => x.ParentProjectId == null || x.ParentProjectId == Guid.Empty));
395      mainProjects.UnionWith(mainDisabledParentProjects);
396      var subProbjects = new HashSet<Project>(projects.Union(disabledParentProjects).Except(mainProjects));
[15401]397
398      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
[16117]399      if (selectedProject != null) SelectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault();
[15760]400      bool nodeSelected = false;
[15401]401
402      TreeNode currentNode = null;
403      Project currentProject = null;
[16117]404
[15401]405      while (stack.Any()) {
406        var newProject = stack.Pop();
407        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
[16117]408        StyleTreeNode(newNode, newProject);
[16430]409
[16117]410        if (selectedProject == null && !disabledParentProjects.Contains(newProject)) {
411          SelectedProject = newProject;
[15742]412        }
[16117]413        if (!nodeSelected && selectedProject != null && selectedProject.Id == newProject.Id) {
414          newNode.BackColor = selectedBackColor;
415          newNode.ForeColor = selectedForeColor;
416          newNode.Text += SELECTED_TAG;
[15760]417          nodeSelected = true;
[15559]418        }
[15401]419
[15742]420        // search for parent node of newNode and save in currentNode
421        // necessary since newNodes (stack top items) might be siblings
422        // or grand..grandparents of previous node (currentNode)
[15401]423        while (currentNode != null && newProject.ParentProjectId != currentProject.Id) {
424          currentNode = currentNode.Parent;
425          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
426        }
427
428        if (currentNode == null) {
429          projectsTreeView.Nodes.Add(newNode);
430          newNode.ImageIndex = greenFlagImageIndex;
431        } else {
432          currentNode.Nodes.Add(newNode);
433          newNode.ImageIndex = redFlagImageIndex;
434        }
435
436        newNode.SelectedImageIndex = newNode.ImageIndex;
437
[16117]438        if (disabledParentProjects.Contains(newProject)) {
439          newNode.Checked = false;
440          newNode.ForeColor = grayTextColor;
441        }
442
[15401]443        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
444        if (childProjects.Any()) {
445          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
446            subProbjects.Remove(project);
447            stack.Push(project);
448          }
449          currentNode = newNode;
450          currentProject = newProject;
451        }
452      }
453
454      projectsTreeView.ExpandAll();
[16117]455    }
[15401]456
[16117]457    private void StyleTreeNode(TreeNode n, Project p) {
458      n.Text = p.Name;
459      n.BackColor = Color.Transparent;
460      n.ForeColor = Color.Black;
461
462      if (HiveAdminClient.Instance.DisabledParentProjects.Select(x => x.Id).Contains(p.Id)) {
463        n.ForeColor = grayTextColor;
[16202]464      } else {
465        if (p.Id == Guid.Empty) {
466          n.Text += NOT_STORED_TAG;
467        } else if (p.Modified) {
468          n.Text += CHANGES_NOT_STORED_TAG;
469        }
470
471        if (!IsActive(p)) {
472          n.ForeColor = grayTextColor;
473          n.Text += INACTIVE_TAG;
474        }
[16117]475      }
[15401]476    }
477
[16117]478    private void ResetTreeNodes(TreeNodeCollection nodes) {
479      foreach (TreeNode n in nodes) {
480        StyleTreeNode(n, (Project)n.Tag);
481        if (n.Nodes.Count > 0) {
482          ResetTreeNodes(n.Nodes);
483        }
484      }
485    }
486
487    private void ChangeSelectedProject(Project project) {
488      projectView.Content = project;
489      projectPermissionsView.Content = project;
490      projectResourcesView.Content = project;
491      projectJobsView.Content = project;
492      selectedProject = project;
493      SetEnabledStateOfControls();
494    }
495
496    private void ChangeSelectedProjectNode(TreeNode projectNode) {
497      if (projectNode == null) return;
498      SelectedProject = (Project)projectNode.Tag;
499      ResetTreeNodes(projectsTreeView.Nodes);
500      projectNode.BackColor = selectedBackColor;
501      projectNode.ForeColor = selectedForeColor;
502      projectNode.Text += SELECTED_TAG;
503    }
504
[15401]505    private void UpdateProjects() {
[16446]506      lock (locker) {
507        if (refreshingInternal || refreshingExternal) return;
508        refreshingInternal = true;
509      }
510
[15401]511      try {
[15412]512        HiveAdminClient.Instance.Refresh();
[15401]513      } catch (AnonymousUserException) {
514        ShowHiveInformationDialog();
[16446]515      } finally {
516        refreshingInternal = false;
[15401]517      }
518    }
519
[15412]520    private void RemoveProject(Project project) {
[16117]521      if (project == null) return;
[15742]522
[15412]523      try {
[16117]524        if (project.Id != Guid.Empty) {
525          SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(project.Id).LastOrDefault();
[16430]526          HiveAdminClient.Delete(project);
[16117]527          UpdateProjects();
[16430]528        } else {
[16185]529          SelectedProject = Content.FirstOrDefault(x => x.Id == project.ParentProjectId);
[16117]530          Content.Remove(project);
531        }
[15412]532      } catch (AnonymousUserException) {
533        ShowHiveInformationDialog();
[15401]534      }
535    }
536
[16202]537    private bool IsActive(Project project) {
538      return DateTime.Now >= project.StartDate
539             && (project.EndDate == null
540                 || DateTime.Now < project.EndDate.Value);
541    }
542
[15401]543    private bool IsAuthorized(Project project) {
[15658]544      return project != null && UserInformation.Instance.UserExists;
[15401]545    }
546
[15742]547    private bool IsAdmin() {
548      return HiveRoles.CheckAdminUserPermissions();
549    }
550
[15401]551    private void ShowHiveInformationDialog() {
552      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
553      else {
554        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
555          dialog.ShowDialog(this);
556        }
557      }
558    }
559    #endregion
560  }
561}
Note: See TracBrowser for help on using the repository browser.