diff --git a/.idea/.idea.PSCHelpdesk/.idea/.name b/.idea/.idea.PSCHelpdesk/.idea/.name
new file mode 100644
index 0000000..6f7d947
--- /dev/null
+++ b/.idea/.idea.PSCHelpdesk/.idea/.name
@@ -0,0 +1 @@
+PSCHelpdesk
\ No newline at end of file
diff --git a/.idea/.idea.PSCHelpdesk/.idea/workspace.xml b/.idea/.idea.PSCHelpdesk/.idea/workspace.xml
index c7a52dc..925d175 100644
--- a/.idea/.idea.PSCHelpdesk/.idea/workspace.xml
+++ b/.idea/.idea.PSCHelpdesk/.idea/workspace.xml
@@ -11,52 +11,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -70,57 +36,38 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
{
"customColor": "",
"associatedIndex": 8
@@ -227,6 +174,7 @@
+
@@ -242,56 +190,43 @@
file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
- 86
+ 78
-
+
-
+
file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
- 83
+ 75
-
+
-
+
file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
- 84
+ 76
-
+
-
+
-
- file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/ContentDisplay.axaml.cs
- 71
-
-
-
-
-
-
-
-
-
-
file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/ContentDisplay.axaml.cs
37
@@ -306,17 +241,56 @@
- file://$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
- 48
-
+ file://$PROJECT_DIR$/HetznerServer/Menu/MainMenu.cs
+ 13
+
-
+
-
+
-
+
+
+
+ file://$PROJECT_DIR$/HetznerServer/HetznerServer.cs
+ 26
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/HetznerServer/HetznerServer.cs
+ 28
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/HetznerServer/HetznerServer.cs
+ 27
+
+
+
+
+
+
+
+
+
diff --git a/HetznerServer/HetznerServer.cs b/HetznerServer/HetznerServer.cs
index 5ede775..7d67244 100644
--- a/HetznerServer/HetznerServer.cs
+++ b/HetznerServer/HetznerServer.cs
@@ -1,8 +1,11 @@
+using CommunityToolkit.Mvvm.DependencyInjection;
using Prise.Plugin;
+using PSCHelpdesk.Plugins.HetznerServer.Menu;
using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
using PSCHelpdesk.Plugins.HetznerServer.Views;
using PSCHelpdesk.Shared.Menu;
using PSCHelpdesk.Shared.Plugin;
+using PSCHelpdesk.Shared.Service;
namespace PSCHelpdesk.Plugins.HetznerServer;
@@ -16,11 +19,18 @@ public class HetznerServer : Contract
public async Task> addMenu()
{
- var list = new List- ();
+ var menu = new MainMenu();
+ //menu.addMenu();
+
+
+
+ var menuService = Ioc.Default.GetService();
+ var testService = Ioc.Default.GetService();
+ var list = new List
- ();
var serverTab = new Item()
{
Header = "Server",
- CommandParameter = typeof(ServerViewModel),
+ // CommandParameter = typeof(ServerViewModel)
};
list.Add(serverTab);
return list;
diff --git a/HetznerServer/HetznerServer.csproj b/HetznerServer/HetznerServer.csproj
index 6180a2f..6035396 100644
--- a/HetznerServer/HetznerServer.csproj
+++ b/HetznerServer/HetznerServer.csproj
@@ -20,6 +20,9 @@
+
+
+
@@ -40,7 +43,6 @@
-
diff --git a/HetznerServer/HetznerServerBootstrap.cs b/HetznerServer/HetznerServerBootstrap.cs
index 85eba2c..e6b1136 100644
--- a/HetznerServer/HetznerServerBootstrap.cs
+++ b/HetznerServer/HetznerServerBootstrap.cs
@@ -1,4 +1,5 @@
using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
+using PSCHelpdesk.Shared.Service;
namespace PSCHelpdesk.Plugins.HetznerServer;
@@ -9,8 +10,12 @@ using Prise.Plugin;
[PluginBootstrapper(PluginType = typeof(HetznerServer))]
public class HetznerServerBootstrap : IPluginBootstrapper
{
+ //[BootstrapperService(ServiceType = typeof(IMenuService), ProxyType = typeof(MenuService))]
+ //private readonly IMenuService menuService;
+
public IServiceCollection Bootstrap(IServiceCollection services)
{
+ //services.AddSingleton(this.menuService);
services.AddTransient();
return services;
}
diff --git a/HetznerServer/Menu/MainMenu.cs b/HetznerServer/Menu/MainMenu.cs
new file mode 100644
index 0000000..86b0685
--- /dev/null
+++ b/HetznerServer/Menu/MainMenu.cs
@@ -0,0 +1,20 @@
+using Prise.Plugin;
+using PSCHelpdesk.Shared.Menu;
+using PSCHelpdesk.Shared.Service;
+
+namespace PSCHelpdesk.Plugins.HetznerServer.Menu;
+
+public class MainMenu
+{
+ [PluginService(ProvidedBy = ProvidedBy.Host, ServiceType = typeof(IMenuService))]
+ private readonly IMenuService menuService;
+
+ public void addMenu()
+ {
+ this.menuService.AddMenuItem(new Item()
+ {
+ Header = "Hetzner",
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/HetznerServer/MenuService.cs b/HetznerServer/MenuService.cs
new file mode 100644
index 0000000..fb2c29e
--- /dev/null
+++ b/HetznerServer/MenuService.cs
@@ -0,0 +1,15 @@
+using Prise.Proxy;
+using PSCHelpdesk.Shared.Menu;
+using PSCHelpdesk.Shared.Service;
+
+namespace PSCHelpdesk.Plugins.HetznerServer;
+
+public class MenuService : ReverseProxy, IMenuService
+{
+ public MenuService(object hostService) : base(hostService) { }
+
+ public void AddMenuItem(Item item)
+ {
+ this.InvokeOnHostService
- (new[] { item });
+ }
+}
\ No newline at end of file
diff --git a/PSCHelpdesk.sln b/PSCHelpdesk.sln
index f06fecb..c15c3bc 100644
--- a/PSCHelpdesk.sln
+++ b/PSCHelpdesk.sln
@@ -18,6 +18,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HetznerServer", "HetznerSer
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nextcloud", "Nextcloud\Nextcloud.csproj", "{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vendor", "Vendor", "{D45462B2-C09A-4BC6-A8B1-F03C6533B084}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise", "Vendor\Prise\Prise.csproj", "{AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise.Plugin", "Vendor\Prise.Plugin\Prise.Plugin.csproj", "{F4EF7157-C5F0-4444-9E49-28F6BD56295F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise.ReverseProxy", "Vendor\Prise.ReverseProxy\Prise.ReverseProxy.csproj", "{52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prise.Proxy", "Vendor\Prise.Proxy\Prise.Proxy.csproj", "{79029F78-5113-46E4-8896-436707A7251F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -60,9 +70,29 @@ Global
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AD222F87-EAC5-4120-AA33-EB32DF2DDCD5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F4EF7157-C5F0-4444-9E49-28F6BD56295F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {79029F78-5113-46E4-8896-436707A7251F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {79029F78-5113-46E4-8896-436707A7251F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {79029F78-5113-46E4-8896-436707A7251F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {79029F78-5113-46E4-8896-436707A7251F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{552272D2-9506-4F9E-9D53-E908E53D9039} = {E7A87869-4D47-4292-BE39-77D36933088C}
{4C34E3F4-718D-4E15-97A2-AE61A4ACEE02} = {E7A87869-4D47-4292-BE39-77D36933088C}
+ {AD222F87-EAC5-4120-AA33-EB32DF2DDCD5} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
+ {F4EF7157-C5F0-4444-9E49-28F6BD56295F} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
+ {52EFDD1B-B775-49A1-BD3D-E7B7F6A989EA} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
+ {79029F78-5113-46E4-8896-436707A7251F} = {D45462B2-C09A-4BC6-A8B1-F03C6533B084}
EndGlobalSection
EndGlobal
diff --git a/PSCHelpdesk.sln.DotSettings.user b/PSCHelpdesk.sln.DotSettings.user
index 2134f16..4e31eb8 100644
--- a/PSCHelpdesk.sln.DotSettings.user
+++ b/PSCHelpdesk.sln.DotSettings.user
@@ -5,17 +5,21 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
@@ -23,26 +27,33 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
True
- <AssemblyExplorer>
- <Assembly Path="/home/thomas/.nuget/packages/hcloud-api-net/1.0.0/lib/netstandard2.0/hcloud-api.dll" />
+ <AssemblyExplorer>
+ <Assembly Path="\home\thomas\.nuget\packages\hcloud-api-net\1.0.0\lib\netstandard2.0\hcloud-api.dll" />
+ <Assembly Path="C:\Users\info\.nuget\packages\prise.pluginbridge\1.7.5\lib\netstandard2.0\Prise.PluginBridge.dll" />
+ <Assembly Path="C:\Users\info\.nuget\packages\microsoft.extensions.dependencymodel\6.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyModel.dll" />
</AssemblyExplorer>
True
True
True
+
\ No newline at end of file
diff --git a/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj b/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj
index 109afec..16e1850 100644
--- a/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj
+++ b/PSCHelpdesk/PSCHelpdesk/PSCHelpdesk.csproj
@@ -18,17 +18,17 @@
-
-
+
-
+
+
diff --git a/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs b/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs
index 336edb4..0e3a0dc 100644
--- a/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs
+++ b/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs
@@ -1,5 +1,5 @@
using System;
-using Avalonia.Collections;
+using System.Collections.Generic;
using CommunityToolkit.Mvvm.DependencyInjection;
using PSCHelpdesk.Shared.Menu;
using PSCHelpdesk.Shared.Service;
@@ -10,12 +10,12 @@ namespace PSCHelpdesk.Services;
public class MenuService: ReactiveObject, IMenuService
{
- private AvaloniaList
- menuItems = new AvaloniaList
- ();
- private AvaloniaList
- menuOptionItems = new AvaloniaList
- ();
+ private List
- menuItems = new List
- ();
+ private List
- menuOptionItems = new List
- ();
public event EventHandler MenuChanged;
- public AvaloniaList
- MenuItems
+ public List
- MenuItems
{
get => this.menuItems;
private set => this.RaiseAndSetIfChanged(ref this.menuItems, value);
@@ -24,7 +24,7 @@ public class MenuService: ReactiveObject, IMenuService
///
/// Gets the list of options items shown in the hamburger menu (at the bottom).
///
- public AvaloniaList
- MenuOptionItems
+ public List
- MenuOptionItems
{
get => this.menuOptionItems;
private set => this.RaiseAndSetIfChanged(ref this.menuOptionItems, value);
diff --git a/PSCHelpdesk/PSCHelpdesk/Services/PluginService.cs b/PSCHelpdesk/PSCHelpdesk/Services/PluginService.cs
index 962e183..d05a596 100644
--- a/PSCHelpdesk/PSCHelpdesk/Services/PluginService.cs
+++ b/PSCHelpdesk/PSCHelpdesk/Services/PluginService.cs
@@ -59,6 +59,7 @@ public class PluginService
var pluginLoader = Ioc.Default.GetService(typeof(IPluginLoader)) as IPluginLoader;
var menuService = Ioc.Default.GetRequiredService();
+ var testService = Ioc.Default.GetRequiredService();
var appService = Ioc.Default.GetService(typeof(AppService)) as AppService;
//var settingsService = Ioc.Default.GetService();
@@ -70,10 +71,11 @@ public class PluginService
var plugin = await pluginLoader.LoadPlugin(pluginToEnable, configure: (context) =>
{
context
- //.AddHostTypes(new[] {typeof(Application)})
+
.AddHostService(menuService)
- //.AddHostService(appService)
+ .AddHostService(testService)
//.AddHostService(settingsService)
+
;
});
diff --git a/PSCHelpdesk/PSCHelpdesk/Startup.cs b/PSCHelpdesk/PSCHelpdesk/Startup.cs
index 1af833e..ee5078f 100644
--- a/PSCHelpdesk/PSCHelpdesk/Startup.cs
+++ b/PSCHelpdesk/PSCHelpdesk/Startup.cs
@@ -14,11 +14,12 @@ class Startup
var menuService = new MenuService();
var te = new ServiceCollection()
- .AddPrise()
.AddSingleton()
.AddSingleton(menuService)
+ .AddSingleton(new TestService())
.AddSingleton()
- .AddTransient();
+ .AddTransient()
+ .AddPrise();
var ls = te.BuildServiceProvider();
diff --git a/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs b/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
index 4a8aa2e..e0acb61 100644
--- a/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
+++ b/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs
@@ -29,14 +29,6 @@ public class MainWindowViewModel : ViewModelBase
get => _selectedItem;
set => SetAndRaisePropertyChanged(ref _selectedItem, value);
}
-
- private Item _selectedContent;
- public Item SelectedContent
- {
- get => _selectedContent;
- set => SetAndRaisePropertyChanged(ref _selectedContent, value);
- }
-
public MainWindowViewModel()
{
this.MenuItems = new List
- ();
diff --git a/Shared/Service/ITestService.cs b/Shared/Service/ITestService.cs
new file mode 100644
index 0000000..1d23294
--- /dev/null
+++ b/Shared/Service/ITestService.cs
@@ -0,0 +1,6 @@
+namespace PSCHelpdesk.Shared.Service;
+
+public interface ITestService
+{
+ public void addItem();
+}
\ No newline at end of file
diff --git a/Shared/Service/TestService.cs b/Shared/Service/TestService.cs
new file mode 100644
index 0000000..a69f1b6
--- /dev/null
+++ b/Shared/Service/TestService.cs
@@ -0,0 +1,9 @@
+namespace PSCHelpdesk.Shared.Service;
+
+public class TestService: ITestService
+{
+ public void addItem()
+ {
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj
index 2fddace..e1c2c51 100644
--- a/Shared/Shared.csproj
+++ b/Shared/Shared.csproj
@@ -19,8 +19,4 @@
-
-
-
-
diff --git a/Vendor/Prise.Mvc/ApplicationBuilderExtensions.cs b/Vendor/Prise.Mvc/ApplicationBuilderExtensions.cs
new file mode 100644
index 0000000..87b03d7
--- /dev/null
+++ b/Vendor/Prise.Mvc/ApplicationBuilderExtensions.cs
@@ -0,0 +1,14 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Prise.Mvc
+{
+ public static class ApplicationBuilderExtensions
+ {
+ public static IApplicationBuilder EnsureStaticPluginCache(this IApplicationBuilder app)
+ {
+ app.ApplicationServices.GetRequiredService();
+ return app;
+ }
+ }
+}
diff --git a/Vendor/Prise.Mvc/DefaultMvcPluginLoader.cs b/Vendor/Prise.Mvc/DefaultMvcPluginLoader.cs
new file mode 100644
index 0000000..46d5ff5
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultMvcPluginLoader.cs
@@ -0,0 +1,71 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Prise.Caching;
+using Prise.AssemblyScanning;
+using Prise.AssemblyLoading;
+using System.Linq;
+using Prise.Utils;
+
+namespace Prise.Mvc
+{
+ public class DefaultMvcPluginLoader : IMvcPluginLoader
+ {
+ protected readonly IAssemblyScanner assemblyScanner;
+ protected readonly IPluginTypeSelector pluginTypeSelector;
+ protected readonly IAssemblyLoader assemblyLoader;
+ protected readonly IPluginCache pluginCache;
+ public DefaultMvcPluginLoader(IAssemblyScanner assemblyScanner,
+ IPluginTypeSelector pluginTypeSelector,
+ IAssemblyLoader assemblyLoader,
+ IPluginCache pluginCache)
+ {
+ this.assemblyScanner = assemblyScanner;
+ this.pluginTypeSelector = pluginTypeSelector;
+ this.assemblyLoader = assemblyLoader;
+ this.pluginCache = pluginCache;
+ }
+
+ public async virtual Task> FindPlugins(string pathToPlugins)
+ {
+ return (await this.assemblyScanner.Scan(new AssemblyScannerOptions
+ {
+ StartingPath = pathToPlugins,
+ PluginType = typeof(T)
+ }));
+ }
+
+ public async virtual Task LoadPluginAssembly(AssemblyScanResult plugin, Action configureLoadContext = null)
+ {
+ var pluginLoadContext = ToPluginLoadContext(plugin);
+
+ configureLoadContext?.Invoke(pluginLoadContext);
+
+ pluginLoadContext.AddMvcTypes();
+
+ var pluginAssembly = await this.assemblyLoader.Load(pluginLoadContext);
+
+ this.pluginCache.Add(pluginAssembly, pluginLoadContext.HostServices.Select(s => s.ServiceType));
+
+ return pluginAssembly;
+ }
+
+ public async virtual Task UnloadPluginAssembly(AssemblyScanResult plugin)
+ {
+ var pluginLoadContext = ToPluginLoadContext(plugin);
+ await this.assemblyLoader.Unload(pluginLoadContext);
+
+ var pathToAssembly = Path.Combine(plugin.AssemblyPath, plugin.AssemblyName);
+ this.pluginCache.Remove(pathToAssembly);
+ }
+
+ protected PluginLoadContext ToPluginLoadContext(AssemblyScanResult plugin)
+ {
+ var hostFramework = HostFrameworkUtils.GetHostframeworkFromHost();
+ var pathToAssembly = Path.Combine(plugin.AssemblyPath, plugin.AssemblyName);
+ return PluginLoadContext.DefaultPluginLoadContext(pathToAssembly, typeof(T), hostFramework);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Mvc/DefaultMvcRazorPluginLoader.cs b/Vendor/Prise.Mvc/DefaultMvcRazorPluginLoader.cs
new file mode 100644
index 0000000..6ffc9cc
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultMvcRazorPluginLoader.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Threading.Tasks;
+
+using Prise.Caching;
+using Prise.AssemblyScanning;
+using Prise.AssemblyLoading;
+using System.Linq;
+
+namespace Prise.Mvc
+{
+ public class DefaultMvcRazorPluginLoader : DefaultMvcPluginLoader
+ {
+ public DefaultMvcRazorPluginLoader(IAssemblyScanner assemblyScanner,
+ IPluginTypeSelector pluginTypeSelector,
+ IAssemblyLoader assemblyLoader,
+ IPluginCache pluginCache) : base(assemblyScanner, pluginTypeSelector, assemblyLoader, pluginCache) { }
+
+ public override async Task LoadPluginAssembly(AssemblyScanResult plugin, Action configureLoadContext = null)
+ {
+ var pluginLoadContext = ToPluginLoadContext(plugin);
+
+ configureLoadContext?.Invoke(pluginLoadContext);
+
+ pluginLoadContext
+ .AddMvcRazorTypes();
+
+ var pluginAssembly = await this.assemblyLoader.Load(pluginLoadContext);
+
+ this.pluginCache.Add(pluginAssembly, pluginLoadContext.HostServices.Select(s => s.ServiceType));
+
+ return pluginAssembly;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Mvc/DefaultPriseMvcActionDescriptorChangeProvider.cs b/Vendor/Prise.Mvc/DefaultPriseMvcActionDescriptorChangeProvider.cs
new file mode 100644
index 0000000..56dbb66
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultPriseMvcActionDescriptorChangeProvider.cs
@@ -0,0 +1,25 @@
+using System.Threading;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.Extensions.Primitives;
+
+namespace Prise.Mvc
+{
+ public class DefaultPriseMvcActionDescriptorChangeProvider : IActionDescriptorChangeProvider, IPriseMvcActionDescriptorChangeProvider
+ {
+ public CancellationTokenSource TokenSource { get; private set; }
+
+ public bool HasChanged { get; set; }
+
+ public IChangeToken GetChangeToken()
+ {
+ TokenSource = new CancellationTokenSource();
+ return new CancellationChangeToken(TokenSource.Token);
+ }
+
+ public void TriggerPluginChanged()
+ {
+ HasChanged = true;
+ TokenSource.Cancel();
+ }
+ }
+}
diff --git a/Vendor/Prise.Mvc/DefaultPriseMvcControllerActivator.cs b/Vendor/Prise.Mvc/DefaultPriseMvcControllerActivator.cs
new file mode 100644
index 0000000..e460e1c
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultPriseMvcControllerActivator.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.Extensions.DependencyInjection;
+using Prise.Activation;
+using Prise.Infrastructure;
+using Prise.Plugin;
+using Prise.Proxy;
+using Prise.Caching;
+
+namespace Prise.Mvc
+{
+ public class DefaultPriseMvcControllerActivator : IControllerActivator
+ {
+ public object Create(ControllerContext context)
+ {
+ var cache = context.HttpContext.RequestServices.GetRequiredService();
+ var controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();
+
+ foreach (var cachedPluginAssembly in cache.GetAll())
+ {
+ var pluginAssembly = cachedPluginAssembly.AssemblyShim.Assembly;
+ var pluginControllerType = pluginAssembly.GetTypes().FirstOrDefault(t => t.Name == controllerType.Name);
+ if (pluginControllerType != null)
+ {
+ var activatorContextProvider = context.HttpContext.RequestServices.GetRequiredService();
+ var remotePluginActivator = context.HttpContext.RequestServices.GetRequiredService();
+ var proxyCreator = context.HttpContext.RequestServices.GetRequiredService();
+ var resultConverter = context.HttpContext.RequestServices.GetRequiredService();
+ var parameterConverter = context.HttpContext.RequestServices.GetRequiredService();
+
+ object controller = null;
+ IPluginBootstrapper bootstrapperProxy = null;
+ IServiceCollection hostServices = new ServiceCollection();
+ foreach (var hostServiceType in cachedPluginAssembly.HostTypes)
+ hostServices.Add(new ServiceDescriptor(hostServiceType, context.HttpContext.RequestServices.GetRequiredService(hostServiceType)));
+
+ var pluginActivationContext = activatorContextProvider.ProvideActivationContext(pluginControllerType, cachedPluginAssembly.AssemblyShim);
+
+ if (pluginActivationContext.PluginBootstrapperType != null)
+ {
+ var remoteBootstrapperInstance = remotePluginActivator.CreateRemoteBootstrapper(pluginActivationContext, hostServices);
+
+ var remoteBootstrapperProxy = proxyCreator.CreateBootstrapperProxy(remoteBootstrapperInstance);
+
+ bootstrapperProxy = remoteBootstrapperProxy;
+ }
+
+ controller = remotePluginActivator.CreateRemoteInstance(
+ pluginActivationContext,
+ bootstrapperProxy,
+ hostServices: hostServices
+ );
+
+ var controllerContext = new ControllerContext();
+ controllerContext.HttpContext = context.HttpContext;
+ var controllerContextProperty = controllerType.GetProperty("ControllerContext");
+ controllerContextProperty.SetValue(controller, controllerContext);
+
+ return controller;
+ }
+ }
+
+ // Use MSFT's own activator utilities to create a controller instance
+ // This avoids us to require to register all controllers as services
+ return ActivatorUtilities.CreateInstance(context.HttpContext.RequestServices, controllerType);
+ }
+
+ public void Release(ControllerContext context, object controller)
+ {
+ (controller as IDisposable)?.Dispose();
+ }
+ }
+}
diff --git a/Vendor/Prise.Mvc/DefaultPrisePluginViewsAssemblyFileProvider.cs b/Vendor/Prise.Mvc/DefaultPrisePluginViewsAssemblyFileProvider.cs
new file mode 100644
index 0000000..7d0e989
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultPrisePluginViewsAssemblyFileProvider.cs
@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Primitives;
+using Prise.Caching;
+
+namespace Prise.Mvc
+{
+ public class DefaultPrisePluginViewsAssemblyFileProvider : IFileProvider
+ {
+ protected readonly PhysicalFileProvider webRootFileProvider;
+ protected readonly string pathToPlugins;
+ public DefaultPrisePluginViewsAssemblyFileProvider(string hostingRootPath, string pathToPlugins)
+ {
+ if (!Path.IsPathRooted(pathToPlugins))
+ throw new ArgumentException($"{nameof(pathToPlugins)} must be rooted (absolute path).");
+
+ this.pathToPlugins = pathToPlugins;
+ this.webRootFileProvider = new PhysicalFileProvider(hostingRootPath);
+ }
+
+ private IPluginCache GetLoadedPluginsCache()
+ {
+ return DefaultStaticPluginCacheAccessor.CurrentCache;
+ }
+
+ private IFileProvider GetPluginFileProvider(string subpath)
+ {
+ var cache = GetLoadedPluginsCache();
+ if (cache == null)
+ return null;
+
+ foreach (var loadedPlugin in cache.GetAll())
+ {
+ var pluginAssemblyName = loadedPlugin.AssemblyShim.Assembly.GetName().Name;
+ var pathToPlugin = Path.Combine(pathToPlugins, pluginAssemblyName);
+ var pathCandidate = Path.Combine(pathToPlugin, SanitizeSubPath(subpath));
+ if (File.Exists(pathCandidate))
+ return new PhysicalFileProvider(pathToPlugin);
+ }
+ return null;
+ }
+
+ private string SanitizeSubPath(string subPath)
+ {
+ if (subPath.StartsWith('/'))
+ return subPath.Substring(1);
+ return subPath;
+ }
+
+ public IDirectoryContents GetDirectoryContents(string subpath)
+ {
+ var pluginFileProvider = GetPluginFileProvider(subpath);
+ if (pluginFileProvider != null)
+ return pluginFileProvider.GetDirectoryContents(subpath);
+ return this.webRootFileProvider.GetDirectoryContents(subpath);
+ }
+
+ public IFileInfo GetFileInfo(string subpath)
+ {
+ var pluginFileProvider = GetPluginFileProvider(subpath);
+ if (pluginFileProvider != null)
+ return pluginFileProvider.GetFileInfo(subpath);
+ return this.webRootFileProvider.GetFileInfo(subpath);
+ }
+
+ public IChangeToken Watch(string filter)
+ {
+ return this.webRootFileProvider.Watch(filter);
+ }
+ }
+}
diff --git a/Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessor.cs b/Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessor.cs
new file mode 100644
index 0000000..8900ad0
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessor.cs
@@ -0,0 +1,9 @@
+using Prise.Caching;
+
+namespace Prise.Mvc
+{
+ public static class DefaultStaticPluginCacheAccessor
+ {
+ public static IPluginCache CurrentCache { get; internal set; }
+ }
+}
diff --git a/Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessorBootstrapper.cs b/Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessorBootstrapper.cs
new file mode 100644
index 0000000..99b5fd1
--- /dev/null
+++ b/Vendor/Prise.Mvc/DefaultStaticPluginCacheAccessorBootstrapper.cs
@@ -0,0 +1,23 @@
+using System;
+using Prise.Caching;
+
+namespace Prise.Mvc
+{
+ public class DefaultStaticPluginCacheAccessorBootstrapper : IPluginCacheAccessorBootstrapper
+ {
+ protected bool isBootstrapped;
+ public DefaultStaticPluginCacheAccessorBootstrapper(IPluginCache cache)
+ {
+ if (this.isBootstrapped)
+ throw new NotSupportedException($"IPluginCache was already bootstrapped");
+
+ this.SetCurrentCache(cache);
+ this.isBootstrapped = true;
+ }
+
+ public void SetCurrentCache(IPluginCache cache)
+ {
+ DefaultStaticPluginCacheAccessor.CurrentCache = cache;
+ }
+ }
+}
diff --git a/Vendor/Prise.Mvc/IMvcPluginLoader.cs b/Vendor/Prise.Mvc/IMvcPluginLoader.cs
new file mode 100644
index 0000000..66c06f8
--- /dev/null
+++ b/Vendor/Prise.Mvc/IMvcPluginLoader.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+
+namespace Prise.Mvc
+{
+ public interface IMvcPluginLoader
+ {
+ Task> FindPlugins(string pathToPlugins);
+ Task LoadPluginAssembly(AssemblyScanResult plugin, Action configure = null);
+ Task UnloadPluginAssembly(AssemblyScanResult plugin);
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Mvc/IPluginCacheAccessorBootstrapper.cs b/Vendor/Prise.Mvc/IPluginCacheAccessorBootstrapper.cs
new file mode 100644
index 0000000..1c2f5b6
--- /dev/null
+++ b/Vendor/Prise.Mvc/IPluginCacheAccessorBootstrapper.cs
@@ -0,0 +1,9 @@
+using Prise.Caching;
+
+namespace Prise.Mvc
+{
+ public interface IPluginCacheAccessorBootstrapper
+ {
+ void SetCurrentCache(IPluginCache cache);
+ }
+}
diff --git a/Vendor/Prise.Mvc/IPriseMvcActionDescriptorChangeProvider.cs b/Vendor/Prise.Mvc/IPriseMvcActionDescriptorChangeProvider.cs
new file mode 100644
index 0000000..2d03770
--- /dev/null
+++ b/Vendor/Prise.Mvc/IPriseMvcActionDescriptorChangeProvider.cs
@@ -0,0 +1,7 @@
+namespace Prise.Mvc
+{
+ public interface IPriseMvcActionDescriptorChangeProvider
+ {
+ void TriggerPluginChanged();
+ }
+}
diff --git a/Vendor/Prise.Mvc/PluginAssemblyPart.cs b/Vendor/Prise.Mvc/PluginAssemblyPart.cs
new file mode 100644
index 0000000..f92ff61
--- /dev/null
+++ b/Vendor/Prise.Mvc/PluginAssemblyPart.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Microsoft.AspNetCore.Mvc.ApplicationParts;
+
+namespace Prise.Mvc
+{
+ public class PluginAssemblyPart : AssemblyPart, ICompilationReferencesProvider
+ {
+ public PluginAssemblyPart(Assembly assembly) : base(assembly) { }
+
+ // This solves the NullRef bug for in-memory assemblies from Prise
+ IEnumerable ICompilationReferencesProvider.GetReferencePaths() => Array.Empty();
+ }
+}
diff --git a/Vendor/Prise.Mvc/Prise.Mvc.csproj b/Vendor/Prise.Mvc/Prise.Mvc.csproj
new file mode 100644
index 0000000..7f1c0cd
--- /dev/null
+++ b/Vendor/Prise.Mvc/Prise.Mvc.csproj
@@ -0,0 +1,98 @@
+
+
+
+ netcoreapp2.1;netcoreapp3.1;net5.0;net6.0
+ Prise.Mvc
+ Prise.Mvc
+ Adds ASP.NET MVC features to an existing Prise setup!
+ Maarten Merken
+ MRKN
+ mvc;controllers;plugin
+ https://raw.githubusercontent.com/merken/Prise/master/LICENSE
+ https://github.com/merken/Prise
+ https://github.com/merken/Prise.git
+ git
+ $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
+
+
+
+
+
+
+
+
+
+ NETCORE2_1
+
+
+
+
+
+ NETCORE3_1
+
+
+
+ icon.png
+ true
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ Prise.Plugin.dll
+
+
+ true
+ Prise.Proxy.dll
+
+
+ true
+ Prise.dll
+
+
+
+
diff --git a/Vendor/Prise.Mvc/PriseMvcExtensions.cs b/Vendor/Prise.Mvc/PriseMvcExtensions.cs
new file mode 100644
index 0000000..81ffbcd
--- /dev/null
+++ b/Vendor/Prise.Mvc/PriseMvcExtensions.cs
@@ -0,0 +1,118 @@
+using System;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Mvc.Razor;
+#if !NETCORE2_1
+using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
+#endif
+using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Prise.DependencyInjection;
+
+using Prise.Activation;
+using Prise.AssemblyLoading;
+using Prise.AssemblyScanning;
+using Prise.Proxy;
+using Prise.Caching;
+
+namespace Prise.Mvc
+{
+ public static class PriseMvcExtensions
+ {
+ internal static PluginLoadContext AddMvcTypes(this PluginLoadContext loadContext)
+ {
+ return loadContext.AddHostTypes(new[] { typeof(ControllerBase) });
+ }
+
+ internal static PluginLoadContext AddMvcRazorTypes(this PluginLoadContext loadContext)
+ {
+ return loadContext.AddHostTypes(new[] { typeof(ControllerBase), typeof(ITempDataDictionaryFactory) });
+ }
+
+ public static IServiceCollection AddCorePriseServices(this IServiceCollection services)
+ {
+ return services
+ .AddFactory(DefaultFactories.DefaultAssemblyScanner, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultPluginTypeSelector, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultParameterConverter, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultResultConverter, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultPluginActivationContextProvider, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultRemotePluginActivator, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultPluginProxyCreator, ServiceLifetime.Scoped)
+ .AddFactory(DefaultFactories.DefaultAssemblyLoader, ServiceLifetime.Singleton);
+ }
+
+ ///
+ /// Does all of the plumbing to load API Controllers as Prise Plugins.
+ /// Limitiations:
+ /// - No DispatchProxy can be used, backwards compatability is compromised (DispatchProxy requires an interface as base class, not ControllerBase)
+ /// - Plugin Cache is set to Singleton because we cannot unload assemblies, this would disable the controller routing (and result in 404)
+ /// - Assembly unloading is disabled, memory leaks can occur
+ ///
+ /// A fully configured Prise setup that allows you to load plugins via the IMvcPluginLoader
+ public static IServiceCollection AddPriseMvc(this IServiceCollection services)
+ {
+
+ return services
+ .AddCorePriseServices()
+ .AddSingleton()
+ .AddScoped()
+ .ConfigureMvcServices()
+ ;
+ }
+
+ ///
+ /// Does all of the plumbing to load API Controllers and RAZOR Controllers as Prise Plugins.
+ /// Limitiations:
+ /// - No DispatchProxy can be used, backwards compatability is compromised (DispatchProxy requires an interface as base class, not ControllerBase)
+ /// - Plugin Cache is set to Singleton because we cannot unload assemblies, this would disable the controller routing (and result in 404)
+ /// - Assembly unloading is disabled, memory leaks can occur
+ ///
+ ///
+ ///
+ /// By default, this should be the IWebHostEnvironment.WebRootPaht or IHostingEnvironment.WebRootPath
+ ///
+ public static IServiceCollection AddPriseRazorPlugins(this IServiceCollection services, string webRootPath, string pathToPlugins)
+ {
+ return services
+ .AddCorePriseServices()
+ .AddSingleton()
+ .AddScoped()
+ .ConfigureMvcServices()
+ .ConfigureRazorServices(webRootPath, pathToPlugins)
+ ;
+ }
+
+ private static IServiceCollection ConfigureMvcServices(this IServiceCollection services)
+ {
+ var actionDescriptorChangeProvider = new DefaultPriseMvcActionDescriptorChangeProvider();
+ // Registers the change provider
+ return services
+ .AddSingleton(actionDescriptorChangeProvider)
+ .AddSingleton(actionDescriptorChangeProvider)
+ // Registers the activator for controllers from plugin assemblies
+ .Replace(ServiceDescriptor.Transient());
+ }
+
+ private static IServiceCollection ConfigureRazorServices(this IServiceCollection services, string webRootPath, string pathToPlugins)
+ {
+ return services
+#if NETCORE2_1
+ .Configure(options =>
+ {
+ options.FileProviders.Add(new DefaultPrisePluginViewsAssemblyFileProvider(webRootPath, pathToPlugins));
+ })
+#else
+ .Configure(options =>
+ {
+ options.FileProviders.Add(new DefaultPrisePluginViewsAssemblyFileProvider(webRootPath, pathToPlugins));
+ })
+#endif
+ // Registers the static Plugin Cache Accessor
+ .AddSingleton();
+ }
+
+ }
+}
diff --git a/Vendor/Prise.Plugin/BootstrapperServiceAttribute.cs b/Vendor/Prise.Plugin/BootstrapperServiceAttribute.cs
new file mode 100644
index 0000000..023b96e
--- /dev/null
+++ b/Vendor/Prise.Plugin/BootstrapperServiceAttribute.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Prise.Plugin
+{
+ [System.AttributeUsage(System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
+ public sealed class BootstrapperServiceAttribute : System.Attribute
+ {
+ Type serviceType;
+ public Type ServiceType
+ {
+ get { return this.serviceType; }
+ set { this.serviceType = value; }
+ }
+
+ Type bridgeType;
+ [Obsolete("Usage of a BridgeType is obsolete, please use ProxyType instead. Existing plugins will continue to function as normal.", false)]
+ public Type BridgeType
+ {
+ get { return this.bridgeType; }
+ set { this.bridgeType = value; }
+ }
+
+ Type proxyType;
+ public Type ProxyType
+ {
+ get { return this.proxyType; }
+ set { this.proxyType = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Plugin/IPluginBootstrapper.cs b/Vendor/Prise.Plugin/IPluginBootstrapper.cs
new file mode 100644
index 0000000..54dfd23
--- /dev/null
+++ b/Vendor/Prise.Plugin/IPluginBootstrapper.cs
@@ -0,0 +1,9 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Prise.Plugin
+{
+ public interface IPluginBootstrapper
+ {
+ IServiceCollection Bootstrap(IServiceCollection services);
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Plugin/PluginActivatedAttribute.cs b/Vendor/Prise.Plugin/PluginActivatedAttribute.cs
new file mode 100644
index 0000000..ecc9ede
--- /dev/null
+++ b/Vendor/Prise.Plugin/PluginActivatedAttribute.cs
@@ -0,0 +1,5 @@
+namespace Prise.Plugin
+{
+ [System.AttributeUsage(System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ public sealed class PluginActivatedAttribute : System.Attribute { }
+}
diff --git a/Vendor/Prise.Plugin/PluginAttribute.cs b/Vendor/Prise.Plugin/PluginAttribute.cs
new file mode 100644
index 0000000..21ce1a9
--- /dev/null
+++ b/Vendor/Prise.Plugin/PluginAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Prise.Plugin
+{
+ [System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class PluginAttribute : System.Attribute
+ {
+ Type pluginType;
+ public Type PluginType
+ {
+ get { return this.pluginType; }
+ set { this.pluginType = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Plugin/PluginBootstrapperAttribute.cs b/Vendor/Prise.Plugin/PluginBootstrapperAttribute.cs
new file mode 100644
index 0000000..1647e0b
--- /dev/null
+++ b/Vendor/Prise.Plugin/PluginBootstrapperAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Prise.Plugin
+{
+ [System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
+ public sealed class PluginBootstrapperAttribute : System.Attribute
+ {
+ Type pluginType;
+ public Type PluginType
+ {
+ get { return this.pluginType; }
+ set { this.pluginType = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Plugin/PluginFactoryAttribute.cs b/Vendor/Prise.Plugin/PluginFactoryAttribute.cs
new file mode 100644
index 0000000..b3726c3
--- /dev/null
+++ b/Vendor/Prise.Plugin/PluginFactoryAttribute.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Prise.Plugin
+{
+ [Obsolete("Usage of a PluginFactory is obsolete, please use field injection instead. Existing plugins will continue to function as normal.", false)]
+ [System.AttributeUsage(System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ public sealed class PluginFactoryAttribute : System.Attribute { }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Plugin/PluginServiceAttribute.cs b/Vendor/Prise.Plugin/PluginServiceAttribute.cs
new file mode 100644
index 0000000..d8e88d2
--- /dev/null
+++ b/Vendor/Prise.Plugin/PluginServiceAttribute.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Prise.Plugin
+{
+ [System.AttributeUsage(System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
+ public sealed class PluginServiceAttribute : System.Attribute
+ {
+ Type serviceType;
+ public Type ServiceType
+ {
+ get { return this.serviceType; }
+ set { this.serviceType = value; }
+ }
+
+ ProvidedBy providedBy;
+ public ProvidedBy ProvidedBy
+ {
+ get { return this.providedBy; }
+ set { this.providedBy = value; }
+ }
+
+ Type bridgeType;
+ [Obsolete("Usage of a BridgeType is obsolete, please use ProxyType instead. Existing plugins will continue to function as normal.", false)]
+ public Type BridgeType
+ {
+ get { return this.bridgeType; }
+ set { this.bridgeType = value; }
+ }
+
+ Type proxyType;
+ public Type ProxyType
+ {
+ get { return this.proxyType; }
+ set { this.proxyType = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Plugin/Prise.Plugin.csproj b/Vendor/Prise.Plugin/Prise.Plugin.csproj
new file mode 100644
index 0000000..20503f8
--- /dev/null
+++ b/Vendor/Prise.Plugin/Prise.Plugin.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net9.0
+ Prise.Plugin
+ Prise.Plugin
+ Prise, A .NET Plugin Framework!
+ Maarten Merken
+ MRKN
+ plugin;framework;prise;decoupling;assembly;dispatchproxy;proxy
+ https://raw.githubusercontent.com/merken/Prise/master/LICENSE
+ https://github.com/merken/Prise
+ https://github.com/merken/Prise.git
+ git
+ default
+
+
+
+ icon.png
+
+
+
+
+ True
+
+
+
+
+
+
+
+
diff --git a/Vendor/Prise.Plugin/ProvidedBy.cs b/Vendor/Prise.Plugin/ProvidedBy.cs
new file mode 100644
index 0000000..78bd929
--- /dev/null
+++ b/Vendor/Prise.Plugin/ProvidedBy.cs
@@ -0,0 +1,8 @@
+namespace Prise.Plugin
+{
+ public enum ProvidedBy
+ {
+ Plugin = 0,
+ Host
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Proxy/Infrastructure/IParameterConverter.cs b/Vendor/Prise.Proxy/Infrastructure/IParameterConverter.cs
new file mode 100644
index 0000000..d149656
--- /dev/null
+++ b/Vendor/Prise.Proxy/Infrastructure/IParameterConverter.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Prise.Proxy
+{
+ public interface IParameterConverter : IDisposable
+ {
+ object ConvertToRemoteType(Type localType, object value);
+ }
+}
diff --git a/Vendor/Prise.Proxy/Infrastructure/IResultConverter.cs b/Vendor/Prise.Proxy/Infrastructure/IResultConverter.cs
new file mode 100644
index 0000000..cd2c758
--- /dev/null
+++ b/Vendor/Prise.Proxy/Infrastructure/IResultConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Prise.Proxy
+{
+ public interface IResultConverter : IDisposable
+ {
+ ///
+ /// This method should convert a remote value into a local one.
+ /// This method will be called in case the remote type is not a Task.
+ ///
+ /// The return type of the host
+ /// The return type of the remote
+ /// The value from the remote, this can be different from the remoteType if the remote uses an old contract, in this case, convert to correct type.
+ /// A converted local instance
+ object ConvertToLocalType(Type localType, Type remoteType, object value);
+
+ ///
+ /// This method should convert a remote value into a local one.
+ /// This method will be called in case the remote type is a Task.
+ ///
+ /// The return type of the host, Task
+ /// The return type of the remote, Task
+ /// The Task that holds the value from the remote, this can be different from the remoteType if the remote uses an old contract, in this case, convert to correct type.
+ /// A Task containing the local type with the remote value
+ object ConvertToLocalTypeAsync(Type localType, Type remoteType, Task task);
+ }
+}
diff --git a/Vendor/Prise.Proxy/Method.cs b/Vendor/Prise.Proxy/Method.cs
new file mode 100644
index 0000000..b2b8b8b
--- /dev/null
+++ b/Vendor/Prise.Proxy/Method.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Prise.Proxy
+{
+ public struct Method
+ {
+ public string Name { get; }
+ public Type? ReturnType { get; }
+
+ public Method(string name, Type? returnType = null)
+ {
+ this.Name = name;
+ this.ReturnType = returnType;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Proxy/MethodFindingStrategy.cs b/Vendor/Prise.Proxy/MethodFindingStrategy.cs
new file mode 100644
index 0000000..62fcc06
--- /dev/null
+++ b/Vendor/Prise.Proxy/MethodFindingStrategy.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Prise.Proxy
+{
+ [Flags]
+ public enum MethodFindingStrategy
+ {
+ MethodNameMustMatch,
+ MethodReturnTypeMustMatch,
+ ParameterCountMustMatch,
+ ParameterTypeMustMatch
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Proxy/Parameter.cs b/Vendor/Prise.Proxy/Parameter.cs
new file mode 100644
index 0000000..d6a1d51
--- /dev/null
+++ b/Vendor/Prise.Proxy/Parameter.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Prise.Proxy
+{
+ public struct Parameter
+ {
+ public string Name { get; }
+ public Type? Type { get; }
+
+ public Parameter(string name, Type? type = null)
+ {
+ this.Name = name;
+ this.Type = type;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Proxy/PassthroughParameterConverter.cs b/Vendor/Prise.Proxy/PassthroughParameterConverter.cs
new file mode 100644
index 0000000..1a02b0b
--- /dev/null
+++ b/Vendor/Prise.Proxy/PassthroughParameterConverter.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Prise.Proxy
+{
+ public class PassthroughParameterConverter : IParameterConverter
+ {
+ protected bool disposed = false;
+
+ public object ConvertToRemoteType(Type localType, object value)
+ {
+ return value;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this.disposed && disposing)
+ {
+ // Nothing to do here
+ }
+ this.disposed = true;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Proxy/PassthroughResultConverter.cs b/Vendor/Prise.Proxy/PassthroughResultConverter.cs
new file mode 100644
index 0000000..d998ae1
--- /dev/null
+++ b/Vendor/Prise.Proxy/PassthroughResultConverter.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Prise.Proxy
+{
+ public class PassthroughResultConverter : ResultConverter
+ {
+ public override object Deserialize(Type localType, Type remoteType, object value)
+ {
+ return value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Vendor/Prise.Proxy/Prise.Proxy.csproj b/Vendor/Prise.Proxy/Prise.Proxy.csproj
new file mode 100644
index 0000000..74983f6
--- /dev/null
+++ b/Vendor/Prise.Proxy/Prise.Proxy.csproj
@@ -0,0 +1,42 @@
+
+
+
+ net9.0
+ Prise.Proxy
+ Prise.Proxy
+ Prise, A .NET Plugin Framework!
+ Maarten Merken
+ MRKN
+ plugin;framework;prise;decoupling;assembly;dispatchproxy;proxy
+ $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
+ enable
+ default
+ https://raw.githubusercontent.com/merken/Prise/master/LICENSE
+ https://github.com/merken/Prise
+ https://github.com/merken/Prise.git
+ git
+
+
+
+
+
+
+
+
+
+
+
+
+
+ icon.png
+ true
+
+
+
+
+ True
+
+
+
+
+
diff --git a/Vendor/Prise.Proxy/PriseProxy.cs b/Vendor/Prise.Proxy/PriseProxy.cs
new file mode 100644
index 0000000..c90fd83
--- /dev/null
+++ b/Vendor/Prise.Proxy/PriseProxy.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Prise.Proxy
+{
+ ///
+ /// This is the PriseProxy static class that encapsulates most of the boilerplate static methods in order to interact with the remote object (Plugin)
+ /// TODO
+ /// - Generic Methods (Task Do(T stuff))
+ /// - Events (not sure if this is ever possible)
+ ///
+ public static class PriseProxy
+ {
+ public static object Invoke(object remoteObject, MethodInfo targetMethod, object[] args)
+ => Invoke(remoteObject, targetMethod, args, new PassthroughParameterConverter(),
+ new PassthroughResultConverter());
+
+ public static object Invoke(object remoteObject, MethodInfo targetMethod, object[] args,
+ IParameterConverter parameterConverter, IResultConverter resultConverter)
+ {
+ try
+ {
+ var localType = targetMethod.ReturnType;
+ var remoteMethod = FindMethodOnObject(targetMethod, remoteObject);
+ if (remoteMethod == null)
+ throw new PriseProxyException(
+ $"Target method {targetMethod.Name} is not found on Proxy Type {remoteObject.GetType().Name}.");
+
+ object result = null;
+ if (remoteMethod.IsGenericMethod)
+ {
+ var generic = remoteMethod.MakeGenericMethod(targetMethod.GetGenericArguments());
+ result = generic.Invoke(remoteObject, SerializeParameters(parameterConverter, remoteMethod, args));
+ }
+ else
+ result = remoteMethod.Invoke(remoteObject,
+ SerializeParameters(parameterConverter, remoteMethod, args));
+
+ var remoteType = remoteMethod.ReturnType;
+ if (remoteType.BaseType == typeof(System.Threading.Tasks.Task))
+ return resultConverter.ConvertToLocalTypeAsync(localType, remoteType,
+ result as System.Threading.Tasks.Task);
+
+ if (remoteType == typeof(System.Threading.Tasks.Task))
+ return resultConverter.ConvertToLocalTypeAsync(localType, remoteType,
+ result as System.Threading.Tasks.Task);
+
+
+ if (remoteType == typeof(void))
+ return null;
+
+ return resultConverter.ConvertToLocalType(localType, remoteType, result);
+ }
+ catch (Exception ex) when (ex is TargetInvocationException)
+ {
+ throw ex.InnerException ?? ex;
+ }
+ }
+
+ public static MethodInfo FindMethodOnObject(MethodInfo callingMethod, object targetObject)
+ => FindMethodOnObject(
+ targetObject,
+ new Method(callingMethod.Name, callingMethod.ReturnType),
+ callingMethod.GetParameters().Select(p => new Parameter(p.Name, p.ParameterType)).ToArray(),
+ throwOnError: true)!; // Throws error when null
+
+ public static MethodInfo? TryFindMethodOnObject(MethodInfo callingMethod, object targetObject)
+ => FindMethodOnObject(
+ targetObject,
+ new Method(callingMethod.Name, callingMethod.ReturnType),
+ callingMethod.GetParameters().Select(p => new Parameter(p.Name, p.ParameterType)).ToArray());
+
+ public static MethodInfo? FindMethodOnObject(
+ object targetObject,
+ Method method,
+ Parameter[] parameters,
+ MethodFindingStrategy strategy = MethodFindingStrategy.MethodNameMustMatch |
+ MethodFindingStrategy.MethodReturnTypeMustMatch |
+ MethodFindingStrategy.ParameterCountMustMatch |
+ MethodFindingStrategy.ParameterTypeMustMatch,
+ bool throwOnError = false)
+ {
+ bool isNameCorrect(MethodInfo targetMethod) => targetMethod.Name == method.Name;
+
+ // First, find by method name
+ var targetMethods = targetObject.GetType().GetMethods().AsEnumerable();
+ if (strategy.HasFlag(MethodFindingStrategy.MethodNameMustMatch))
+ targetMethods = targetMethods.Where(isNameCorrect);
+
+ if (!targetMethods.Any())
+ if (throwOnError)
+ throw new PriseProxyException(
+ $"Target method {method.Name} is not found on Proxy Type {targetObject.GetType().Name}.");
+ else
+ return null;
+
+ if (targetMethods.Count() == 1)
+ return targetMethods.First();
+
+ bool isReturnTypeCorrect(MethodInfo targetMethod) => targetMethod.ReturnType == method.ReturnType;
+
+ if (strategy.HasFlag(MethodFindingStrategy.MethodReturnTypeMustMatch))
+ // Second, find by method name AND return type
+ targetMethods = targetMethods.Where(isReturnTypeCorrect);
+
+ if (targetMethods.Count() == 1)
+ return targetMethods.First();
+
+ bool isParameterCountCorrect(MethodInfo targetMethod) =>
+ targetMethod.GetParameters().Count() == parameters.Length;
+
+ bool doAllParametersMatch(MethodInfo targetMethod)
+ {
+ var callingMethodParameters = parameters;
+ var targetMethodParameters = targetMethod.GetParameters();
+ for (var index = 0; index < callingMethodParameters.Count(); index++)
+ {
+ var callingParam = callingMethodParameters[index];
+ if (callingParam.Type is null)
+ throw new PriseProxyException(
+ $"When using {nameof(MethodFindingStrategy.ParameterTypeMustMatch)}, Parameter.Type must be provided");
+ var targetParam = targetMethodParameters[index];
+ if (!(targetParam.Name == callingParam.Name &&
+ targetParam.ParameterType.Name == callingParam.Type.Name))
+ return false;
+ }
+
+ return true;
+ }
+
+ targetMethods = targetMethods.Where(targetMethod =>
+ (!strategy.HasFlag(MethodFindingStrategy.ParameterCountMustMatch) ||
+ isParameterCountCorrect(targetMethod)) &&
+ (!strategy.HasFlag(MethodFindingStrategy.ParameterTypeMustMatch) || doAllParametersMatch(targetMethod))
+ );
+
+ if (!targetMethods.Any())
+ if (throwOnError)
+ throw new PriseProxyException(
+ $"Target method {method.Name} is not found on Proxy Type {targetObject.GetType().Name}.");
+ else
+ return null;
+
+ if (targetMethods.Count() > 1)
+ if (throwOnError)
+ throw new PriseProxyException(
+ $"Target method {method.Name} could not be determined on object {targetObject.GetType().Name}.");
+ else
+ return null;
+
+ return targetMethods.First();
+ }
+
+ internal static object[] SerializeParameters(IParameterConverter parameterConverter, MethodInfo targetMethod,
+ object[] args)
+ {
+ var parameters = targetMethod.GetParameters();
+ var results = new List