source: branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs @ 15742

Last change on this file since 15742 was 15742, checked in by jzenisek, 19 months ago

#2839 worked on HiveAdministrator:

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