Merge pull request #364 from sbwalker/master

added uninstall support for modules
This commit is contained in:
Shaun Walker 2020-04-12 20:08:11 -04:00 committed by GitHub
commit 7f9b1e596f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 100 deletions

View File

@ -1,5 +1,5 @@
/*
Create [Module] table
Create [Owner][Module] table
*/
CREATE TABLE [dbo].[[Owner][Module]](

View File

@ -0,0 +1,6 @@
/*
Remove [Owner][Module] table
*/
DROP TABLE [dbo].[[Owner][Module]]
GO

View File

@ -1,5 +1,5 @@
/*
Create [Module] table
Create [Owner][Module] table
*/
CREATE TABLE [dbo].[[Owner][Module]](

View File

@ -1,26 +0,0 @@
/*
Create [Module] table
*/
CREATE TABLE [dbo].[[Module]](
[[Module]Id] [int] IDENTITY(1,1) NOT NULL,
[ModuleId] [int] NOT NULL,
[Name] [nvarchar](256) NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
CONSTRAINT [PK_[Module]] PRIMARY KEY CLUSTERED
(
[[Module]Id] ASC
)
)
GO
/*
Create foreign key relationships
*/
ALTER TABLE [dbo].[[Module]] WITH CHECK ADD CONSTRAINT [FK_[Module]_Module] FOREIGN KEY([ModuleId])
REFERENCES [dbo].Module ([ModuleId])
ON DELETE CASCADE
GO

View File

@ -0,0 +1,6 @@
/*
Remove [Owner][Module] table
*/
DROP TABLE [dbo].[[Owner][Module]]
GO

View File

@ -10,7 +10,7 @@
@inject IPageModuleService PageModuleService
@inject ILogService logger
@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
{
<div class="app-controlpanel" style="@_display">
@ -251,6 +251,20 @@
{
_pages?.Clear();
foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions))
{
_pages.Add(p);
}
}
var panes = PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
_pane = panes.Count() == 1 ? panes.SingleOrDefault() : "";
var themes = await ThemeService.GetThemesAsync();
_containers = ThemeService.GetContainerTypes(themes);
_containerType = PageState.Site.DefaultContainerType;
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
foreach (ModuleDefinition moduledefinition in _allModuleDefinitions)
@ -268,20 +282,6 @@
}
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories == "").ToList();
foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions))
{
_pages.Add(p);
}
}
var panes = PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
_pane = panes.Count() == 1 ? panes.SingleOrDefault() : "";
var themes = await ThemeService.GetThemesAsync();
_containers = ThemeService.GetContainerTypes(themes);
_containerType = PageState.Site.DefaultContainerType;
}
}

View File

@ -26,10 +26,11 @@ namespace Oqtane.Controllers
private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment;
private readonly ITenantResolver _resolver;
private readonly ITenantRepository _tenants;
private readonly ISqlRepository _sql;
private readonly ILogManager _logger;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantResolver resolver, ISqlRepository sql, ILogManager logger)
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantResolver resolver, ITenantRepository tenants, ISqlRepository sql, ILogManager logger)
{
_moduleDefinitions = moduleDefinitions;
_modules = modules;
@ -37,6 +38,7 @@ namespace Oqtane.Controllers
_installationManager = installationManager;
_environment = environment;
_resolver = resolver;
_tenants = tenants;
_sql = sql;
_logger = logger;
}
@ -102,23 +104,59 @@ namespace Oqtane.Controllers
ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionId == id).FirstOrDefault();
if (moduledefinition != null)
{
if (!string.IsNullOrEmpty(moduledefinition.ServerAssemblyName))
{
string uninstallScript = "";
Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(a => a.GetName().Name == moduledefinition.ServerAssemblyName);
if (assembly != null)
{
Stream resourceStream = assembly.GetManifestResourceStream(moduledefinition.ServerAssemblyName + ".Scripts.Uninstall.sql");
if (resourceStream != null)
{
using (var reader = new StreamReader(resourceStream))
{
uninstallScript = reader.ReadToEnd();
}
}
}
foreach (Tenant tenant in _tenants.GetTenants())
{
// uninstall module database schema
if (!string.IsNullOrEmpty(uninstallScript))
{
_sql.ExecuteScript(tenant, uninstallScript);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Uninstall Script Executed For {ServerAssemblyName}", moduledefinition.ServerAssemblyName);
}
// clean up module schema versions
_sql.ExecuteNonQuery(tenant, "DELETE FROM [dbo].[SchemaVersions] WHERE ScriptName LIKE '" + moduledefinition.ServerAssemblyName + "%'");
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Schema Versions Removed For {ServerAssemblyName}", moduledefinition.ServerAssemblyName);
}
}
string moduledefinitionname = moduledefinition.ModuleDefinitionName.Substring(0, moduledefinition.ModuleDefinitionName.IndexOf(","));
// clean up module static resource folder
string folder = Path.Combine(_environment.WebRootPath, "Modules\\" + moduledefinitionname);
if (Directory.Exists(folder))
{
Directory.Delete(folder, true);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {ModuleDefinitionName}", moduledefinitionname);
}
// remove module assembly from /bin
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
foreach (string file in Directory.EnumerateFiles(binfolder, moduledefinitionname + "*.dll"))
foreach (string file in Directory.EnumerateFiles(binfolder, moduledefinitionname + "*.*"))
{
System.IO.File.Delete(file);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assemblies Removed For {ModuleDefinitionName}", moduledefinitionname);
}
// remove module definition
_moduleDefinitions.DeleteModuleDefinition(id, siteid);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleDefinitionId}", id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition Deleted {ModuleDefinitionName}", moduledefinition.Name);
// restart application
_installationManager.RestartApplication();
}
}
@ -209,10 +247,7 @@ namespace Oqtane.Controllers
if (Path.GetExtension(filePath) == ".sql")
{
// execute script in curent tenant
foreach (string query in text.Split("GO", StringSplitOptions.RemoveEmptyEntries))
{
_sql.ExecuteNonQuery(_resolver.GetTenant(), query);
}
_sql.ExecuteScript(_resolver.GetTenant(), text);
}
}

View File

@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Oqtane.Controllers;
using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Repository;
@ -109,7 +108,7 @@ namespace Oqtane.Infrastructure
}
else
{
Message = "Database is not avaiable";
Message = "Database is not available";
}
}
else
@ -214,7 +213,7 @@ namespace Oqtane.Infrastructure
Console.WriteLine($"Migrating assembly {assembly.FullName}");
var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources
.WithScriptsEmbeddedInAssembly(assembly, s => !s.ToLower().Contains("uninstall.sql")); // scripts must be included as Embedded Resources
var dbUpgrade = dbUpgradeConfig.Build();
if (dbUpgrade.IsUpgradeRequired())
{
@ -368,8 +367,6 @@ namespace Oqtane.Infrastructure
return value;
}
private static void CreateHostUser(IFolderRepository folderRepository, IUserRoleRepository userRoleRepository, IRoleRepository roleRepository, IUserRepository userRepository, UserManager<IdentityUser> identityUserManager, User user)
{
var identityUser = new IdentityUser {UserName = user.Username, Email = user.Email, EmailConfirmed = true};
@ -427,7 +424,6 @@ namespace Oqtane.Infrastructure
}
}
public static bool TableExists(DbContext context, string tableName)
{
return TableExists(context, "dbo", tableName);

View File

@ -5,6 +5,7 @@ namespace Oqtane.Repository
{
public interface ISqlRepository
{
void ExecuteScript(Tenant tenant, string script);
int ExecuteNonQuery(Tenant tenant, string query);
SqlDataReader ExecuteReader(Tenant tenant, string query);
}

View File

@ -15,6 +15,7 @@ namespace Oqtane.Repository
private MasterDBContext _db;
private readonly IMemoryCache _cache;
private readonly IPermissionRepository _permissions;
private List<ModuleDefinition> _moduleDefinitions; // lazy load
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions)
{
@ -61,12 +62,12 @@ namespace Oqtane.Repository
private List<ModuleDefinition> LoadSiteModuleDefinitions(int siteId)
{
// get module assemblies
List<ModuleDefinition> moduleDefinitions = _cache.GetOrCreate("moduledefinitions", entry =>
if (_moduleDefinitions == null)
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return LoadModuleDefinitionsFromAssemblies();
});
// get module assemblies
_moduleDefinitions = LoadModuleDefinitionsFromAssemblies();
}
List<ModuleDefinition> moduleDefinitions = _moduleDefinitions;
// get module definition permissions for site
List<Permission> permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
@ -210,14 +211,5 @@ namespace Oqtane.Repository
return moduledefinitions;
}
private string GetProperty(Dictionary<string, string> properties, string key)
{
string value = "";
if (properties.ContainsKey(key))
{
value = properties[key];
}
return value;
}
}
}

View File

@ -8,6 +8,15 @@ namespace Oqtane.Repository
public class SqlRepository : ISqlRepository
{
public void ExecuteScript(Tenant tenant, string script)
{
// execute script in curent tenant
foreach (string query in script.Split("GO", StringSplitOptions.RemoveEmptyEntries))
{
ExecuteNonQuery(tenant, query);
}
}
public int ExecuteNonQuery(Tenant tenant, string query)
{
SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString));

View File

@ -9,7 +9,24 @@ namespace Oqtane.Repository
{
public class ThemeRepository : IThemeRepository
{
private List<Theme> _themes; // lazy load
public IEnumerable<Theme> GetThemes()
{
return LoadThemes();
}
private List<Theme> LoadThemes()
{
if (_themes == null)
{
// get themes
_themes = LoadThemesFromAssemblies();
}
return _themes;
}
private List<Theme> LoadThemesFromAssemblies()
{
List<Theme> themes = new List<Theme>();
@ -103,20 +120,5 @@ namespace Oqtane.Repository
}
return themes;
}
private string GetProperty(Dictionary<string, string> properties, string key)
{
string value = "";
if (properties.ContainsKey(key))
{
value = properties[key];
}
return value;
}
public IEnumerable<Theme> GetThemes()
{
return LoadThemes();
}
}
}