Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure.GUI/ManagerForm.cs @ 91

Last change on this file since 91 was 91, checked in by gkronber, 16 years ago
  • added an attribute for the build time of an assembly.
  • build-date is automatically updated via SubWCRev in the pre-build action.
  • adapted PluginManager to correctly handle plugins with same version but newer build date

fixes ticket #39

File size: 41.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 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.Data;
26using System.Drawing;
27using System.Text;
28using System.Windows.Forms;
29using System.Diagnostics;
30using System.Net;
31using System.IO;
32using ICSharpCode.SharpZipLib.Zip;
33
34namespace HeuristicLab.PluginInfrastructure.GUI {
35  public partial class ManagerForm : Form {
36    private TreeNode installedPlugins;
37    private TreeNode availablePlugins;
38    private TreeNode disabledPlugins;
39    private List<PluginTag> allTags = new List<PluginTag>();
40    private Dictionary<PluginTag, PluginAction> actions = new Dictionary<PluginTag, PluginAction>();
41    private List<PluginDescription> allAvailablePlugins = new List<PluginDescription>();
42    private string pluginDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.PluginDir;
43    private string cacheDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.CacheDir;
44    private string backupDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.BackupDir;
45    private string tempDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.TempDir;
46
47    public ManagerForm() {
48      InitializeComponent();
49      InitializePlugins();
50    }
51
52    private void InitializePlugins() {
53      pluginTreeView.Nodes.Clear();
54      allTags.Clear();
55      actions.Clear();
56
57      publishButton.Enabled = false;
58      publishMenuItem.Enabled = false;
59      installButton.Enabled = false;
60      installButton.Checked = false;
61      installMenuItem.Enabled = false;
62      installMenuItem.Checked = false;
63      deleteButton.Enabled = false;
64      deleteButton.Checked = false;
65      deleteMenuItem.Enabled = false;
66      deleteMenuItem.Checked = false;
67
68      installedPlugins = new TreeNode("Installed plugins");
69      installedPlugins.ImageIndex = 1;
70      installedPlugins.SelectedImageIndex = 1;
71      availablePlugins = new TreeNode("Available plugins");
72      availablePlugins.ImageIndex = 1;
73      availablePlugins.SelectedImageIndex = 1;
74      disabledPlugins = new TreeNode("Disabled plugins");
75      disabledPlugins.ImageIndex = 1;
76      disabledPlugins.SelectedImageIndex = 1;
77
78      pluginTreeView.Nodes.Add(installedPlugins);
79      pluginTreeView.Nodes.Add(availablePlugins);
80      pluginTreeView.Nodes.Add(disabledPlugins);
81
82      foreach(PluginInfo pluginInfo in PluginManager.Manager.ActivePlugins) {
83        // create a new PluginAction tag for the plugin
84        PluginTag tag = new PluginTag(allTags, pluginInfo, PluginState.Installed);
85        allTags.Add(tag);
86        // add to "installed plugins" node
87        TreeNode installedPluginsNode = new TreeNode(pluginInfo.Name);
88        installedPluginsNode.ContextMenuStrip = pluginContextMenuStrip;
89        installedPluginsNode.Tag = tag;
90        installedPluginsNode.ImageIndex = 0;
91        installedPlugins.Nodes.Add(installedPluginsNode);
92      }
93      foreach(PluginInfo pluginInfo in PluginManager.Manager.DisabledPlugins) {
94        PluginTag tag = new PluginTag(allTags, pluginInfo, PluginState.Disabled);
95        allTags.Add(tag);
96        TreeNode disabledPluginsNode = new TreeNode(pluginInfo.Name);
97        disabledPluginsNode.ContextMenuStrip = pluginContextMenuStrip;
98        disabledPluginsNode.Tag = tag;
99        disabledPluginsNode.ImageIndex = 0;
100        disabledPlugins.Nodes.Add(disabledPluginsNode);
101      }
102
103      allAvailablePlugins = FilterMostRecentPluginVersions(allAvailablePlugins);
104      // find all plugins that are installed for which a new version is available
105      List<PluginDescription> upgrades = FindUpgrades(allTags, allAvailablePlugins);
106      // find all available plugins that are not installed and new (= new name not new version) since the last update
107      List<PluginDescription> newPlugins = FindNewPlugins(allTags, allAvailablePlugins);
108      // find all plugins that are available (not installed) for which a new version has been released since the last update
109      List<PluginDescription> overridingPlugins = FindOverridingPlugins(allTags, allAvailablePlugins);
110      newPlugins.ForEach(delegate(PluginDescription plugin) {
111        PluginTag tag = new PluginTag(allTags, plugin, PluginState.Available);
112        allTags.Add(tag);
113        TreeNode availableNode = new TreeNode(plugin.Name);
114        availableNode.ContextMenuStrip = pluginContextMenuStrip;
115        availableNode.Tag = tag;
116        availableNode.ImageIndex = 0;
117        availablePlugins.Nodes.Add(availableNode);
118
119      });
120      upgrades.ForEach(delegate(PluginDescription upgrade) {
121        // find the installed plugins that have the same name
122        List<PluginTag> oldPlugins = allTags.FindAll(delegate(PluginTag tag) {
123          return tag.PluginName == upgrade.Name;
124        });
125        PluginTag oldPlugin = oldPlugins[0];
126        // store the upgrade in the old plugin
127        oldPlugin.UpgradePluginDescription = upgrade;
128        UpdateTreeNodes(oldPlugins);
129      });
130      overridingPlugins.ForEach(delegate(PluginDescription overridingPlugin) {
131        List<PluginTag> currentPlugins = allTags.FindAll(delegate(PluginTag tag) {
132          return tag.PluginName == overridingPlugin.Name;
133        });
134        PluginTag currentPlugin = currentPlugins[0];
135        // replace the plugin description of the available plugin to point to the overriding plugin
136        currentPlugin.PluginDescription = overridingPlugin;
137      });
138      RebuildActionHulls();
139      pluginTreeView.Sort();
140    }
141
142    private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
143      AboutBox box = new AboutBox();
144      box.ShowDialog();
145    }
146
147    private void publishButton_Click(object sender, EventArgs args) {
148      PluginInfo plugin = ((PluginTag)pluginTreeView.SelectedNode.Tag).Plugin;
149      publishPlugin(plugin);
150    }
151
152    private void publishPlugin(PluginInfo plugin) {
153      try {
154        string packageFileName = plugin.Name + "-" + plugin.Version + ".zip";
155        ZipFile zipFile = ZipFile.Create(packageFileName);
156        zipFile.NameTransform = new PluginNameTransform();
157        zipFile.BeginUpdate();
158
159        infoTextBox.Text = "Publishing plugin:\nCreating " + packageFileName + "...\n";
160        foreach(string filename in plugin.Files) {
161          infoTextBox.Text += "Adding " + filename + "\n";
162          zipFile.Add(filename);
163        }
164
165        zipFile.CommitUpdate();
166        zipFile.Close();
167        FileInfo fileInfo = new FileInfo(packageFileName);
168        infoTextBox.Text += "\nCreated " + packageFileName + " (" + fileInfo.Length + " bytes)\n";
169        infoTextBox.Text += "Upload this file to your plugin source and add the following entry to" +
170" the file plugins.xml residing in the base directory of your plugin source.\n\n";
171        infoTextBox.Text += "  <Plugin Name=\"" + plugin.Name + "\" Version=\""
172          + plugin.Version + "\" Build=\"" + plugin.BuildDate.ToUniversalTime().ToString(System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat) + "\">\n";
173        foreach(PluginInfo dependency in plugin.Dependencies) {
174          infoTextBox.Text += "    <Dependency Name=\"" + dependency.Name + "\" />\n";
175        }
176        infoTextBox.Text += "  </Plugin>";
177      } catch(Exception exception) {
178        infoTextBox.Text += "\nThere was an error!\n" + exception;
179      }
180    }
181
182    private void updateButton_Click(object sender, EventArgs e) {
183      // connect to all plugin sources and get a list of available plugins
184      // log progress in the infoPane
185      BackgroundWorker worker = new BackgroundWorker();
186      worker.WorkerSupportsCancellation = true;
187      worker.WorkerReportsProgress = true;
188
189      DownloaderDialog dialog = new DownloaderDialog();
190      dialog.OnCancel += delegate() {
191        worker.CancelAsync();
192      };
193
194      worker.ProgressChanged += delegate(object progressChangedSender, ProgressChangedEventArgs args) {
195        dialog.SetProgress(args.ProgressPercentage);
196      };
197
198      worker.DoWork += delegate(object doWorkSender, DoWorkEventArgs args) {
199        allAvailablePlugins.Clear();
200        dialog.SetDownloadDescription("Updating available plugins...");
201        int i = 0;
202        int n = HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.PluginSources.Count;
203        foreach(string pluginSourceUrl in HeuristicLab.PluginInfrastructure.GUI.Properties.Settings.Default.PluginSources) {
204          if(!worker.CancellationPending) {
205            dialog.SetDownloadDescription("Connecting to " + pluginSourceUrl + "...");
206            PluginSource source = PluginSource.TryCreate(pluginSourceUrl);
207            if(source == null) {
208              Invoke((MethodInvoker)delegate() {
209                infoTextBox.Text += "Error! Couldn't access the plugin source " + pluginSourceUrl + ".\n";
210              });
211            } else {
212              dialog.SetDownloadDescription("Getting list of available plugins from " + pluginSourceUrl + "...");
213              List<PluginDescription> availablePlugins = source.AvailablePlugins();
214              allAvailablePlugins.AddRange(availablePlugins);
215              Invoke((MethodInvoker)delegate() {
216                infoTextBox.Text += pluginSourceUrl + " " + availablePlugins.Count + " plugins available.\n";
217              });
218
219              worker.ReportProgress((int)((++i / (double)n) * 100.0));
220            }
221          }
222        }
223        if(worker.CancellationPending) {
224          args.Cancel = true;
225        } else {
226          args.Cancel = false;
227          args.Result = allAvailablePlugins;
228        }
229      };
230
231      worker.RunWorkerCompleted += delegate(object runWorkerCompletedSender, RunWorkerCompletedEventArgs args) {
232        if(!args.Cancelled && args.Error == null) {
233          InitializePlugins();
234        }
235        dialog.Close();
236      };
237
238      dialog.Show();
239      worker.RunWorkerAsync();
240
241      // NOTE: ignore version conflicts
242    }
243
244
245    private List<PluginDescription> FilterMostRecentPluginVersions(List<PluginDescription> list) {
246      List<PluginDescription> newList = new List<PluginDescription>();
247
248      list.ForEach(delegate(PluginDescription tag) {
249        // find all entries with the same plugin name
250        List<PluginDescription> samePlugins = list.FindAll(delegate(PluginDescription otherTag) {
251          return tag.Name == otherTag.Name;
252        });
253
254        // keep only the most recent one
255        PluginDescription mostRecentVersion = samePlugins[0];
256        if(samePlugins.Count > 1) {
257          samePlugins.ForEach(delegate(PluginDescription tag2) {
258            if(tag2.Version > mostRecentVersion.Version ||
259              (tag2.Version == mostRecentVersion.Version && tag2.BuildDate>mostRecentVersion.BuildDate)) {
260              mostRecentVersion = tag2;
261            }
262          });
263        }
264        if(!newList.Contains(mostRecentVersion)) {
265          newList.Add(mostRecentVersion);
266        }
267
268      });
269
270      return newList;
271    }
272
273    private List<PluginDescription> FindNewPlugins(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
274      List<PluginDescription> newPlugins = new List<PluginDescription>();
275      // for each of the available plugins check if there is an installed plugin that has the same name
276      // only if there is no installed plugin with the same name then it is a new plugin
277      // NOTE: make sure to keep only the most recent entry of a plugin in the allAvailablePlugins list
278      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
279        if(!allTags.Exists(delegate(PluginTag tag2) {
280          return availablePlugin.Name == tag2.PluginName;
281        })) {
282          newPlugins.Add(availablePlugin);
283        }
284      });
285
286      return newPlugins;
287    }
288
289    private List<PluginDescription> FindUpgrades(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
290      List<PluginDescription> upgrades = new List<PluginDescription>();
291      // for each of the available plugins check if there is an installed plugin that has the same name
292      // only if there is an installed plugin with the same name and the available plugin has a more recent version it is an upgrade
293      // NOTE: make sure to keep only the most recent entry of a plugin in the allAvailablePlugins list
294      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
295        List<PluginTag> oldPlugins = allTags.FindAll(delegate(PluginTag currentPlugin) {
296          return currentPlugin.PluginName == availablePlugin.Name &&
297            (currentPlugin.State & (PluginState.Installed | PluginState.Disabled)) != 0;
298        });
299
300        if(oldPlugins.Count == 1) {
301          if(oldPlugins[0].PluginVersion < availablePlugin.Version ||
302            (oldPlugins[0].PluginVersion == availablePlugin.Version && oldPlugins[0].PluginBuildDate<availablePlugin.BuildDate)) {
303            upgrades.Add(availablePlugin);
304          }
305        }
306      });
307
308      return upgrades;
309    }
310
311    private List<PluginDescription> FindOverridingPlugins(List<PluginTag> allTags, List<PluginDescription> allAvailablePlugins) {
312      List<PluginDescription> overrides = new List<PluginDescription>();
313      allAvailablePlugins.ForEach(delegate(PluginDescription availablePlugin) {
314        List<PluginTag> currentPlugins = allTags.FindAll(delegate(PluginTag currentPlugin) {
315          return currentPlugin.PluginName == availablePlugin.Name && currentPlugin.State == PluginState.Available;
316        });
317
318        if(currentPlugins.Count == 1 && (currentPlugins[0].PluginVersion < availablePlugin.Version ||
319          (currentPlugins[0].PluginVersion==availablePlugin.Version && currentPlugins[0].PluginBuildDate<availablePlugin.BuildDate))) {
320          overrides.Add(availablePlugin);
321        }
322      });
323
324      return overrides;
325    }
326
327    private void RebuildActionHulls() {
328      Dictionary<PluginTag, PluginAction> oldActions = new Dictionary<PluginTag, PluginAction>(actions);
329      actions.Clear();
330
331      foreach(PluginAction action in oldActions.Values) {
332        if(action.Action == ManagerAction.Install) {
333          MarkInstall(action.Plugin);
334        } else if(action.Action == ManagerAction.Remove) {
335          MarkRemove(action.Plugin);
336        } else
337          throw new InvalidOperationException();
338      }
339
340      // update the GUI to represent new state of the selected plugin
341      if(pluginTreeView.SelectedNode != null && pluginTreeView.SelectedNode.Tag is PluginTag) {
342        UpdateActionButtons((PluginTag)pluginTreeView.SelectedNode.Tag);
343        DisplayPluginInfo(((PluginTag)pluginTreeView.SelectedNode.Tag).GetPluginDetails());
344      }
345    }
346
347
348
349    private void MarkInstall(PluginTag actionTag) {
350      if(!CheckInstallConflicts(actionTag)) {
351        CreateNewInstallAction(actionTag);
352      } else {
353        HandleInstallConflict(actionTag);
354      }
355    }
356
357    private void UnmarkInstall(PluginTag actionTag) {
358      if(!CheckNoActionConflict(actionTag)) {
359        List<PluginAction> rootActions = GetActionsInvolving(actionTag);
360        PluginAction rootAction = rootActions[0];
361        actions.Remove(rootAction.Plugin);
362        UpdateTreeNodes(rootAction.Hull);
363      } else {
364        HandleNoActionConflict(actionTag);
365      }
366    }
367
368    private void HandleNoActionConflict(PluginTag actionTag) {
369      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Any);
370      PluginAction theAction = GetSmallestActionInvolving(actionTag, conflictingActions);
371      conflictingActions.Remove(theAction);
372      string action = theAction.GetInverseActionString();
373      DialogResult userResult = ShowGenericConflictDialog(action, theAction, conflictingActions);
374      if(userResult == DialogResult.OK) {
375        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
376          actions.Remove(conflictingAction.Plugin);
377          UpdateTreeNodes(conflictingAction.Hull);
378        });
379
380        PluginAction rootAction = GetSmallestActionInvolving(actionTag, GetOverlappingActions(actionTag, ManagerAction.Any));
381        actions.Remove(rootAction.Plugin);
382        UpdateTreeNodes(rootAction.Hull);
383      }
384    }
385
386    private bool CheckNoActionConflict(PluginTag actionTag) {
387      return GetOverlappingActions(actionTag, ManagerAction.Any).Count > 1;
388    }
389
390    private void MarkRemove(PluginTag actionTag) {
391      if(!CheckRemoveConflicts(actionTag)) {
392        CreateNewRemoveAction(actionTag);
393      } else {
394        HandleRemoveConflict(actionTag);
395      }
396    }
397
398    private void UnmarkRemove(PluginTag pluginTag) {
399      if(!CheckNoActionConflict(pluginTag)) {
400        List<PluginAction> rootActions = GetActionsInvolving(pluginTag);
401        // if there is no conflict then there can only be one action connected to the pluginTag
402        PluginAction rootAction = rootActions[0];
403        // kill the root action
404        actions.Remove(rootAction.Plugin);
405        UpdateTreeNodes(rootAction.Hull);
406      } else {
407        HandleNoActionConflict(pluginTag);
408      }
409    }
410
411    private void CreateNewRemoveAction(PluginTag actionTag) {
412      CreateNewAction(actionTag, ManagerAction.Remove, actionTag.GetDependentTags());
413    }
414
415    private void CreateNewInstallAction(PluginTag actionTag) {
416      CreateNewAction(actionTag, ManagerAction.Install, actionTag.GetDependencyTags());
417    }
418
419    private void CreateNewAction(PluginTag actionTag, ManagerAction action, List<PluginTag> hull) {
420      PluginAction newAction = new PluginAction();
421      newAction.Action = action;
422      newAction.Plugin = actionTag;
423      newAction.Hull = hull;
424      newAction.Hull.Add(actionTag);
425
426
427      actions[actionTag] = newAction;
428      UpdateTreeNodes(newAction.Hull);
429    }
430
431    private bool CheckRemoveConflicts(PluginTag actionTag) {
432      return GetOverlappingActions(actionTag, ManagerAction.Install).Count > 0;
433    }
434
435    private void HandleRemoveConflict(PluginTag actionTag) {
436      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Install);
437
438      // create a planned action to display in the conflict message box
439      PluginAction plannedRemoveAction = new PluginAction();
440      plannedRemoveAction.Action = ManagerAction.Remove;
441      plannedRemoveAction.Plugin = actionTag;
442
443      DialogResult userResult = ShowGenericConflictDialog("Remove", plannedRemoveAction, conflictingActions);
444      if(userResult == DialogResult.OK) {
445        // if user says ok then remove the old actions and create a new remove action
446        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
447          actions.Remove(conflictingAction.Plugin);
448          UpdateTreeNodes(conflictingAction.Hull);
449        });
450
451        CreateNewRemoveAction(actionTag);
452      }
453    }
454
455    private bool CheckInstallConflicts(PluginTag actionTag) {
456      return GetOverlappingActions(actionTag, ManagerAction.Remove).Count > 0;
457    }
458
459    private void HandleInstallConflict(PluginTag actionTag) {
460      List<PluginAction> conflictingActions = GetOverlappingActions(actionTag, ManagerAction.Remove);
461
462      // create a planned action to display in the conflict message box
463      PluginAction plannedInstallAction = new PluginAction();
464      plannedInstallAction.Action = ManagerAction.Install;
465      plannedInstallAction.Plugin = actionTag;
466
467      DialogResult userResult = ShowGenericConflictDialog("Install", plannedInstallAction, conflictingActions);
468
469      if(userResult == DialogResult.OK) {
470        conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
471          actions.Remove(conflictingAction.Plugin);
472          UpdateTreeNodes(conflictingAction.Hull);
473        });
474
475        CreateNewInstallAction(actionTag);
476      }
477    }
478
479    private DialogResult ShowGenericConflictDialog(string action, PluginAction theAction, List<PluginAction> conflictingActions) {
480      string message = "The actions:\n\n";
481      conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
482        message += " - " + conflictingAction.Action + " " + conflictingAction.Plugin.PluginName + "\n";
483      });
484      message += "\nconflict with the action: " + action + " " + theAction.Plugin.PluginName + ".\n\n";
485      message += "\nSolution:\n";
486      conflictingActions.ForEach(delegate(PluginAction conflictingAction) {
487        message += " - " + conflictingAction.GetInverseActionString() + " " + conflictingAction.Plugin.PluginName + "\n";
488      });
489      message += " - " + action + " " + theAction.Plugin.PluginName + "\n\n";
490      message += "Do you want to continue?";
491
492      return MessageBox.Show(message, "Resolve conflicting actions", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
493
494    }
495
496    private List<PluginAction> GetActionsInvolving(PluginTag actionTag) {
497      List<PluginAction> filteredActions = new List<PluginAction>();
498      foreach(PluginAction action in actions.Values) {
499        if(action.Hull.Contains(actionTag)) {
500          filteredActions.Add(action);
501        }
502      }
503      return filteredActions;
504    }
505
506    private PluginAction GetSmallestActionInvolving(PluginTag actionTag, List<PluginAction> actions) {
507      PluginAction smallestAction;
508      // if there is an action defined for which actionTag is the root then us it as root tag
509      // otherwise use the root of the action that contains pluginTag in its hull and has the smallest hull of all actions
510      smallestAction = actions.Find(delegate(PluginAction action) {
511        return action.Plugin == actionTag;
512      });
513      // if nothing is found then get the action with the smallest hull
514      if(smallestAction == null) {
515        // find the action with the smallest hull
516        smallestAction = actions[0];
517        actions.ForEach(delegate(PluginAction action) {
518          if(action.Hull.Count < smallestAction.Hull.Count)
519            smallestAction = action;
520        });
521      }
522
523      return smallestAction;
524    }
525
526    /// <summary>
527    /// Similar to GetRootActions but filter only specific ManagerActions
528    /// </summary>
529    /// <param name="pluginTag"></param>
530    /// <param name="overlapAction"></param>
531    /// <returns></returns>
532    private List<PluginAction> GetOverlappingActions(PluginTag actionTag, ManagerAction overlapAction) {
533      List<PluginAction> overlappingActions = new List<PluginAction>();
534      foreach(PluginAction action in actions.Values) {
535        if(((action.Action & overlapAction) > 0) &&
536          action.Hull.Contains(actionTag)) {
537          overlappingActions.Add(action);
538        }
539      }
540
541      return overlappingActions;
542    }
543
544    private void UpdateTreeNodes(List<PluginTag> hull) {
545      hull.ForEach(delegate(PluginTag tag) {
546        FindPluginNodes(tag).ForEach(delegate(TreeNode node) {
547          ManagerAction action = GetAction(tag);
548          if(action != ManagerAction.None) {
549            node.Text = tag.PluginName + " - Action: " + action;
550            if(action == ManagerAction.Remove) {
551              node.ImageIndex = 3;
552              node.SelectedImageIndex = 3;
553            } else if(action == ManagerAction.Install) {
554              node.ImageIndex = 2;
555              node.SelectedImageIndex = 2;
556            }
557          } else if(tag.State == PluginState.Upgradeable) {
558            node.Text = tag.PluginName + " - Upgrade: " + tag.PluginVersion + " -> " + tag.UpgradePluginDescription.Version;
559            node.ImageIndex = 2;
560            node.SelectedImageIndex = 2;
561          } else {
562            node.Text = tag.PluginName;
563            node.ImageIndex = 0;
564            node.SelectedImageIndex = 0;
565          }
566        });
567      });
568    }
569
570    private void UpdateActionButtons(PluginTag tag) {
571      publishButton.Enabled = (tag.State & PluginState.Installed) == PluginState.Installed;
572      installButton.Enabled = (tag.State & PluginState.Available) == PluginState.Available;
573      deleteButton.Enabled = (tag.State & (PluginState.Installed | PluginState.Upgradeable | PluginState.Disabled)) != 0;
574
575      installButton.Checked = GetAction(tag) == ManagerAction.Install;
576      deleteButton.Checked = GetAction(tag) == ManagerAction.Remove;
577
578      publishMenuItem.Enabled = publishButton.Enabled;
579      installMenuItem.Enabled = installButton.Enabled;
580      deleteMenuItem.Enabled = deleteButton.Enabled;
581      installMenuItem.Checked = installButton.Checked;
582      deleteMenuItem.Checked = deleteButton.Checked;
583    }
584
585    private ManagerAction GetAction(PluginTag tag) {
586      ManagerAction plannedAction = ManagerAction.None;
587      if(actions.ContainsKey(tag)) {
588        plannedAction = actions[tag].Action;
589      } else {
590        foreach(PluginAction action in actions.Values) {
591          if(action.Hull.Contains(tag)) {
592            plannedAction = action.Action;
593          }
594        }
595      }
596
597      if(plannedAction == ManagerAction.Install
598        && (tag.State == PluginState.Installed || tag.State == PluginState.Upgradeable)) {
599        return ManagerAction.None;
600      } else if(plannedAction == ManagerAction.Remove
601        && tag.State == PluginState.Available) {
602        return ManagerAction.None;
603      } else {
604        return plannedAction;
605      }
606    }
607
608    private List<TreeNode> FindPluginNodes(PluginTag pluginTag) {
609      List<TreeNode> nodes = new List<TreeNode>();
610      foreach(TreeNode rootNode in new TreeNode[] { installedPlugins, availablePlugins, disabledPlugins }) {
611        foreach(TreeNode node in rootNode.Nodes) {
612          if(pluginTag.Equals(node.Tag)) {
613            nodes.Add(node);
614          }
615        }
616      }
617      return nodes;
618    }
619    private void DisplayPluginInfo(string pluginInformation) {
620      infoTextBox.Text = pluginInformation;
621    }
622    private void upgradeButton_Click(object sender, EventArgs args) {
623      try {
624        ClearTemporaryFiles();
625        if(!DownloadFiles()) {
626          return;
627        }
628        OnDeletePlugins();
629        OnPreUpgradePlugins();
630        PluginManager.Manager.UnloadAllPlugins();
631        BackupOldFiles();
632        DeleteOldFiles();
633        InstallNewFiles();
634        PluginManager.Manager.LoadAllPlugins();
635        InitializePlugins();
636        OnPostUpgradePlugins();
637        OnInstallPlugins();
638        ClearTemporaryFiles();
639      } catch(Exception e) {
640        ShowErrorDialog(e + "");
641      }
642    }
643    private void OnDeletePlugins() {
644      allTags.ForEach(delegate(PluginTag tag) {
645        if(tag.State == PluginState.Installed) {
646          List<PluginAction> actions = GetActionsInvolving(tag);
647          if(actions.Count > 0 && actions[0].Action == ManagerAction.Remove) {
648            PluginManager.Manager.OnDelete(tag.Plugin);
649          }
650        }
651      });
652    }
653    private void OnInstallPlugins() {
654      allTags.ForEach(delegate(PluginTag tag) {
655        if(tag.State == PluginState.Available) {
656          List<PluginAction> actions = GetActionsInvolving(tag);
657          if(actions.Count > 0 && actions[0].Action == ManagerAction.Install) {
658            PluginManager.Manager.OnInstall(tag.Plugin);
659          }
660        }
661      });
662    }
663    private List<string> upgradedPlugins = new List<string>();
664    private void OnPreUpgradePlugins() {
665      upgradedPlugins.Clear();
666      allTags.ForEach(delegate(PluginTag tag) {
667        if(tag.State == PluginState.Upgradeable) {
668          PluginManager.Manager.OnPreUpdate(tag.Plugin);
669
670          // save the name of the plugin in  a list that is used later to call OnPostUpdate for all plugins
671          upgradedPlugins.Add(tag.PluginName);
672        }
673      });
674    }
675    private void OnPostUpgradePlugins() {
676      allTags.ForEach(delegate(PluginTag tag) {
677        if(upgradedPlugins.Contains(tag.PluginName)) {
678          PluginManager.Manager.OnPostUpdate(tag.Plugin);
679        }
680      });
681      upgradedPlugins.Clear();
682    }
683    /// <summary>
684    /// Deletes all files in the directories cacheDir, backupDir, tempDir
685    /// </summary>
686    private void ClearTemporaryFiles() {
687      // can't really handle exceptions here -> let higher layer handle them
688      string[] filenames = Directory.GetFiles(cacheDir, "*", SearchOption.AllDirectories);
689      foreach(string filename in filenames) {
690        File.Delete(filename);
691      }
692      filenames = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
693      foreach(string filename in filenames) {
694        File.Delete(filename);
695      }
696      filenames = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
697      foreach(string filename in filenames) {
698        File.Delete(filename);
699      }
700    }
701
702    /// <summary>
703    /// Extracts zip packages in cacheDir to tempDir and then copies the files from tempDir to pluginDir
704    /// When there is a problem on extraction or later when copying the files to the pluginDir the method
705    /// delets all files that have already been copied from tempDir to pluginDir (the filename exists in both
706    /// locations) and then copies all files in the backup directory back to the plugin directory.
707    /// </summary>
708    /// <returns></returns>
709    private void InstallNewFiles() {
710      try {
711        // extract all packages
712        string[] packages = Directory.GetFiles(cacheDir, "*", SearchOption.AllDirectories);
713        FastZip fastZip = new FastZip();
714        foreach(string package in packages) {
715          fastZip.ExtractZip(package, tempDir, String.Empty);
716        }
717
718        // copy extracted files to plugin dir
719        string[] extractedFiles = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
720        foreach(string extractedFile in extractedFiles) {
721          File.Copy(extractedFile, pluginDir + extractedFile.Remove(0, tempDir.Length));
722        }
723      } catch(Exception e) {
724        infoTextBox.Text = e + "";
725        // remove already copied files
726        string[] extractedFiles = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
727        foreach(string extractedFile in extractedFiles) {
728          string filename = pluginDir + extractedFile.Remove(0, tempDir.Length);
729          if(File.Exists(filename)) {
730            File.Delete(filename);
731          }
732        }
733
734        // restore files from backup
735        string[] backupFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
736        foreach(string backupFile in backupFiles) {
737          File.Copy(backupFile, pluginDir + backupFile.Remove(0, backupDir.Length));
738        }
739
740        throw e;
741      }
742    }
743
744    /// <summary>
745    /// Deletes all files of plugins that have been marked as 'Remove' or 'Upgrade'.
746    /// If there is a problem when removing the files then all files that have been removed earlier
747    /// (the filename existis in backupDir but not in pluginDir) are copied from the backupDir to the pluginDir
748    /// </summary>
749    /// <returns></returns>
750    private void DeleteOldFiles() {
751      try {
752        allTags.ForEach(delegate(PluginTag tag) {
753          List<PluginAction> involvingActions = GetActionsInvolving(tag);
754
755          if(tag.State == PluginState.Upgradeable || (involvingActions.Count > 0 && involvingActions[0].Action == ManagerAction.Remove)) {
756            tag.Plugin.Files.ForEach(delegate(string filename) {
757              File.Delete(filename);
758            });
759          }
760        });
761      } catch(Exception e) {
762        infoTextBox.Text = e + "";
763        // restore deleted files from backup
764        string[] backupFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
765        foreach(string backupFile in backupFiles) {
766          string oldFileName = pluginDir + backupFile.Remove(0, backupDir.Length);
767          if(!File.Exists(oldFileName)) {
768            File.Move(backupFile, oldFileName);
769          }
770        }
771        throw e;
772      }
773    }
774
775    /// <summary>
776    /// Copies all files of plugins that are marked with 'Remove' or 'Upgrade' to the backup directory.
777    /// When there is a problem all files in the backup directory are deleted.
778    /// </summary>
779    /// <returns></returns>
780    private void BackupOldFiles() {
781      try {
782        allTags.ForEach(delegate(PluginTag tag) {
783          List<PluginAction> actionsInvolving = GetActionsInvolving(tag);
784
785          if(tag.State == PluginState.Upgradeable || (actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Remove)) {
786            tag.Plugin.Files.ForEach(delegate(string filename) {
787              File.Copy(filename, backupDir + filename.Remove(0, pluginDir.Length));
788            });
789          }
790        });
791      } catch(Exception e) {
792        infoTextBox.Text = e + "";
793        // delete all files in the backup directory
794        string[] copiedFiles = Directory.GetFiles(backupDir, "*", SearchOption.AllDirectories);
795        string filesString = "";
796        foreach(string fs in copiedFiles) {
797          filesString += fs + "\n";
798        }
799        foreach(string copiedFile in copiedFiles) {
800          File.Delete(copiedFile);
801        }
802        throw e;
803      }
804    }
805
806    private bool DownloadFiles() {
807      DownloaderDialog dialog = new DownloaderDialog();
808      IEnumerator<PluginTag> pluginEnumerator = allTags.GetEnumerator();
809      BackgroundWorker worker = new BackgroundWorker();
810      worker.WorkerReportsProgress = true;
811      worker.WorkerSupportsCancellation = true;
812
813      worker.DoWork += delegate(object sender, DoWorkEventArgs args) {
814        // count number of plugins to download
815        int numberOfPlugins = 0;
816        allTags.ForEach(delegate(PluginTag current) {
817          if(current.UpgradeAvailable()) {
818            numberOfPlugins++;
819          } else {
820            List<PluginAction> actionsInvolving = GetActionsInvolving(current);
821
822            if(actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Install && current.State == PluginState.Available) {
823              numberOfPlugins++;
824            }
825          }
826        });
827
828        // download
829        int downloaded = 0;
830        Invoke((MethodInvoker)delegate() {
831          infoTextBox.Text = "Downloading " + numberOfPlugins + " plugins.\n";
832        });
833
834        allTags.ForEach(delegate(PluginTag current) {
835          if(worker.CancellationPending) {
836            args.Cancel = true;
837            return;
838          }
839          List<PluginAction> actionsInvolving = GetActionsInvolving(current);
840          if(current.UpgradeAvailable() ||
841            (actionsInvolving.Count > 0 && actionsInvolving[0].Action == ManagerAction.Install && current.State == PluginState.Available)) {
842            dialog.SetDownloadDescription("Downloading " + current.PluginName + " ...");
843            Invoke((MethodInvoker)delegate() { infoTextBox.Text += "Downloading " + current.PluginName + " ..."; });
844
845            long nBytes = 0;
846            if(current.UpgradeAvailable()) {
847              nBytes = current.UpgradePluginDescription.Source.DownloadPlugin(current.UpgradePluginDescription);
848            } else {
849              nBytes = current.PluginDescription.Source.DownloadPlugin(current.PluginDescription);
850            }
851
852            worker.ReportProgress(downloaded / numberOfPlugins);
853            Invoke((MethodInvoker)delegate() { infoTextBox.Text += " " + nBytes + " bytes.\n"; });
854          }
855        });
856      };
857
858      worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs args) {
859        dialog.SetProgress(args.ProgressPercentage);
860      };
861
862      worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs args) {
863        if(args.Cancelled) {
864          infoTextBox.Text += "Cancelled downloading plugins";
865          dialog.DialogResult = DialogResult.Cancel;
866        } else if(args.Error != null) {
867          infoTextBox.Text += "Error while downloading plugins:\n" + args.Error;
868          dialog.DialogResult = DialogResult.Cancel;
869        }
870        dialog.DialogResult = DialogResult.OK;
871        dialog.Close();
872      };
873
874
875      dialog.Shown += delegate(object sender, EventArgs args) {
876        worker.RunWorkerAsync();
877      };
878
879      dialog.OnCancel += delegate() {
880        worker.CancelAsync();
881        dialog.SetDownloadDescription("Cancelling download...");
882        infoTextBox.Text += "Cancelled!\n";
883      };
884
885      if(dialog.ShowDialog() == DialogResult.Cancel) {
886        return false;
887      } else {
888        return true;
889      }
890    }
891
892    private void removeButton_Clicked(object sender, EventArgs e) {
893      // get the tag of the selected treeNode
894      PluginTag actionTag = (PluginTag)pluginTreeView.SelectedNode.Tag;
895      List<PluginAction> rootActions = GetActionsInvolving(actionTag);
896      if(rootActions.Count > 0) {
897        UnmarkRemove(actionTag);
898      } else {
899        MarkRemove(actionTag);
900      }
901
902      // reflect the change of plugin actions in the install/remove buttons
903      UpdateActionButtons(actionTag);
904      // update the plugin detail information of the selected plugin
905      DisplayPluginInfo(actionTag.GetPluginDetails());
906    }
907
908    private void installButton_Clicked(object sender, EventArgs e) {
909      // get the tag of the selected treeNode
910      PluginTag actionTag = (PluginTag)pluginTreeView.SelectedNode.Tag;
911      List<PluginAction> rootActions = GetActionsInvolving(actionTag);
912
913      if(rootActions.Count > 0) {
914        UnmarkInstall(actionTag);
915      } else {
916        MarkInstall(actionTag);
917      }
918
919      // reflect the change of plugin actions in the install/remove buttons
920      UpdateActionButtons(actionTag);
921      // update the plugin detail information of the selected plugin
922      DisplayPluginInfo(actionTag.GetPluginDetails());
923    }
924
925    private void ShowErrorDialog(string message) {
926      ErrorDialog dialog = new ErrorDialog(message, "Exception");
927      dialog.ShowDialog();
928    }
929
930    private void managePluginSourcesToolStripMenuItem_Click(object sender, EventArgs e) {
931      PluginSourceEditor sourceEditor = new PluginSourceEditor();
932      sourceEditor.ShowDialog();
933    }
934
935    private void refreshPluginListToolStripMenuItem_Click(object sender, EventArgs e) {
936      PluginManager.Manager.UnloadAllPlugins();
937      PluginManager.Manager.LoadAllPlugins();
938
939      InitializePlugins();
940    }
941
942    private void pluginTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
943      if(e.Node.Tag != null) {
944
945        UpdateActionButtons((PluginTag)e.Node.Tag);
946        // display the plugin details in the lower pane
947        DisplayPluginInfo(((PluginTag)e.Node.Tag).GetPluginDetails());
948
949      } else {
950        // when a node was selected that doesn't represent a plugin (root node) then install and remove are not possible
951        publishButton.Enabled = false;
952        installButton.Enabled = false;
953        deleteButton.Enabled = false;
954      }
955    }
956
957    private void pluginTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
958      // dumb solution to automatically select the node on right clicks which opens the context menu because
959      // per default the treeview doesn't select nodes on right click
960      if(e.Button == MouseButtons.Right) {
961        pluginTreeView.SelectedNode = e.Node;
962      }
963    }
964
965    private void closeToolStripMenuItem_Click(object sender, EventArgs e) {
966      Close();
967    }
968
969    private void installPluginFromFileToolStripMenuItem_Click(object sender, EventArgs args) {
970      OpenFileDialog dialog = new OpenFileDialog();
971      dialog.Multiselect = false;
972      DialogResult result = dialog.ShowDialog();
973      if(result == DialogResult.OK) {
974        string packageName = dialog.FileName;
975        try {
976          ClearTemporaryFiles();
977
978          FastZip fastZip = new FastZip();
979          fastZip.ExtractZip(packageName, cacheDir, String.Empty);
980
981          // check if none of the files exist
982          foreach(string filename in Directory.GetFiles(cacheDir)) {
983            if(File.Exists(pluginDir + filename.Remove(0, cacheDir.Length))) {
984              ShowErrorDialog("Sorry can't install the plugin " + packageName + "\nThe file: " + filename.Remove(0, cacheDir.Length) + " already exist in " + pluginDir + "\nIt seems the plugin is already installed.");
985              ClearTemporaryFiles();
986              return;
987            }
988          }
989
990          PluginManager.Manager.UnloadAllPlugins();
991          // move the files
992          foreach(string filename in Directory.GetFiles(cacheDir)) {
993            File.Move(filename, pluginDir + filename.Remove(0, cacheDir.Length));
994          }
995          PluginManager.Manager.LoadAllPlugins();
996          InitializePlugins();
997
998        } catch(Exception e) {
999          ShowErrorDialog(e + "");
1000        } finally {
1001          ClearTemporaryFiles();
1002        }
1003      }
1004    }
1005
1006    private void pluginTreeView_KeyPress(object sender, KeyPressEventArgs e) {
1007      e.Handled = true;
1008      if(e.KeyChar == 'i' && installButton.Enabled) {
1009        installButton_Clicked(sender, e);
1010      } else if(e.KeyChar == 'd' && deleteButton.Enabled) {
1011        removeButton_Clicked(sender, e);
1012      }
1013    }
1014  }
1015}
Note: See TracBrowser for help on using the repository browser.