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

Last change on this file since 16185 was 16185, checked in by jzenisek, 12 months ago

#2839:

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