From 21700aaad5966a12c9bf3ab1cdc303bfb302406c Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 30 Apr 2021 17:39:58 +0200 Subject: [PATCH] Plugins - Reworked bootstrapper --- .../Plugins/IPluginBootstrapper.cs | 26 ----- src/Artemis.Core/Plugins/Plugin.cs | 9 +- .../Plugins/PluginBootstrapper.cs | 100 ++++++++++++++++++ src/Artemis.Core/Plugins/PluginFeatureInfo.cs | 17 +-- src/Artemis.Core/Plugins/PluginInfo.cs | 16 +-- .../Prerequisites/IPrerequisitesSubject.cs | 21 ++++ .../Prerequisites/PluginPrerequisite.cs | 33 +----- .../Services/PluginManagementService.cs | 6 +- ...uginPrerequisitesInstallDialogViewModel.cs | 29 ++--- ...inPrerequisitesUninstallDialogViewModel.cs | 34 +++--- .../Tabs/Plugins/PluginFeatureView.xaml | 50 ++++++--- .../Tabs/Plugins/PluginFeatureViewModel.cs | 44 +++++++- .../Tabs/Plugins/PluginSettingsViewModel.cs | 38 +++++-- 13 files changed, 288 insertions(+), 135 deletions(-) delete mode 100644 src/Artemis.Core/Plugins/IPluginBootstrapper.cs create mode 100644 src/Artemis.Core/Plugins/PluginBootstrapper.cs create mode 100644 src/Artemis.Core/Plugins/Prerequisites/IPrerequisitesSubject.cs diff --git a/src/Artemis.Core/Plugins/IPluginBootstrapper.cs b/src/Artemis.Core/Plugins/IPluginBootstrapper.cs deleted file mode 100644 index bc2c20cb0..000000000 --- a/src/Artemis.Core/Plugins/IPluginBootstrapper.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Artemis.Core -{ - /// - /// An optional entry point for your plugin - /// - public interface IPluginBootstrapper - { - /// - /// Called when the plugin is loaded - /// - /// - void OnPluginLoaded(Plugin plugin); - - /// - /// Called when the plugin is activated - /// - /// The plugin instance of your plugin - void OnPluginEnabled(Plugin plugin); - - /// - /// Called when the plugin is deactivated or when Artemis shuts down - /// - /// The plugin instance of your plugin - void OnPluginDisabled(Plugin plugin); - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/Plugin.cs b/src/Artemis.Core/Plugins/Plugin.cs index 6dab36b99..e5e00841c 100644 --- a/src/Artemis.Core/Plugins/Plugin.cs +++ b/src/Artemis.Core/Plugins/Plugin.cs @@ -71,7 +71,7 @@ namespace Artemis.Core /// /// Gets the plugin bootstrapper /// - public IPluginBootstrapper? Bootstrapper { get; internal set; } + public PluginBootstrapper? Bootstrapper { get; internal set; } /// /// The Ninject kernel of the plugin @@ -118,10 +118,11 @@ namespace Artemis.Core /// Looks up the feature info the feature of type /// /// The type of feature to find - /// If found, feature info of the feature - public PluginFeatureInfo? GetFeatureInfo() where T : PluginFeature + /// Feature info of the feature + public PluginFeatureInfo GetFeatureInfo() where T : PluginFeature { - return _features.FirstOrDefault(i => i.FeatureType == typeof(T)); + // This should be a safe assumption because any type of PluginFeature is registered and added + return _features.First(i => i.FeatureType == typeof(T)); } /// diff --git a/src/Artemis.Core/Plugins/PluginBootstrapper.cs b/src/Artemis.Core/Plugins/PluginBootstrapper.cs new file mode 100644 index 000000000..0d41ad36d --- /dev/null +++ b/src/Artemis.Core/Plugins/PluginBootstrapper.cs @@ -0,0 +1,100 @@ +namespace Artemis.Core +{ + /// + /// An optional entry point for your plugin + /// + public abstract class PluginBootstrapper + { + private Plugin? _plugin; + + /// + /// Called when the plugin is loaded + /// + /// + public virtual void OnPluginLoaded(Plugin plugin) + { + } + + /// + /// Called when the plugin is activated + /// + /// The plugin instance of your plugin + public virtual void OnPluginEnabled(Plugin plugin) + { + } + + /// + /// Called when the plugin is deactivated or when Artemis shuts down + /// + /// The plugin instance of your plugin + public virtual void OnPluginDisabled(Plugin plugin) + { + } + + /// + /// Adds the provided prerequisite to the plugin. + /// + /// The prerequisite to add + public void AddPluginPrerequisite(PluginPrerequisite prerequisite) + { + // TODO: We can keep track of them and add them after load, same goes for the others + if (_plugin == null) + throw new ArtemisPluginException("Cannot add plugin prerequisites before the plugin is loaded"); + + if (!_plugin.Info.Prerequisites.Contains(prerequisite)) + _plugin.Info.Prerequisites.Add(prerequisite); + } + + /// + /// Removes the provided prerequisite from the plugin. + /// + /// The prerequisite to remove + /// + /// is successfully removed; otherwise . This method also returns + /// if the prerequisite was not found. + /// + public bool RemovePluginPrerequisite(PluginPrerequisite prerequisite) + { + if (_plugin == null) + throw new ArtemisPluginException("Cannot add plugin prerequisites before the plugin is loaded"); + + return _plugin.Info.Prerequisites.Remove(prerequisite); + } + + /// + /// Adds the provided prerequisite to the feature of type . + /// + /// The prerequisite to add + public void AddFeaturePrerequisite(PluginPrerequisite prerequisite) where T : PluginFeature + { + if (_plugin == null) + throw new ArtemisPluginException("Cannot add feature prerequisites before the plugin is loaded"); + + PluginFeatureInfo info = _plugin.GetFeatureInfo(); + if (!info.Prerequisites.Contains(prerequisite)) + info.Prerequisites.Add(prerequisite); + } + + /// + /// Removes the provided prerequisite from the feature of type . + /// + /// The prerequisite to remove + /// + /// is successfully removed; otherwise . This method also returns + /// if the prerequisite was not found. + /// + public bool RemoveFeaturePrerequisite(PluginPrerequisite prerequisite) where T : PluginFeature + { + if (_plugin == null) + throw new ArtemisPluginException("Cannot add feature prerequisites before the plugin is loaded"); + + return _plugin.GetFeatureInfo().Prerequisites.Remove(prerequisite); + } + + internal void InternalOnPluginLoaded(Plugin plugin) + { + _plugin = plugin; + OnPluginLoaded(plugin); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs index 9bb78b186..9e8be12cf 100644 --- a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs +++ b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs @@ -15,7 +15,7 @@ namespace Artemis.Core /// Represents basic info about a plugin feature and contains a reference to the instance of said feature /// [JsonObject(MemberSerialization.OptIn)] - public class PluginFeatureInfo : CorePropertyChanged + public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject { private string? _description; private string? _icon; @@ -31,7 +31,7 @@ namespace Artemis.Core Description = attribute?.Description; Icon = attribute?.Icon; AlwaysEnabled = attribute?.AlwaysEnabled ?? false; - + if (Icon != null) return; if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType)) Icon = "TableAdd"; @@ -130,18 +130,11 @@ namespace Artemis.Core internal set => SetAndNotify(ref _instance, value); } - /// - /// Gets a list of prerequisites for this plugin feature - /// + /// public List Prerequisites { get; } = new(); - /// - /// Determines whether the prerequisites of this feature are met - /// - public bool ArePrerequisitesMet() - { - return Prerequisites.All(p => p.IsMet()); - } + /// + public bool ArePrerequisitesMet() => Prerequisites.All(p => p.IsMet()); /// public override string ToString() diff --git a/src/Artemis.Core/Plugins/PluginInfo.cs b/src/Artemis.Core/Plugins/PluginInfo.cs index 0ae163d14..de8a77e7f 100644 --- a/src/Artemis.Core/Plugins/PluginInfo.cs +++ b/src/Artemis.Core/Plugins/PluginInfo.cs @@ -10,7 +10,7 @@ namespace Artemis.Core /// Represents basic info about a plugin and contains a reference to the instance of said plugin /// [JsonObject(MemberSerialization.OptIn)] - public class PluginInfo : CorePropertyChanged + public class PluginInfo : CorePropertyChanged, IPrerequisitesSubject { private bool _autoEnableFeatures = true; private string? _description; @@ -119,19 +119,11 @@ namespace Artemis.Core internal set => SetAndNotify(ref _plugin, value); } - /// - /// Gets a list of prerequisites for this plugin - /// + /// public List Prerequisites { get; } = new(); - - /// - /// Determines whether the prerequisites of this plugin are met - /// - public bool ArePrerequisitesMet() - { - return Prerequisites.All(p => p.IsMet()); - } + /// + public bool ArePrerequisitesMet() => Prerequisites.All(p => p.IsMet()); /// public override string ToString() diff --git a/src/Artemis.Core/Plugins/Prerequisites/IPrerequisitesSubject.cs b/src/Artemis.Core/Plugins/Prerequisites/IPrerequisitesSubject.cs new file mode 100644 index 000000000..5401455ed --- /dev/null +++ b/src/Artemis.Core/Plugins/Prerequisites/IPrerequisitesSubject.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Artemis.Core +{ + /// + /// Represents a type that has prerequisites + /// + public interface IPrerequisitesSubject + { + /// + /// Gets a list of prerequisites for this plugin + /// + List Prerequisites { get; } + + /// + /// Determines whether the prerequisites of this plugin are met + /// + bool ArePrerequisitesMet(); + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/Prerequisites/PluginPrerequisite.cs b/src/Artemis.Core/Plugins/Prerequisites/PluginPrerequisite.cs index 5267b4d33..a762d7e7c 100644 --- a/src/Artemis.Core/Plugins/Prerequisites/PluginPrerequisite.cs +++ b/src/Artemis.Core/Plugins/Prerequisites/PluginPrerequisite.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -11,24 +12,6 @@ namespace Artemis.Core { private PluginPrerequisiteAction? _currentAction; - /// - /// Creates a new instance of the class - /// - /// The plugin this is a prerequisite for - protected PluginPrerequisite(Plugin plugin) - { - Plugin = plugin; - } - - /// - /// Creates a new instance of the class - /// - /// The plugin feature this is a prerequisite for - protected PluginPrerequisite(PluginFeature pluginFeature) - { - PluginFeature = pluginFeature; - } - /// /// Gets the name of the prerequisite /// @@ -63,18 +46,6 @@ namespace Artemis.Core private set => SetAndNotify(ref _currentAction, value); } - /// - /// Gets or sets the plugin this prerequisite is for - /// Note: Only one plugin or a plugin feature can be set at once - /// - public Plugin? Plugin { get; } - - /// - /// Gets or sets the feature this prerequisite is for - /// Note: Only one plugin or a plugin feature can be set at once - /// - public PluginFeature? PluginFeature { get; } - /// /// Execute all install actions /// diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index c28b8d44a..fe839e4bc 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -345,13 +345,13 @@ namespace Artemis.Core.Services if (!featureTypes.Any()) _logger.Warning("Plugin {plugin} contains no features", plugin); - List bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList(); + List bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(PluginBootstrapper).IsAssignableFrom(t)).ToList(); if (bootstrappers.Count > 1) _logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}"); if (bootstrappers.Any()) { - plugin.Bootstrapper = (IPluginBootstrapper?) Activator.CreateInstance(bootstrappers.First()); - plugin.Bootstrapper?.OnPluginLoaded(plugin); + plugin.Bootstrapper = (PluginBootstrapper?) Activator.CreateInstance(bootstrappers.First()); + plugin.Bootstrapper?.InternalOnPluginLoaded(plugin); } lock (_plugins) diff --git a/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesInstallDialogViewModel.cs b/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesInstallDialogViewModel.cs index 9b0fb0323..1f955f691 100644 --- a/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesInstallDialogViewModel.cs +++ b/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesInstallDialogViewModel.cs @@ -20,19 +20,19 @@ namespace Artemis.UI.Screens.Plugins private bool _isFinished; private CancellationTokenSource _tokenSource; - public PluginPrerequisitesInstallDialogViewModel(object pluginOrFeature, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService) + public PluginPrerequisitesInstallDialogViewModel(IPrerequisitesSubject subject, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService) { _dialogService = dialogService; // Constructor overloading doesn't work very well with Kernel.Get :( - if (pluginOrFeature is Plugin plugin) + if (subject is PluginInfo plugin) { - Plugin = plugin; - Prerequisites = new BindableCollection(plugin.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false))); + PluginInfo = plugin; + Prerequisites = new BindableCollection(plugin.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false))); } - else if (pluginOrFeature is PluginFeature feature) + else if (subject is PluginFeatureInfo feature) { - Feature = feature; - Prerequisites = new BindableCollection(feature.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false))); + FeatureInfo = feature; + Prerequisites = new BindableCollection(feature.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false))); } else throw new ArtemisUIException($"Expected plugin or feature to be passed to {nameof(PluginPrerequisitesInstallDialogViewModel)}"); @@ -42,8 +42,8 @@ namespace Artemis.UI.Screens.Plugins } - public PluginFeature Feature { get; } - public Plugin Plugin { get; } + public PluginInfo PluginInfo { get; } + public PluginFeatureInfo FeatureInfo { get; } public BindableCollection Prerequisites { get; } public PluginPrerequisiteViewModel ActivePrerequisite @@ -64,6 +64,9 @@ namespace Artemis.UI.Screens.Plugins set => SetAndNotify(ref _isFinished, value); } + public bool IsSubjectPlugin => PluginInfo != null; + public bool IsSubjectFeature => FeatureInfo != null; + #region Overrides of DialogViewModelBase /// @@ -111,7 +114,7 @@ namespace Artemis.UI.Screens.Plugins "Confirm", "" ); - await _dialogService.ShowDialog(new Dictionary {{"pluginOrFeature", Plugin}}); + await _dialogService.ShowDialog(new Dictionary {{"subject", PluginInfo}}); } catch (OperationCanceledException) { @@ -136,10 +139,10 @@ namespace Artemis.UI.Screens.Plugins protected override void OnInitialActivate() { CanInstall = false; - if (Plugin != null) - Task.Run(() => CanInstall = !Plugin.Info.ArePrerequisitesMet()); + if (PluginInfo != null) + Task.Run(() => CanInstall = !PluginInfo.ArePrerequisitesMet()); else - Task.Run(() => CanInstall = !Feature.Info.ArePrerequisitesMet()); + Task.Run(() => CanInstall = !FeatureInfo.ArePrerequisitesMet()); base.OnInitialActivate(); } diff --git a/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesUninstallDialogViewModel.cs b/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesUninstallDialogViewModel.cs index d4d27cc8d..919e58098 100644 --- a/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesUninstallDialogViewModel.cs +++ b/src/Artemis.UI/Screens/Plugins/PluginPrerequisitesUninstallDialogViewModel.cs @@ -22,22 +22,22 @@ namespace Artemis.UI.Screens.Plugins private bool _isFinished; private CancellationTokenSource _tokenSource; - public PluginPrerequisitesUninstallDialogViewModel(object pluginOrFeature, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService, + public PluginPrerequisitesUninstallDialogViewModel(IPrerequisitesSubject subject, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService, IPluginManagementService pluginManagementService) { _dialogService = dialogService; _pluginManagementService = pluginManagementService; // Constructor overloading doesn't work very well with Kernel.Get :( - if (pluginOrFeature is Plugin plugin) + if (subject is PluginInfo plugin) { - Plugin = plugin; - Prerequisites = new BindableCollection(plugin.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true))); + PluginInfo = plugin; + Prerequisites = new BindableCollection(plugin.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true))); } - else if (pluginOrFeature is PluginFeature feature) + else if (subject is PluginFeatureInfo feature) { - Feature = feature; - Prerequisites = new BindableCollection(feature.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true))); + FeatureInfo = feature; + Prerequisites = new BindableCollection(feature.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true))); } else throw new ArtemisUIException($"Expected plugin or feature to be passed to {nameof(PluginPrerequisitesInstallDialogViewModel)}"); @@ -47,8 +47,8 @@ namespace Artemis.UI.Screens.Plugins } - public PluginFeature Feature { get; } - public Plugin Plugin { get; } + public PluginFeatureInfo FeatureInfo { get; } + public PluginInfo PluginInfo { get; } public BindableCollection Prerequisites { get; } public PluginPrerequisiteViewModel ActivePrerequisite @@ -69,6 +69,9 @@ namespace Artemis.UI.Screens.Plugins set => SetAndNotify(ref _isFinished, value); } + public bool IsSubjectPlugin => PluginInfo != null; + public bool IsSubjectFeature => FeatureInfo != null; + #region Overrides of DialogViewModelBase /// @@ -84,7 +87,10 @@ namespace Artemis.UI.Screens.Plugins { CanUninstall = false; - _pluginManagementService.DisablePlugin(Plugin, true); + if (PluginInfo != null) + _pluginManagementService.DisablePlugin(PluginInfo.Plugin, true); + else if (FeatureInfo?.Instance != null) + _pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true); _tokenSource = new CancellationTokenSource(); try @@ -118,7 +124,7 @@ namespace Artemis.UI.Screens.Plugins "Confirm", "" ); - await _dialogService.ShowDialog(new Dictionary {{"pluginOrFeature", Plugin}}); + await _dialogService.ShowDialog(new Dictionary {{"subject", PluginInfo}}); } catch (OperationCanceledException) { @@ -144,10 +150,10 @@ namespace Artemis.UI.Screens.Plugins { CanUninstall = false; // Could be slow so take it off of the UI thread - if (Plugin != null) - Task.Run(() => CanUninstall = Plugin.Info.Prerequisites.Any(p => p.IsMet())); + if (PluginInfo != null) + Task.Run(() => CanUninstall = PluginInfo.Prerequisites.Any(p => p.IsMet())); else - Task.Run(() => CanUninstall = Feature.Info.Prerequisites.Any(p => p.IsMet())); + Task.Run(() => CanUninstall = FeatureInfo.Prerequisites.Any(p => p.IsMet())); base.OnInitialActivate(); } diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml index 5055b02ac..92b177023 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml @@ -12,7 +12,13 @@ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:PluginFeatureViewModel}"> - + + + + + + @@ -23,11 +29,11 @@ + Icon="{Binding FeatureInfo.Icon}" + Width="20" + VerticalAlignment="Center" + HorizontalAlignment="Center" + Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" /> + + + diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs index a4e8587dc..ee086b451 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Threading.Tasks; using Artemis.Core; using Artemis.Core.Services; +using Artemis.UI.Screens.Plugins; using Artemis.UI.Shared.Services; using Stylet; @@ -16,6 +19,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins private readonly IPluginManagementService _pluginManagementService; private bool _enabling; private readonly IMessageService _messageService; + private bool _isSettingsPopupOpen; + private bool _canInstallPrerequisites; + private bool _canRemovePrerequisites; public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield, @@ -51,6 +57,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled; + public bool CanInstallPrerequisites => FeatureInfo.Prerequisites.Any(); + public bool CanRemovePrerequisites => FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any()); + public bool IsPopupEnabled => CanInstallPrerequisites || CanRemovePrerequisites; public void ShowLogsFolder() { @@ -96,6 +105,21 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins base.OnClose(); } + public async Task InstallPrerequisites() + { + if (FeatureInfo.Prerequisites.Any()) + await ShowPrerequisitesDialog(false, FeatureInfo); + } + + public async Task RemovePrerequisites() + { + if (FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any())) + { + await ShowPrerequisitesDialog(true, FeatureInfo); + NotifyOfPropertyChange(nameof(IsEnabled)); + } + } + private async Task UpdateEnabled(bool enable) { if (IsEnabled == enable || FeatureInfo.Instance == null) @@ -120,7 +144,18 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } } - await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance, true)); + // Check if all prerequisites are met async + if (!FeatureInfo.ArePrerequisitesMet()) + { + await ShowPrerequisitesDialog(false, FeatureInfo); + if (!FeatureInfo.ArePrerequisitesMet()) + { + NotifyOfPropertyChange(nameof(IsEnabled)); + return; + } + } + + await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true)); } catch (Exception e) { @@ -138,6 +173,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } } + private async Task ShowPrerequisitesDialog(bool uninstall, IPrerequisitesSubject subject) + { + if (uninstall) + return await _dialogService.ShowDialog(new Dictionary { { "subject", subject } }); + return await _dialogService.ShowDialog(new Dictionary { { "subject", subject } }); + } + #region Event handlers private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e) diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs index bf7e78361..9c30349f1 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs @@ -136,24 +136,26 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins if (wasEnabled) await UpdateEnabled(true); + + _messageService.ShowMessage("Reloaded plugin."); } public async Task InstallPrerequisites() { if (Plugin.Info.Prerequisites.Any()) - await ShowPrerequisitesDialog(false); + await ShowPrerequisitesDialog(false, Plugin.Info); } public async Task RemovePrerequisites() { if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any())) { - await ShowPrerequisitesDialog(true); + await ShowPrerequisitesDialog(true, Plugin.Info); NotifyOfPropertyChange(nameof(IsEnabled)); NotifyOfPropertyChange(nameof(CanOpenSettings)); } } - + public async Task RemoveSettings() { bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?"); @@ -180,9 +182,31 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins return; // If the plugin or any of its features has uninstall actions, offer to run these + List featuresToUninstall = Plugin.Features.Where(f => f.Prerequisites.Any(fp => fp.UninstallActions.Any())).ToList(); if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()) || Plugin.Features.Any(f => f.Prerequisites.Any(fp => fp.UninstallActions.Any()))) { + bool remove = await _dialogService.ShowConfirmDialog( + "Remove plugin", + "This plugin installed one or more prerequisites.\r\nDo you want to remove these?", + "Uninstall", + "Skip" + ); + if (remove) + { + if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any())) + { + object result = await ShowPrerequisitesDialog(true, Plugin.Info); + if (result is bool resultBool && !resultBool) + return; + } + foreach (PluginFeatureInfo pluginFeatureInfo in featuresToUninstall) + { + object result = await ShowPrerequisitesDialog(true, pluginFeatureInfo); + if (result is bool resultBool && !resultBool) + return; + } + } } try @@ -244,7 +268,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins // Check if all prerequisites are met async if (!Plugin.Info.ArePrerequisitesMet()) { - await ShowPrerequisitesDialog(false); + await ShowPrerequisitesDialog(false, Plugin.Info); if (!Plugin.Info.ArePrerequisitesMet()) { CancelEnable(); @@ -285,11 +309,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins CanRemovePrerequisites = Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()); } - private async Task ShowPrerequisitesDialog(bool uninstall) + private async Task ShowPrerequisitesDialog(bool uninstall, IPrerequisitesSubject subject) { if (uninstall) - return await _dialogService.ShowDialog(new Dictionary { { "pluginOrFeature", Plugin } }); - return await _dialogService.ShowDialog(new Dictionary { { "pluginOrFeature", Plugin } }); + return await _dialogService.ShowDialog(new Dictionary {{"subject", subject } }); + return await _dialogService.ShowDialog(new Dictionary {{ "subject", subject } }); } } } \ No newline at end of file