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}" />