From aa97dd4d0dd879de51e8f4cc8a2767cd8127b774 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 14 May 2020 11:54:28 -0400 Subject: [PATCH] Allow modules to be installed/uninstalled with embedded scripts or IInstallable interface. Fix module uninstall issues. --- .../Templates/External/Client/Edit.razor | 2 +- .../Templates/External/Client/Index.razor | 2 +- .../Templates/External/Client/ModuleInfo.cs | 2 +- .../Templates/External/Client/Settings.razor | 2 +- .../Templates/External/Client/_Imports.razor | 4 +- ....1.0.0.sql => [Owner].[Module]s.1.0.0.sql} | 0 ...ll.sql => [Owner].[Module]s.Uninstall.sql} | 0 .../Oqtane.Client/Modules/[Module]/Edit.razor | 2 +- .../Modules/[Module]/Index.razor | 2 +- .../Modules/[Module]/ModuleInfo.cs | 2 +- .../Modules/[Module]/Settings.razor | 2 +- ....1.0.0.sql => [Owner].[Module]s.1.0.0.sql} | 0 ...ll.sql => [Owner].[Module]s.Uninstall.sql} | 0 .../Controllers/ModuleDefinitionController.cs | 59 ++++++++++--------- Oqtane.Server/Controllers/ThemeController.cs | 16 +++-- .../Infrastructure/DatabaseManager.cs | 20 ++++--- 16 files changed, 63 insertions(+), 52 deletions(-) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/{[Owner].[Module].1.0.0.sql => [Owner].[Module]s.1.0.0.sql} (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/{[Owner].[Module].Uninstall.sql => [Owner].[Module]s.Uninstall.sql} (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/{[Owner].[Module].1.0.0.sql => [Owner].[Module]s.1.0.0.sql} (100%) rename Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/{[Owner].[Module].Uninstall.sql => [Owner].[Module]s.Uninstall.sql} (100%) diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor index 4faa3dd5..525590f5 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Edit.razor @@ -2,7 +2,7 @@ @using [Owner].[Module]s.Services @using [Owner].[Module]s.Models -@namespace [Owner].[Module]s.Modules +@namespace [Owner].[Module]s @inherits ModuleBase @inject I[Module]Service [Module]Service @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor index fceedfea..df16458b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Index.razor @@ -1,7 +1,7 @@ @using [Owner].[Module]s.Services @using [Owner].[Module]s.Models -@namespace [Owner].[Module]s.Modules +@namespace [Owner].[Module]s @inherits ModuleBase @inject I[Module]Service [Module]Service @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs index 6a1f21b1..17af8d41 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/ModuleInfo.cs @@ -1,7 +1,7 @@ using Oqtane.Models; using Oqtane.Modules; -namespace [Owner].[Module]s.Modules +namespace [Owner].[Module]s { public class ModuleInfo : IModule { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor index a53dc57c..fac25c7e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/Settings.razor @@ -1,4 +1,4 @@ -@namespace [Owner].[Module]s.Modules +@namespace [Owner].[Module]s @inherits ModuleBase @inject ISettingService SettingService diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor index 147a5eee..b6c565fc 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Client/_Imports.razor @@ -15,4 +15,6 @@ @using Oqtane.Services @using Oqtane.Shared @using Oqtane.Themes -@using Oqtane.Themes.Controls \ No newline at end of file +@using Oqtane.Themes.Controls +@using Oqtane.UI +@using Oqtane.Enums \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].1.0.0.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module]s.1.0.0.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].1.0.0.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module]s.1.0.0.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].Uninstall.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module]s.Uninstall.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module].Uninstall.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Server/Scripts/[Owner].[Module]s.Uninstall.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor index 4faa3dd5..525590f5 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Edit.razor @@ -2,7 +2,7 @@ @using [Owner].[Module]s.Services @using [Owner].[Module]s.Models -@namespace [Owner].[Module]s.Modules +@namespace [Owner].[Module]s @inherits ModuleBase @inject I[Module]Service [Module]Service @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor index c2c129de..0656cb80 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Index.razor @@ -1,7 +1,7 @@ @using [Owner].[Module]s.Services @using [Owner].[Module]s.Models -@namespace [Owner].[Module]s.Modules +@namespace [Owner].[Module]s @inherits ModuleBase @inject I[Module]Service [Module]Service @inject NavigationManager NavigationManager diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs index 6a1f21b1..17af8d41 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/ModuleInfo.cs @@ -1,7 +1,7 @@ using Oqtane.Models; using Oqtane.Modules; -namespace [Owner].[Module]s.Modules +namespace [Owner].[Module]s { public class ModuleInfo : IModule { diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor index a53dc57c..fac25c7e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Client/Modules/[Module]/Settings.razor @@ -1,4 +1,4 @@ -@namespace [Owner].[Module]s.Modules +@namespace [Owner].[Module]s @inherits ModuleBase @inject ISettingService SettingService diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].1.0.0.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.1.0.0.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].1.0.0.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.1.0.0.sql diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].Uninstall.sql b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.Uninstall.sql similarity index 100% rename from Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module].Uninstall.sql rename to Oqtane.Client/Modules/Admin/ModuleCreator/Templates/Internal/Oqtane.Server/Modules/[Module]/Scripts/[Owner].[Module]s.Uninstall.sql diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index fcd9dcd8..139ec366 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -23,17 +23,19 @@ namespace Oqtane.Controllers private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleRepository _modules; private readonly ITenantRepository _tenants; + private readonly ISqlRepository _sql; private readonly IUserPermissions _userPermissions; private readonly IInstallationManager _installationManager; private readonly IWebHostEnvironment _environment; private readonly IServiceProvider _serviceProvider; private readonly ILogManager _logger; - public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger) + public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger) { _moduleDefinitions = moduleDefinitions; _modules = modules; _tenants = tenants; + _sql = sql; _userPermissions = userPermissions; _installationManager = installationManager; _environment = environment; @@ -99,57 +101,60 @@ namespace Oqtane.Controllers public void Delete(int id, int siteid) { ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid); - if (moduledefinition != null) + if (moduledefinition != null ) { - if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType)) + if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server") { Type moduletype = Type.GetType(moduledefinition.ServerManagerType); - if (moduletype != null && moduletype.GetInterface("IInstallable") != null) + + foreach (Tenant tenant in _tenants.GetTenants()) { - foreach (Tenant tenant in _tenants.GetTenants()) + try { - try + if (moduletype.GetInterface("IInstallable") != null) { var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); ((IInstallable)moduleobject).Uninstall(tenant); } - catch + else { - // an error occurred executing the uninstall + _sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + ".Uninstall.sql"); } + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "{ModuleDefinitionName} Uninstalled For Tenant {Tenant}", moduledefinition.ModuleDefinitionName, tenant.Name); + } + catch (Exception ex) + { + _logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} For Tenant {Tenant} {Error}", moduledefinition.ModuleDefinitionName, tenant.Name, ex.Message); } } - } - - // format root assembly name - string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName); - if (assemblyname != "Oqtane.Client") - { - assemblyname = assemblyname.Replace(".Client", ""); - + // clean up module static resource folder - string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules",assemblyname)); + string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName))); if (Directory.Exists(folder)) { Directory.Delete(folder, true); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {AssemblynName}", assemblyname); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); } - // remove module assembly from /bin + // get root assembly name ( note that this only works if modules follow a specific naming convention for their assemblies ) + string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName).ToLower(); + assemblyname = assemblyname.Replace(".client", "").Replace(".oqtane", ""); + + // remove module assemblies from /bin string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*")) { System.IO.File.Delete(file); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly {Filename} Removed For {ModuleDefinitionName}", file, moduledefinition.ModuleDefinitionName); } + + // remove module definition + _moduleDefinitions.DeleteModuleDefinition(id, siteid); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name); + + // restart application + _installationManager.RestartApplication(); } - - // remove module definition - _moduleDefinitions.DeleteModuleDefinition(id, siteid); - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition Deleted {ModuleDefinitionName}", moduledefinition.Name); - - // restart application - _installationManager.RestartApplication(); } } diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index fa182dfa..d2b5d542 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -54,22 +54,20 @@ namespace Oqtane.Controllers { List themes = _themes.GetThemes().ToList(); Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault(); - if (theme != null) + if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client") { - themename = theme.ThemeName.Substring(0, theme.ThemeName.IndexOf(",")); - - string folder = Path.Combine(_environment.WebRootPath, "Themes" , themename); + // clean up theme static resource folder + string folder = Path.Combine(_environment.WebRootPath, "Themes" , Utilities.GetTypeName(theme.ThemeName)); if (Directory.Exists(folder)) { Directory.Delete(folder, true); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Static Resources Removed For {ThemeName}", theme.ThemeName); } + // remove theme assembly from /bin string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - foreach (string file in Directory.EnumerateFiles(binfolder, themename + "*.dll")) - { - System.IO.File.Delete(file); - } - _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Deleted {ThemeName}", themename); + System.IO.File.Delete(Path.Combine(binfolder, Utilities.GetAssemblyName(theme.ThemeName) + ".dll")); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assembly {Filename} Removed For {ThemeName}", Utilities.GetAssemblyName(theme.ThemeName) + ".dll", themename); _installationManager.RestartApplication(); } diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 57551bbb..6c3d50e1 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -332,10 +332,13 @@ namespace Oqtane.Infrastructure using (var scope = _serviceScopeFactory.CreateScope()) { var moduledefinitions = scope.ServiceProvider.GetRequiredService(); + var sql = scope.ServiceProvider.GetRequiredService(); foreach (var moduledefinition in moduledefinitions.GetModuleDefinitions()) { - if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && !string.IsNullOrEmpty(moduledefinition.ReleaseVersions)) + if (!string.IsNullOrEmpty(moduledefinition.ReleaseVersions) && !string.IsNullOrEmpty(moduledefinition.ServerManagerType)) { + Type moduletype = Type.GetType(moduledefinition.ServerManagerType); + string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) { @@ -351,19 +354,22 @@ namespace Oqtane.Infrastructure if (index == -1) index = 0; for (int i = index; i < versions.Length; i++) { - Type moduletype = Type.GetType(moduledefinition.ServerManagerType); - if (moduletype != null && moduletype.GetInterface("IInstallable") != null) + try { - try + if (moduletype.GetInterface("IInstallable") != null) { var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype); - ((IInstallable)moduleobject).Install(tenant, versions[i]); + ((IInstallable)moduleobject).Install(tenant, versions[i]); } - catch (Exception ex) + else { - result.Message = "An Error Occurred Installing " + moduledefinition.Name + " - " + ex.Message.ToString(); + sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "." + versions[i] + ".sql"); } } + catch (Exception ex) + { + result.Message = "An Error Occurred Installing " + moduledefinition.Name + " Version " + versions[i] + " - " + ex.Message.ToString(); + } } } }