diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index a69f1d372..366d146b9 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -96,7 +96,7 @@ namespace Artemis.Core
public override void Reset()
{
DisplayConditionMet = false;
- Timeline.JumpToStart();
+ Timeline.JumpToEnd();
foreach (ProfileElement child in Children)
child.Reset();
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 4485405d5..5d7bcdb99 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -183,6 +183,8 @@ namespace Artemis.Core
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
ApplyShapeType();
ActivateLayerBrush();
+
+ Reset();
}
#region Storage
@@ -272,7 +274,7 @@ namespace Artemis.Core
public override void Reset()
{
DisplayConditionMet = false;
- Timeline.JumpToStart();
+ Timeline.JumpToEnd();
}
///
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index fbcb9a22d..86c8cb571 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -168,8 +168,28 @@ namespace Artemis.Core.Services
private void ApplyLoggingLevel()
{
- _logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
- LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
+ string? argument = StartupArguments.FirstOrDefault(a => a.StartsWith("--logging"));
+ if (argument != null)
+ {
+ // Parse the provided log level
+ string[] parts = argument.Split('=');
+ if (parts.Length == 2 && Enum.TryParse(typeof(LogEventLevel), parts[1], true, out object? logLevelArgument))
+ {
+ _logger.Information("Setting logging level to {loggingLevel} from startup argument", (LogEventLevel) logLevelArgument!);
+ LoggerProvider.LoggingLevelSwitch.MinimumLevel = (LogEventLevel) logLevelArgument;
+ }
+ else
+ {
+ _logger.Warning("Failed to set log level from startup argument {argument}", argument);
+ _logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
+ LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
+ }
+ }
+ else
+ {
+ _logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
+ LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
+ }
}
private void SurfaceOnUpdating(UpdatingEventArgs args)
diff --git a/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs b/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs
index 0aa3a66a3..83bab9211 100644
--- a/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs
@@ -63,6 +63,13 @@ namespace Artemis.Core.Services
/// Whether or not to save the new enabled state
void DisablePlugin(Plugin plugin, bool saveState);
+ ///
+ /// Imports the plugin contained in the provided ZIP file
+ ///
+ /// The full path to the ZIP file that contains the plugin
+ /// The resulting plugin
+ Plugin ImportPlugin(string fileName);
+
///
/// Enables the provided plugin feature
///
diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs
index 30e085d6e..633b6d45b 100644
--- a/src/Artemis.Core/Services/PluginManagementService.cs
+++ b/src/Artemis.Core/Services/PluginManagementService.cs
@@ -273,7 +273,7 @@ namespace Artemis.Core.Services
FileInfo[] fileInfos = directory.GetFiles();
if (!fileInfos.Any(f => string.Equals(f.Name, plugin.Info.Main, StringComparison.InvariantCulture)))
throw new ArtemisPluginException(plugin, "Plugin main entry casing mismatch at " + plugin.Info.Main);
-
+
// Load the plugin, all types implementing Plugin and register them with DI
plugin.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile!, configure =>
{
@@ -437,6 +437,55 @@ namespace Artemis.Core.Services
OnPluginDisabled(new PluginEventArgs(plugin));
}
+ ///
+ public Plugin ImportPlugin(string fileName)
+ {
+ DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
+
+ // Find the metadata file in the zip
+ using ZipArchive archive = ZipFile.OpenRead(fileName);
+ ZipArchiveEntry? metaDataFileEntry = archive.Entries.FirstOrDefault(e => e.Name == "plugin.json");
+ if (metaDataFileEntry == null)
+ throw new ArtemisPluginException("Couldn't find a plugin.json in " + fileName);
+
+
+ using StreamReader reader = new(metaDataFileEntry.Open());
+ PluginInfo pluginInfo = CoreJson.DeserializeObject(reader.ReadToEnd())!;
+ if (!pluginInfo.Main.EndsWith(".dll"))
+ throw new ArtemisPluginException("Main entry in plugin.json must point to a .dll file" + fileName);
+
+ Plugin? existing = _plugins.FirstOrDefault(p => p.Guid == pluginInfo.Guid);
+ if (existing != null)
+ throw new ArtemisPluginException($"A plugin with the same GUID is already loaded: {existing.Info}");
+
+ string targetDirectory = pluginInfo.Main.Split(".dll")[0].Replace("/", "").Replace("\\", "");
+ string uniqueTargetDirectory = targetDirectory;
+ int attempt = 2;
+
+ // Find a unique folder
+ while (pluginDirectory.EnumerateDirectories().Any(d => d.Name == uniqueTargetDirectory))
+ {
+ uniqueTargetDirectory = targetDirectory + "-" + attempt;
+ attempt++;
+ }
+
+ // Extract everything in the same archive directory to the unique plugin directory
+ DirectoryInfo directoryInfo = new(Path.Combine(pluginDirectory.FullName, uniqueTargetDirectory));
+ Directory.CreateDirectory(directoryInfo.FullName);
+ string metaDataDirectory = metaDataFileEntry.FullName.Replace(metaDataFileEntry.Name, "");
+ foreach (ZipArchiveEntry zipArchiveEntry in archive.Entries)
+ {
+ if (zipArchiveEntry.FullName.StartsWith(metaDataDirectory))
+ {
+ string target = Path.Combine(directoryInfo.FullName, zipArchiveEntry.FullName.Remove(0, metaDataDirectory.Length));
+ zipArchiveEntry.ExtractToFile(target);
+ }
+ }
+
+ // Load the newly extracted plugin and return the result
+ return LoadPlugin(directoryInfo);
+ }
+
#endregion
#region Features
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index d185a83fb..a6d91aa1c 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -142,6 +142,7 @@
+
diff --git a/src/Artemis.UI/Properties/launchSettings.json b/src/Artemis.UI/Properties/launchSettings.json
index be8d62f45..e8d4f8dfb 100644
--- a/src/Artemis.UI/Properties/launchSettings.json
+++ b/src/Artemis.UI/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"Artemis.UI": {
"commandName": "Project",
- "commandLineArgs": "--force-elevation"
+ "commandLineArgs": "--force-elevation --logging=debug"
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabView.xaml
index 13e5b8fc1..7a031426c 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabView.xaml
@@ -14,25 +14,35 @@
-
+
-
+
+
- The list below shows all loaded plugins. If you're missing something, view your logs folder.
+ The list below shows all loaded plugins.
+ If you're missing something, view your logs folder.
-
+ Margin="5 0"
+ Text="{Binding SearchPluginInput, Delay=300, UpdateSourceTrigger=PropertyChanged}" />
+
+
-
+
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs
index ffb530cd3..57f54847c 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsTabViewModel.cs
@@ -2,8 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
+using Artemis.UI.Shared.Services;
+using Ookii.Dialogs.Wpf;
using Stylet;
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
@@ -11,15 +14,17 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
public class PluginSettingsTabViewModel : Conductor.Collection.AllActive
{
private readonly IPluginManagementService _pluginManagementService;
+ private readonly IMessageService _messageService;
private readonly ISettingsVmFactory _settingsVmFactory;
private string _searchPluginInput;
private List _instances;
- public PluginSettingsTabViewModel(IPluginManagementService pluginManagementService, ISettingsVmFactory settingsVmFactory)
+ public PluginSettingsTabViewModel(IPluginManagementService pluginManagementService, IMessageService messageService, ISettingsVmFactory settingsVmFactory)
{
DisplayName = "PLUGINS";
_pluginManagementService = pluginManagementService;
+ _messageService = messageService;
_settingsVmFactory = settingsVmFactory;
}
@@ -63,5 +68,25 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
base.OnActivate();
}
+
+ public void ImportPlugin()
+ {
+ VistaOpenFileDialog dialog = new();
+ dialog.Filter = "ZIP files (*.zip)|*.zip";
+ dialog.Title = "Import Artemis plugin";
+ bool? result = dialog.ShowDialog();
+ if (result == true)
+ {
+ Plugin plugin = _pluginManagementService.ImportPlugin(dialog.FileName);
+
+ _instances = _pluginManagementService.GetAllPlugins()
+ .Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
+ .OrderBy(i => i.Plugin.Info.Name)
+ .ToList();
+ SearchPluginInput = plugin.Info.Name;
+
+ _messageService.ShowMessage($"Imported plugin: {plugin.Info.Name}");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/packages.lock.json b/src/Artemis.UI/packages.lock.json
index 8a022ea4a..2688602e6 100644
--- a/src/Artemis.UI/packages.lock.json
+++ b/src/Artemis.UI/packages.lock.json
@@ -80,6 +80,12 @@
"Ninject.Extensions.Factory": "3.3.2"
}
},
+ "Ookii.Dialogs.Wpf": {
+ "type": "Direct",
+ "requested": "[3.1.0, )",
+ "resolved": "3.1.0",
+ "contentHash": "EpUWoSLLO1aVkUmo2dPE4xO+zQZRxbp13agbbQzGBL1ACALgCD69cTwdLdPdg2LIsPcZ4uA3iID+YaazdZxyww=="
+ },
"RawInput.Sharp": {
"type": "Direct",
"requested": "[0.0.3, )",