Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs @ 15966

Last change on this file since 15966 was 15966, checked in by jzenisek, 6 years ago

#2839: Fixed several bugs and added project duration handling

  • fixed project duration handling (client- & service-side)
  • fixed tagging in HiveJobAdmin
  • added ProjectJobs view (under construction)
  • added necessary service methods
File size: 19.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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;
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]";
45
46    private readonly Color selectedBackColor = Color.DodgerBlue;
47    private readonly Color selectedForeColor = Color.White;
48
49    private Project selectedProject = null;
50    public Project SelectedProject {
51      get { return selectedProject; }
52      set { if (selectedProject != value) ChangeSelectedProject(value); }
53    }
54
55    private readonly object locker = new object();
56
57    public new IItemList<Project> Content {
58      get { return (IItemList<Project>)base.Content; }
59      set { base.Content = value; }
60    }
61
62    public ProjectsView() {
63      InitializeComponent();
64
65      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagGreen);
66      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagRed);
67
68      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
69      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
70      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
71      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
72    }
73
74    #region Overrides
75    protected override void OnClosing(FormClosingEventArgs e) {
76      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
77      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
78      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
79      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
80      base.OnClosing(e);
81    }
82
83    protected override void RegisterContentEvents() {
84      base.RegisterContentEvents();
85      Content.ItemsAdded += Content_ItemsAdded;
86      Content.ItemsRemoved += Content_ItemsRemoved;
87    }
88
89    protected override void DeregisterContentEvents() {
90      Content.ItemsRemoved -= Content_ItemsRemoved;
91      Content.ItemsAdded -= Content_ItemsAdded;
92      base.DeregisterContentEvents();
93    }
94
95    protected override void OnContentChanged() {
96      base.OnContentChanged();
97      if (Content == null) {
98        projectsTreeView.Nodes.Clear();
99        projectView.Content = null;
100        projectPermissionsView.Content = null;
101        projectResourcesView.Content = null;
102        projectJobsView.Content = null;
103      } else {
104        BuildProjectTree(Content);
105      }
106    }
107
108    protected override void SetEnabledStateOfControls() {
109      base.SetEnabledStateOfControls();
110      bool enabled = Content != null && !Locked && !ReadOnly;
111      refreshButton.Enabled = enabled;
112      addButton.Enabled = enabled;
113      removeButton.Enabled = enabled;
114      saveProjectButton.Enabled = enabled;
115      projectView.Enabled = enabled;
116      projectPermissionsView.Enabled = enabled;
117      projectResourcesView.Enabled = enabled;
118      projectJobsView.Enabled = enabled;
119    }
120    #endregion
121
122    #region Event Handlers
123    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
124      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsAdded, sender, e);
125      else {
126        OnContentChanged();
127      }
128    }
129
130    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
131      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsRemoved, sender, e);
132      else {
133        OnContentChanged();
134      }
135    }
136
137    private void ProjectViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
138      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)ProjectViewContent_PropertyChanged, sender, e);
139      else {
140        OnContentChanged();
141      }
142    }
143
144    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
145      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
146      else {
147        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
148        mainForm.AddOperationProgressToView(this, "Refreshing ...");
149        SetEnabledStateOfControls();
150      }
151    }
152
153    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
154      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
155      else {
156        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
157        mainForm.RemoveOperationProgressFromView(this);
158        SetEnabledStateOfControls();
159      }
160    }
161
162    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
163      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
164      else {
165        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
166        mainForm.AddOperationProgressToView(this, "Refreshing ...");
167        SetEnabledStateOfControls();
168      }
169    }
170
171    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
172      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
173      else {
174        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
175        mainForm.RemoveOperationProgressFromView(this);
176        SetEnabledStateOfControls();
177      }
178    }
179
180    private async void ProjectsView_Load(object sender, EventArgs e) {
181      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
182        action: () => UpdateProjects());
183    }
184
185    private async void refreshButton_Click(object sender, EventArgs e) {
186      lock (locker) {
187        if (!refreshButton.Enabled) return;
188        refreshButton.Enabled = false;
189      }
190
191      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
192        action: () => UpdateProjects(),
193        finallyCallback: () => {
194          refreshButton.Enabled = true;
195        });
196    }
197
198    private void addButton_Click(object sender, EventArgs e) {
199      Guid? parentProjectId = null;
200
201      if (selectedProject != null && selectedProject.Id == Guid.Empty) {
202        MessageBox.Show(
203          "You cannot add a project to a not yet stored project.",
204          "HeuristicLab Hive Administrator",
205          MessageBoxButtons.OK,
206          MessageBoxIcon.Information);
207        return;
208      }
209
210      if (selectedProject != null) parentProjectId = selectedProject.Id;
211      var project = new Project {
212        Name = "New Project",
213        OwnerUserId = UserInformation.Instance.User.Id,
214        ParentProjectId = parentProjectId
215      };
216
217      SelectedProject = project;
218      Content.Add(project);
219    }
220
221    private async void removeButton_Click(object sender, EventArgs e) {
222      if (selectedProject == null) return;
223
224      lock (locker) {
225        if (!removeButton.Enabled) return;
226        removeButton.Enabled = false;
227      }
228
229      if (Content.Any(x => x.ParentProjectId == selectedProject.Id)) {
230        MessageBox.Show(
231          "Only empty projects can be deleted.",
232          "HeuristicLab Hive Administrator",
233          MessageBoxButtons.OK,
234          MessageBoxIcon.Error);
235        return;
236      }
237
238      if (HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id)
239        && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null
240        && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any()) {
241        MessageBox.Show(
242          "There are " + HiveAdminClient.Instance.Jobs[selectedProject.Id].Count + " jobs using this project and/or child-projects. It is necessary to delete them before the project.",
243          "HeuristicLab Hive Administrator",
244          MessageBoxButtons.OK,
245          MessageBoxIcon.Error);
246        return;
247      }
248
249      var result = MessageBox.Show(
250        "Do you really want to delete " + selectedProject.Name + "?",
251        "HeuristicLab Hive Administrator",
252        MessageBoxButtons.YesNo,
253        MessageBoxIcon.Question);
254
255      if (result == DialogResult.Yes) {
256        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
257          action: () => {
258            RemoveProject(selectedProject); 
259          },
260          finallyCallback: () => {
261            removeButton.Enabled = true;
262          });
263      }
264    }
265
266    private async void saveProjectButton_Click(object sender, EventArgs e) {
267      lock (locker) {
268        if (!saveProjectButton.Enabled) return;
269        saveProjectButton.Enabled = false;
270      }
271
272      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
273        action: () => {
274          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
275          foreach (var project in projectsToSave)
276            project.Store();
277          UpdateProjects();
278        },
279        finallyCallback: () => saveProjectButton.Enabled = true);
280
281      OnContentChanged();
282    }
283
284    private void projectsTreeView_MouseDown(object sender, MouseEventArgs e) {
285      var node = projectsTreeView.GetNodeAt(e.Location);
286      if(node != null) ChangeSelectedProjectNode(node);
287    }
288
289    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
290      e.Cancel = true;
291    }
292
293    private void projectsTreeView_DragDrop(object sender, DragEventArgs e) {
294      if (e.Effect == DragDropEffects.None) return;
295
296      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
297      if (sourceNode == null) return;
298      var sourceProject = ((Project)sourceNode.Tag);
299      if (sourceProject == null) return;
300
301      var treeView = (TreeView)sender;
302      if (sourceNode.TreeView != treeView) return;
303
304      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
305      var targetNode = treeView.GetNodeAt(targetPoint);
306
307      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
308
309      if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
310        MessageBox.Show(
311          "You cannot drag projects to this project.",
312          "HeuristicLab Hive Administrator",
313          MessageBoxButtons.OK,
314          MessageBoxIcon.Information);
315        return;
316      }
317
318      if (sourceNode.Parent == null) {
319        treeView.Nodes.Remove(sourceNode);
320      } else {
321        sourceNode.Parent.Nodes.Remove(sourceNode);
322        sourceProject.ParentProjectId = null;
323      }
324
325      if (targetNode == null) {
326        treeView.Nodes.Add(sourceNode);
327      } else if(targetProject.Id != Guid.Empty) {
328        targetNode.Nodes.Add(sourceNode);
329        sourceProject.ParentProjectId = targetProject.Id;
330      }
331
332      SelectedProject = sourceProject;
333      OnContentChanged();
334    }
335
336    private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
337      var sourceNode = (TreeNode)e.Item;
338      if (IsAuthorized((Project)sourceNode.Tag))
339        DoDragDrop(sourceNode, DragDropEffects.All);
340    }
341
342    private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) {
343      e.Effect = DragDropEffects.Move;
344
345      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
346      var sourceProject = ((Project)sourceNode.Tag);
347
348      var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y));
349      var targetNode = projectsTreeView.GetNodeAt(targetPoint);
350      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
351
352      if ((!IsAdmin() && (targetNode == null || targetProject == null))
353      || sourceNode == null
354      || sourceProject == null
355      || sourceNode == targetNode
356      || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
357        e.Effect = DragDropEffects.None;
358      }
359    }
360
361    private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
362      e.Action = DragAction.Continue;
363    }
364    #endregion
365
366    #region Helpers
367    private void BuildProjectTree(IEnumerable<Project> projects) {
368      projectsTreeView.Nodes.Clear();
369      if (!projects.Any()) return;
370
371      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
372      var parentedMainProjects = new HashSet<Project>(projects
373        .Where(x => x.ParentProjectId.HasValue
374        && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)));
375      mainProjects.UnionWith(parentedMainProjects);
376      var subProbjects = new HashSet<Project>(projects.Except(mainProjects));
377
378      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
379      if (selectedProject != null) SelectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault();
380      bool nodeSelected = false;
381
382      TreeNode currentNode = null;
383      Project currentProject = null;
384
385      while (stack.Any()) {
386        var newProject = stack.Pop();
387        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
388        StyleTreeNode(newNode, newProject);
389     
390        if (selectedProject == null) {
391          SelectedProject = newProject;
392        }
393        if (newProject.Id == selectedProject.Id && !nodeSelected) {
394          newNode.BackColor = selectedBackColor;
395          newNode.ForeColor = selectedForeColor;
396          newNode.Text += SELECTED_TAG;
397          nodeSelected = true;
398        }
399
400        // search for parent node of newNode and save in currentNode
401        // necessary since newNodes (stack top items) might be siblings
402        // or grand..grandparents of previous node (currentNode)
403        while (currentNode != null && newProject.ParentProjectId != currentProject.Id) {
404          currentNode = currentNode.Parent;
405          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
406        }
407
408        if (currentNode == null) {
409          projectsTreeView.Nodes.Add(newNode);
410          newNode.ImageIndex = greenFlagImageIndex;
411        } else {
412          currentNode.Nodes.Add(newNode);
413          newNode.ImageIndex = redFlagImageIndex;
414        }
415
416        newNode.SelectedImageIndex = newNode.ImageIndex;
417
418        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
419        if (childProjects.Any()) {
420          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
421            subProbjects.Remove(project);
422            stack.Push(project);
423          }
424          currentNode = newNode;
425          currentProject = newProject;
426        }
427      }
428
429      projectsTreeView.ExpandAll();
430    }
431
432    private void StyleTreeNode(TreeNode n, Project p) {
433      n.Text = p.Name;
434      n.BackColor = Color.Transparent;
435      n.ForeColor = Color.Black;
436
437      if (p.Id == Guid.Empty) {
438        n.Text += NOT_STORED_TAG;
439      } else if (p.Modified) {
440        n.Text += CHANGES_NOT_STORED_TAG;
441      }
442    }
443
444    private void ResetTreeNodes(TreeNodeCollection nodes) {
445      foreach (TreeNode n in nodes) {
446        StyleTreeNode(n, (Project)n.Tag);
447        if (n.Nodes.Count > 0) {
448          ResetTreeNodes(n.Nodes);
449        }
450      }
451    }
452
453    private void ChangeSelectedProject(Project project) {
454      projectView.Content = project;
455      projectPermissionsView.Content = project;
456      projectResourcesView.Content = project;
457      projectJobsView.Content = project;
458
459      bool projectIsNew = project != null && project.Id == Guid.Empty;
460      bool locked = project == null || projectIsNew;
461      bool deleteEnabled = project != null && (
462        projectIsNew
463        || HiveAdminClient.Instance.CheckOwnershipOfParentProject(project, UserInformation.Instance.User.Id)
464        || !HiveAdminClient.Instance.ProjectDescendants[project.Id].Any());
465
466      addButton.Enabled = !locked;
467      removeButton.Enabled = deleteEnabled;
468      projectPermissionsView.Locked = locked;
469      projectResourcesView.Locked = locked;
470      projectJobsView.Locked = locked;
471      selectedProject = project;
472    }
473
474    private void ChangeSelectedProjectNode(TreeNode projectNode) {
475      if (projectNode == null) return;
476      SelectedProject = (Project)projectNode.Tag;
477      ResetTreeNodes(projectsTreeView.Nodes);
478      projectNode.BackColor = selectedBackColor;
479      projectNode.ForeColor = selectedForeColor;
480      projectNode.Text += SELECTED_TAG;
481    }
482
483    private void UpdateProjects() {
484      try {
485        HiveAdminClient.Instance.Refresh();
486        Content = HiveAdminClient.Instance.Projects;
487      } catch (AnonymousUserException) {
488        ShowHiveInformationDialog();
489      }
490    }
491
492    private void RemoveProject(Project project) {
493      if (project == null) return;
494
495      try {
496        if (project.Id != Guid.Empty) {
497          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
498          foreach (var p in projectsToSave)
499            p.Store();
500          HiveAdminClient.Delete(project);
501          UpdateProjects();
502        } else {
503          Content.Remove(project);
504        }
505      } catch (AnonymousUserException) {
506        ShowHiveInformationDialog();
507      }
508    }
509
510    private bool IsAuthorized(Project project) {
511      return project != null && UserInformation.Instance.UserExists;
512    }
513
514    private bool IsAdmin() {
515      return HiveRoles.CheckAdminUserPermissions();
516    }
517
518    private void ShowHiveInformationDialog() {
519      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
520      else {
521        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
522          dialog.ShowDialog(this);
523        }
524      }
525    }
526    #endregion
527  }
528}
Note: See TracBrowser for help on using the repository browser.