optimize client assembly download service, add support for site level scripts

This commit is contained in:
sbwalker 2023-05-25 12:32:21 -04:00
parent 98c2f012ee
commit 95ba87945b
11 changed files with 268 additions and 162 deletions

View File

@ -531,10 +531,13 @@
{
foreach (var resource in resources)
{
if (!resource.Url.Contains("://") && resource.Url.StartsWith("~/"))
if (resource.Url.StartsWith("~"))
{
// create local path
resource.Url = resource.Url.Replace("~", alias.BaseUrl + "/" + type + "/" + name);
resource.Url = resource.Url.Replace("~", "/" + type + "/" + name + "/").Replace("//", "/");
}
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
{
resource.Url = alias.BaseUrl + resource.Url;
}
// ensure resource does not exist already

View File

@ -33,8 +33,10 @@ namespace Oqtane.Controllers
private readonly IHttpContextAccessor _accessor;
private readonly IAliasRepository _aliases;
private readonly ILogger<InstallationController> _filelogger;
private readonly ITenantManager _tenantManager;
private readonly ServerStateManager _serverState;
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger)
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger, ITenantManager tenantManager, ServerStateManager serverState)
{
_configManager = configManager;
_installationManager = installationManager;
@ -44,6 +46,8 @@ namespace Oqtane.Controllers
_accessor = accessor;
_aliases = aliases;
_filelogger = filelogger;
_tenantManager = tenantManager;
_serverState = serverState;
}
// POST api/<controller>
@ -115,7 +119,9 @@ namespace Oqtane.Controllers
private List<ClientAssembly> GetAssemblyList()
{
return _cache.GetOrCreate("assemblieslist", entry =>
int siteId = _tenantManager.GetAlias().SiteId;
return _cache.GetOrCreate($"assemblieslist:{siteId}", entry =>
{
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var assemblyList = new List<ClientAssembly>();
@ -126,78 +132,43 @@ namespace Oqtane.Controllers
{
hashfilename = false;
}
// get list of assemblies which should be downloaded to client
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
var list = assemblies.Select(a => a.GetName().Name).ToList();
// populate assemblies
for (int i = 0; i < list.Count; i++)
// get site assemblies which should be downloaded to client
var assemblies = _serverState.GetServerState(siteId).Assemblies;
// populate assembly list
foreach (var assembly in assemblies)
{
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, list[i] + ".dll"), hashfilename));
if (assembly != Constants.ClientId)
{
var filepath = Path.Combine(binFolder, assembly) + ".dll";
if (System.IO.File.Exists(filepath))
{
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, assembly + ".dll"), hashfilename));
}
}
}
// insert satellite assemblies at beginning of list
foreach (var culture in _localizationManager.GetInstalledCultures())
{
var assembliesFolderPath = Path.Combine(binFolder, culture);
if (culture == Constants.DefaultCulture)
if (culture != Constants.DefaultCulture)
{
continue;
}
if (Directory.Exists(assembliesFolderPath))
{
foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath))
var assembliesFolderPath = Path.Combine(binFolder, culture);
if (Directory.Exists(assembliesFolderPath))
{
assemblyList.Insert(0, new ClientAssembly(resourceFile, hashfilename));
}
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
}
}
// insert module and theme dependencies at beginning of list
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
{
var instance = Activator.CreateInstance(type) as IModule;
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
{
var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
if (System.IO.File.Exists(filepath))
foreach (var assembly in assemblies)
{
if (!assemblyList.Exists(item => item.FilePath == filepath))
var filepath = Path.Combine(assembliesFolderPath, assembly) + ".resources.dll";
if (System.IO.File.Exists(filepath))
{
assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
assemblyList.Insert(0, new ClientAssembly(Path.Combine(assembliesFolderPath, assembly + ".resources.dll"), hashfilename));
}
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, $"Module {instance.ModuleDefinition.ModuleDefinitionName} Dependency {name}.dll Does Not Exist"));
}
}
}
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
{
var instance = Activator.CreateInstance(type) as ITheme;
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
else
{
var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
if (System.IO.File.Exists(filepath))
{
if (!assemblyList.Exists(item => item.FilePath == filepath))
{
assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
}
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, $"Theme {instance.Theme.ThemeName} Dependency {name}.dll Does Not Exist"));
}
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
}
}
}
@ -240,21 +211,24 @@ namespace Oqtane.Controllers
{
foreach (var assembly in assemblies)
{
if (System.IO.File.Exists(assembly.FilePath))
if (Path.GetFileNameWithoutExtension(assembly.FilePath) != Constants.ClientId)
{
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
if (System.IO.File.Exists(assembly.FilePath))
{
filestream.CopyTo(entrystream);
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
{
filestream.CopyTo(entrystream);
}
}
}
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
if (System.IO.File.Exists(pdb))
{
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
if (System.IO.File.Exists(pdb))
{
filestream.CopyTo(entrystream);
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
{
filestream.CopyTo(entrystream);
}
}
}
}

View File

@ -61,6 +61,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
services.AddSingleton<AutoValidateAntiforgeryTokenFilter>();
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
services.AddSingleton<ServerStateManager>();
return services;
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class ServerState
{
public int SiteId { get; set; }
public List<string> Assemblies { get; set; } = new List<string>();
public List<Resource>Scripts { get; set; } = new List<Resource>();
}
}

View File

@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
// singleton
public class ServerStateManager
{
private List<ServerState> _serverStates { get; set; }
public ServerStateManager()
{
_serverStates = new List<ServerState>();
}
public ServerState GetServerState(int siteId)
{
var serverState = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
if (serverState == null)
{
serverState = new ServerState();
serverState.SiteId = siteId;
serverState.Assemblies = new List<string>();
serverState.Scripts = new List<Resource>();
return serverState;
}
else
{
return serverState;
}
}
public void SetServerState(int siteId, ServerState serverState)
{
var serverstate = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
if (serverstate == null)
{
serverState.SiteId = siteId;
_serverStates.Add(serverState);
}
else
{
serverstate.Assemblies = serverState.Assemblies;
serverstate.Scripts = serverState.Scripts;
}
}
}
}

View File

@ -36,9 +36,10 @@ namespace Oqtane.Pages
private readonly IVisitorRepository _visitors;
private readonly IAliasRepository _aliases;
private readonly ISettingRepository _settings;
private readonly ServerStateManager _serverState;
private readonly ILogManager _logger;
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger)
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ServerStateManager serverState, ILogManager logger)
{
_configuration = configuration;
_tenantManager = tenantManager;
@ -52,6 +53,7 @@ namespace Oqtane.Pages
_visitors = visitors;
_aliases = aliases;
_settings = settings;
_serverState = serverState;
_logger = logger;
}
@ -117,33 +119,11 @@ namespace Oqtane.Pages
{
Runtime = site.Runtime;
}
if (!string.IsNullOrEmpty(site.RenderMode))
if (!string.IsNullOrEmpty(site.RenderMode))
{
RenderMode = site.RenderMode;
}
if (Runtime == "Server")
{
ReconnectScript = CreateReconnectScript();
}
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
{
PWAScript = CreatePWAScript(alias, site, route);
}
// site level scripts
HeadResources += ParseScripts(site.HeadContent);
BodyResources += ParseScripts(site.BodyContent);
// get jwt token for downstream APIs
if (User.Identity.IsAuthenticated)
{
var sitesettings = HttpContext.GetSiteSettings();
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
if (!string.IsNullOrEmpty(secret))
{
AuthorizationToken = _jwtManager.GenerateToken(alias, (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
}
}
if (site.VisitorTracking)
{
TrackVisitor(site.SiteId);
@ -172,11 +152,33 @@ namespace Oqtane.Pages
}
}
// include global resources
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
foreach (Assembly assembly in assemblies)
// get jwt token for downstream APIs
if (User.Identity.IsAuthenticated)
{
ProcessHostResources(assembly, alias);
var sitesettings = HttpContext.GetSiteSettings();
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
if (!string.IsNullOrEmpty(secret))
{
AuthorizationToken = _jwtManager.GenerateToken(alias, (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
}
}
// inject scripts
if (Runtime == "Server")
{
ReconnectScript = CreateReconnectScript();
}
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
{
PWAScript = CreatePWAScript(alias, site, route);
}
HeadResources += ParseScripts(site.HeadContent);
BodyResources += ParseScripts(site.BodyContent);
_sites.InitializeSite(site.SiteId); // populates server state
var scripts = _serverState.GetServerState(site.SiteId).Scripts;
foreach (var script in scripts)
{
AddScript(script, alias);
}
// set culture if not specified
@ -409,20 +411,6 @@ namespace Oqtane.Pages
"</script>";
}
private void ProcessHostResources(Assembly assembly, Alias alias)
{
var types = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IHostResources)));
foreach (var type in types)
{
var obj = Activator.CreateInstance(type) as IHostResources;
foreach (var resource in obj.Resources)
{
resource.Level = ResourceLevel.App;
ProcessResource(resource, 0, alias);
}
}
}
private string ParseScripts(string headcontent)
{
// iterate scripts
@ -439,60 +427,39 @@ namespace Oqtane.Pages
return scripts;
}
private void ProcessResource(Resource resource, int count, Alias alias)
private void AddScript(Resource resource, Alias alias)
{
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
switch (resource.ResourceType)
var script = CreateScript(resource, alias);
if (resource.Location == Shared.ResourceLocation.Head)
{
case ResourceType.Stylesheet:
if (!HeadResources.Contains(url, StringComparison.OrdinalIgnoreCase))
{
string id = "";
if (resource.Level == ResourceLevel.Page)
{
id = "id=\"app-stylesheet-" + resource.Level.ToString().ToLower() + "-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-" + count.ToString("00") + "\" ";
}
HeadResources += "<link " + id + "rel=\"stylesheet\" href=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + " type=\"text/css\"/>" + Environment.NewLine;
}
break;
case ResourceType.Script:
if (resource.Location == Shared.ResourceLocation.Body)
{
if (!BodyResources.Contains(url, StringComparison.OrdinalIgnoreCase))
{
BodyResources += "<script src=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + "></script>" + Environment.NewLine;
}
}
else
{
if (!HeadResources.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
{
HeadResources += "<script src=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + "></script>" + Environment.NewLine;
}
}
break;
}
}
private string CrossOrigin(string crossorigin)
{
if (!string.IsNullOrEmpty(crossorigin))
{
return " crossorigin=\"" + crossorigin + "\"";
if (!HeadResources.Contains(script))
{
HeadResources += script + Environment.NewLine;
}
}
else
{
return "";
if (!BodyResources.Contains(script))
{
BodyResources += script + Environment.NewLine;
}
}
}
private string Integrity(string integrity)
private string CreateScript(Resource resource, Alias alias)
{
if (!string.IsNullOrEmpty(integrity))
if (!string.IsNullOrEmpty(resource.Url))
{
return " integrity=\"" + integrity + "\"";
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
return "<script src=\"" + url + "\"" +
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
"></script>";
}
else
{
return "";
// inline script
return "<script>" + resource.Content + "</script>";
}
}

View File

@ -11,6 +11,7 @@ namespace Oqtane.Repository
Site GetSite(int siteId);
Site GetSite(int siteId, bool tracking);
void DeleteSite(int siteId);
void InitializeSite(int siteId);
void CreatePages(Site site, List<PageTemplate> pageTemplates);
}
}

View File

@ -4,12 +4,14 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Themes;
namespace Oqtane.Repository
{
@ -20,15 +22,17 @@ namespace Oqtane.Repository
private readonly IPermissionRepository _permissions;
private readonly ITenantManager _tenants;
private readonly ISettingRepository _settings;
private readonly ServerStateManager _serverState;
private readonly string settingprefix = "SiteEnabled:";
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings)
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
{
_db = context;
_cache = cache;
_permissions = permissions;
_tenants = tenants;
_settings = settings;
_serverState = serverState;
}
public IEnumerable<ModuleDefinition> GetModuleDefinitions()
@ -182,6 +186,7 @@ namespace Oqtane.Repository
var settings = _settings.GetSettings(EntityNames.ModuleDefinition).ToList();
// populate module definition site settings and permissions
var serverState = _serverState.GetServerState(siteId);
foreach (ModuleDefinition moduledefinition in ModuleDefinitions)
{
moduledefinition.SiteId = siteId;
@ -196,6 +201,36 @@ namespace Oqtane.Repository
moduledefinition.IsEnabled = moduledefinition.IsAutoEnabled;
}
if (moduledefinition.IsEnabled)
{
// build list of assemblies for site
if (!serverState.Assemblies.Contains(moduledefinition.AssemblyName))
{
serverState.Assemblies.Add(moduledefinition.AssemblyName);
}
if (!string.IsNullOrEmpty(moduledefinition.Dependencies))
{
foreach (var assembly in moduledefinition.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
{
if (!serverState.Assemblies.Contains(assembly))
{
serverState.Assemblies.Insert(0, assembly);
}
}
}
// build list of scripts for site
if (moduledefinition.Resources != null)
{
foreach (var resource in moduledefinition.Resources.Where(item => item.Level == ResourceLevel.Site))
{
if (!serverState.Scripts.Contains(resource))
{
serverState.Scripts.Add(resource);
}
}
}
}
if (permissions.Count == 0)
{
// no module definition permissions exist for this site
@ -216,6 +251,7 @@ namespace Oqtane.Repository
}
}
}
_serverState.SetServerState(siteId, serverState);
// clean up any orphaned permissions
var ids = new HashSet<int>(ModuleDefinitions.Select(item => item.ModuleDefinitionId));
@ -295,6 +331,16 @@ namespace Oqtane.Repository
moduledefinition.ModuleDefinitionName = qualifiedModuleType;
moduledefinition.ControlTypeTemplate = modulecontroltype.Namespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
moduledefinition.AssemblyName = assembly.GetName().Name;
if (moduledefinition.Resources != null)
{
foreach (var resource in moduledefinition.Resources)
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/Modules/" + Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "/").Replace("//", "/");
}
}
}
moduledefinition.IsPortable = false;
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType))

View File

@ -24,11 +24,12 @@ namespace Oqtane.Repository
private readonly IModuleRepository _moduleRepository;
private readonly IPageModuleRepository _pageModuleRepository;
private readonly IModuleDefinitionRepository _moduleDefinitionRepository;
private readonly IThemeRepository _themeRepository;
private readonly IServiceProvider _serviceProvider;
private readonly IConfigurationRoot _config;
public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IServiceProvider serviceProvider,
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
IConfigurationRoot config)
{
_db = context;
@ -39,6 +40,7 @@ namespace Oqtane.Repository
_moduleRepository = moduleRepository;
_pageModuleRepository = pageModuleRepository;
_moduleDefinitionRepository = moduleDefinitionRepository;
_themeRepository = themeRepository;
_serviceProvider = serviceProvider;
_config = config;
}
@ -88,6 +90,12 @@ namespace Oqtane.Repository
_db.SaveChanges();
}
public void InitializeSite(int siteId)
{
_moduleDefinitionRepository.GetModuleDefinitions(siteId);
_themeRepository.GetThemes();
}
private void CreateSite(Site site)
{
// create default entities for site

View File

@ -21,14 +21,16 @@ namespace Oqtane.Repository
private readonly IMemoryCache _cache;
private readonly ITenantManager _tenants;
private readonly ISettingRepository _settings;
private readonly ServerStateManager _serverState;
private readonly string settingprefix = "SiteEnabled:";
public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings)
public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
{
_db = context;
_cache = cache;
_tenants = tenants;
_settings = settings;
_serverState = serverState;
}
public IEnumerable<Theme> GetThemes()
@ -147,6 +149,7 @@ namespace Oqtane.Repository
var settings = _settings.GetSettings(EntityNames.Theme).ToList();
// populate theme site settings
var serverState = _serverState.GetServerState(siteId);
foreach (Theme theme in Themes)
{
theme.SiteId = siteId;
@ -160,7 +163,38 @@ namespace Oqtane.Repository
{
theme.IsEnabled = theme.IsAutoEnabled;
}
if (theme.IsEnabled)
{
// build list of assemblies for site
if (!serverState.Assemblies.Contains(theme.AssemblyName))
{
serverState.Assemblies.Add(theme.AssemblyName);
}
if (!string.IsNullOrEmpty(theme.Dependencies))
{
foreach (var assembly in theme.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
{
if (!serverState.Assemblies.Contains(assembly))
{
serverState.Assemblies.Insert(0, assembly);
}
}
}
// build list of scripts for site
if (theme.Resources != null)
{
foreach (var resource in theme.Resources.Where(item => item.Level == ResourceLevel.Site))
{
if (!serverState.Scripts.Contains(resource))
{
serverState.Scripts.Add(resource);
}
}
}
}
}
_serverState.SetServerState(siteId, serverState);
}
return Themes;
@ -225,12 +259,22 @@ namespace Oqtane.Repository
Version = new Version(1, 0, 0).ToString()
};
}
// set internal properties
theme.ThemeName = qualifiedThemeType;
theme.Themes = new List<ThemeControl>();
theme.Containers = new List<ThemeControl>();
theme.AssemblyName = assembly.FullName.Split(",")[0];
if (theme.Resources != null)
{
foreach (var resource in theme.Resources)
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(theme.ThemeName) + "/").Replace("//", "/");
}
}
}
Debug.WriteLine($"Oqtane Info: Registering Theme {theme.ThemeName}");
themes.Add(theme);
index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);

View File

@ -3,6 +3,7 @@ namespace Oqtane.Shared
public enum ResourceLevel
{
App,
Site,
Page,
Module
}