diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index f1b9379fc..6b53f1982 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using System.Xml.Linq; +using System.Text.Json; namespace Oqtane.Controllers { @@ -110,6 +111,7 @@ public void Delete(int id, int siteid) { Type moduletype = Type.GetType(moduledefinition.ServerManagerType); + // execute uninstall logic foreach (Tenant tenant in _tenants.GetTenants()) { try @@ -130,25 +132,28 @@ public void Delete(int id, int siteid) _logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} For Tenant {Tenant} {Error}", moduledefinition.ModuleDefinitionName, tenant.Name, ex.Message); } } - + + // use assets.json to clean up file resources + string assetfilepath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName), "assets.json"); + if (System.IO.File.Exists(assetfilepath)) + { + List assets = JsonSerializer.Deserialize>(System.IO.File.ReadAllText(assetfilepath)); + foreach(string asset in assets) + { + if (System.IO.File.Exists(asset)) + { + System.IO.File.Delete(asset); + } + } + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); + } + // clean up module static resource folder 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 {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); - } - - // 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 {Filename} Removed For {ModuleDefinitionName}", file, moduledefinition.ModuleDefinitionName); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); } // remove module definition diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 038366809..84c4c821e 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -10,6 +10,7 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; +using System.Text.Json; // ReSharper disable StringIndexOfIsCultureSpecific.1 @@ -56,19 +57,29 @@ public void Delete(string themename) Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault(); if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client") { + // use assets.json to clean up file resources + string assetfilepath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(theme.ThemeName), "assets.json"); + if (System.IO.File.Exists(assetfilepath)) + { + List assets = JsonSerializer.Deserialize>(System.IO.File.ReadAllText(assetfilepath)); + foreach (string asset in assets) + { + if (System.IO.File.Exists(asset)) + { + System.IO.File.Delete(asset); + } + } + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assets Removed For {ThemeName}", theme.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); + _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Resource Folder Removed For {ThemeName}", theme.ThemeName); } - // remove theme assembly from /bin - string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - 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/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index 3fbf40cdb..42e0176fc 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Reflection; +using System.Text.Json; using System.Xml; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Caching.Memory; @@ -80,6 +82,8 @@ public static bool InstallPackages(string folders, string webRootPath) // if compatible with framework version if (frameworkversion == "" || Version.Parse(Constants.Version).CompareTo(Version.Parse(frameworkversion)) >= 0) { + List assets = new List(); + // module and theme packages must be in form of name.1.0.0.nupkg string name = Path.GetFileNameWithoutExtension(packagename); string[] segments = name?.Split('.'); @@ -96,18 +100,32 @@ public static bool InstallPackages(string folders, string webRootPath) case "lib": filename = Path.Combine(binFolder, filename); ExtractFile(entry, filename); + assets.Add(filename); break; case "wwwroot": filename = Path.Combine(webRootPath.Replace(Path.DirectorySeparatorChar + "wwwroot", ""), Utilities.PathCombine(entry.FullName.Split('/'))); ExtractFile(entry, filename); + assets.Add(filename); break; case "runtimes": var destSubFolder = Path.GetDirectoryName(entry.FullName); filename = Path.Combine(binFolder, destSubFolder, filename); ExtractFile(entry, filename); + assets.Add(filename); break; } } + + // save list of assets + if (assets.Count != 0) + { + string assetfilepath = Path.Combine(webRootPath, "Modules", name, "assets.json"); + if (File.Exists(assetfilepath)) + { + File.Delete(assetfilepath); + } + File.WriteAllText(assetfilepath, JsonSerializer.Serialize(assets)); + } } }