Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2839: fixed couple of minor issues

  • changed tags in resource selector
  • added project information in job list and adapted sortation
  • fixed hand-down save by withdrawing additional offset-rights (permissions, resources),...
File size: 19.8 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 && !IsAdmin()) {
202        MessageBox.Show(
203          "You are not allowed to add a root project - please select a parent project.",
204          "HeuristicLab Hive Administrator",
205          MessageBoxButtons.OK,
206          MessageBoxIcon.Information);
207        return;
208      }
209     
210      if (selectedProject != null && selectedProject.Id == Guid.Empty) {
211        MessageBox.Show(
212          "You cannot add a project to a not yet stored project.",
213          "HeuristicLab Hive Administrator",
214          MessageBoxButtons.OK,
215          MessageBoxIcon.Information);
216        return;
217      }
218
219      if (selectedProject != null) parentProjectId = selectedProject.Id;
220      var project = new Project {
221        Name = "New Project",
222        OwnerUserId = UserInformation.Instance.User.Id,
223        ParentProjectId = parentProjectId
224      };
225
226      SelectedProject = project;
227      Content.Add(project);
228    }
229
230    private async void removeButton_Click(object sender, EventArgs e) {
231      if (selectedProject == null) return;
232
233      lock (locker) {
234        if (!removeButton.Enabled) return;
235        removeButton.Enabled = false;
236      }
237
238      if (Content.Any(x => x.ParentProjectId == selectedProject.Id)) {
239        MessageBox.Show(
240          "Only empty projects can be deleted.",
241          "HeuristicLab Hive Administrator",
242          MessageBoxButtons.OK,
243          MessageBoxIcon.Error);
244        return;
245      }
246
247      if (HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id)
248        && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null
249        && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any()) {
250        MessageBox.Show(
251          "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.",
252          "HeuristicLab Hive Administrator",
253          MessageBoxButtons.OK,
254          MessageBoxIcon.Error);
255        return;
256      }
257
258      var result = MessageBox.Show(
259        "Do you really want to delete " + selectedProject.Name + "?",
260        "HeuristicLab Hive Administrator",
261        MessageBoxButtons.YesNo,
262        MessageBoxIcon.Question);
263
264      if (result == DialogResult.Yes) {
265        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
266          action: () => {
267            RemoveProject(selectedProject); 
268          },
269          finallyCallback: () => {
270            removeButton.Enabled = true;
271          });
272      }
273    }
274
275    private async void saveProjectButton_Click(object sender, EventArgs e) {
276      lock (locker) {
277        if (!saveProjectButton.Enabled) return;
278        saveProjectButton.Enabled = false;
279      }
280
281      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
282        action: () => {
283          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
284          foreach (var project in projectsToSave)
285            project.Store();
286          UpdateProjects();
287        },
288        finallyCallback: () => saveProjectButton.Enabled = true);
289
290      OnContentChanged();
291    }
292
293    private void projectsTreeView_MouseDown(object sender, MouseEventArgs e) {
294      var node = projectsTreeView.GetNodeAt(e.Location);
295      if(node != null) ChangeSelectedProjectNode(node);
296    }
297
298    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
299      e.Cancel = true;
300    }
301
302    private void projectsTreeView_DragDrop(object sender, DragEventArgs e) {
303      if (e.Effect == DragDropEffects.None) return;
304
305      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
306      if (sourceNode == null) return;
307      var sourceProject = ((Project)sourceNode.Tag);
308      if (sourceProject == null) return;
309
310      var treeView = (TreeView)sender;
311      if (sourceNode.TreeView != treeView) return;
312
313      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
314      var targetNode = treeView.GetNodeAt(targetPoint);
315
316      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
317
318      if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
319        MessageBox.Show(
320          "You cannot drag projects to this project.",
321          "HeuristicLab Hive Administrator",
322          MessageBoxButtons.OK,
323          MessageBoxIcon.Information);
324        return;
325      }
326
327      if (sourceNode.Parent == null) {
328        treeView.Nodes.Remove(sourceNode);
329      } else {
330        sourceNode.Parent.Nodes.Remove(sourceNode);
331        sourceProject.ParentProjectId = null;
332      }
333
334      if (targetNode == null) {
335        treeView.Nodes.Add(sourceNode);
336      } else if(targetProject.Id != Guid.Empty) {
337        targetNode.Nodes.Add(sourceNode);
338        sourceProject.ParentProjectId = targetProject.Id;
339      }
340
341      SelectedProject = sourceProject;
342      OnContentChanged();
343    }
344
345    private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
346      var sourceNode = (TreeNode)e.Item;
347      if (IsAuthorized((Project)sourceNode.Tag))
348        DoDragDrop(sourceNode, DragDropEffects.All);
349    }
350
351    private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) {
352      e.Effect = DragDropEffects.Move;
353
354      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
355      var sourceProject = ((Project)sourceNode.Tag);
356
357      var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y));
358      var targetNode = projectsTreeView.GetNodeAt(targetPoint);
359      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
360
361      if ((!IsAdmin() && (targetNode == null || targetProject == null))
362      || sourceNode == null
363      || sourceProject == null
364      || sourceNode == targetNode
365      || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
366        e.Effect = DragDropEffects.None;
367      }
368    }
369
370    private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
371      e.Action = DragAction.Continue;
372    }
373    #endregion
374
375    #region Helpers
376    private void BuildProjectTree(IEnumerable<Project> projects) {
377      projectsTreeView.Nodes.Clear();
378      if (!projects.Any()) return;
379
380      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
381      var parentedMainProjects = new HashSet<Project>(projects
382        .Where(x => x.ParentProjectId.HasValue
383        && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)));
384      mainProjects.UnionWith(parentedMainProjects);
385      var subProbjects = new HashSet<Project>(projects.Except(mainProjects));
386
387      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
388      if (selectedProject != null) SelectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault();
389      bool nodeSelected = false;
390
391      TreeNode currentNode = null;
392      Project currentProject = null;
393
394      while (stack.Any()) {
395        var newProject = stack.Pop();
396        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
397        StyleTreeNode(newNode, newProject);
398     
399        if (selectedProject == null) {
400          SelectedProject = newProject;
401        }
402        if (newProject.Id == selectedProject.Id && !nodeSelected) {
403          newNode.BackColor = selectedBackColor;
404          newNode.ForeColor = selectedForeColor;
405          newNode.Text += SELECTED_TAG;
406          nodeSelected = true;
407        }
408
409        // search for parent node of newNode and save in currentNode
410        // necessary since newNodes (stack top items) might be siblings
411        // or grand..grandparents of previous node (currentNode)
412        while (currentNode != null && newProject.ParentProjectId != currentProject.Id) {
413          currentNode = currentNode.Parent;
414          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
415        }
416
417        if (currentNode == null) {
418          projectsTreeView.Nodes.Add(newNode);
419          newNode.ImageIndex = greenFlagImageIndex;
420        } else {
421          currentNode.Nodes.Add(newNode);
422          newNode.ImageIndex = redFlagImageIndex;
423        }
424
425        newNode.SelectedImageIndex = newNode.ImageIndex;
426
427        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
428        if (childProjects.Any()) {
429          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
430            subProbjects.Remove(project);
431            stack.Push(project);
432          }
433          currentNode = newNode;
434          currentProject = newProject;
435        }
436      }
437
438      projectsTreeView.ExpandAll();
439    }
440
441    private void StyleTreeNode(TreeNode n, Project p) {
442      n.Text = p.Name;
443      n.BackColor = Color.Transparent;
444      n.ForeColor = Color.Black;
445
446      if (p.Id == Guid.Empty) {
447        n.Text += NOT_STORED_TAG;
448      } else if (p.Modified) {
449        n.Text += CHANGES_NOT_STORED_TAG;
450      }
451    }
452
453    private void ResetTreeNodes(TreeNodeCollection nodes) {
454      foreach (TreeNode n in nodes) {
455        StyleTreeNode(n, (Project)n.Tag);
456        if (n.Nodes.Count > 0) {
457          ResetTreeNodes(n.Nodes);
458        }
459      }
460    }
461
462    private void ChangeSelectedProject(Project project) {
463      projectView.Content = project;
464      projectPermissionsView.Content = project;
465      projectResourcesView.Content = project;
466      projectJobsView.Content = project;
467
468      bool projectIsNew = project != null && project.Id == Guid.Empty;
469      bool locked = project == null || projectIsNew;
470
471      // if the project is new, or:
472      // the project does have any descendant projects and
473      // the user is an admin or owner of a parent project
474      bool deleteEnabled = project != null && (
475        projectIsNew
476        ||  (!HiveAdminClient.Instance.ProjectDescendants[project.Id].Any()
477              && (HiveAdminClient.Instance.CheckOwnershipOfParentProject(project, UserInformation.Instance.User.Id)
478              || IsAdmin())
479            )
480      );
481
482      bool addEnabled = !locked;
483      if (!IsAdmin() && project == null) addEnabled = false;
484
485      bool saveEnabled = project != null;
486      if (!IsAdmin() && !project.ParentProjectId.HasValue) saveEnabled = false;
487
488      addButton.Enabled = addEnabled;
489      removeButton.Enabled = deleteEnabled;
490      saveProjectButton.Enabled = saveEnabled;
491      projectPermissionsView.Locked = locked;
492      projectResourcesView.Locked = locked;
493      projectJobsView.Locked = locked;
494      selectedProject = project;
495    }
496
497    private void ChangeSelectedProjectNode(TreeNode projectNode) {
498      if (projectNode == null) return;
499      SelectedProject = (Project)projectNode.Tag;
500      ResetTreeNodes(projectsTreeView.Nodes);
501      projectNode.BackColor = selectedBackColor;
502      projectNode.ForeColor = selectedForeColor;
503      projectNode.Text += SELECTED_TAG;
504    }
505
506    private void UpdateProjects() {
507      try {
508        HiveAdminClient.Instance.Refresh();
509        Content = HiveAdminClient.Instance.Projects;
510      } catch (AnonymousUserException) {
511        ShowHiveInformationDialog();
512      }
513    }
514
515    private void RemoveProject(Project project) {
516      if (project == null) return;
517
518      try {
519        if (project.Id != Guid.Empty) {
520          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
521          foreach (var p in projectsToSave)
522            p.Store();
523          HiveAdminClient.Delete(project);
524          UpdateProjects();
525        } else {
526          Content.Remove(project);
527        }
528      } catch (AnonymousUserException) {
529        ShowHiveInformationDialog();
530      }
531    }
532
533    private bool IsAuthorized(Project project) {
534      return project != null && UserInformation.Instance.UserExists;
535    }
536
537    private bool IsAdmin() {
538      return HiveRoles.CheckAdminUserPermissions();
539    }
540
541    private void ShowHiveInformationDialog() {
542      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
543      else {
544        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
545          dialog.ShowDialog(this);
546        }
547      }
548    }
549    #endregion
550  }
551}
Note: See TracBrowser for help on using the repository browser.